void tr_fdInit( int globalPeerLimit ) { int i; assert( gFd == NULL ); gFd = tr_new0( struct tr_fd_s, 1 ); gFd->lock = tr_lockNew( ); #ifdef HAVE_GETRLIMIT { struct rlimit rlim; getrlimit( RLIMIT_NOFILE, &rlim ); rlim.rlim_cur = MIN( rlim.rlim_max, (rlim_t)( globalPeerLimit + NOFILE_BUFFER ) ); setrlimit( RLIMIT_NOFILE, &rlim ); gFd->socketMax = rlim.rlim_cur - NOFILE_BUFFER; tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur ); } #else gFd->socketMax = globalPeerLimit; #endif tr_dbg( "%d usable file descriptors", globalPeerLimit ); for( i = 0; i < TR_MAX_OPEN_FILES; ++i ) gFd->open[i].fd = -1; }
tr_natpmp_t * tr_natpmpInit() { tr_natpmp_t * pmp; pmp = calloc( 1, sizeof( *pmp ) ); if( NULL == pmp ) { return NULL; } pmp->state = PMP_STATE_IDLE; pmp->mcastfd = -1; if( tr_getDefaultRoute( &pmp->dest ) || INADDR_ANY == pmp->dest.s_addr ) { pmp->dest.s_addr = INADDR_NONE; } if( INADDR_NONE == pmp->dest.s_addr ) { tr_dbg( "nat-pmp device is unknown" ); } else { char addrstr[INET_ADDRSTRLEN]; tr_netNtop( &pmp->dest, addrstr, sizeof( addrstr ) ); tr_dbg( "nat-pmp device is %s", addrstr ); } return pmp; }
static void libeventThreadFunc( void * veh ) { tr_event_handle * eh = veh; tr_dbg( "Starting libevent thread" ); #ifndef WIN32 /* Don't exit when writing on a broken socket */ signal( SIGPIPE, SIG_IGN ); #endif eh->h->events = eh; /* listen to the pipe's read fd */ event_set( &eh->pipeEvent, eh->fds[0], EV_READ | EV_PERSIST, readFromPipe, veh ); event_add( &eh->pipeEvent, NULL ); event_set_log_callback( logFunc ); event_dispatch( ); tr_lockFree( eh->lock ); event_base_free( eh->base ); eh->h->events = NULL; tr_free( eh ); tr_dbg( "Closing libevent thread" ); }
static int parseSSDP( char * buf, int len, tr_http_header_t * hdr ) { char *method, *uri, *body; int code; body = NULL; /* check for an HTTP NOTIFY request */ if( 0 <= tr_httpRequestType( buf, len, &method, &uri ) ) { if( 0 == tr_strcasecmp( method, "NOTIFY" ) && 0 == strcmp( uri, "*" ) ) { hdr[0].name = "NT"; body = tr_httpParse( buf, len, hdr ); if( NULL == hdr[1].name || 0 != tr_strncasecmp( SSDP_SUBTYPE, hdr[1].data, hdr[1].len ) ) { body = NULL; } else { tr_dbg( "found upnp ssdp notify request" ); } } free( method ); free( uri ); } else { /* check for a response to our HTTP M-SEARCH request */ code = tr_httpResponseCode( buf, len ); if( TR_HTTP_STATUS_OK( code ) ) { hdr[0].name = "ST"; body = tr_httpParse( buf, len, hdr ); if( NULL != body ) { tr_dbg( "found upnp ssdp m-search response" ); } } } /* did we find enough information to be useful? */ if( NULL != body ) { /* the first header is the type */ if( NULL != hdr[0].data && 0 == tr_strncasecmp( SSDP_TYPE, hdr[0].data, hdr[0].len ) ) { return 1; } } return 0; }
static void mcastpulse( tr_natpmp_t * pmp ) { struct sockaddr_in sin; uint8_t buf[16]; int res; char dbgstr[INET_ADDRSTRLEN]; tr_natpmp_parse_t parse; res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin ); if( TR_NET_BLOCK & res ) { return; } else if( TR_NET_CLOSE & res ) { tr_err( "error reading nat-pmp multicast message" ); killsock( &pmp->mcastfd ); return; } tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) ); tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr ); if( pmp->dest.s_addr != sin.sin_addr.s_addr ) { tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s", dbgstr ); return; } if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) ) { if( checktime( &pmp->uptime, parse.seconds ) ) { pmp->renew = 0; tr_inf( "detected nat-pmp device reset" ); if( NULL != pmp->req ) { resetreq( pmp->req ); } } if( PMP_STATE_NOBODYHOME == pmp->state ) { tr_dbg( "nat-pmp state notfound -> idle" ); pmp->state = PMP_STATE_IDLE; } } }
void tr_threadCreate( tr_thread_t *t, void (* func )(unsigned long, unsigned long), void * arg, void *arg2, char * name ) { t->func = func; t->arg = arg; t->arg2 = arg2; t->name = strdup( name ); OSAL_T_CTSK t_ctsk; OSAL_ID tsk_id; //create the task t_ctsk.itskpri = OSAL_PRI_NORMAL; t_ctsk.stksz = 0x8000; t_ctsk.quantum = 10; t_ctsk.para1 = arg; t_ctsk.para2 = arg2; t_ctsk.name[0] = name[0]; t_ctsk.name[1] = name[1]; t_ctsk.name[2] = name[2]; t_ctsk.task = func; tsk_id = osal_task_create(&t_ctsk); tr_dbg("tsk_id [%s] = %d\n", name, tsk_id); ASSERT(OSAL_INVALID_ID != tsk_id); t->thread = tsk_id; }
static void logFunc( int severity, const char * message ) { if( severity >= _EVENT_LOG_ERR ) tr_err( "%s", message ); else tr_dbg( "%s", message ); }
void tr_upnpForwardPort( tr_upnp_t * upnp, int port ) { tr_lockLock( &upnp->lock ); tr_dbg( "upnp port changed from %i to %i", upnp->port, port ); upnp->port = port; tr_lockUnlock( &upnp->lock ); }
static void metainfoLookupRescan( tr_session * session ) { int i; int n; struct stat sb; const char * dirname = tr_getTorrentDir( session ); DIR * odir = NULL; tr_ctor * ctor = NULL; tr_list * list = NULL; assert( tr_isSession( session ) ); /* walk through the directory and find the mappings */ ctor = tr_ctorNew( session ); tr_ctorSetSave( ctor, FALSE ); /* since we already have them */ if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) ) { struct dirent *d; for( d = readdir( odir ); d != NULL; d = readdir( odir ) ) { if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and .. */ { tr_info inf; char * path = tr_buildPath( dirname, d->d_name, NULL ); tr_ctorSetMetainfoFromFile( ctor, path ); if( !tr_torrentParse( session, ctor, &inf ) ) { tr_list_append( &list, tr_strdup( inf.hashString ) ); tr_list_append( &list, tr_strdup( path ) ); tr_metainfoFree( &inf ); } tr_free( path ); } } closedir( odir ); } tr_ctorFree( ctor ); n = tr_list_size( list ) / 2; session->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n ); session->metainfoLookupCount = n; for( i = 0; i < n; ++i ) { char * hashString = tr_list_pop_front( &list ); char * filename = tr_list_pop_front( &list ); memcpy( session->metainfoLookup[i].hashString, hashString, 2 * SHA_DIGEST_LENGTH + 1 ); tr_free( hashString ); session->metainfoLookup[i].filename = filename; } metainfoLookupResort( session ); tr_dbg( "Found %d torrents in \"%s\"", n, dirname ); }
static int sendSSDP( tr_fd_t * fdlimit, int fd ) { char buf[102]; int len; struct sockaddr_in sin; if( 0 > fd ) { if( tr_fdSocketWillCreate( fdlimit, 0 ) ) { return -1; } fd = tr_netBindUDP( 0 ); if( 0 > fd ) { tr_fdSocketClosed( fdlimit, 0 ); return -1; } } tr_dbg( "sending upnp ssdp discover message" ); len = snprintf( buf, sizeof( buf ), "M-SEARCH * HTTP/1.1\r\n" "Host: %s:%i\r\n" "Man: \"ssdp:discover\"\r\n" "ST: %s\r\n" "MX: 3\r\n" "\r\n", SSDP_ADDR, SSDP_PORT, SSDP_TYPE ); /* if this assertion ever fails then just increase the size of buf */ assert( (int) sizeof( buf ) > len ); memset( &sin, 0, sizeof( sin ) ); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr( SSDP_ADDR ); sin.sin_port = htons( SSDP_PORT ); if( 0 > sendto( fd, buf, len, 0, (struct sockaddr*) &sin, sizeof( sin ) ) ) { if( EAGAIN != errno ) { tr_err( "Could not send SSDP discover message (%s)", strerror( errno ) ); } killSock( fdlimit, &fd ); return -1; } return fd; }
int tr_fdSocketCreate( tr_session * session, int domain, int type ) { int s = -1; struct tr_fdInfo * gFd; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); gFd = session->fdInfo; if( gFd->socketCount < gFd->socketLimit ) if( ( s = socket( domain, type, 0 ) ) < 0 ) { if( sockerrno != EAFNOSUPPORT ) tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) ); } if( s > -1 ) ++gFd->socketCount; assert( gFd->socketCount >= 0 ); if( s >= 0 ) { static tr_bool buf_logged = FALSE; if( !buf_logged ) { int i; socklen_t size = sizeof( int ); buf_logged = TRUE; getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size ); tr_dbg( "SO_SNDBUF size is %d", i ); getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size ); tr_dbg( "SO_RCVBUF size is %d", i ); } } return s; }
void unmap( tr_natpmp_t * pmp ) { switch( pmp->state ) { case PMP_STATE_IDLE: break; case PMP_STATE_ADDING: if( NULL == pmp->req ) { pmp->state = PMP_STATE_IDLE; tr_dbg( "nat-pmp state add -> idle" ); } else { pmp->mappedport = pmp->req->gotport; killreq( &pmp->req ); pmp->state = PMP_STATE_DELETING; tr_dbg( "nat-pmp state add -> del" ); } break; case PMP_STATE_DELETING: break; case PMP_STATE_MAPPED: pmp->state = PMP_STATE_DELETING; tr_dbg( "nat-pmp state mapped -> del" ); break; case PMP_STATE_FAILED: case PMP_STATE_NOBODYHOME: case PMP_STATE_TMPFAIL: break; default: assert( 0 ); break; } }
static int deviceStop( tr_upnp_device_t * dev ) { switch( dev->state ) { case UPNPDEV_STATE_READY: case UPNPDEV_STATE_ERROR: return 1; case UPNPDEV_STATE_MAPPED: tr_dbg( "upnp device %s: stopping upnp, state mapped -> delete", dev->host ); dev->state = UPNPDEV_STATE_DEL; return 0; default: return 0; } }
static int mcastsetup() { int fd; struct in_addr addr; addr.s_addr = inet_addr( PMP_MCAST_ADDR ); fd = tr_netMcastOpen( PMP_PORT, addr ); if( 0 > fd ) { return -1; } tr_dbg( "nat-pmp create multicast socket %i", fd ); return fd; }
static int devicePulse( tr_upnp_device_t * dev, tr_fd_t * fdlimit, int port ) { const char * body; int len, code; uint8_t laststate; switch( dev->state ) { case UPNPDEV_STATE_READY: if( 0 < port ) { tr_dbg( "upnp device %s: want mapping, state ready -> get", dev->host ); dev->mappedport = port; dev->state = UPNPDEV_STATE_GET; break; } return 1; case UPNPDEV_STATE_MAPPED: if( port != dev->mappedport ) { tr_dbg( "upnp device %s: change mapping, " "state mapped -> delete", dev->host ); dev->state = UPNPDEV_STATE_DEL; break; } if( tr_date() > dev->lastcheck + MAPPING_CHECK_INTERVAL ) { tr_dbg( "upnp device %s: check mapping, " "state mapped -> get", dev->host ); dev->state = UPNPDEV_STATE_GET; } return 1; case UPNPDEV_STATE_ERROR: return 0; } code = devicePulseHttp( dev, fdlimit, &body, &len ); if( 0 > code ) { return 1; } if( LOOP_DETECT_THRESHOLD <= dev->looping ) { tr_dbg( "upnp device %s: loop detected, state %hhu -> error", dev->host, dev->state ); dev->state = UPNPDEV_STATE_ERROR; dev->looping = 0; killHttp( fdlimit, &dev->http ); return 1; } laststate = dev->state; dev->state = UPNPDEV_STATE_ERROR; switch( laststate ) { case UPNPDEV_STATE_ROOT: if( !TR_HTTP_STATUS_OK( code ) ) { tr_dbg( "upnp device %s: fetch root failed with http code %i", dev->host, code ); } else if( parseRoot( body, len, &dev->soap, &dev->scpd ) ) { tr_dbg( "upnp device %s: parse root failed", dev->host ); } else { tr_dbg( "upnp device %s: found scpd \"%s\" and soap \"%s\"", dev->root, dev->scpd, dev->soap ); tr_dbg( "upnp device %s: parsed root, state root -> scpd", dev->host ); dev->state = UPNPDEV_STATE_SCPD; } break; case UPNPDEV_STATE_SCPD: if( !TR_HTTP_STATUS_OK( code ) ) { tr_dbg( "upnp device %s: fetch scpd failed with http code %i", dev->host, code ); } else if( parseScpd( body, len, &dev->getcmd, &dev->addcmd, &dev->delcmd ) ) { tr_dbg( "upnp device %s: parse scpd failed", dev->host ); } else { tr_dbg( "upnp device %s: parsed scpd, state scpd -> ready", dev->host ); dev->state = UPNPDEV_STATE_READY; dev->looping = 0; } break; case UPNPDEV_STATE_ADD: dev->looping++; if( IGD_ADD_CONFLICT == code ) { tr_dbg( "upnp device %s: add conflict, state add -> delete", dev->host ); dev->state = UPNPDEV_STATE_DEL; } else if( TR_HTTP_STATUS_OK( code ) || IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code ) { tr_dbg( "upnp device %s: add attempt, state add -> get", dev->host ); dev->state = UPNPDEV_STATE_GET; } else { tr_dbg( "upnp device %s: add failed with http code %i", dev->host, code ); } break; case UPNPDEV_STATE_GET: dev->looping++; if( TR_HTTP_STATUS_OK( code ) ) { switch( parseMapping( dev, body, len ) ) { case -1: break; case 0: tr_dbg( "upnp device %s: invalid mapping, " "state get -> delete", dev->host ); dev->state = UPNPDEV_STATE_DEL; break; case 1: tr_dbg( "upnp device %s: good mapping, " "state get -> mapped", dev->host ); dev->state = UPNPDEV_STATE_MAPPED; dev->looping = 0; dev->lastcheck = tr_date(); tr_inf( "upnp successful for port %i", dev->mappedport ); break; default: assert( 0 ); break; } } else if( IGD_NO_MAPPING_EXISTS == code || IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code ) { tr_dbg( "upnp device %s: no mapping, state get -> add", dev->host ); dev->state = UPNPDEV_STATE_ADD; } else { tr_dbg( "upnp device %s: get failed with http code %i", dev->host, code ); } break; case UPNPDEV_STATE_DEL: dev->looping++; if( TR_HTTP_STATUS_OK( code ) || IGD_NO_MAPPING_EXISTS == code || IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code ) { tr_dbg( "upnp device %s: deleted, state delete -> ready", dev->host ); dev->state = UPNPDEV_STATE_READY; dev->looping = 0; } else { tr_dbg( "upnp device %s: del failed with http code %i", dev->host, code ); } break; default: assert( 0 ); break; } dev->lastrequest = tr_date(); killHttp( fdlimit, &dev->http ); if( UPNPDEV_STATE_ERROR == dev->state ) { tr_dbg( "upnp device %s: error, state %hhu -> error", dev->host, laststate ); return 0; } return 1; }
void tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort ) { if( 0 <= pmp->mcastfd ) { mcastpulse( pmp ); } if( NULL != publicPort ) { *publicPort = -1; } if( pmp->active || PMP_STATE_DELETING == pmp->state ) { switch( pmp->state ) { case PMP_STATE_IDLE: case PMP_STATE_TMPFAIL: if( 0 < pmp->newport ) { tr_dbg( "nat-pmp state %s -> add with port %i", ( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ), pmp->newport ); pmp->state = PMP_STATE_ADDING; } break; case PMP_STATE_ADDING: if( NULL == pmp->req ) { if( 0 >= pmp->newport ) { tr_dbg( "nat-pmp state add -> idle, no port" ); pmp->state = PMP_STATE_IDLE; } else if( INADDR_NONE == pmp->dest.s_addr ) { tr_dbg( "nat-pmp state add -> fail, no default route" ); pmp->state = PMP_STATE_FAILED; } else { pmp->req = newreq( 1, pmp->dest, pmp->newport ); if( NULL == pmp->req ) { pmp->state = PMP_STATE_FAILED; tr_dbg( "nat-pmp state add -> fail on req init" ); } } } if( PMP_STATE_ADDING == pmp->state ) { switch( pulsereq( pmp ) ) { case TR_NET_ERROR: if( pmp->req->nobodyhome ) { pmp->state = PMP_STATE_NOBODYHOME; tr_dbg( "nat-pmp state add -> nobodyhome on pulse" ); } else if( pmp->req->tmpfail ) { pmp->state = PMP_STATE_TMPFAIL; tr_dbg( "nat-pmp state add -> err on pulse" ); if( pmp->req->askport == pmp->newport ) { pmp->newport = 0; } } else { pmp->state = PMP_STATE_FAILED; tr_dbg( "nat-pmp state add -> fail on pulse" ); } killreq( &pmp->req ); break; case TR_NET_OK: pmp->mappedport = pmp->req->gotport; if( pmp->mappedport != pmp->newport && pmp->newport == pmp->req->askport ) { pmp->newport = pmp->req->gotport; } killreq( &pmp->req ); pmp->state = PMP_STATE_MAPPED; pmp->mapped = 1; tr_dbg( "nat-pmp state add -> mapped with port %i", pmp->mappedport); tr_inf( "nat-pmp mapped port %i", pmp->mappedport ); if( NULL != publicPort ) { *publicPort = pmp->mappedport; } break; case TR_NET_WAIT: break; } } break; case PMP_STATE_DELETING: if( NULL == pmp->req ) { assert( 0 < pmp->mappedport ); pmp->req = newreq( 0, pmp->dest, pmp->mappedport ); if( NULL == pmp->req ) { pmp->state = PMP_STATE_FAILED; tr_dbg( "nat-pmp state del -> fail on req init" ); } } if( PMP_STATE_DELETING == pmp->state ) { switch( pulsereq( pmp ) ) { case TR_NET_ERROR: if( pmp->req->nobodyhome ) { pmp->mapped = 0; pmp->state = PMP_STATE_NOBODYHOME; tr_dbg( "nat-pmp state del -> nobodyhome on pulse" ); } else if( pmp->req->tmpfail ) { pmp->mapped = 0; pmp->state = PMP_STATE_TMPFAIL; tr_dbg( "nat-pmp state del -> err on pulse" ); pmp->mappedport = -1; } else { pmp->state = PMP_STATE_FAILED; tr_dbg( "nat-pmp state del -> fail on pulse" ); } killreq( &pmp->req ); break; case TR_NET_OK: tr_dbg( "nat-pmp state del -> idle with port %i", pmp->req->askport); tr_inf( "nat-pmp unmapped port %i", pmp->req->askport ); pmp->mapped = 0; pmp->mappedport = -1; killreq( &pmp->req ); pmp->state = PMP_STATE_IDLE; break; case TR_NET_WAIT: break; } } break; case PMP_STATE_MAPPED: if( pmp->newport != pmp->mappedport ) { tr_dbg( "nat-pmp state mapped -> del, port from %i to %i", pmp->mappedport, pmp->newport ); pmp->state = PMP_STATE_DELETING; } else if( tr_date() > pmp->renew ) { pmp->state = PMP_STATE_ADDING; tr_dbg( "nat-pmp state mapped -> add for renewal" ); } break; case PMP_STATE_FAILED: case PMP_STATE_NOBODYHOME: break; default: assert( 0 ); break; } } }
static void loadBlocklists( tr_session * session ) { int binCount = 0; int newCount = 0; struct stat sb; char * dirname; DIR * odir = NULL; tr_list * list = NULL; const tr_bool isEnabled = session->isBlocklistEnabled; /* walk through the directory and find blocklists */ dirname = tr_buildPath( session->configDir, "blocklists", NULL ); if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) ) { struct dirent *d; for( d = readdir( odir ); d; d = readdir( odir ) ) { char * filename; if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and .. */ continue; filename = tr_buildPath( dirname, d->d_name, NULL ); if( tr_stringEndsWith( filename, ".bin" ) ) { /* if we don't already have this blocklist, add it */ if( !tr_list_find( list, filename, (TrListCompareFunc)strcmp ) ) { tr_list_append( &list, _tr_blocklistNew( filename, isEnabled ) ); ++binCount; } } else { /* strip out the file suffix, if there is one, and add ".bin" instead */ tr_blocklist * b; const char * dot = strrchr( d->d_name, '.' ); const int len = dot ? dot - d->d_name : (int)strlen( d->d_name ); char * tmp = tr_strdup_printf( "%s" TR_PATH_DELIMITER_STR "%*.*s.bin", dirname, len, len, d->d_name ); b = _tr_blocklistNew( tmp, isEnabled ); _tr_blocklistSetContent( b, filename ); tr_list_append( &list, b ); ++newCount; tr_free( tmp ); } tr_free( filename ); } closedir( odir ); } session->blocklists = list; if( binCount ) tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname ); if( newCount ) tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname ); tr_free( dirname ); }
void tr_threadJoin( tr_thread_t * t ) { tr_dbg( "Thread '%s' joined", t->name ); tr_free( t->name ); }
static int devicePulseHttp( tr_upnp_device_t * dev, tr_fd_t * fdlimit, const char ** body, int * len ) { const char * headers; int hlen, code; if( NULL == dev->http ) { if( tr_date() < dev->lastrequest + HTTP_REQUEST_INTERVAL ) { return -1; } dev->lastrequest = tr_date(); dev->http = devicePulseGetHttp( dev, fdlimit ); if( NULL == dev->http ) { tr_dbg( "upnp device %s: http init failed, state %hhu -> error", dev->host, dev->state ); dev->state = UPNPDEV_STATE_ERROR; dev->soapretry = 0; return -1; } } if( NULL == dev->myaddr ) { dev->myaddr = tr_httpWhatsMyAddress( dev->http ); } switch( tr_httpPulse( dev->http, &headers, &hlen ) ) { case TR_OK: code = tr_httpResponseCode( headers, hlen ); if( SOAP_METHOD_NOT_ALLOWED == code && !dev->soapretry ) { dev->soapretry = 1; killHttp( fdlimit, &dev->http ); break; } dev->soapretry = 0; *body = tr_httpParse( headers, hlen, NULL ); *len = ( NULL == body ? 0 : hlen - ( *body - headers ) ); return code; case TR_ERROR: killHttp( fdlimit, &dev->http ); if( dev->soapretry ) { tr_dbg( "upnp device %s: http pulse failed, state %hhu -> error", dev->host, dev->state ); dev->state = UPNPDEV_STATE_ERROR; dev->soapretry = 0; } else { dev->soapretry = 1; } break; case TR_WAIT: break; } return -1; }
static tr_tristate_t parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse ) { int version, respopcode, opcode, wantedopcode, rescode, privport; memset( parse, 0, sizeof( *parse ) ); if( 8 > len ) { tr_err( "read truncated %i byte nat-pmp response packet", len ); return TR_NET_ERROR; } /* parse the first 8 bytes: version, opcode, and result code */ version = buf[0]; respopcode = buf[1]; opcode = PMP_OPCODE_FROM_RESPONSE( respopcode ); wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP ); rescode = PMP_FROMBUF16( buf + 2 ); if( PMP_VERSION != version ) { tr_err( "unknown nat-pmp version %hhu", buf[0] ); return TR_NET_ERROR; } if( !PMP_OPCODE_IS_RESPONSE( respopcode ) ) { tr_dbg( "nat-pmp ignoring request packet" ); return TR_NET_WAIT; } if( wantedopcode != opcode ) { tr_err( "unknown nat-pmp opcode %hhu", opcode ); return TR_NET_ERROR; } switch( rescode ) { case PMP_RESULT_OK: break; case PMP_RESULT_REFUSED: tr_err( "nat-pmp mapping failed: refused/unauthorized/disabled" ); parse->tmpfail = 1; return TR_NET_ERROR; case PMP_RESULT_NETDOWN: tr_err( "nat-pmp mapping failed: network down" ); parse->tmpfail = 1; return TR_NET_ERROR; case PMP_RESULT_NOMEM: tr_err( "nat-pmp mapping refused: insufficient resources" ); parse->tmpfail = 1; return TR_NET_ERROR; default: tr_err( "nat-pmp mapping refused: unknown result code: %hu", rescode ); return TR_NET_ERROR; } parse->seconds = PMP_FROMBUF32( buf + 4 ); if( PMP_OPCODE_ADDTCP == opcode ) { if( 16 > len ) { tr_err( "read truncated %i byte nat-pmp response packet", len ); return TR_NET_ERROR; } privport = PMP_FROMBUF16( buf + 8 ); parse->port = PMP_FROMBUF16( buf + 10 ); parse->lifetime = PMP_FROMBUF32( buf + 12 ); if( port != privport ) { tr_dbg( "nat-pmp ignoring message for port %i, expected port %i", privport, port ); return TR_NET_WAIT; } } return TR_NET_OK; }
static tr_tristate_t pulsereq( tr_natpmp_t * pmp ) { tr_natpmp_req_t * req = pmp->req; struct sockaddr_in sin; uint8_t buf[16]; int res; uint64_t now; tr_tristate_t ret; tr_natpmp_parse_t parse; now = tr_date(); /* check for timeout */ if( now >= req->timeout ) { tr_dbg( "nat-pmp request timed out" ); req->nobodyhome = 1; return TR_NET_ERROR; } /* send another request if it's been long enough */ if( now >= req->retry && sendreq( req ) ) { return TR_NET_ERROR; } /* check for incoming packets */ res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin ); if( TR_NET_BLOCK & res ) { return TR_NET_WAIT; } else if( TR_NET_CLOSE & res ) { if( ECONNRESET == errno || ECONNREFUSED == errno ) { tr_dbg( "nat-pmp not supported by device" ); req->nobodyhome = 1; } else { tr_inf( "error reading nat-pmp response (%s)", strerror( errno ) ); } return TR_NET_ERROR; } /* parse the packet */ tr_dbg( "nat-pmp read %i byte response", res ); ret = parseresponse( buf, res, req->askport, &parse ); req->tmpfail = parse.tmpfail; /* check for device reset */ if( checktime( &pmp->uptime, parse.seconds ) ) { pmp->renew = 0; tr_inf( "detected nat-pmp device reset" ); resetreq( req ); ret = TR_NET_WAIT; } if( TR_NET_OK == ret && req->adding ) { if( req->askport != parse.port ) { tr_dbg( "nat-pmp received %i for public port instead of %i", parse.port, req->askport ); req->gotport = parse.port; } tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime ); pmp->renew = now + ( parse.lifetime / 2 * 1000 ); } return ret; }
/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp () and open (). */ static int cached_file_open (struct tr_cached_file * o, const char * filename, bool writable, tr_preallocation_mode allocation, uint64_t file_size) { int flags; struct stat sb; bool alreadyExisted; /* create subfolders, if any */ if (writable) { char * dir = tr_dirname (filename); const int err = tr_mkdirp (dir, 0777) ? errno : 0; if (err) { tr_err (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err)); tr_free (dir); return err; } tr_free (dir); } alreadyExisted = !stat (filename, &sb) && S_ISREG (sb.st_mode); if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_FULL)) if (preallocate_file_full (filename, file_size)) tr_dbg ("Preallocated file \"%s\"", filename); /* open the file */ flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY; flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL; o->fd = open (filename, flags, 0666); if (o->fd == -1) { const int err = errno; tr_err (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err)); return err; } /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. * http://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if (alreadyExisted && (file_size < (uint64_t)sb.st_size)) { if (ftruncate (o->fd, file_size) == -1) { const int err = errno; tr_err (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err)); return err; } } if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_SPARSE)) preallocate_file_sparse (o->fd, file_size); /* Many (most?) clients request blocks in ascending order, * so increase the readahead buffer. * Also, disable OS-level caching because "inactive memory" angers users. */ tr_set_file_for_single_pass (o->fd); return 0; }
/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp() and open(). */ static int TrOpenFile( tr_session * session, int i, const char * filename, tr_bool doWrite, tr_preallocation_mode preallocationMode, uint64_t desiredFileSize ) { int flags; struct stat sb; tr_bool alreadyExisted; struct tr_openfile * file; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); file = &session->fdInfo->openFiles[i]; /* create subfolders, if any */ if( doWrite ) { char * dir = tr_dirname( filename ); const int err = tr_mkdirp( dir, 0777 ) ? errno : 0; if( err ) { tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) ); tr_free( dir ); return err; } tr_free( dir ); } alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_FULL ) ) if( preallocateFileFull( filename, desiredFileSize ) ) tr_dbg( _( "Preallocated file \"%s\"" ), filename ); /* open the file */ flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY; #ifdef O_SEQUENTIAL flags |= O_SEQUENTIAL; #endif #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif #ifdef WIN32 flags |= O_BINARY; #endif file->fd = open( filename, flags, 0666 ); if( file->fd == -1 ) { const int err = errno; tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) ); return err; } /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. * http://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if( alreadyExisted && ( desiredFileSize < (uint64_t)sb.st_size ) ) ftruncate( file->fd, desiredFileSize ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_SPARSE ) ) preallocateFileSparse( file->fd, desiredFileSize ); #ifdef HAVE_POSIX_FADVISE /* this doubles the OS level readahead buffer, which in practice * turns out to be a good thing, because many (most?) clients request * chunks of blocks in order. * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; posix_fadvise( file->fd, 0, 0, POSIX_FADV_SEQUENTIAL ); errno = err; } #endif #if defined( SYS_DARWIN ) /** * 1. Enable readahead for reasons described above w/POSIX_FADV_SEQUENTIAL. * * 2. Disable OS-level caching due to user reports of adverse effects of * excessive inactive memory. However this is experimental because * previous attempts at this have *also* had adverse effects (see r8198) * * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; fcntl( file->fd, F_NOCACHE, 1 ); fcntl( file->fd, F_RDAHEAD, 1 ); errno = err; } #endif return 0; }