+Version 1.1pre2 Juli 17 2011
+
+ * .cookie files are renamed to .pid files, which are compatible with 1.0.x.
+
+ * Experimental protocol enhancements that can be enabled with the option
+ ExperimentalProtocol = yes:
+
+ * Ephemeral ECDH key exchange will be used for both the meta protocol and
+ UDP session keys.
+ * Key exchanges are signed with ECDSA.
+ * ECDSA public keys are automatically exchanged after RSA authentication if
+ nodes do not know each other's ECDSA public key yet.
+
+Version 1.1pre1 June 25 2011
+
+ * Control interface allows control of a running tinc daemon. Used by:
+ * tincctl, a commandline utility
+ * tinc-gui, a preliminary GUI implemented in Python/wxWidgets
+
+ * Code cleanups and reorganization.
+
+ * Repleacable cryptography backend, currently supports OpenSSL and libgcrypt.
+
+ * Use libevent to handle I/O events and timeouts.
+
+ * Use splay trees instead of AVL trees to manage internal datastructures.
+
+ Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
+ version of tinc.
+
+ Version 1.0.19 June 25 2012
+
+ * Allow :: notation in IPv6 Subnets.
+
+ * Add support for systemd style socket activation.
+
+ * Allow environment variables to be used for the Name option.
+
+ * Add basic support for SOCKS proxies, HTTP proxies, and proxying through an
+ external command.
+
Version 1.0.18 March 25 2012
* Fixed IPv6 in switch mode by turning off DecrementTTL by default.
return false;
}
- oldlen = c->buflen;
- c->buflen += lenin;
+ do {
+ if(c->protocol_minor >= 2) {
+ logger(DEBUG_META, LOG_DEBUG, "Receiving %d bytes of SPTPS data", inlen);
+ return sptps_receive_data(&c->sptps, bufp, inlen);
+ }
+
+ if(!c->status.decryptin) {
+ endp = memchr(bufp, '\n', inlen);
+ if(endp)
+ endp++;
+ else
+ endp = bufp + inlen;
- while(lenin > 0) {
- /* Decrypt */
+ buffer_add(&c->inbuf, bufp, endp - bufp);
- if(c->status.decryptin && !decrypted) {
- result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin);
- if(!result || lenout != lenin) {
- logger(LOG_ERR, "Error while decrypting metadata from %s (%s): %s",
- c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
+ inlen -= endp - bufp;
+ bufp = endp;
+ } else {
+ size_t outlen = inlen;
+ logger(DEBUG_META, LOG_DEBUG, "Received encrypted %d bytes", inlen);
+
+ if(!cipher_decrypt(&c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting metadata from %s (%s)",
+ c->name, c->hostname);
return false;
}
- memcpy(c->buffer + oldlen, inbuf, lenin);
- decrypted = true;
- }
- /* Are we receiving a TCPpacket? */
-
- if(c->tcplen) {
- if(c->tcplen <= c->buflen) {
- if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
- if(c->buffer[0] == 0 && c->buffer[1] == 0x5a) {
- logger(LOG_DEBUG, "Proxy request granted");
- } else {
- logger(LOG_ERR, "Proxy request rejected");
- return false;
- }
- } else
- receive_tcppacket(c, c->buffer, c->tcplen);
-
- c->buflen -= c->tcplen;
- lenin -= c->tcplen - oldlen;
- memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
- oldlen = 0;
- c->tcplen = 0;
- continue;
- } else {
- break;
- }
+ inlen = 0;
}
- /* Otherwise we are waiting for a request */
+ while(c->inbuf.len) {
+ /* Are we receiving a TCPpacket? */
+
+ if(c->tcplen) {
+ char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
+ if(tcpbuffer) {
- receive_tcppacket(c, tcpbuffer, c->tcplen);
++ if(proxytype == PROXY_SOCKS4 && c->allow_request == ID) {
++ if(tcpbuffer[0] == 0 && tcpbuffer[1] == 0x5a) {
++ logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
++ } else {
++ logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
++ return false;
++ }
++ } else
++ receive_tcppacket(c, tcpbuffer, c->tcplen);
+ c->tcplen = 0;
+ continue;
+ } else {
+ break;
+ }
+ }
- reqlen = 0;
+ /* Otherwise we are waiting for a request */
- for(i = oldlen; i < c->buflen; i++) {
- if(c->buffer[i] == '\n') {
- c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
- reqlen = i + 1;
+ char *request = buffer_readline(&c->inbuf);
+ if(request) {
+ bool result = receive_request(c, request);
+ if(!result)
+ return false;
+ continue;
+ } else {
break;
}
}
extern int contradicting_add_edge;
extern int contradicting_del_edge;
-extern volatile bool running;
-
+ extern char *proxyhost;
+ extern char *proxyport;
+ extern char *proxyuser;
+ extern char *proxypass;
+ typedef enum proxytype_t {
+ PROXY_NONE = 0,
+ PROXY_SOCKS4,
+ PROXY_SOCKS4A,
+ PROXY_SOCKS5,
+ PROXY_HTTP,
+ PROXY_EXEC,
+ } proxytype_t;
+ extern proxytype_t proxytype;
+
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
#include "connection.h"
#include "node.h"
extern void retry_outgoing(outgoing_t *);
-extern void handle_incoming_vpn_data(int);
+extern void handle_incoming_vpn_data(int, short, void *);
extern void finish_connecting(struct connection_t *);
-extern void do_outgoing_connection(struct connection_t *);
-extern bool handle_new_meta_connection(int);
+extern bool do_outgoing_connection(struct connection_t *);
+extern void handle_new_meta_connection(int, short, void *);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
-extern void send_packet(const struct node_t *, vpn_packet_t *);
+extern void send_packet(struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, const char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
+ extern char *get_name(void);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
- avl_node_t *node;
+ splay_node_t *node;
connection_t *c;
+ node_t *n;
+
+ // Always give ourself a copy of the packet.
+ if(from != myself)
+ send_packet(myself, packet);
+
+ // In TunnelServer mode, do not forward broadcast packets.
+ // The MST might not be valid and create loops.
+ if(tunnelserver || broadcast_mode == BMODE_NONE)
+ return;
- ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
packet->len, from->name, from->hostname);
- if(from != myself) {
- send_packet(myself, packet);
+ switch(broadcast_mode) {
+ // In MST mode, broadcast packets travel via the Minimum Spanning Tree.
+ // This guarantees all nodes receive the broadcast packet, and
+ // usually distributes the sending of broadcast packets over all nodes.
+ case BMODE_MST:
+ for(node = connection_tree->head; node; node = node->next) {
+ c = node->data;
- // In TunnelServer mode, do not forward broadcast packets.
- // The MST might not be valid and create loops.
- if(tunnelserver)
- return;
- }
+ if(c->status.active && c->status.mst && c != from->nexthop->connection)
+ send_packet(c->node, packet);
+ }
+ break;
+
+ // In direct mode, we send copies to each node we know of.
+ // However, this only reaches nodes that can be reached in a single hop.
+ // We don't have enough information to forward broadcast packets in this case.
+ case BMODE_DIRECT:
+ if(from != myself)
+ break;
+
+ for(node = node_udp_tree->head; node; node = node->next) {
+ n = node->data;
- for(node = connection_tree->head; node; node = node->next) {
- c = node->data;
+ if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n))
+ send_packet(n, packet);
+ }
+ break;
- if(c->status.active && c->status.mst && c != from->nexthop->connection)
- send_packet(c->node, packet);
+ default:
+ break;
}
}
#include "xalloc.h"
char *myport;
+static struct event device_ev;
devops_t devops;
-bool read_rsa_public_key(connection_t *c) {
+ char *proxyhost;
+ char *proxyport;
+ char *proxyuser;
+ char *proxypass;
+ proxytype_t proxytype;
+
+bool node_read_ecdsa_public_key(node_t *n) {
+ if(ecdsa_active(&n->ecdsa))
+ return true;
+
+ splay_tree_t *config_tree;
FILE *fp;
char *fname;
- char *key;
+ char *p;
+ bool result = false;
- if(!c->rsa_key) {
- c->rsa_key = RSA_new();
-// RSA_blinding_on(c->rsa_key, NULL);
- }
+ xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
- /* First, check for simple PublicKey statement */
+ init_configuration(&config_tree);
+ if(!read_config_file(config_tree, fname))
+ goto exit;
- if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
- BN_hex2bn(&c->rsa_key->n, key);
- BN_hex2bn(&c->rsa_key->e, "FFFF");
- free(key);
- return true;
+ /* First, check for simple ECDSAPublicKey statement */
+
+ if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
+ result = ecdsa_set_base64_public_key(&n->ecdsa, p);
+ free(p);
+ goto exit;
}
- /* Else, check for PublicKeyFile statement and read it */
+ /* Else, check for ECDSAPublicKeyFile statement and read it */
- if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
- fp = fopen(fname, "r");
+ free(fname);
- if(!fp) {
- logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
- fname, strerror(errno));
- free(fname);
- return false;
- }
+ if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname))
+ xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
- free(fname);
- c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
+ fp = fopen(fname, "r");
- if(c->rsa_key)
- return true; /* Woohoo. */
+ if(!fp) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno));
+ goto exit;
+ }
- /* If it fails, try PEM_read_RSA_PUBKEY. */
- fp = fopen(fname, "r");
+ result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
+ fclose(fp);
- if(!fp) {
- logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
- fname, strerror(errno));
- free(fname);
- return false;
- }
+exit:
+ exit_configuration(&config_tree);
+ free(fname);
+ return result;
+}
- free(fname);
- c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
- fclose(fp);
+bool read_ecdsa_public_key(connection_t *c) {
+ FILE *fp;
+ char *fname;
+ char *p;
+ bool result;
- if(c->rsa_key) {
-// RSA_blinding_on(c->rsa_key, NULL);
- return true;
- }
+ /* First, check for simple ECDSAPublicKey statement */
- logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
- fname, strerror(errno));
- return false;
+ if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
+ result = ecdsa_set_base64_public_key(&c->ecdsa, p);
+ free(p);
+ return result;
}
- /* Else, check if a harnessed public key is in the config file */
+ /* Else, check for ECDSAPublicKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
+ xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
- xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
fp = fopen(fname, "r");
if(!fp) {
closedir(dir);
}
- logger(LOG_ERR, "Invalid name for myself!");
+ char *get_name(void) {
+ char *name = NULL;
+
+ get_config_string(lookup_config(config_tree, "Name"), &name);
+
+ if(!name)
+ return NULL;
+
+ if(*name == '$') {
+ char *envname = getenv(name + 1);
+ if(!envname) {
+ if(strcmp(name + 1, "HOST")) {
+ fprintf(stderr, "Invalid Name: environment variable %s does not exist\n", name + 1);
+ return false;
+ }
+ envname = alloca(32);
+ if(gethostname(envname, 32)) {
+ fprintf(stderr, "Could not get hostname: %s\n", strerror(errno));
+ return false;
+ }
+ envname[31] = 0;
+ }
+ free(name);
+ name = xstrdup(envname);
+ for(char *c = name; *c; c++)
+ if(!isalnum(*c))
+ *c = '_';
+ }
+
+ if(!check_id(name)) {
++ logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
+ free(name);
+ return false;
+ }
+
+ return name;
+ }
+
/*
Configure node_t myself and set up the local sockets (listen only)
*/
myself->connection->hostname = xstrdup("MYSELF");
myself->connection->options = 0;
- myself->connection->protocol_version = PROT_CURRENT;
+ myself->connection->protocol_major = PROT_MAJOR;
+ myself->connection->protocol_minor = PROT_MINOR;
- if(!get_config_string(lookup_config(config_tree, "Name"), &name)) { /* Not acceptable */
+ if(!(name = get_name())) {
- logger(LOG_ERR, "Name for tinc daemon required!");
+ logger(DEBUG_ALWAYS, LOG_ERR, "Name for tinc daemon required!");
return false;
}
sockaddr2str(&sa, NULL, &myport);
}
- logger(LOG_ERR, "Unknown proxy type %s!", proxy);
+ get_config_string(lookup_config(config_tree, "Proxy"), &proxy);
+ if(proxy) {
+ if((space = strchr(proxy, ' ')))
+ *space++ = 0;
+
+ if(!strcasecmp(proxy, "none")) {
+ proxytype = PROXY_NONE;
+ } else if(!strcasecmp(proxy, "socks4")) {
+ proxytype = PROXY_SOCKS4;
+ } else if(!strcasecmp(proxy, "socks4a")) {
+ proxytype = PROXY_SOCKS4A;
+ } else if(!strcasecmp(proxy, "socks5")) {
+ proxytype = PROXY_SOCKS5;
+ } else if(!strcasecmp(proxy, "http")) {
+ proxytype = PROXY_HTTP;
+ } else if(!strcasecmp(proxy, "exec")) {
+ proxytype = PROXY_EXEC;
+ } else {
- logger(LOG_ERR, "Argument expected for proxy type exec!");
++ logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type %s!", proxy);
+ return false;
+ }
+
+ switch(proxytype) {
+ case PROXY_NONE:
+ default:
+ break;
+
+ case PROXY_EXEC:
+ if(!space || !*space) {
- logger(LOG_ERR, "Host and port argument expected for proxy!");
++ logger(DEBUG_ALWAYS, LOG_ERR, "Argument expected for proxy type exec!");
+ return false;
+ }
+ proxyhost = xstrdup(space);
+ break;
+
+ case PROXY_SOCKS4:
+ case PROXY_SOCKS4A:
+ case PROXY_SOCKS5:
+ case PROXY_HTTP:
+ proxyhost = space;
+ if(space && (space = strchr(space, ' ')))
+ *space++ = 0, proxyport = space;
+ if(space && (space = strchr(space, ' ')))
+ *space++ = 0, proxyuser = space;
+ if(space && (space = strchr(space, ' ')))
+ *space++ = 0, proxypass = space;
+ if(!proxyhost || !*proxyhost || !proxyport || !*proxyport) {
++ logger(DEBUG_ALWAYS, LOG_ERR, "Host and port argument expected for proxy!");
+ return false;
+ }
+ proxyhost = xstrdup(proxyhost);
+ proxyport = xstrdup(proxyport);
+ if(proxyuser && *proxyuser)
+ proxyuser = xstrdup(proxyuser);
+ if(proxypass && *proxypass)
+ proxypass = xstrdup(proxypass);
+ break;
+ }
+
+ free(proxy);
+ }
+
/* Read in all the subnets specified in the host configuration file */
cfg = lookup_config(config_tree, "Subnet");
get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance);
get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl);
- get_config_bool(lookup_config(config_tree, "Broadcast"), &broadcast);
+ if(get_config_string(lookup_config(config_tree, "Broadcast"), &mode)) {
+ if(!strcasecmp(mode, "no"))
+ broadcast_mode = BMODE_NONE;
+ else if(!strcasecmp(mode, "yes") || !strcasecmp(mode, "mst"))
+ broadcast_mode = BMODE_MST;
+ else if(!strcasecmp(mode, "direct"))
+ broadcast_mode = BMODE_DIRECT;
+ else {
- logger(LOG_ERR, "Invalid broadcast mode!");
++ logger(DEBUG_ALWAYS, LOG_ERR, "Invalid broadcast mode!");
+ return false;
+ }
+ free(mode);
+ }
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
}
void finish_connecting(connection_t *c) {
- ifdebug(CONNECTIONS) logger(LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
- configure_tcp(c);
+ if(proxytype != PROXY_EXEC)
+ configure_tcp(c);
- c->last_ping_time = now;
+ c->last_ping_time = time(NULL);
+ c->status.connecting = false;
send_id(c);
}
- logger(LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
+ static void do_outgoing_pipe(connection_t *c, char *command) {
+ #ifndef HAVE_MINGW
+ int fd[2];
+
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
- logger(LOG_DEBUG, "Using proxy %s", command);
++ logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
+ return;
+ }
+
+ if(fork()) {
+ c->socket = fd[0];
+ close(fd[1]);
- logger(LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
++ logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
+ return;
+ }
+
+ close(0);
+ close(1);
+ close(fd[0]);
+ dup2(fd[1], 0);
+ dup2(fd[1], 1);
+ close(fd[1]);
+
+ // Other filedescriptors should be closed automatically by CLOEXEC
+
+ char *host = NULL;
+ char *port = NULL;
+
+ sockaddr2str(&c->address, &host, &port);
+ setenv("REMOTEADDRESS", host, true);
+ setenv("REMOTEPORT", port, true);
+ setenv("NODE", c->name, true);
+ setenv("NAME", myself->name, true);
+ if(netname)
+ setenv("NETNAME", netname, true);
+
+ int result = system(command);
+ if(result < 0)
- logger(LOG_ERR, "%s exited with non-zero status %d", command, result);
++ logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
+ else if(result)
- logger(LOG_ERR, "Proxy type exec not supported on this platform!");
++ logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
+ exit(result);
+ #else
-void do_outgoing_connection(connection_t *c) {
++ logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
+ return;
+ #endif
+ }
+
+bool do_outgoing_connection(connection_t *c) {
char *address, *port, *space;
+ struct addrinfo *proxyai = NULL;
int result;
if(!c->outgoing) {
c->hostname = sockaddr2hostname(&c->address);
- ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", c->name,
c->hostname);
- c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-
- #ifdef FD_CLOEXEC
- fcntl(c->socket, F_SETFD, FD_CLOEXEC);
- #endif
+ if(!proxytype) {
+ c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+ configure_tcp(c);
+ } else if(proxytype == PROXY_EXEC) {
+ do_outgoing_pipe(c, proxyhost);
+ } else {
+ proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
+ if(!proxyai)
+ goto begin;
- ifdebug(CONNECTIONS) logger(LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
++ logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
+ c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+ }
if(c->socket == -1) {
- ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
+ logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
return false;
}
- ifdebug(PROTOCOL) {
- sscanf(buffer, "%d", &request);
- ifdebug(META)
- logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
- request_name[request], c->name, c->hostname, buffer);
- else
- logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[request],
- c->name, c->hostname);
- }
+ logger(DEBUG_META, LOG_DEBUG, "Sending %s to %s (%s): %s", request_name[atoi(request)], c->name, c->hostname, request);
- buffer[len++] = '\n';
+ request[len++] = '\n';
if(c == everyone) {
- broadcast_meta(NULL, buffer, len);
+ broadcast_meta(NULL, request, len);
return true;
} else
- return send_meta(c, buffer, len);
+ return send_meta(c, request, len);
}
-void forward_request(connection_t *from) {
- int request;
-
- ifdebug(PROTOCOL) {
- sscanf(from->buffer, "%d", &request);
- ifdebug(META)
- logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
- request_name[request], from->name, from->hostname,
- from->buffer);
- else
- logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
- request_name[request], from->name, from->hostname);
- }
-
- from->buffer[from->reqlen - 1] = '\n';
+void forward_request(connection_t *from, const char *request) {
+ logger(DEBUG_META, LOG_DEBUG, "Forwarding %s from %s (%s): %s", request_name[atoi(request)], from->name, from->hostname, request);
- broadcast_meta(from, from->buffer, from->reqlen);
+ // Create a temporary newline-terminated copy of the request
+ int len = strlen(request);
+ char tmp[len + 1];
+ memcpy(tmp, request, len);
+ tmp[len] = '\n';
+ broadcast_meta(from, tmp, sizeof tmp);
}
-bool receive_request(connection_t *c) {
- int request;
-
+bool receive_request(connection_t *c, const char *request) {
+ if(proxytype == PROXY_HTTP && c->allow_request == ID) {
- if(!c->buffer[0] || c->buffer[0] == '\r')
++ if(!request[0] || request[0] == '\r')
+ return true;
- if(!strncasecmp(c->buffer, "HTTP/1.1 ", 9)) {
- if(!strncmp(c->buffer + 9, "200", 3)) {
- logger(LOG_DEBUG, "Proxy request granted");
++ if(!strncasecmp(request, "HTTP/1.1 ", 9)) {
++ if(!strncmp(request + 9, "200", 3)) {
++ logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
+ return true;
+ } else {
- logger(LOG_DEBUG, "Proxy request rejected: %s", c->buffer + 9);
++ logger(DEBUG_ALWAYS, LOG_DEBUG, "Proxy request rejected: %s", request + 9);
+ return false;
+ }
+ }
+ }
+
- if(sscanf(c->buffer, "%d", &request) == 1) {
- if((request < 0) || (request >= LAST) || !request_handlers[request]) {
- ifdebug(META)
- logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
- c->name, c->hostname, c->buffer);
- else
- logger(LOG_ERR, "Unknown request from %s (%s)",
- c->name, c->hostname);
+ int reqno = atoi(request);
+ if(reqno || *request == '0') {
+ if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
+ logger(DEBUG_META, LOG_DEBUG, "Unknown request from %s (%s): %s", c->name, c->hostname, request);
return false;
} else {
- ifdebug(PROTOCOL) {
- ifdebug(META)
- logger(LOG_DEBUG, "Got %s from %s (%s): %s",
- request_name[request], c->name, c->hostname,
- c->buffer);
- else
- logger(LOG_DEBUG, "Got %s from %s (%s)",
- request_name[request], c->name, c->hostname);
- }
+ logger(DEBUG_META, LOG_DEBUG, "Got %s from %s (%s): %s", request_name[reqno], c->name, c->hostname, request);
}
- if((c->allow_request != ALL) && (c->allow_request != request)) {
- logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
- c->hostname);
+ if((c->allow_request != ALL) && (c->allow_request != reqno)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Unauthorized request from %s (%s)", c->name, c->hostname);
return false;
}
#include "utils.h"
#include "xalloc.h"
- logger(LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
+ static bool send_proxyrequest(connection_t *c) {
+ switch(proxytype) {
+ case PROXY_HTTP: {
+ char *host;
+ char *port;
+
+ sockaddr2str(&c->address, &host, &port);
+ send_request(c, "CONNECT %s:%s HTTP/1.1\r\n\r", host, port);
+ free(host);
+ free(port);
+ return true;
+ }
+ case PROXY_SOCKS4: {
+ if(c->address.sa.sa_family != AF_INET) {
- logger(LOG_ERR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
++ logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
+ return false;
+ }
+ char s4req[9 + (proxyuser ? strlen(proxyuser) : 0)];
+ s4req[0] = 4;
+ s4req[1] = 1;
+ memcpy(s4req + 2, &c->address.in.sin_port, 2);
+ memcpy(s4req + 4, &c->address.in.sin_addr, 4);
+ if(proxyuser)
+ strcpy(s4req + 8, proxyuser);
+ s4req[sizeof s4req - 1] = 0;
+ c->tcplen = 8;
+ return send_meta(c, s4req, sizeof s4req);
+ }
+ case PROXY_SOCKS5: {
+ int len = 3 + 6 + (c->address.sa.sa_family == AF_INET ? 4 : 16);
+ c->tcplen = 2;
+ if(proxypass)
+ len += 3 + strlen(proxyuser) + strlen(proxypass);
+ char s5req[len];
+ int i = 0;
+ s5req[i++] = 5;
+ s5req[i++] = 1;
+ if(proxypass) {
+ s5req[i++] = 2;
+ s5req[i++] = 1;
+ s5req[i++] = strlen(proxyuser);
+ strcpy(s5req + i, proxyuser);
+ i += strlen(proxyuser);
+ s5req[i++] = strlen(proxypass);
+ strcpy(s5req + i, proxypass);
+ i += strlen(proxypass);
+ c->tcplen += 2;
+ } else {
+ s5req[i++] = 0;
+ }
+ s5req[i++] = 5;
+ s5req[i++] = 1;
+ s5req[i++] = 0;
+ if(c->address.sa.sa_family == AF_INET) {
+ s5req[i++] = 1;
+ memcpy(s5req + i, &c->address.in.sin_addr, 4);
+ i += 4;
+ memcpy(s5req + i, &c->address.in.sin_port, 2);
+ i += 2;
+ c->tcplen += 10;
+ } else if(c->address.sa.sa_family == AF_INET6) {
+ s5req[i++] = 3;
+ memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
+ i += 16;
+ memcpy(s5req + i, &c->address.in6.sin6_port, 2);
+ i += 2;
+ c->tcplen += 22;
+ } else {
- logger(LOG_ERR, "Proxy type not implemented yet");
++ logger(DEBUG_ALWAYS, LOG_ERR, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
+ return false;
+ }
+ if(i > len)
+ abort();
+ return send_meta(c, s5req, sizeof s5req);
+ }
+ case PROXY_SOCKS4A:
- logger(LOG_ERR, "Unknown proxy type");
++ logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
+ return false;
+ case PROXY_EXEC:
+ return true;
+ default:
++ logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
+ return false;
+ }
+ }
+
bool send_id(connection_t *c) {
- return send_request(c, "%d %s %d", ID, myself->connection->name,
- myself->connection->protocol_version);
+ gettimeofday(&c->start, NULL);
+
+ int minor = 0;
+
+ if(experimental) {
+ if(c->config_tree && !read_ecdsa_public_key(c))
+ minor = 1;
+ else
+ minor = myself->connection->protocol_minor;
+ }
+
+ if(proxytype)
+ if(!send_proxyrequest(c))
+ return false;
+
+ return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
}
-bool id_h(connection_t *c) {
+bool id_h(connection_t *c, const char *request) {
char name[MAX_STRING_SIZE];
- if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
- logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
+ if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
c->hostname);
return false;
}
bool priorityinheritance = false;
int macexpire = 600;
bool overwrite_mac = false;
- bool broadcast = true;
mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
+bool pcap = false;
/* Sizes of various headers */
extern bool decrement_ttl;
extern bool directonly;
extern bool overwrite_mac;
- extern bool broadcast;
extern bool priorityinheritance;
extern int macexpire;
+extern bool pcap;
extern mac_t mymac;
--- /dev/null
- static char buf[1024], *newline;
+/*
+ utils.c -- gathering of some stupid small functions
+ Copyright (C) 1999-2005 Ivo Timmermans
+ 2000-2009 Guus Sliepen <guus@tinc-vpn.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "../src/logger.h"
+#include "utils.h"
+
+static const char hexadecimals[] = "0123456789ABCDEF";
+static const char base64imals[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int charhex2bin(char c) {
+ if(isdigit(c))
+ return c - '0';
+ else
+ return toupper(c) - 'A' + 10;
+}
+
+static int charb64decode(char c) {
+ if(c >= 'a')
+ return c - 'a' + 26;
+ else if(c >= 'A')
+ return c - 'A';
+ else if(c >= '0')
+ return c - '0' + 52;
+ else if(c == '+')
+ return 62;
+ else
+ return 63;
+}
+
+int hex2bin(const char *src, char *dst, int length) {
+ int i;
+ for(i = 0; i < length && src[i * 2] && src[i * 2 + 1]; i++)
+ dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]);
+ return i;
+}
+
+int bin2hex(const char *src, char *dst, int length) {
+ int i;
+ for(i = length - 1; i >= 0; i--) {
+ dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15];
+ dst[i * 2] = hexadecimals[(unsigned char) src[i] >> 4];
+ }
+ dst[length * 2] = 0;
+ return length * 2;
+}
+
+int b64decode(const char *src, char *dst, int length) {
+ int i;
+ uint32_t triplet = 0;
+ unsigned char *udst = (unsigned char *)dst;
+
+ for(i = 0; i < length / 3 * 4 && src[i]; i++) {
+ triplet |= charb64decode(src[i]) << (6 * (i & 3));
+ if((i & 3) == 3) {
+ udst[0] = triplet & 0xff; triplet >>= 8;
+ udst[1] = triplet & 0xff; triplet >>= 8;
+ udst[2] = triplet;
+ triplet = 0;
+ udst += 3;
+ }
+ }
+ if((i & 3) == 3) {
+ udst[0] = triplet & 0xff; triplet >>= 8;
+ udst[1] = triplet & 0xff;
+ return i / 4 * 3 + 2;
+ } else if((i & 3) == 2) {
+ udst[0] = triplet & 0xff;
+ return i / 4 * 3 + 1;
+ } else {
+ return i / 4 * 3;
+ }
+}
+
+int b64encode(const char *src, char *dst, int length) {
+ uint32_t triplet;
+ const unsigned char *usrc = (unsigned char *)src;
+ int si = length / 3 * 3;
+ int di = length / 3 * 4;
+
+ switch(length % 3) {
+ case 2:
+ triplet = usrc[si] | usrc[si + 1] << 8;
+ dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 2] = base64imals[triplet];
+ dst[di + 3] = 0;
+ length = di + 2;
+ break;
+ case 1:
+ triplet = usrc[si];
+ dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 1] = base64imals[triplet];
+ dst[di + 2] = 0;
+ length = di + 1;
+ break;
+ default:
+ dst[di] = 0;
+ length = di;
+ break;
+ }
+
+ while(si > 0) {
+ di -= 4;
+ si -= 3;
+ triplet = usrc[si] | usrc[si + 1] << 8 | usrc[si + 2] << 16;
+ dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 2] = base64imals[triplet & 63]; triplet >>= 6;
+ dst[di + 3] = base64imals[triplet];
+ }
+
+ return length;
+}
+
+#if defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
+#ifdef HAVE_CYGWIN
+#include <w32api/windows.h>
+#endif
+
+const char *winerror(int err) {
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
++ static char buf[1024], *ptr;
++
++ ptr = buf + sprintf(buf, "(%d) ", err);
+
+ if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- if((newline = strchr(buf, '\r')))
- *newline = '\0';
++ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) {
+ strncpy(buf, "(unable to format errormessage)", sizeof(buf));
+ };
+
++ if((ptr = strchr(buf, '\r')))
++ *ptr = '\0';
+
+ return buf;
+}
+#endif
+
+unsigned int bitfield_to_int(const void *bitfield, size_t size) {
+ unsigned int value = 0;
+ if(size > sizeof value)
+ size = sizeof value;
+ memcpy(&value, bitfield, size);
+ return value;
+}