From: Guus Sliepen Date: Tue, 26 Jun 2012 11:24:20 +0000 (+0200) Subject: Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1 X-Git-Tag: release-1.1pre3~119 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=19be9cf7150858311f7898fa3fb525d692d02f64;p=oweals%2Ftinc.git Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1 Conflicts: NEWS README configure.in lib/utils.c src/linux/device.c src/meta.c src/net.h src/net_setup.c src/net_socket.c src/protocol.c src/protocol_auth.c src/tincd.c --- 19be9cf7150858311f7898fa3fb525d692d02f64 diff --cc NEWS index a385047,4887ee4..191c2f2 --- a/NEWS +++ b/NEWS @@@ -1,33 -1,14 +1,44 @@@ +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. diff --cc src/meta.c index 13c8495,1b34246..a272baf --- a/src/meta.c +++ b/src/meta.c @@@ -160,59 -155,57 +160,67 @@@ bool receive_meta(connection_t *c) 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; } } diff --cc src/net.h index 27b5eb5,2b50c5a..d1dde61 --- a/src/net.h +++ b/src/net.h @@@ -121,20 -121,38 +121,35 @@@ extern char *myport extern int contradicting_add_edge; extern int contradicting_del_edge; + 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; + -extern volatile bool running; - /* 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); diff --cc src/net_packet.c index ca6aff3,cd8d98a..cbdc15c --- a/src/net_packet.c +++ b/src/net_packet.c @@@ -574,26 -582,52 +574,52 @@@ void send_packet(node_t *n, vpn_packet_ /* 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; } } diff --cc src/net_setup.c index 4afb31b,eec438a..3285a32 --- a/src/net_setup.c +++ b/src/net_setup.c @@@ -43,75 -45,79 +43,81 @@@ #include "xalloc.h" char *myport; +static struct event device_ev; devops_t devops; + char *proxyhost; + char *proxyport; + char *proxyuser; + char *proxypass; + proxytype_t proxytype; + -bool read_rsa_public_key(connection_t *c) { +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) { @@@ -340,6 -275,44 +346,44 @@@ void load_all_subnets(void) closedir(dir); } + 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(LOG_ERR, "Invalid name for myself!"); ++ 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) */ @@@ -362,11 -337,10 +408,11 @@@ static bool setup_myself(void) 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; } @@@ -404,6 -367,68 +444,68 @@@ sockaddr2str(&sa, NULL, &myport); } + 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, "Unknown proxy type %s!", proxy); ++ 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, "Argument expected for proxy type exec!"); ++ 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(LOG_ERR, "Host and port argument expected for proxy!"); ++ 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"); @@@ -474,7 -499,19 +576,19 @@@ 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) diff --cc src/net_socket.c index af3f2fe,2d1ecc5..7f3db5c --- a/src/net_socket.c +++ b/src/net_socket.c @@@ -290,18 -292,67 +290,68 @@@ void retry_outgoing(outgoing_t *outgoin } 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); } + 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_ERR, "Could not create socketpair: %s\n", strerror(errno)); ++ 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_DEBUG, "Using proxy %s", command); ++ 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, "Could not execute %s: %s\n", command, strerror(errno)); ++ logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno)); + else if(result) - logger(LOG_ERR, "%s exited with non-zero status %d", command, result); ++ logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result); + exit(result); + #else - logger(LOG_ERR, "Proxy type exec not supported on this platform!"); ++ logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!"); + return; + #endif + } + -void do_outgoing_connection(connection_t *c) { +bool do_outgoing_connection(connection_t *c) { char *address, *port, *space; + struct addrinfo *proxyai = NULL; int result; if(!c->outgoing) { @@@ -354,17 -405,24 +404,24 @@@ begin 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; } diff --cc src/protocol.c index 1e63f2e,f36538e..1c5b6cf --- a/src/protocol.c +++ b/src/protocol.c @@@ -85,41 -84,86 +85,55 @@@ bool send_request(connection_t *c, cons 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; } diff --cc src/protocol_auth.c index 057b88e,4c721a4..ccb7976 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@@ -42,26 -39,106 +42,116 @@@ #include "utils.h" #include "xalloc.h" + 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, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!"); ++ 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, "Address family %hx not supported for SOCKS 5 proxies!", c->address.sa.sa_family); ++ 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, "Proxy type not implemented yet"); ++ logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet"); + return false; + case PROXY_EXEC: + return true; + default: - logger(LOG_ERR, "Unknown proxy type"); ++ logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type"); + return false; + } + } + bool send_id(connection_t *c) { + 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", ID, myself->connection->name, - myself->connection->protocol_version); + 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; } diff --cc src/route.c index 5bf6e92,74ad9a3..4c4312c --- a/src/route.c +++ b/src/route.c @@@ -41,9 -40,7 +42,8 @@@ bool directonly = 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 */ diff --cc src/route.h index 46dc3bd,7b45e76..6f4a4e5 --- a/src/route.h +++ b/src/route.h @@@ -41,10 -48,8 +48,9 @@@ extern bmode_t broadcast_mode 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; diff --cc src/utils.c index cf46221,0000000..e750450 mode 100644,000000..100644 --- a/src/utils.c +++ b/src/utils.c @@@ -1,160 -1,0 +1,162 @@@ +/* + utils.c -- gathering of some stupid small functions + Copyright (C) 1999-2005 Ivo Timmermans + 2000-2009 Guus Sliepen + + 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 +#endif + +const char *winerror(int err) { - static char buf[1024], *newline; ++ static char buf[1024], *ptr; ++ ++ ptr = buf + sprintf(buf, "(%d) ", err); + + if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) { ++ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ptr, sizeof(buf) - (ptr - buf), NULL)) { + strncpy(buf, "(unable to format errormessage)", sizeof(buf)); + }; + - if((newline = strchr(buf, '\r'))) - *newline = '\0'; ++ 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; +}