static int rebind_ipv6(tr_bool force) { struct sockaddr_in6 sin6; const unsigned char *ipv6 = tr_globalIPv6(); static unsigned char *last_bound = NULL; int s, rc; int one = 1; /* We currently have no way to enable or disable IPv6 once the DHT has been initialised. Oh, well. */ if(ipv6 == NULL || (!force && dht6_socket < 0)) { if(last_bound) { free(last_bound); last_bound = NULL; } return 0; } if(last_bound != NULL && memcmp(ipv6, last_bound, 16) == 0) return 0; s = socket(PF_INET6, SOCK_DGRAM, 0); if(s < 0) return -1; #ifdef IPV6_V6ONLY /* Since we always open an IPv4 socket on the same port, this shouldn't matter. But I'm superstitious. */ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); #endif memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; if(ipv6) memcpy(&sin6.sin6_addr, ipv6, 16); sin6.sin6_port = htons(dht_port); rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6)); if(rc < 0) return -1; if(dht6_socket < 0) { dht6_socket = s; } else { rc = dup2(s, dht6_socket); close(s); if(rc < 0) return -1; } if(last_bound == NULL) last_bound = malloc(16); if(last_bound) memcpy(last_bound, ipv6, 16); return 1; }
static char* announce_url_new (const tr_session * session, const tr_announce_request * req) { const char * str; const unsigned char * ipv6; struct evbuffer * buf = evbuffer_new (); char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1]; tr_http_escape_sha1 (escaped_info_hash, req->info_hash); evbuffer_expand (buf, 1024); evbuffer_add_printf (buf, "%s" "%c" "info_hash=%s" "&peer_id=%*.*s" "&port=%d" "&uploaded=%" PRIu64 "&downloaded=%" PRIu64 "&left=%" PRIu64 "&numwant=%d" "&key=%x" "&compact=1" "&supportcrypto=1", req->url, strchr (req->url, '?') ? '&' : '?', escaped_info_hash, PEER_ID_LEN, PEER_ID_LEN, req->peer_id, req->port, req->up, req->down, req->leftUntilComplete, req->numwant, req->key); if (session->encryptionMode == TR_ENCRYPTION_REQUIRED) evbuffer_add_printf (buf, "&requirecrypto=1"); if (req->corrupt) evbuffer_add_printf (buf, "&corrupt=%" PRIu64, req->corrupt); str = get_event_string (req); if (str && *str) evbuffer_add_printf (buf, "&event=%s", str); str = req->tracker_id_str; if (str && *str) evbuffer_add_printf (buf, "&trackerid=%s", str); /* There are two incompatible techniques for announcing an IPv6 address. BEP-7 suggests adding an "ipv6=" parameter to the announce URL, while OpenTracker requires that peers announce twice, once over IPv4 and once over IPv6. To be safe, we should do both: add the "ipv6=" parameter and announce twice. At any rate, we're already computing our IPv6 address (for the LTEP handshake), so this comes for free. */ ipv6 = tr_globalIPv6 (); if (ipv6) { char ipv6_readable[INET6_ADDRSTRLEN]; evutil_inet_ntop (AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN); evbuffer_add_printf (buf, "&ipv6="); tr_http_escape (buf, ipv6_readable, -1, true); } return evbuffer_free_to_str (buf); }
void tr_udpInit (tr_session *ss) { bool is_default; const struct tr_address * public_addr; struct sockaddr_in sin; int rc; assert (ss->udp_socket < 0); assert (ss->udp6_socket < 0); ss->udp_port = tr_sessionGetPeerPort (ss); if (ss->udp_port <= 0) return; ss->udp_socket = socket (PF_INET, SOCK_DGRAM, 0); if (ss->udp_socket < 0) { tr_logAddNamedError ("UDP", "Couldn't create IPv4 socket"); goto ipv6; } memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET, &is_default); if (public_addr && !is_default) memcpy (&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr)); sin.sin_port = htons (ss->udp_port); rc = bind (ss->udp_socket, (struct sockaddr*)&sin, sizeof (sin)); if (rc < 0) { tr_logAddNamedError ("UDP", "Couldn't bind IPv4 socket"); close (ss->udp_socket); ss->udp_socket = -1; goto ipv6; } ss->udp_event = event_new (ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST, event_callback, ss); if (ss->udp_event == NULL) tr_logAddNamedError ("UDP", "Couldn't allocate IPv4 event"); ipv6: if (tr_globalIPv6 ()) rebind_ipv6 (ss, true); if (ss->udp6_socket >= 0) { ss->udp6_event = event_new (ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST, event_callback, ss); if (ss->udp6_event == NULL) tr_logAddNamedError ("UDP", "Couldn't allocate IPv6 event"); } tr_udpSetSocketBuffers (ss); if (ss->isDHTEnabled) tr_dhtInit (ss); if (ss->udp_event) event_add (ss->udp_event, NULL); if (ss->udp6_event) event_add (ss->udp6_event, NULL); }
static void rebind_ipv6 (tr_session *ss, bool force) { bool is_default; const struct tr_address * public_addr; struct sockaddr_in6 sin6; const unsigned char *ipv6 = tr_globalIPv6 (); int s = -1, rc; int one = 1; /* We currently have no way to enable or disable IPv6 after initialisation. No way to fix that without some surgery to the DHT code itself. */ if (ipv6 == NULL || (!force && ss->udp6_socket < 0)) { if (ss->udp6_bound) { free (ss->udp6_bound); ss->udp6_bound = NULL; } return; } if (ss->udp6_bound != NULL && memcmp (ipv6, ss->udp6_bound, 16) == 0) return; s = socket (PF_INET6, SOCK_DGRAM, 0); if (s < 0) goto fail; #ifdef IPV6_V6ONLY /* Since we always open an IPv4 socket on the same port, this shouldn't matter. But I'm superstitious. */ setsockopt (s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof (one)); #endif memset (&sin6, 0, sizeof (sin6)); sin6.sin6_family = AF_INET6; if (ipv6) memcpy (&sin6.sin6_addr, ipv6, 16); sin6.sin6_port = htons (ss->udp_port); public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET6, &is_default); if (public_addr && !is_default) sin6.sin6_addr = public_addr->addr.addr6; rc = bind (s, (struct sockaddr*)&sin6, sizeof (sin6)); if (rc < 0) goto fail; if (ss->udp6_socket < 0) { ss->udp6_socket = s; } else { rc = dup2 (s, ss->udp6_socket); if (rc < 0) goto fail; close (s); } if (ss->udp6_bound == NULL) ss->udp6_bound = malloc (16); if (ss->udp6_bound) memcpy (ss->udp6_bound, ipv6, 16); return; fail: /* Something went wrong. It's difficult to recover, so let's simply set things up so that we try again next time. */ tr_logAddNamedError ("UDP", "Couldn't rebind IPv6 socket"); if (s >= 0) close (s); if (ss->udp6_bound) { free (ss->udp6_bound); ss->udp6_bound = NULL; } }
int tr_dhtInit(tr_session *ss, const tr_address * tr_addr) { struct sockaddr_in sin; tr_benc benc; int rc; tr_bool have_id = FALSE; char * dat_file; uint8_t * nodes = NULL, * nodes6 = NULL; const uint8_t * raw; size_t len, len6; struct bootstrap_closure * cl; if( session ) /* already initialized */ return -1; dht_port = tr_sessionGetPeerPort(ss); if(dht_port <= 0) return -1; tr_ndbg( "DHT", "Initializing DHT" ); dht_socket = socket(PF_INET, SOCK_DGRAM, 0); if(dht_socket < 0) goto fail; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, &tr_addr->addr.addr4, sizeof (struct in_addr)); sin.sin_port = htons(dht_port); rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin)); if(rc < 0) goto fail; if(tr_globalIPv6()) rebind_ipv6(TRUE); if( getenv( "TR_DHT_VERBOSE" ) != NULL ) dht_debug = stderr; dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL ); rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file ); tr_free( dat_file ); if(rc == 0) { have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len); if( have_id && len==20 ) memcpy( myid, raw, len ); if( dht_socket >= 0 && tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) { nodes = tr_memdup( raw, len ); } if( dht6_socket > 0 && tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) { nodes6 = tr_memdup( raw, len6 ); } tr_bencFree( &benc ); } if(nodes == NULL) len = 0; if(nodes6 == NULL) len6 = 0; if( have_id ) tr_ninf( "DHT", "Reusing old id" ); else { /* Note that DHT ids need to be distributed uniformly, * so it should be something truly random. */ tr_ninf( "DHT", "Generating new id" ); tr_cryptoRandBuf( myid, 20 ); } rc = dht_init( dht_socket, dht6_socket, myid, NULL ); if( rc < 0 ) goto fail; session = ss; cl = tr_new( struct bootstrap_closure, 1 ); cl->session = session; cl->nodes = nodes; cl->nodes6 = nodes6; cl->len = len; cl->len6 = len6; tr_threadNew( dht_bootstrap, cl ); dht_event = event_new( session->event_base, dht_socket, EV_READ, event_callback, NULL ); tr_timerAdd( dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) ); if( dht6_socket >= 0 ) { dht6_event = event_new( session->event_base, dht6_socket, EV_READ, event_callback, NULL ); tr_timerAdd( dht6_event, 0, tr_cryptoWeakRandInt( 1000000 ) ); } tr_ndbg( "DHT", "DHT initialized" ); return 1; fail: { const int save = errno; close(dht_socket); if( dht6_socket >= 0 ) close(dht6_socket); dht_socket = dht6_socket = -1; session = NULL; tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", save ); errno = save; } return -1; }