diff options
author | Nick Mathewson <nickm@torproject.org> | 2005-08-03 20:42:17 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2005-08-03 20:42:17 +0000 |
commit | 11ff0aba80d7ccc77e52040e99d20d38046c25d1 (patch) | |
tree | 9ca1fc07701b7ff81917b69af599302e31dd7136 /src | |
parent | ceb02cee2c021ae34d07d431a6e13a02827adf5d (diff) | |
download | tor-11ff0aba80d7ccc77e52040e99d20d38046c25d1.tar tor-11ff0aba80d7ccc77e52040e99d20d38046c25d1.tar.gz |
Make clients regenerate their keys when their IP address changes.
svn:r4688
Diffstat (limited to 'src')
-rw-r--r-- | src/common/util.c | 59 | ||||
-rw-r--r-- | src/common/util.h | 1 | ||||
-rw-r--r-- | src/or/connection.c | 84 | ||||
-rw-r--r-- | src/or/router.c | 2 |
4 files changed, 130 insertions, 16 deletions
diff --git a/src/common/util.c b/src/common/util.c index bbe31ff11..390889d4c 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -695,7 +695,8 @@ int parse_iso_time(const char *cp, time_t *t) { * must be 1 if fd was returned by socket() or accept(), and 0 if fd * was returned by open(). Return the number of bytes written, or -1 * on error. Only use if fd is a blocking fd. */ -int write_all(int fd, const char *buf, size_t count, int isSocket) { +int +write_all(int fd, const char *buf, size_t count, int isSocket) { size_t written = 0; int result; @@ -716,7 +717,8 @@ int write_all(int fd, const char *buf, size_t count, int isSocket) { * was returned by socket() or accept(), and 0 if fd was returned by * open(). Return the number of bytes read, or -1 on error. Only use * if fd is a blocking fd. */ -int read_all(int fd, char *buf, size_t count, int isSocket) { +int +read_all(int fd, char *buf, size_t count, int isSocket) { size_t numread = 0; int result; @@ -1319,6 +1321,59 @@ is_plausible_address(const char *name) return 1; } +/** + * Set *<b>addr</b> to the host-order IPv4 address (if any) of whatever + * interface connects to the internet. This address should only be used in + * checking whether our address has changed. Return 0 on success, -1 on + * failure. + */ +int +get_interface_address(uint32_t *addr) +{ + int sock=-1, r=-1; + struct sockaddr_in target_addr, my_addr; + socklen_t my_addr_len = sizeof(my_addr); + + tor_assert(addr); + *addr = 0; + + sock = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (sock < 0) { + int e = tor_socket_errno(-1); + log_fn(LOG_WARN, "unable to create socket: %s", tor_socket_strerror(e)); + goto err; + } + + memset(&target_addr, 0, sizeof(target_addr)); + target_addr.sin_family = AF_INET; + /* discard port */ + target_addr.sin_port = 9; + /* 18.0.0.1 (Don't worry: no packets are sent. We just need a real address + * on the internet.) */ + target_addr.sin_addr.s_addr = htonl(0x12000001); + + if (connect(sock,(struct sockaddr *)&target_addr,sizeof(target_addr))<0) { + int e = tor_socket_errno(sock); + log_fn(LOG_WARN, "connnect() failed: %s", tor_socket_strerror(e)); + goto err; + } + + /* XXXX Can this be right on IPv6 clients? */ + if (getsockname(sock, &my_addr, &my_addr_len)) { + int e = tor_socket_errno(sock); + log_fn(LOG_WARN, "getsockname() failed: %s", tor_socket_strerror(e)); + goto err; + } + + *addr = ntohl(my_addr.sin_addr.s_addr); + + r=0; + err: + if (sock >= 0) + tor_close_socket(sock); + return r; +} + /* ===== * Process helpers * ===== */ diff --git a/src/common/util.h b/src/common/util.h index f6504abc7..2197618c3 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -135,6 +135,7 @@ int parse_addr_and_port_range(const char *s, uint32_t *addr_out, #define INET_NTOA_BUF_LEN 16 int tor_inet_ntoa(struct in_addr *in, char *buf, size_t buf_len); int is_plausible_address(const char *name); +int get_interface_address(uint32_t *addr); /* Process helpers */ void start_daemon(void); diff --git a/src/or/connection.c b/src/or/connection.c index 8b9667bdb..a28bd4296 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -24,6 +24,10 @@ static int connection_reached_eof(connection_t *conn); static int connection_read_to_buf(connection_t *conn, int *max_to_read); static int connection_process_inbuf(connection_t *conn, int package_partial); static int connection_bucket_read_limit(connection_t *conn); +static void client_check_address_changed(int sock); + +static uint32_t last_interface_ip = 0; +static smartlist_t *outgoing_addrs = NULL; /**************************************************************/ @@ -251,11 +255,16 @@ connection_free(connection_t *conn) } connection_unregister(conn); _connection_free(conn); + + SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr)); + smartlist_free(outgoing_addrs); + outgoing_addrs = NULL; } -/** Call _connection_free() on every connection in our array. - * This is used by cpuworkers and dnsworkers when they fork, - * so they don't keep resources held open (especially sockets). +/** Call _connection_free() on every connection in our array, and release all + * storage helpd by connection.c. This is used by cpuworkers and dnsworkers + * when they fork, so they don't keep resources held open (especially + * sockets). * * Don't do the checks in connection_free(), because they will * fail. @@ -701,7 +710,7 @@ int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port) { - int s; + int s, inprogress = 0; struct sockaddr_in dest_addr; or_options_t *options = get_options(); @@ -754,21 +763,21 @@ connection_connect(connection_t *conn, char *address, tor_close_socket(s); return -1; } else { - /* it's in progress. set state appropriately and return. */ - conn->s = s; - if (connection_add(conn) < 0) /* no space, forget it */ - return -1; - log_fn(LOG_DEBUG,"connect in progress, socket %d.",s); - return 0; + inprogress = 1; } } + if (!server_mode(options)) + client_check_address_changed(s); + /* it succeeded. we're connected. */ - log_fn(LOG_INFO,"Connection to %s:%u established.",safe_str(address),port); + log_fn(inprogress?LOG_DEBUG:LOG_INFO, + "Connection to %s:%u %s (sock %d).",safe_str(address),port, + inprogress?"in progress":"established",s); conn->s = s; if (connection_add(conn) < 0) /* no space, forget it */ return -1; - return 1; + return inprogress ? 0 : 1; } /** @@ -1680,6 +1689,57 @@ alloc_http_authenticator(const char *authenticator) return base64_authenticator; } +/** DOCDOC + * XXXX ipv6 NM + */ +static void +client_check_address_changed(int sock) +{ + uint32_t iface_ip, ip_out; + struct sockaddr_in out_addr; + socklen_t out_addr_len = sizeof(out_addr); + uint32_t *ip; + + if (!last_interface_ip) + get_interface_address(&last_interface_ip); + if (!outgoing_addrs) + outgoing_addrs = smartlist_create(); + + if (getsockname(sock, (struct sockaddr*)&out_addr, &out_addr_len)<0) { + int e = tor_socket_errno(sock); + log_fn(LOG_WARN, "getsockname() failed: %s", tor_socket_strerror(e)); + return; + } + + /* Okay. If we've used this address previously, we're okay. */ + ip_out = ntohl(out_addr.sin_addr.s_addr); + SMARTLIST_FOREACH(outgoing_addrs, uint32_t*, ip, + if (*ip == ip_out) return; + ); + + /* Uh-oh. We haven't connected from this address before. Has the interface + * address changed? */ + if (get_interface_address(&iface_ip)<0) + return; + ip = tor_malloc(sizeof(uint32_t)); + *ip = ip_out; + + if (iface_ip == last_interface_ip) { + /* Nope, it hasn't changed. Add this address to the list. */ + smartlist_add(outgoing_addrs, ip); + } else { + /* The interface changed. We're a client, so we need to regenerate our + * keys. First, reset the state. */ + log_fn(LOG_NOTICE, "Our IP has changed. Rotating keys..."); + last_interface_ip = iface_ip; + SMARTLIST_FOREACH(outgoing_addrs, void*, ip, tor_free(ip)); + smartlist_clear(outgoing_addrs); + smartlist_add(outgoing_addrs, ip); + /* Okay, now change our keys. */ + init_keys(); /* XXXX NM return value-- safe to ignore? */ + } +} + /** Process new bytes that have arrived on conn-\>inbuf. * * This function just passes conn to the connection-specific diff --git a/src/or/router.c b/src/or/router.c index f9eb99eb6..94c1613d9 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -245,8 +245,6 @@ init_keys(void) /* XXX009 Two problems with how this is called: * 1. It should be idempotent for servers, so we can call init_keys * as much as we need to. - * 2. Clients should rotate their identity keys at least whenever - * their IPs change. */ char keydir[512]; char keydir2[512]; |