void tr_dhtUninit(tr_session *ss) { if(session != ss) return; tr_ndbg( "DHT", "Uninitializing DHT" ); if( dht_timer != NULL ) { event_free( dht_timer ); dht_timer = NULL; } /* Since we only save known good nodes, avoid erasing older data if we don't know enough nodes. */ if(tr_dhtStatus(ss, AF_INET, NULL) < TR_DHT_FIREWALLED) tr_ninf( "DHT", "Not saving nodes, DHT not ready" ); else { tr_benc benc; struct sockaddr_in sins[300]; struct sockaddr_in6 sins6[300]; char compact[300 * 6], compact6[300 * 18]; char *dat_file; int i, j, num = 300, num6 = 300; int n = dht_get_nodes(sins, &num, sins6, &num6); tr_ninf( "DHT", "Saving %d (%d + %d) nodes", n, num, num6 ); j = 0; for( i=0; i<num; ++i ) { memcpy( compact + j, &sins[i].sin_addr, 4 ); memcpy( compact + j + 4, &sins[i].sin_port, 2 ); j += 6; } j = 0; for( i=0; i<num6; ++i ) { memcpy( compact6 + j, &sins6[i].sin6_addr, 16 ); memcpy( compact6 + j + 16, &sins6[i].sin6_port, 2 ); j += 18; } tr_bencInitDict( &benc, 3 ); tr_bencDictAddRaw( &benc, "id", myid, 20 ); if(num > 0) tr_bencDictAddRaw( &benc, "nodes", compact, num * 6 ); if(num6 > 0) tr_bencDictAddRaw( &benc, "nodes6", compact6, num6 * 18 ); dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL ); tr_bencToFile( &benc, TR_FMT_BENC, dat_file ); tr_bencFree( &benc ); tr_free( dat_file ); } dht_uninit(); tr_ndbg("DHT", "Done uninitializing DHT"); session = NULL; }
static void event_callback (int s, short type UNUSED, void *sv) { int rc; socklen_t fromlen; unsigned char buf[4096]; struct sockaddr_storage from; tr_session *ss = sv; assert (tr_isSession (sv)); assert (type == EV_READ); fromlen = sizeof (from); rc = recvfrom (s, buf, 4096 - 1, 0, (struct sockaddr*)&from, &fromlen); /* Since most packets we receive here are µTP, make quick inline checks for the other protocols. The logic is as follows: - all DHT packets start with 'd'; - all UDP tracker packets start with a 32-bit (!) "action", which is between 0 and 3; - the above cannot be µTP packets, since these start with a 4-bit version number (1). */ if (rc > 0) { if (buf[0] == 'd') { if (tr_sessionAllowsDHT (ss)) { buf[rc] = '\0'; /* required by the DHT code */ tr_dhtCallback (buf, rc, (struct sockaddr*)&from, fromlen, sv); } } else if (rc >= 8 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3) { rc = tau_handle_message (ss, buf, rc); if (!rc) tr_ndbg ("UDP", "Couldn't parse UDP tracker packet."); } else { if (tr_sessionIsUTPEnabled (ss)) { rc = tr_utpPacket (buf, rc, (struct sockaddr*)&from, fromlen, ss); if (!rc) tr_ndbg ("UDP", "Unexpected UDP packet"); } } } }
int tr_cacheSetLimit( tr_cache * cache, int64_t max_bytes ) { char buf[128]; cache->max_bytes = max_bytes; cache->max_blocks = getMaxBlocks( max_bytes ); tr_formatter_mem_B( buf, cache->max_bytes, sizeof( buf ) ); tr_ndbg( MY_NAME, "Maximum cache size set to %s (%d blocks)", buf, cache->max_blocks ); return cacheTrim( cache ); }
static void logVal( const char * func, int ret ) { if( ret == NATPMP_TRYAGAIN ) return; if( ret >= 0 ) tr_ninf( getKey( ), _( "%s succeeded (%d)" ), func, ret ); else tr_ndbg( getKey( ), "%s failed. Natpmp returned %d (%s); errno is %d (%s)", func, ret, strnatpmperr( ret ), errno, tr_strerror( errno ) ); }
static int flushContiguous( tr_cache * cache, int pos, int n ) { int i; int err = 0; uint8_t * buf = tr_new( uint8_t, n * MAX_BLOCK_SIZE ); uint8_t * walk = buf; struct cache_block ** blocks = (struct cache_block**) tr_ptrArrayBase( &cache->blocks ); struct cache_block * b = blocks[pos]; tr_torrent * tor = b->tor; const tr_piece_index_t piece = b->piece; const uint32_t offset = b->offset; //fprintf( stderr, "flushing %d contiguous blocks [%d-%d) from cache to disk\n", n, pos, n+pos ); for( i=pos; i<pos+n; ++i ) { b = blocks[i]; memcpy( walk, b->buf, b->length ); walk += b->length; tr_free( b->buf ); tr_free( b ); } tr_ptrArrayErase( &cache->blocks, pos, pos+n ); #if 0 tr_tordbg( tor, "Writing to disk piece %d, offset %d, len %d", (int)piece, (int)offset, (int)(walk-buf) ); tr_ndbg( MY_NAME, "Removing %d blocks from cache, rank: %d - %d left", n, rank, tr_ptrArraySize(&cache->blocks) ); fprintf( stderr, "%s - Writing to disk piece %d, offset %d, len %d\n", tr_torrentName(tor), (int)piece, (int)offset, (int)(walk-buf) ); fprintf( stderr, "%s - Removing %d blocks from cache; %d left\n", MY_NAME, n, tr_ptrArraySize(&cache->blocks) ); #endif err = tr_ioWrite( tor, piece, offset, walk-buf, buf ); tr_free( buf ); ++cache->disk_writes; cache->disk_write_bytes += walk-buf; return err; }
int tr_upnpPulse( tr_upnp * handle, int port, int isEnabled, int doPortCheck ) { int ret; if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) ) { struct UPNPDev * devlist; errno = 0; devlist = upnpDiscover( 2000, NULL, NULL, 0 ); if( devlist == NULL ) { tr_ndbg( getKey( ), "upnpDiscover failed (errno %d - %s)", errno, tr_strerror( errno ) ); } errno = 0; if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof( handle->lanaddr ) ) == UPNP_IGD_VALID_CONNECTED ) { tr_ninf( getKey( ), _( "Found Internet Gateway Device \"%s\"" ), handle->urls.controlURL ); tr_ninf( getKey( ), _( "Local Address is \"%s\"" ), handle->lanaddr ); handle->state = TR_UPNP_IDLE; handle->hasDiscovered = 1; } else { handle->state = TR_UPNP_ERR; tr_ndbg( getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)", errno, tr_strerror( errno ) ); tr_ndbg( getKey( ), "If your router supports UPnP, please make sure UPnP is enabled!" ); } freeUPNPDevlist( devlist ); } if( handle->state == TR_UPNP_IDLE ) { if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) ) handle->state = TR_UPNP_UNMAP; } if( isEnabled && handle->isMapped && doPortCheck ) { char portStr[8]; char intPort[8]; char intClient[16]; tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port ); if( UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype, portStr, "TCP", intClient, intPort ) != UPNPCOMMAND_SUCCESS || UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype, portStr, "UDP", intClient, intPort ) != UPNPCOMMAND_SUCCESS ) { tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port ); handle->isMapped = FALSE; } } if( handle->state == TR_UPNP_UNMAP ) { char portStr[16]; tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port ); UPNP_DeletePortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, "TCP", NULL ); UPNP_DeletePortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, "UDP", NULL ); tr_ninf( getKey( ), _( "Stopping port forwarding through \"%s\", service \"%s\"" ), handle->urls.controlURL, handle->data.first.servicetype ); handle->isMapped = 0; handle->state = TR_UPNP_IDLE; handle->port = -1; } if( handle->state == TR_UPNP_IDLE ) { if( isEnabled && !handle->isMapped ) handle->state = TR_UPNP_MAP; } if( handle->state == TR_UPNP_MAP ) { int err_tcp = -1; int err_udp = -1; errno = 0; if( !handle->urls.controlURL || !handle->data.first.servicetype ) handle->isMapped = 0; else { char portStr[16]; char desc[64]; const int prev_errno = errno; tr_snprintf( portStr, sizeof( portStr ), "%d", port ); tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port ); errno = 0; err_tcp = UPNP_AddPortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, "TCP", NULL ); if( err_tcp ) tr_ndbg( getKey( ), "TCP Port forwarding failed with error %d (errno %d - %s)", err_tcp, errno, tr_strerror( errno ) ); errno = 0; err_udp = UPNP_AddPortMapping( handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, "UDP", NULL ); if( err_udp ) tr_ndbg( getKey( ), "UDP Port forwarding failed with error %d (errno %d - %s)", err_udp, errno, tr_strerror( errno ) ); errno = prev_errno; handle->isMapped = !err_tcp | !err_udp; } tr_ninf( getKey( ), _( "Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)" ), handle->urls.controlURL, handle->data.first.servicetype, handle->lanaddr, port ); if( handle->isMapped ) { tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) ); handle->port = port; handle->state = TR_UPNP_IDLE; } else { tr_ndbg( getKey( ), "If your router supports UPnP, please make sure UPnP is enabled!" ); handle->port = -1; handle->state = TR_UPNP_ERR; } } switch( handle->state ) { case TR_UPNP_DISCOVER: ret = TR_PORT_UNMAPPED; break; case TR_UPNP_MAP: ret = TR_PORT_MAPPING; break; case TR_UPNP_UNMAP: ret = TR_PORT_UNMAPPING; break; case TR_UPNP_IDLE: ret = handle->isMapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED; break; default: ret = TR_PORT_ERROR; break; } return ret; }
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; }
static void dht_bootstrap(void *closure) { struct bootstrap_closure *cl = closure; int i; int num = cl->len / 6, num6 = cl->len6 / 18; if(session != cl->session) return; if(cl->len > 0) tr_ninf( "DHT", "Bootstrapping from %d nodes", num ); if(cl->len6 > 0) tr_ninf( "DHT", "Bootstrapping from %d IPv6 nodes", num6 ); for(i = 0; i < MAX(num, num6); i++) { if( i < num && !bootstrap_done(cl->session, AF_INET) ) { tr_port port; struct tr_address addr; memset(&addr, 0, sizeof(addr)); addr.type = TR_AF_INET; memcpy(&addr.addr.addr4, &cl->nodes[i * 6], 4); memcpy(&port, &cl->nodes[i * 6 + 4], 2); port = ntohs(port); tr_dhtAddNode(cl->session, &addr, port, 1); } if( i < num6 && !bootstrap_done(cl->session, AF_INET6) ) { tr_port port; struct tr_address addr; memset(&addr, 0, sizeof(addr)); addr.type = TR_AF_INET6; memcpy(&addr.addr.addr6, &cl->nodes6[i * 18], 16); memcpy(&port, &cl->nodes6[i * 18 + 16], 2); port = ntohs(port); tr_dhtAddNode(cl->session, &addr, port, 1); } /* Our DHT code is able to take up to 9 nodes in a row without dropping any. After that, it takes some time to split buckets. So ping the first 8 nodes quickly, then slow down. */ if(i < 8) nap(2); else nap(15); if(bootstrap_done( session, 0 )) break; } if(!bootstrap_done(cl->session, 0)) { char *bootstrap_file; FILE *f = NULL; bootstrap_file = tr_buildPath(cl->session->configDir, "dht.bootstrap", NULL); if(bootstrap_file) f = fopen(bootstrap_file, "rb"); if(f != NULL) { tr_ninf("DHT", "Attempting manual bootstrap"); for(;;) { char buf[201]; char *p; int port = 0; p = fgets(buf, 200, f); if( p == NULL ) break; p = memchr(buf, ' ', strlen(buf)); if(p != NULL) port = atoi(p + 1); if(p == NULL || port <= 0 || port >= 0x10000) { tr_nerr("DHT", "Couldn't parse %s", buf); continue; } *p = '\0'; bootstrap_from_name( buf, port, bootstrap_af(session) ); if(bootstrap_done(cl->session, 0)) break; } } tr_free( bootstrap_file ); } /* We really don't want to abuse our bootstrap nodes. Be glacially slow. */ if(!bootstrap_done(cl->session, 0)) nap(30); if(!bootstrap_done(cl->session, 0)) { tr_ninf("DHT", "Attempting bootstrap from dht.transmissionbt.com"); bootstrap_from_name( "dht.transmissionbt.com", 6881, bootstrap_af(session) ); } if( cl->nodes ) tr_free( cl->nodes ); if( cl->nodes6 ) tr_free( cl->nodes6 ); tr_free( closure ); tr_ndbg( "DHT", "Finished bootstrapping" ); }
int tr_dhtInit(tr_session *ss) { tr_benc benc; int rc; 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; tr_ndbg( "DHT", "Initializing DHT" ); 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( ss->udp_socket >= 0 && tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) { nodes = tr_memdup( raw, len ); } if( ss->udp6_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( ss->udp_socket, ss->udp6_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_timer = evtimer_new( session->event_base, timer_callback, session ); tr_timerAdd( dht_timer, 0, tr_cryptoWeakRandInt( 1000000 ) ); tr_ndbg( "DHT", "DHT initialized" ); return 1; fail: tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", errno ); session = NULL; return -1; }