static void livesync_handle_peersync( ssize_t datalen ) { int off = sizeof( g_tracker_id ) + sizeof( uint32_t ); fprintf( stderr, "." ); while( off + (ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ) <= datalen ) { ot_peer *peer = (ot_peer*)(g_inbuffer + off + sizeof(ot_hash)); ot_hash *hash = (ot_hash*)(g_inbuffer + off); if( OT_PEERFLAG(peer) & PEER_FLAG_STOPPED ) remove_peer_from_torrent_proxy( *hash, peer ); else add_peer_to_torrent_proxy( *hash, peer ); off += sizeof( ot_hash ) + sizeof( ot_peer ); } }
size_t add_peer_to_torrent_proxy( ot_hash hash, ot_peer *peer ) { int exactmatch; ot_torrent *torrent; ot_peer *peer_dest; ot_vector *torrents_list = mutex_bucket_lock_by_hash( hash ); torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); if( !torrent ) return -1; if( !exactmatch ) { /* Create a new torrent entry, then */ memcpy( torrent->hash, hash, sizeof(ot_hash) ); if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { vector_remove_torrent( torrents_list, torrent ); mutex_bucket_unlock_by_hash( hash, 0 ); return -1; } byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); } /* Check for peer in torrent */ peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), peer, &exactmatch ); if( !peer_dest ) { mutex_bucket_unlock_by_hash( hash, 0 ); return -1; } /* Tell peer that it's fresh */ OT_PEERTIME( peer ) = 0; /* If we hadn't had a match create peer there */ if( !exactmatch ) { torrent->peer_list->peer_count++; if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) torrent->peer_list->seed_count++; } memcpy( peer_dest, peer, sizeof(ot_peer) ); mutex_bucket_unlock_by_hash( hash, 0 ); return 0; }
/* This is the non-generic delete from vector-operation specialized for peers in pools. It returns 0 if no peer was found (and thus not removed) 1 if a non-seeding peer was removed 2 if a seeding peer was removed */ int vector_remove_peer( ot_vector *vector, ot_peer *peer ) { int exactmatch; ot_peer *match, *end; if( !vector->size ) return 0; /* If space is zero but size is set, we're dealing with a list of vector->size buckets */ if( vector->space < vector->size ) vector = ((ot_vector*)vector->data) + vector_hash_peer(peer, vector->size ); end = ((ot_peer*)vector->data) + vector->size; match = (ot_peer*)binary_search( peer, vector->data, vector->size, sizeof(ot_peer), OT_PEER_COMPARE_SIZE, &exactmatch ); if( !exactmatch ) return 0; exactmatch = ( OT_PEERFLAG( match ) & PEER_FLAG_SEEDING ) ? 2 : 1; memmove( match, match + 1, sizeof(ot_peer) * ( end - match - 1 ) ); vector->size--; vector_fixup_peers( vector ); return exactmatch; }
static void livesync_handle_peersync( struct ot_workstruct *ws ) { int off = sizeof( g_tracker_id ) + sizeof( uint32_t ); /* Now basic sanity checks have been done on the live sync packet We might add more testing and logging. */ while( off + (ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ) <= ws->request_size ) { memcpy( &ws->peer, ws->request + off + sizeof(ot_hash), sizeof( ot_peer ) ); ws->hash = (ot_hash*)(ws->request + off); if( !g_opentracker_running ) return; if( OT_PEERFLAG(&ws->peer) & PEER_FLAG_STOPPED ) remove_peer_from_torrent( FLAG_MCA, ws ); else add_peer_to_torrent_and_return_peers( FLAG_MCA, ws, /* amount = */ 0 ); off += sizeof( ot_hash ) + sizeof( ot_peer ); } stats_issue_event(EVENT_SYNC, 0, (ws->request_size - sizeof( g_tracker_id ) - sizeof( uint32_t ) ) / ((ssize_t)sizeof( ot_hash ) + (ssize_t)sizeof( ot_peer ))); }
/* Returns amount of removed peers */ static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { ot_peer *last_peer = peers + peer_count, *insert_point; time_t timediff; /* Two scan modes: unless there is one peer removed, just increase ot_peertime */ while( peers < last_peer ) { if( ( timediff = timedout + OT_PEERTIME( peers ) ) >= OT_PEER_TIMEOUT ) break; OT_PEERTIME( peers++ ) = timediff; } /* If we at least remove one peer, we have to copy */ insert_point = peers; while( peers < last_peer ) if( ( timediff = timedout + OT_PEERTIME( peers ) ) < OT_PEER_TIMEOUT ) { OT_PEERTIME( peers ) = timediff; memcpy( insert_point++, peers++, sizeof(ot_peer)); } else if( OT_PEERFLAG( peers++ ) & PEER_FLAG_SEEDING ) (*removed_seeders)++; return peers - insert_point; }
/* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */ int handle_udp6( int64 serversocket, struct ot_workstruct *ws ) { ot_ip6 remoteip; uint32_t *inpacket = (uint32_t*)ws->inbuf; uint32_t *outpacket = (uint32_t*)ws->outbuf; uint32_t numwant, left, event, scopeid; uint32_t connid[2]; uint16_t port, remoteport; size_t byte_count, scrape_count; byte_count = socket_recv6( serversocket, ws->inbuf, G_INBUF_SIZE, remoteip, &remoteport, &scopeid ); if( !byte_count ) return 0; stats_issue_event( EVENT_ACCEPT, FLAG_UDP, (uintptr_t)remoteip ); stats_issue_event( EVENT_READ, FLAG_UDP, byte_count ); /* Minimum udp tracker packet size, also catches error */ if( byte_count < 16 ) return 1; /* Generate the connection id we give out and expect to and from the requesting ip address, this prevents udp spoofing */ udp_make_connectionid( connid, remoteip, 0 ); /* Initialise hash pointer */ ws->hash = NULL; ws->peer_id = NULL; /* If action is not a ntohl(a) == a == 0, then we expect the derived connection id in first 64 bit */ if( inpacket[2] && ( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) ) { /* If connection id does not match, try the one that was valid in the previous hour. Only if this also does not match, return an error packet */ udp_make_connectionid( connid, remoteip, 1 ); if( inpacket[0] != connid[0] || inpacket[1] != connid[1] ) { const size_t s = sizeof( "Connection ID missmatch." ); outpacket[0] = 3; outpacket[1] = inpacket[3]; memcpy( &outpacket[2], "Connection ID missmatch.", s ); socket_send6( serversocket, ws->outbuf, 8 + s, remoteip, remoteport, 0 ); stats_issue_event( EVENT_CONNID_MISSMATCH, FLAG_UDP, 8 + s ); return 1; } } switch( ntohl( inpacket[2] ) ) { case 0: /* This is a connect action */ /* look for udp bittorrent magic id */ if( (ntohl(inpacket[0]) != 0x00000417) || (ntohl(inpacket[1]) != 0x27101980) ) return 1; outpacket[0] = 0; outpacket[1] = inpacket[3]; outpacket[2] = connid[0]; outpacket[3] = connid[1]; socket_send6( serversocket, ws->outbuf, 16, remoteip, remoteport, 0 ); stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 ); break; case 1: /* This is an announce action */ /* Minimum udp announce packet size */ if( byte_count < 98 ) return 1; /* We do only want to know, if it is zero */ left = inpacket[64/4] | inpacket[68/4]; numwant = ntohl( inpacket[92/4] ); if (numwant > 200) numwant = 200; event = ntohl( inpacket[80/4] ); port = *(uint16_t*)( ((char*)inpacket) + 96 ); ws->hash = (ot_hash*)( ((char*)inpacket) + 16 ); OT_SETIP( &ws->peer, remoteip ); OT_SETPORT( &ws->peer, &port ); OT_PEERFLAG( &ws->peer ) = 0; switch( event ) { case 1: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break; case 3: OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break; default: break; } if( !left ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; outpacket[0] = htonl( 1 ); /* announce action */ outpacket[1] = inpacket[12/4]; if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) { /* Peer is gone. */ ws->reply = ws->outbuf; ws->reply_size = remove_peer_from_torrent( FLAG_UDP, ws ); } else { ws->reply = ws->outbuf + 8; ws->reply_size = 8 + add_peer_to_torrent_and_return_peers( FLAG_UDP, ws, numwant ); } socket_send6( serversocket, ws->outbuf, ws->reply_size, remoteip, remoteport, 0 ); stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, ws->reply_size ); break; case 2: /* This is a scrape action */ outpacket[0] = htonl( 2 ); /* scrape action */ outpacket[1] = inpacket[12/4]; for( scrape_count = 0; ( scrape_count * 20 < byte_count - 16) && ( scrape_count <= 74 ); scrape_count++ ) return_udp_scrape_for_torrent( *(ot_hash*)( ((char*)inpacket) + 16 + 20 * scrape_count ), ((char*)outpacket) + 8 + 12 * scrape_count ); socket_send6( serversocket, ws->outbuf, 8 + 12 * scrape_count, remoteip, remoteport, 0 ); stats_issue_event( EVENT_SCRAPE, FLAG_UDP, scrape_count ); break; } return 1; }
static int persist_add_peer(ot_hash *hash, ot_peerlist *peer_list, ot_peer *peer) { int exactmatch, delta_torrentcount = 0; ot_torrent *torrent; ot_peer *peer_dest; /* eliminate compiler warnings */ (void)peer_list; ot_vector *torrents_list = mutex_bucket_lock_by_hash(*hash); if( !accesslist_hashisvalid( hash ) ) { mutex_bucket_unlock_by_hash( *hash, 0 ); return 0; } torrent = vector_find_or_insert( torrents_list, (void*)hash, sizeof( ot_torrent ), OT_HASH_COMPARE_SIZE, &exactmatch ); if( !torrent ) { mutex_bucket_unlock_by_hash( *hash, 0 ); return 0; } if( !exactmatch ) { /* Create a new torrent entry, then */ memcpy( torrent->hash, hash, sizeof(ot_hash) ); if( !( torrent->peer_list = malloc( sizeof (ot_peerlist) ) ) ) { vector_remove_torrent( torrents_list, torrent ); mutex_bucket_unlock_by_hash( *hash, 0 ); return 0; } byte_zero( torrent->peer_list, sizeof( ot_peerlist ) ); delta_torrentcount = 1; } /* Ignore torrent base in odb file, just use current clock. */ torrent->peer_list->base = g_now_minutes; /* Check for peer in torrent */ peer_dest = vector_find_or_insert_peer( &(torrent->peer_list->peers), peer, &exactmatch ); if( !peer_dest ) { mutex_bucket_unlock_by_hash( *hash, delta_torrentcount ); return 0; } /* If we hadn't had a match, create peer there */ if( !exactmatch ) { torrent->peer_list->peer_count++; if( OT_PEERFLAG(peer) & PEER_FLAG_COMPLETED ) torrent->peer_list->down_count++; if( OT_PEERFLAG(peer) & PEER_FLAG_SEEDING ) torrent->peer_list->seed_count++; } else { LOG_ERR("Repeat peer in a same torrent\n"); assert(0); } memcpy( peer_dest, peer, sizeof(ot_peer) ); mutex_bucket_unlock_by_hash( *hash, delta_torrentcount ); return 0; }
static ssize_t http_handle_announce( const int64 sock, struct ot_workstruct *ws, char *read_ptr ) { int numwant, tmp, scanon; unsigned short port = 0; char *write_ptr; ssize_t len; struct http_data *cookie = io_getcookie( sock ); /* This is to hack around stupid clients that send "announce ?info_hash" */ if( read_ptr[-1] != '?' ) { while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; ++read_ptr; } #ifdef WANT_IP_FROM_PROXY if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_PROXY ) ) { ot_ip6 proxied_ip; char *fwd = http_header( ws->request, ws->header_size, "x-forwarded-for" ); if( fwd && scan_ip6( fwd, proxied_ip ) ) OT_SETIP( &ws->peer, proxied_ip ); else OT_SETIP( &ws->peer, cookie->ip ); } else #endif OT_SETIP( &ws->peer, cookie->ip ); ws->peer_id = NULL; ws->hash = NULL; OT_SETPORT( &ws->peer, &port ); OT_PEERFLAG( &ws->peer ) = 0; numwant = 50; scanon = 1; while( scanon ) { switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { case -2: scanon = 0; break; /* TERMINATOR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: scan_urlencoded_skipvalue( &read_ptr ); break; case 1: /* matched "port" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; port = htons( tmp ); OT_SETPORT( &ws->peer, &port ); break; case 2: /* matched "left" */ if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; if( !tmp ) OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_SEEDING; break; case 3: /* matched "event" */ switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { case -1: HTTPERROR_400_PARAM; case 1: /* matched "completed" */ OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_COMPLETED; break; case 2: /* matched "stopped" */ OT_PEERFLAG( &ws->peer ) |= PEER_FLAG_STOPPED; break; default: break; } break; case 4: /* matched "numwant" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; if( numwant < 0 ) numwant = 50; if( numwant > 200 ) numwant = 200; break; case 5: /* matched "compact" */ len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; if( !tmp ) HTTPERROR_400_COMPACT; break; case 6: /* matched "info_hash" */ if( ws->hash ) HTTPERROR_400_DOUBLEHASH; /* ignore this, when we have less than 20 bytes */ if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; ws->hash = (ot_hash*)write_ptr; break; #ifdef WANT_IP_FROM_QUERY_STRING case 7: /* matched "ip" */ { char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply+16; len = scan_urlencoded_query( &read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE ); tmp_buf2[len] = 0; if( ( len <= 0 ) || !scan_ip6( tmp_buf2, tmp_buf1 ) ) HTTPERROR_400_PARAM; OT_SETIP( &ws->peer, tmp_buf1 ); } break; #endif #ifdef WANT_FULLLOG_NETWORKS case 8: /* matched "lognet" */ { //if( accesslist_isblessed( cookie->ip, OT_PERMISSION_MAY_STAT ) ) { char *tmp_buf = ws->reply; ot_net net; signed short parsed, bits; len = scan_urlencoded_query( &read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE ); tmp_buf[len] = 0; if( len <= 0 ) HTTPERROR_400_PARAM; if( *tmp_buf == '-' ) { loglist_reset( ); return ws->reply_size = sprintf( ws->reply, "Successfully removed.\n" ); } parsed = scan_ip6( tmp_buf, net.address ); if( !parsed ) HTTPERROR_400_PARAM; if( tmp_buf[parsed++] != '/' ) bits = 128; else { parsed = scan_short( tmp_buf + parsed, &bits ); if( !parsed ) HTTPERROR_400_PARAM; if( ip6_isv4mapped( net.address ) ) bits += 96; } net.bits = bits; loglist_add_network( &net ); return ws->reply_size = sprintf( ws->reply, "Successfully added.\n" ); //} } #endif break; case 9: /* matched "peer_id" */ /* ignore this, when we have less than 20 bytes */ if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; ws->peer_id = write_ptr; break; } } #ifdef WANT_LOG_NUMWANT numwants[numwant]++; #endif /* XXX DEBUG stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ws->reply ); */ /* Scanned whole query string */ if( !ws->hash ) return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); if( OT_PEERFLAG( &ws->peer ) & PEER_FLAG_STOPPED ) ws->reply_size = remove_peer_from_torrent( FLAG_TCP, ws ); else ws->reply_size = add_peer_to_torrent_and_return_peers( FLAG_TCP, ws, numwant ); stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); return ws->reply_size; }