int tr_peerIoReconnect( tr_peerIo * io ) { short int pendingEvents; tr_session * session; assert( tr_isPeerIo( io ) ); assert( !tr_peerIoIsIncoming( io ) ); session = tr_peerIoGetSession( io ); pendingEvents = io->pendingEvents; event_disable( io, EV_READ | EV_WRITE ); if( io->socket >= 0 ) tr_netClose( session, io->socket ); io->socket = tr_netOpenPeerSocket( session, &io->addr, io->port, io->isSeed ); event_set( &io->event_read, io->socket, EV_READ, event_read_cb, io ); event_set( &io->event_write, io->socket, EV_WRITE, event_write_cb, io ); if( io->socket >= 0 ) { event_enable( io, pendingEvents ); tr_netSetTOS( io->socket, session->peerSocketTOS ); maybeSetCongestionAlgorithm( io->socket, session->peer_congestion_algorithm ); return 0; } return -1; }
static void killsock( int * fd ) { if( 0 <= *fd ) { tr_netClose( *fd ); *fd = -1; } }
static void killSock( tr_fd_t * fdlimit, int * sock ) { if( 0 <= *sock ) { tr_netClose( *sock ); *sock = -1; tr_fdSocketClosed( fdlimit, 0 ); } }
tr_socket_t tr_netAccept(tr_session* session, tr_socket_t b, tr_address* addr, tr_port* port) { tr_socket_t fd = tr_fdSocketAccept(session, b, addr, port); if (fd != TR_BAD_SOCKET && evutil_make_socket_nonblocking(fd) == -1) { tr_netClose(session, fd); fd = TR_BAD_SOCKET; } return fd; }
void tr_trackerClose( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; if( tc->status == TC_STATUS_RESOLVE ) { tr_netResolveClose( tc->resolve ); } else if( tc->status > TC_STATUS_RESOLVE ) { tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); } free( tc->buf ); free( tc ); }
void tr_httpClose( tr_http_t * http ) { if( NULL != http->resolve ) { tr_netResolveClose( http->resolve ); } free( http->host ); if( 0 <= http->sock ) { tr_netClose( http->sock ); } free( http->header.buf ); free( http->body.buf ); free( http ); }
static void io_dtor( void * vio ) { tr_peerIo * io = vio; assert( tr_isPeerIo( io ) ); assert( tr_amInEventThread( io->session ) ); assert( io->session->events != NULL ); dbgmsg( io, "in tr_peerIo destructor" ); event_disable( io, EV_READ | EV_WRITE ); tr_bandwidthDestruct( &io->bandwidth ); evbuffer_free( io->outbuf ); evbuffer_free( io->inbuf ); tr_netClose( io->session, io->socket ); tr_cryptoFree( io->crypto ); tr_list_free( &io->outbuf_datatypes, tr_free ); memset( io, ~0, sizeof( tr_peerIo ) ); tr_free( io ); }
void tr_trackerStopped( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; if( tc->status > TC_STATUS_CONNECT ) { /* If we are already sendy a query at the moment, we need to reconnect */ tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); tc->status = TC_STATUS_IDLE; } tc->started = 0; tc->completed = 0; tc->stopped = 1; /* Even if we have connected recently, reconnect right now */ if( tc->status & TC_STATUS_IDLE ) { tc->dateTry = 0; } }
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed) { TR_ASSERT(tr_address_is_valid(addr)); struct tr_peer_socket ret = TR_PEER_SOCKET_INIT; static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 }; tr_socket_t s; struct sockaddr_storage sock; socklen_t addrlen; tr_address const* source_addr; socklen_t sourcelen; struct sockaddr_storage source_sock; char err_buf[512]; if (!tr_address_is_valid_for_peers(addr, port)) { return ret; } s = tr_fdSocketCreate(session, domains[addr->type], SOCK_STREAM); if (s == TR_BAD_SOCKET) { return ret; } /* seeds don't need much of a read buffer... */ if (clientIsSeed) { int n = 8192; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void const*)&n, sizeof(n)) == -1) { tr_logAddInfo("Unable to set SO_RCVBUF on socket %" PRIdMAX ": %s", (intmax_t)s, tr_net_strerror(err_buf, sizeof(err_buf), sockerrno)); } } if (evutil_make_socket_nonblocking(s) == -1) { tr_netClose(session, s); return ret; } addrlen = setup_sockaddr(addr, port, &sock); /* set source address */ source_addr = tr_sessionGetPublicAddress(session, addr->type, NULL); TR_ASSERT(source_addr != NULL); sourcelen = setup_sockaddr(source_addr, 0, &source_sock); if (bind(s, (struct sockaddr*)&source_sock, sourcelen) == -1) { tr_logAddError(_("Couldn't set source address %s on %" PRIdMAX ": %s"), tr_address_to_string(source_addr), (intmax_t)s, tr_net_strerror(err_buf, sizeof(err_buf), sockerrno)); tr_netClose(session, s); return ret; } if (connect(s, (struct sockaddr*)&sock, addrlen) == -1 && #ifdef _WIN32 sockerrno != WSAEWOULDBLOCK && #endif sockerrno != EINPROGRESS) { int const tmperrno = sockerrno; if ((tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr->type == TR_AF_INET) { tr_logAddError(_("Couldn't connect socket %" PRIdMAX " to %s, port %d (errno %d - %s)"), (intmax_t)s, tr_address_to_string(addr), (int)ntohs(port), tmperrno, tr_net_strerror(err_buf, sizeof(err_buf), tmperrno)); } tr_netClose(session, s); } else { ret = tr_peer_socket_tcp_create(s); } tr_logAddDeep(__FILE__, __LINE__, NULL, "New OUTGOING connection %" PRIdMAX " (%s)", (intmax_t)s, tr_peerIoAddrStr(addr, port)); return ret; }
int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers ) { tr_info_t * inf = &tor->info; int s, i, ret; uint8_t buf[1024]; benc_val_t scrape, * val1, * val2; struct in_addr addr; uint64_t date; int pos, len; tr_resolve_t * resolve; if( !tor->scrape[0] ) { /* scrape not supported */ return 1; } resolve = tr_netResolveInit( inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netResolvePulse( resolve, &addr ); if( ret == TR_RESOLVE_OK ) { tr_netResolveClose( resolve ); break; } if( ret == TR_RESOLVE_ERROR || ( ret == TR_RESOLVE_WAIT && tr_date() > date + 10000 ) ) { fprintf( stderr, "Could not resolve %s\n", inf->trackerAddress ); tr_netResolveClose( resolve ); return 1; } tr_wait( 10 ); } s = tr_netOpen( addr, htons( inf->trackerPort ) ); if( s < 0 ) { return 1; } len = snprintf( (char *) buf, sizeof( buf ), "GET %s?info_hash=%s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n", tor->scrape, tor->hashString, inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netSend( s, buf, len ); if( ret & TR_NET_CLOSE ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } } else { break; } tr_wait( 10 ); } pos = 0; for( date = tr_date();; ) { ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos ); if( ret & TR_NET_CLOSE ) { break; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } } else { pos += ret; } tr_wait( 10 ); } if( pos < 1 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } for( i = 0; i < pos - 8; i++ ) { if( !memcmp( &buf[i], "d5:files", 8 ) ) { break; } } if( i >= pos - 8 ) { return 1; } if( tr_bencLoad( &buf[i], pos - i, &scrape, NULL ) ) { return 1; } val1 = tr_bencDictFind( &scrape, "files" ); if( !val1 ) { return 1; } val1 = &val1->val.l.vals[1]; if( !val1 ) { return 1; } val2 = tr_bencDictFind( val1, "complete" ); if( !val2 ) { return 1; } *seeders = val2->val.i; val2 = tr_bencDictFind( val1, "incomplete" ); if( !val2 ) { return 1; } *leechers = val2->val.i; tr_bencFree( &scrape ); return 0; }
static void recvAnswer( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; int ret; int i; benc_val_t beAll; benc_val_t * bePeers, * beFoo; uint8_t * body; int bodylen; if( tc->pos == tc->size ) { tc->size *= 2; tc->buf = realloc( tc->buf, tc->size ); } ret = tr_netRecv( tc->socket, &tc->buf[tc->pos], tc->size - tc->pos ); if( ret & TR_NET_BLOCK ) { return; } if( !( ret & TR_NET_CLOSE ) ) { // printf( "got %d bytes\n", ret ); tc->pos += ret; return; } tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); // printf( "connection closed, got total %d bytes\n", tc->pos ); tc->status = TC_STATUS_IDLE; tc->dateTry = tr_date(); if( tc->pos < 12 || ( 0 != memcmp( tc->buf, "HTTP/1.0 ", 9 ) && 0 != memcmp( tc->buf, "HTTP/1.1 ", 9 ) ) ) { /* We don't have a complete HTTP status line */ tr_inf( "Tracker: incomplete HTTP status line" ); tc->lastAttempt = TC_ATTEMPT_NOREACH; return; } if( '2' != tc->buf[9] ) { /* we didn't get a 2xx status code */ tr_err( "Tracker: invalid HTTP status code: %c%c%c", tc->buf[9], tc->buf[10], tc->buf[11] ); tc->lastAttempt = TC_ATTEMPT_ERROR; return; } /* find the end of the http headers */ body = tr_memmem( tc->buf, tc->pos, "\015\012\015\012", 4 ); if( NULL != body ) { body += 4; } /* hooray for trackers that violate the HTTP spec */ else if( NULL != ( body = tr_memmem( tc->buf, tc->pos, "\015\015", 2 ) ) || NULL != ( body = tr_memmem( tc->buf, tc->pos, "\012\012", 2 ) ) ) { body += 2; } else { tr_err( "Tracker: could not find end of HTTP headers" ); tc->lastAttempt = TC_ATTEMPT_NOREACH; return; } bodylen = tc->pos - (body - tc->buf); /* Find and load the dictionary */ for( i = 0; i < bodylen; i++ ) { if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) ) { break; } } if( i >= bodylen ) { if( tc->stopped || 0 < tc->newPort ) { tc->lastAttempt = TC_ATTEMPT_OK; goto nodict; } tr_err( "Tracker: no valid dictionary found in answer" ); tc->lastAttempt = TC_ATTEMPT_ERROR; return; } // tr_bencPrint( &beAll ); if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) ) { tr_err( "Tracker: %s", bePeers->val.s.s ); tor->error |= TR_ETRACKER; snprintf( tor->trackerError, sizeof( tor->trackerError ), "%s", bePeers->val.s.s ); tc->lastAttempt = TC_ATTEMPT_ERROR; goto cleanup; } tor->error &= ~TR_ETRACKER; tc->lastAttempt = TC_ATTEMPT_OK; if( !tc->interval ) { /* Get the tracker interval, ignore it if it is not between 10 sec and 5 mins */ if( !( beFoo = tr_bencDictFind( &beAll, "interval" ) ) || !( beFoo->type & TYPE_INT ) ) { tr_err( "Tracker: no 'interval' field" ); goto cleanup; } tc->interval = beFoo->val.i; tc->interval = MIN( tc->interval, 300 ); tc->interval = MAX( 10, tc->interval ); tr_inf( "Tracker: interval = %d seconds", tc->interval ); } if( ( beFoo = tr_bencDictFind( &beAll, "complete" ) ) && ( beFoo->type & TYPE_INT ) ) { tc->seeders = beFoo->val.i; } if( ( beFoo = tr_bencDictFind( &beAll, "incomplete" ) ) && ( beFoo->type & TYPE_INT ) ) { tc->leechers = beFoo->val.i; } if( tc->seeders + tc->leechers >= 50 ) { tc->hasManyPeers = 1; } if( !( bePeers = tr_bencDictFind( &beAll, "peers" ) ) ) { if( tc->stopped || 0 < tc->newPort ) { goto nodict; } tr_err( "Tracker: no \"peers\" field" ); goto cleanup; } if( bePeers->type & TYPE_LIST ) { char * ip; int port; /* Original protocol */ tr_inf( "Tracker: got %d peers", bePeers->val.l.count ); for( i = 0; i < bePeers->val.l.count; i++ ) { beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" ); if( !beFoo ) continue; ip = beFoo->val.s.s; beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" ); if( !beFoo ) continue; port = beFoo->val.i; tr_peerAddOld( tor, ip, port ); } if( bePeers->val.l.count >= 50 ) { tc->hasManyPeers = 1; } } else if( bePeers->type & TYPE_STR ) { struct in_addr addr; in_port_t port; /* "Compact" extension */ if( bePeers->val.s.i % 6 ) { tr_err( "Tracker: \"peers\" of size %d", bePeers->val.s.i ); tr_lockUnlock( &tor->lock ); goto cleanup; } tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 ); for( i = 0; i < bePeers->val.s.i / 6; i++ ) { memcpy( &addr, &bePeers->val.s.s[6*i], 4 ); memcpy( &port, &bePeers->val.s.s[6*i+4], 2 ); tr_peerAddCompact( tor, addr, port ); } if( bePeers->val.s.i / 6 >= 50 ) { tc->hasManyPeers = 1; } } nodict: /* Success */ tc->started = 0; tc->completed = 0; tc->dateOk = tr_date(); if( tc->stopped ) { tor->status = TR_STATUS_STOPPED; tc->stopped = 0; } else if( 0 < tc->newPort ) { tc->started = 1; tc->download = tor->downloaded; tc->upload = tor->uploaded; } cleanup: tr_bencFree( &beAll ); }
static void sendQuery( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; tr_info_t * inf = &tor->info; char * event; uint64_t left; int ret; uint64_t down; uint64_t up; down = tor->downloaded - tc->download; up = tor->uploaded - tc->upload; if( tc->started ) { event = "&event=started"; down = up = 0; if( 0 < tc->newPort ) { tc->bindPort = tc->newPort; tc->newPort = -1; } } else if( tc->completed ) { event = "&event=completed"; } else if( tc->stopped || 0 < tc->newPort ) { event = "&event=stopped"; } else { event = ""; } left = tr_cpLeftBytes( tor->completion ); ret = snprintf( (char *) tc->buf, tc->size, "GET %s?" "info_hash=%s&" "peer_id=%s&" "port=%d&" "uploaded=%"PRIu64"&" "downloaded=%"PRIu64"&" "left=%"PRIu64"&" "compact=1&" "numwant=50&" "key=%s" "%s " "HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: Transmission/%d.%d\r\n" "Connection: close\r\n\r\n", inf->trackerAnnounce, tor->hashString, tc->id, tc->bindPort, up, down, left, tor->key, event, inf->trackerAddress, VERSION_MAJOR, VERSION_MINOR ); ret = tr_netSend( tc->socket, tc->buf, ret ); if( ret & TR_NET_CLOSE ) { tr_inf( "Tracker: connection failed" ); tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); tc->status = TC_STATUS_IDLE; tc->dateTry = tr_date(); } else if( !( ret & TR_NET_BLOCK ) ) { // printf( "Tracker: sent %s", tc->buf ); tc->status = TC_STATUS_RECV; tc->pos = 0; } }
int tr_trackerPulse( tr_tracker_t * tc ) { tr_torrent_t * tor = tc->tor; tr_info_t * inf = &tor->info; uint64_t now = tr_date(); if( ( tc->status & TC_STATUS_IDLE ) && shouldConnect( tc ) ) { tc->resolve = tr_netResolveInit( inf->trackerAddress ); tr_inf( "Tracker: connecting to %s:%d (%s)", inf->trackerAddress, inf->trackerPort, tc->started ? "sending 'started'" : ( tc->completed ? "sending 'completed'" : ( tc->stopped ? "sending 'stopped'" : ( 0 < tc->newPort ? "sending 'stopped' to change port" : "getting peers" ) ) ) ); tc->status = TC_STATUS_RESOLVE; tc->dateTry = tr_date(); } if( tc->status & TC_STATUS_RESOLVE ) { int ret; struct in_addr addr; ret = tr_netResolvePulse( tc->resolve, &addr ); if( ret == TR_RESOLVE_WAIT ) { return 0; } else { tr_netResolveClose( tc->resolve ); } if( ret == TR_RESOLVE_ERROR ) { tc->status = TC_STATUS_IDLE; return 0; } if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) ) { tc->status = TC_STATUS_IDLE; return 0; } tc->socket = tr_netOpen( addr, htons( inf->trackerPort ) ); if( tc->socket < 0 ) { tr_fdSocketClosed( tor->fdlimit, 1 ); tc->status = TC_STATUS_IDLE; return 0; } tc->status = TC_STATUS_CONNECT; } if( tc->status & TC_STATUS_CONNECT ) { /* We are connecting to the tracker. Try to send the query */ sendQuery( tc ); } if( tc->status & TC_STATUS_RECV ) { /* Try to get something */ recvAnswer( tc ); } if( tc->status > TC_STATUS_IDLE && now > tc->dateTry + 60000 ) { /* Give up if the request wasn't successful within 60 seconds */ tr_inf( "Tracker: timeout reached (60 s)" ); tr_netClose( tc->socket ); tr_fdSocketClosed( tor->fdlimit, 1 ); tc->status = TC_STATUS_IDLE; tc->dateTry = tr_date(); } return 0; }