static void three_digits( char * buf, size_t buflen, const char * name, const uint8_t * digits ) { tr_snprintf( buf, buflen, "%s %d.%d.%d", name, charint( digits[0] ), charint( digits[1] ), charint( digits[2] ) ); }
static char* tr_strlratio( char * buf, double ratio, size_t buflen ) { if( (int)ratio == TR_RATIO_NA ) tr_strlcpy( buf, _( "None" ), buflen ); else if( (int)ratio == TR_RATIO_INF ) tr_strlcpy( buf, "Inf", buflen ); else if( ratio < 10.0 ) tr_snprintf( buf, buflen, "%.2f", ratio ); else if( ratio < 100.0 ) tr_snprintf( buf, buflen, "%.1f", ratio ); else tr_snprintf( buf, buflen, "%.0f", ratio ); return buf; }
static void saveRealFunc (const tr_variant * val, void * evbuf) { int len; char buf[128]; len = tr_snprintf (buf, sizeof (buf), "%f", val->val.d); evbuffer_add_printf (evbuf, "%d:", len); evbuffer_add (evbuf, buf, len); }
/* * P2P plaintext format: "comment:x.x.x.x-y.y.y.y" * http://wiki.phoenixlabs.org/wiki/P2P_Format * http://en.wikipedia.org/wiki/PeerGuardian#P2P_plaintext_format */ static bool parseLine1(char const* line, struct tr_ipv4_range* range) { char* walk; int b[4]; int e[4]; char str[64]; tr_address addr; walk = strrchr(line, ':'); if (walk == NULL) { return false; } ++walk; /* walk past the colon */ if (sscanf(walk, "%d.%d.%d.%d-%d.%d.%d.%d", &b[0], &b[1], &b[2], &b[3], &e[0], &e[1], &e[2], &e[3]) != 8) { return false; } tr_snprintf(str, sizeof(str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); if (!tr_address_from_string(&addr, str)) { return false; } range->begin = ntohl(addr.addr.addr4.s_addr); tr_snprintf(str, sizeof(str), "%d.%d.%d.%d", e[0], e[1], e[2], e[3]); if (!tr_address_from_string(&addr, str)) { return false; } range->end = ntohl(addr.addr.addr4.s_addr); return true; }
static void escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */ { const uint8_t *end = in + in_len; while( in != end ) if( is_rfc2396_alnum(*in) ) *out++ = (char) *in++; else out += tr_snprintf( out, 4, "%%%02X", (unsigned int)*in++ ); *out = '\0'; }
static int decodeBitSpiritClient( char * buf, size_t buflen, const uint8_t * id ) { const int isBS = !memcmp( id+2, "BS", 2 ); if( isBS ) { const int version = id[1] ? id[1] : 1; tr_snprintf( buf, buflen, "BitSpirit v%d", version ); } return isBS; }
static void tr_upnpDeletePortMapping (const tr_upnp * handle, const char * proto, tr_port port) { char portStr[16]; tr_snprintf (portStr, sizeof (portStr), "%d", (int)port); UPNP_DeletePortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, NULL); }
char *tr_strlpercent(char *buf, double x, size_t buflen) { int precision; if (x < 10.0) precision = 2; else if (x < 100.0) precision = 1; else precision = 0; tr_snprintf(buf, buflen, "%.*f%%", precision, tr_truncd(x, precision)); return buf; }
static int tr_upnpGetSpecificPortMappingEntry (tr_upnp * handle, const char * proto) { int err; char intClient[16]; char intPort[16]; char portStr[16]; *intClient = '\0'; *intPort = '\0'; tr_snprintf (portStr, sizeof(portStr), "%d", (int)handle->port); #if (MINIUPNPC_API_VERSION >= 10) /* adds remoteHost arg */ err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, NULL /*remoteHost*/, intClient, intPort, NULL /*desc*/, NULL /*enabled*/, NULL /*duration*/); #elif (MINIUPNPC_API_VERSION >= 8) /* adds desc, enabled and leaseDuration args */ err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort, NULL /*desc*/, NULL /*enabled*/, NULL /*duration*/); #else err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort); #endif return err; }
void tr_http_escape_sha1( char * out, const uint8_t * sha1_digest ) { const uint8_t * in = sha1_digest; const uint8_t * end = in + SHA_DIGEST_LENGTH; while( in != end ) if( is_rfc2396_alnum( *in ) ) *out++ = (char) *in++; else out += tr_snprintf( out, 4, "%%%02x", (unsigned int)*in++ ); *out = '\0'; }
char* tr_getLogTimeStr( char * buf, int buflen ) { char tmp[64]; struct tm now_tm; struct timeval tv; time_t seconds; int milliseconds; gettimeofday( &tv, NULL ); seconds = tv.tv_sec; tr_localtime_r( &seconds, &now_tm ); strftime( tmp, sizeof( tmp ), "%H:%M:%S", &now_tm ); milliseconds = (int)( tv.tv_usec / 1000 ); tr_snprintf( buf, buflen, "%s.%03d", tmp, milliseconds ); return buf; }
char* tr_logGetTimeStr (char * buf, size_t buflen) { char tmp[64]; struct tm now_tm; struct timeval tv; time_t seconds; int milliseconds; tr_gettimeofday (&tv); seconds = tv.tv_sec; tr_localtime_r (&seconds, &now_tm); strftime (tmp, sizeof (tmp), "%Y-%m-%d %H:%M:%S.%%03d", &now_tm); milliseconds = tv.tv_usec / 1000; tr_snprintf (buf, buflen, tmp, milliseconds); return buf; }
static int test_truncd (void) { char buf[32]; const double nan = sqrt (-1); tr_snprintf (buf, sizeof (buf), "%.2f%%", 99.999); check_streq ("100.00%", buf); tr_snprintf (buf, sizeof (buf), "%.2f%%", tr_truncd (99.999, 2)); check_streq ("99.99%", buf); tr_snprintf (buf, sizeof (buf), "%.4f", tr_truncd (403650.656250, 4)); check_streq ("403650.6562", buf); tr_snprintf (buf, sizeof (buf), "%.2f", tr_truncd (2.15, 2)); check_streq ("2.15", buf); tr_snprintf (buf, sizeof (buf), "%.2f", tr_truncd (2.05, 2)); check_streq ("2.05", buf); tr_snprintf (buf, sizeof (buf), "%.2f", tr_truncd (3.3333, 2)); check_streq ("3.33", buf); tr_snprintf (buf, sizeof (buf), "%.0f", tr_truncd (3.3333, 0)); check_streq ("3", buf); #if !(defined (_MSC_VER) || (defined (__MINGW32__) && defined (__MSVCRT__))) /* FIXME: MSCVRT behaves differently in case of nan */ tr_snprintf (buf, sizeof (buf), "%.2f", tr_truncd (nan, 2)); check (strstr (buf, "nan") != NULL); #else (void) nan; #endif return 0; }
/** * @brief Return the value of a named parameter * * @param[in] str Input string of "\r\nName: Value" pairs without HTTP-style method part * @param[in] name Name of parameter to extract * @param[in] n Maximum available storage for value to return * @param[out] val Output parameter for the actual value * @return Returns 1 if value could be copied successfully * * Extracts the associated value of a named parameter from a HTTP-style header by * performing the following steps: * - assemble search string "\r\nName: " and locate position * - copy back value from end to next "\r\n" */ static int lpd_extractParam (const char* const str, const char* const name, int n, char* const val) { /* configure maximum length of search string here */ enum { maxLength = 30 }; char sstr[maxLength] = { }; const char* pos; assert (str != NULL && name != NULL); assert (val != NULL); if (strlen (name) > maxLength - strlen (CRLF ": ")) return 0; /* compose the string token to search for */ tr_snprintf (sstr, maxLength, CRLF "%s: ", name); pos = strstr (str, sstr); if (pos == NULL) return 0; /* search was not successful */ { const char* const beg = pos + strlen (sstr); const char* const new_line = strstr (beg, CRLF); /* the value is delimited by the next CRLF */ int len = new_line - beg; /* if value string hits the length limit n, * leave space for a trailing '\0' character */ if (len < n--) n = len; strncpy (val, beg, n); val[n] = 0; } /* we successfully returned the value string */ return 1; }
static int tr_upnpAddPortMapping (const tr_upnp * handle, const char * proto, tr_port port, const char * desc) { int err; const int old_errno = errno; char portStr[16]; errno = 0; tr_snprintf (portStr, sizeof (portStr), "%d", (int)port); #if (MINIUPNPC_API_VERSION >= 8) err = UPNP_AddPortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL, NULL); #else err = UPNP_AddPortMapping (handle->urls.controlURL, handle->data.first.servicetype, portStr, portStr, handle->lanaddr, desc, proto, NULL); #endif if (err) tr_logAddNamedDbg (getKey (), "%s Port forwarding failed with error %d (errno %d - %s)", proto, err, errno, tr_strerror (errno)); errno = old_errno; return err; }
static int tr_upnpGetSpecificPortMappingEntry (tr_upnp * handle, const char * proto) { int err; char intClient[16]; char intPort[16]; char portStr[16]; *intClient = '\0'; *intPort = '\0'; tr_snprintf (portStr, sizeof (portStr), "%d", (int)handle->port); #if defined (HAVE_MINIUPNP_16) err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort, NULL, NULL, NULL); #elif defined (HAVE_MINIUPNP_15) err = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, handle->data.first.servicetype, portStr, proto, intClient, intPort); #else err = UPNPCOMMAND_UNKNOWN_ERROR; #endif return err; }
static int callback( void * vdata, int type, const JSON_value * value ) { struct json_benc_data * data = vdata; tr_benc * node; switch( type ) { case JSON_T_ARRAY_BEGIN: node = getNode( data ); tr_bencInitList( node, 0 ); tr_ptrArrayAppend( data->stack, node ); break; case JSON_T_ARRAY_END: tr_ptrArrayPop( data->stack ); break; case JSON_T_OBJECT_BEGIN: node = getNode( data ); tr_bencInitDict( node, 0 ); tr_ptrArrayAppend( data->stack, node ); break; case JSON_T_OBJECT_END: tr_ptrArrayPop( data->stack ); break; case JSON_T_FLOAT: { char buf[128]; tr_snprintf( buf, sizeof( buf ), "%f", (double)value->vu.float_value ); tr_bencInitStr( getNode( data ), buf, -1 ); break; } case JSON_T_NULL: tr_bencInitStr( getNode( data ), "", 0 ); break; case JSON_T_INTEGER: tr_bencInitInt( getNode( data ), value->vu.integer_value ); break; case JSON_T_TRUE: tr_bencInitInt( getNode( data ), 1 ); break; case JSON_T_FALSE: tr_bencInitInt( getNode( data ), 0 ); break; case JSON_T_STRING: tr_bencInitStr( getNode( data ), value->vu.str.value, value->vu.str.length ); break; case JSON_T_KEY: assert( !data->key ); data->key = tr_strdup( value->vu.str.value ); break; } return 1; }
static int test_single_directory_impl (const tr_tracker_info * trackers, const size_t trackerCount, const void ** payloads, const size_t * payloadSizes, const size_t payloadCount, const char * comment, const bool isPrivate) { char* sandbox; char* torrent_file; tr_metainfo_builder* builder; tr_ctor * ctor; tr_parse_result parse_result; tr_info inf; char * top; char ** files; size_t totalSize; size_t i; char* tmpstr; /* set up our local test sandbox */ sandbox = libtest_sandbox_create(); /* create the top temp directory */ top = tr_buildPath (sandbox, "folder.XXXXXX", NULL); tr_sys_dir_create_temp (top, NULL); /* build the payload files that go into the top temp directory */ files = tr_new (char*, payloadCount); totalSize = 0; for (i=0; i<payloadCount; i++) { char tmpl[16]; tr_snprintf (tmpl, sizeof(tmpl), "file.%04zu%s", i, "XXXXXX"); files[i] = tr_buildPath (top, tmpl, NULL); libtest_create_tmpfile_with_contents (files[i], payloads[i], payloadSizes[i]); totalSize += payloadSizes[i]; } libttest_sync (); /* init the builder */ builder = tr_metaInfoBuilderCreate (top); check (!builder->abortFlag); check_streq (top, builder->top); check_int_eq (payloadCount, builder->fileCount); check_int_eq (totalSize, builder->totalSize); check (builder->isFolder); for (i=0; i<builder->fileCount; i++) { check_streq (files[i], builder->files[i].filename); check_int_eq (payloadSizes[i], builder->files[i].size); } /* call tr_makeMetaInfo() to build the .torrent file */ torrent_file = tr_strdup_printf ("%s.torrent", top); tr_makeMetaInfo (builder, torrent_file, trackers, trackerCount, comment, isPrivate); check (isPrivate == builder->isPrivate); check_streq (torrent_file, builder->outputFile); check_streq (comment, builder->comment); check_int_eq (trackerCount, builder->trackerCount); while (!builder->isDone) tr_wait_msec (100); /* now let's check our work: parse the .torrent file */ ctor = tr_ctorNew (NULL); libttest_sync (); tr_ctorSetMetainfoFromFile (ctor, torrent_file); parse_result = tr_torrentParse (ctor, &inf); check_int_eq (TR_PARSE_OK, parse_result); /* quick check of some of the parsed metainfo */ check_int_eq (totalSize, inf.totalSize); tmpstr = tr_sys_path_basename (top, NULL); check_streq (tmpstr, inf.name); tr_free (tmpstr); check_streq (comment, inf.comment); check_int_eq (payloadCount, inf.fileCount); check_int_eq (isPrivate, inf.isPrivate); check_int_eq (builder->isFolder, inf.isFolder); check_int_eq (trackerCount, inf.trackerCount); /* cleanup */ tr_free (torrent_file); tr_ctorFree (ctor); tr_metainfoFree (&inf); tr_metaInfoBuilderFree (builder); for (i=0; i<payloadCount; i++) tr_free (files[i]); tr_free (files); libtest_sandbox_destroy (sandbox); tr_free (sandbox); tr_free (top); return 0; }
static int testParse( void ) { tr_benc val; tr_benc * child; tr_benc * child2; uint8_t buf[512]; const uint8_t * end; int err; int len; int64_t i; char * saved; tr_snprintf( (char*)buf, sizeof( buf ), "i64e" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( tr_bencGetInt( &val, &i ) ); check( i == 64 ); check( end == buf + 4 ); tr_bencFree( &val ); tr_snprintf( (char*)buf, sizeof( buf ), "li64ei32ei16ee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + strlen( (char*)buf ) ); check( val.val.l.count == 3 ); check( tr_bencGetInt( &val.val.l.vals[0], &i ) ); check( i == 64 ); check( tr_bencGetInt( &val.val.l.vals[1], &i ) ); check( i == 32 ); check( tr_bencGetInt( &val.val.l.vals[2], &i ) ); check( i == 16 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, (char*)buf ) ); tr_free( saved ); tr_bencFree( &val ); end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "lllee" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); check( end == NULL ); end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "le" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + 2 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "le" ) ); tr_free( saved ); tr_bencFree( &val ); if( ( err = testString( "llleee", TRUE ) ) ) return err; if( ( err = testString( "d3:cow3:moo4:spam4:eggse", TRUE ) ) ) return err; if( ( err = testString( "d4:spaml1:a1:bee", TRUE ) ) ) return err; if( ( err = testString( "d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", TRUE ) ) ) return err; if( ( err = testString( "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", TRUE ) ) ) return err; if( ( err = testString( "d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", TRUE ) ) ) return err; if( ( err = testString( "d1:ai0e1:be", FALSE ) ) ) /* odd number of children */ return err; if( ( err = testString( "", FALSE ) ) ) return err; if( ( err = testString( " ", FALSE ) ) ) return err; /* nested containers * parse an unsorted dict * save as a sorted dict */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "lld1:bi32e1:ai64eeee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + strlen( (const char*)buf ) ); check( ( child = tr_bencListChild( &val, 0 ) ) ); check( ( child2 = tr_bencListChild( child, 0 ) ) ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "lld1:ai64e1:bi32eeee" ) ); tr_free( saved ); tr_bencFree( &val ); /* too many endings */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "leee" ); err = tr_bencParse( buf, buf + sizeof( buf ), &val, &end ); check( !err ); check( end == buf + 2 ); saved = tr_bencSave( &val, &len ); check( !strcmp( saved, "le" ) ); tr_free( saved ); tr_bencFree( &val ); /* no ending */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "l1:a1:b1:c" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); /* incomplete string */ end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "1:" ); err = tr_bencParse( buf, buf + strlen( (char*)buf ), &val, &end ); check( err ); return 0; }
void tr_clientForId( char * buf, size_t buflen, const void * id_in ) { const uint8_t * id = id_in; *buf = '\0'; if( !id ) return; /* Azureus-style */ if( id[0] == '-' && id[7] == '-' ) { if( !memcmp( id+1, "UT", 2 ) ) { tr_snprintf( buf, buflen, "\xc2\xb5Torrent %d.%d.%d%s", strint(id+3,1), strint(id+4,1), strint(id+5,1), getMnemonicEnd(id[6]) ); } if( !memcmp( id+1, "UM", 2 ) ) { tr_snprintf( buf, buflen, "\xc2\xb5Torrent Mac %d.%d.%d%s", strint(id+3,1), strint(id+4,1), strint(id+5,1), getMnemonicEnd(id[6]) ); } else if( !memcmp( id+1, "TR", 2 ) ) { if( !memcmp( id+3, "000", 3 ) ) /* very old client style: -TR0006- is 0.6 */ tr_snprintf( buf, buflen, "Transmission 0.%c", id[6] ); else if( !memcmp( id+3, "00", 2) ) /* previous client style: -TR0072- is 0.72 */ tr_snprintf( buf, buflen, "Transmission 0.%02d", strint(id+5,2) ); else /* current client style: -TR111Z- is 1.11+ */ tr_snprintf( buf, buflen, "Transmission %d.%02d%s", strint(id+3,1), strint(id+4,2), id[6]=='Z' || id[6]=='X' ? "+" : "" ); } else if( !memcmp( id+1, "AZ", 2 ) ) { if( id[3] > '3' || ( id[3] == '3' && id[4] >= '1' ) ) /* Vuze starts at version 3.1.0.0 */ four_digits( buf, buflen, "Vuze", id+3 ); else four_digits( buf, buflen, "Azureus", id+3 ); } else if( !memcmp( id+1, "KT", 2 ) ) { if( id[5] == 'D' ) tr_snprintf( buf, buflen, "KTorrent %d.%d Dev %d", charint(id[3]), charint(id[4]), charint(id[6]) ); else if( id[5] == 'R' ) tr_snprintf( buf, buflen, "KTorrent %d.%d RC %d", charint(id[3]), charint(id[4]), charint(id[6]) ); else three_digits( buf, buflen, "KTorrent", id+3 ); } else if( !memcmp( id+1, "AR", 2 ) ) four_digits( buf, buflen, "Ares", id+3 ); else if( !memcmp( id+1, "AT", 2 ) ) four_digits( buf, buflen, "Artemis", id+3 ); else if( !memcmp( id+1, "AV", 2 ) ) four_digits( buf, buflen, "Avicora", id+3 ); else if( !memcmp( id+1, "BG", 2 ) ) four_digits( buf, buflen, "BTGetit", id+3 ); else if( !memcmp( id+1, "BM", 2 ) ) four_digits( buf, buflen, "BitMagnet", id+3 ); else if( !memcmp( id+1, "BP", 2 ) ) four_digits( buf, buflen, "BitTorrent Pro (Azureus + Spyware)", id+3 ); else if( !memcmp( id+1, "BX", 2 ) ) four_digits( buf, buflen, "BittorrentX", id+3 ); else if( !memcmp( id+1, "bk", 2 ) ) four_digits( buf, buflen, "BitKitten (libtorrent)", id+3 ); else if( !memcmp( id+1, "BS", 2 ) ) four_digits( buf, buflen, "BTSlave", id+3 ); else if( !memcmp( id+1, "BW", 2 ) ) four_digits( buf, buflen, "BitWombat", id+3 ); else if( !memcmp( id+1, "BX", 2 ) ) four_digits( buf, buflen, "BittorrentX", id+3 ); else if( !memcmp( id+1, "EB", 2 ) ) four_digits( buf, buflen, "EBit", id+3 ); else if( !memcmp( id+1, "DE", 2 ) ) four_digits( buf, buflen, "Deluge", id+3 ); else if( !memcmp( id+1, "DP", 2 ) ) four_digits( buf, buflen, "Propogate Data Client", id+3 ); else if( !memcmp( id+1, "FC", 2 ) ) four_digits( buf, buflen, "FileCroc", id+3 ); else if( !memcmp( id+1, "FT", 2 ) ) four_digits( buf, buflen, "FoxTorrent/RedSwoosh", id+3 ); else if( !memcmp( id+1, "GR", 2 ) ) four_digits( buf, buflen, "GetRight", id+3 ); else if( !memcmp( id+1, "HN", 2 ) ) four_digits( buf, buflen, "Hydranode", id+3 ); else if( !memcmp( id+1, "LC", 2 ) ) four_digits( buf, buflen, "LeechCraft", id+3 ); else if( !memcmp( id+1, "LH", 2 ) ) four_digits( buf, buflen, "LH-ABC", id+3 ); else if( !memcmp( id+1, "NX", 2 ) ) four_digits( buf, buflen, "Net Transport", id+3 ); else if( !memcmp( id+1, "MO", 2 ) ) four_digits( buf, buflen, "MonoTorrent", id+3 ); else if( !memcmp( id+1, "MR", 2 ) ) four_digits( buf, buflen, "Miro", id+3 ); else if( !memcmp( id+1, "MT", 2 ) ) four_digits( buf, buflen, "Moonlight", id+3 ); else if( !memcmp( id+1, "OT", 2 ) ) four_digits( buf, buflen, "OmegaTorrent", id+3 ); else if( !memcmp( id+1, "PD", 2 ) ) four_digits( buf, buflen, "Pando", id+3 ); else if( !memcmp( id+1, "QD", 2 ) ) four_digits( buf, buflen, "QQDownload", id+3 ); else if( !memcmp( id+1, "RS", 2 ) ) four_digits( buf, buflen, "Rufus", id+3 ); else if( !memcmp( id+1, "RT", 2 ) ) four_digits( buf, buflen, "Retriever", id+3 ); else if( !memcmp( id+1, "SS", 2 ) ) four_digits( buf, buflen, "SwarmScope", id+3 ); else if( !memcmp( id+1, "SZ", 2 ) ) four_digits( buf, buflen, "Shareaza", id+3 ); else if( !memcmp( id+1, "S~", 2 ) ) four_digits( buf, buflen, "Shareaza", id+3 ); else if( !memcmp( id+1, "st", 2 ) ) four_digits( buf, buflen, "SharkTorrent", id+3 ); else if( !memcmp( id+1, "TN", 2 ) ) four_digits( buf, buflen, "Torrent .NET", id+3 ); else if( !memcmp( id+1, "TS", 2 ) ) four_digits( buf, buflen, "TorrentStorm", id+3 ); else if( !memcmp( id+1, "UL", 2 ) ) four_digits( buf, buflen, "uLeecher!", id+3 ); else if( !memcmp( id+1, "VG", 2 ) ) four_digits( buf, buflen, "Vagaa", id+3 ); else if( !memcmp( id+1, "WT", 2 ) ) four_digits( buf, buflen, "BitLet", id+3 ); else if( !memcmp( id+1, "WY", 2 ) ) four_digits( buf, buflen, "Wyzo", id+3 ); else if( !memcmp( id+1, "XL", 2 ) ) four_digits( buf, buflen, "Xunlei", id+3 ); else if( !memcmp( id+1, "XT", 2 ) ) four_digits( buf, buflen, "XanTorrent", id+3 ); else if( !memcmp( id+1, "ZT", 2 ) ) four_digits( buf, buflen, "Zip Torrent", id+3 ); else if( !memcmp( id+1, "AG", 2 ) ) three_digits( buf, buflen, "Ares", id+3 ); else if( !memcmp( id+1, "A~", 2 ) ) three_digits( buf, buflen, "Ares", id+3 ); else if( !memcmp( id+1, "ES", 2 ) ) three_digits( buf, buflen, "Electric Sheep", id+3 ); else if( !memcmp( id+1, "HL", 2 ) ) three_digits( buf, buflen, "Halite", id+3 ); else if( !memcmp( id+1, "LT", 2 ) ) three_digits( buf, buflen, "libtorrent (Rasterbar)", id+3 ); else if( !memcmp( id+1, "lt", 2 ) ) three_digits( buf, buflen, "libTorrent (Rakshasa)", id+3 ); else if( !memcmp( id+1, "MP", 2 ) ) three_digits( buf, buflen, "MooPolice", id+3 ); else if( !memcmp( id+1, "TT", 2 ) ) three_digits( buf, buflen, "TuoTu", id+3 ); else if( !memcmp( id+1, "qB", 2 ) ) three_digits( buf, buflen, "qBittorrent", id+3 ); else if( !memcmp( id+1, "AX", 2 ) ) two_major_two_minor( buf, buflen, "BitPump", id+3 ); else if( !memcmp( id+1, "BC", 2 ) ) two_major_two_minor( buf, buflen, "BitComet", id+3 ); else if( !memcmp( id+1, "CD", 2 ) ) two_major_two_minor( buf, buflen, "Enhanced CTorrent", id+3 ); else if( !memcmp( id+1, "LP", 2 ) ) two_major_two_minor( buf, buflen, "Lphant", id+3 ); else if( !memcmp( id+1, "BF", 2 ) ) no_version( buf, buflen, "BitFlu" ); else if( !memcmp( id+1, "LW", 2 ) ) no_version( buf, buflen, "LimeWire" ); else if( !memcmp( id+1, "BB", 2 ) ) { tr_snprintf( buf, buflen, "BitBuddy %c.%c%c%c", id[3], id[4], id[5], id[6] ); } else if( !memcmp( id+1, "BR", 2 ) ) { tr_snprintf( buf, buflen, "BitRocket %c.%c (%c%c)", id[3], id[4], id[5], id[6] ); } else if( !memcmp( id+1, "CT", 2 ) ) { tr_snprintf( buf, buflen, "CTorrent %d.%d.%02d", charint(id[3]), charint(id[4]), strint(id+5,2) ); } else if( !memcmp( id+1, "XX", 2 ) ) { tr_snprintf( buf, buflen, "Xtorrent %d.%d (%d)", charint(id[3]), charint(id[4]), strint(id+5,2) ); } else if( !memcmp( id+1, "BOW", 3 ) ) { if( !memcmp( &id[4], "A0B", 3 ) ) tr_snprintf( buf, buflen, "Bits on Wheels 1.0.5" ); else if( !memcmp( &id[4], "A0C", 3 ) ) tr_snprintf( buf, buflen, "Bits on Wheels 1.0.6" ); else tr_snprintf( buf, buflen, "Bits on Wheels %c.%c.%c", id[4], id[5], id[5] ); } if( *buf ) return; } /* Mainline */ if( isMainlineStyle( id ) ) { if( *id=='M' ) mainline_style( buf, buflen, "BitTorrent", id ); if( *id=='Q' ) mainline_style( buf, buflen, "Queen Bee", id ); if( *buf ) return; } if( decodeBitCometClient( buf, buflen, id ) ) return; if( decodeBitSpiritClient( buf, buflen, id ) ) return; /* Clients with no version */ if( !memcmp( id, "AZ2500BT", 8 ) ) no_version( buf, buflen, "BitTyrant (Azureus Mod)" ); else if( !memcmp( id, "LIME", 4 ) ) no_version( buf, buflen, "Limewire" ); else if( !memcmp( id, "martini", 7 ) ) no_version( buf, buflen, "Martini Man" ); else if( !memcmp( id, "Pando", 5 ) ) no_version( buf, buflen, "Pando" ); else if( !memcmp( id, "a00---0", 7 ) ) no_version( buf, buflen, "Swarmy" ); else if( !memcmp( id, "a02---0", 7 ) ) no_version( buf, buflen, "Swarmy" ); else if( !memcmp( id, "-G3", 3 ) ) no_version( buf, buflen, "G3 Torrent" ); else if( !memcmp( id, "10-------", 9 ) ) no_version( buf, buflen, "JVtorrent" ); else if( !memcmp( id, "346-", 4 ) ) no_version( buf, buflen, "TorrentTopia" ); else if( !memcmp( id, "eX", 2 ) ) no_version( buf, buflen, "eXeem" ); else if( !memcmp( id, "-FG", 3 ) ) two_major_two_minor( buf, buflen, "FlashGet", id+3 ); /* Everything else */ else if( !memcmp( id, "S3", 2 ) && id[2] == '-' && id[4] == '-' && id[6] == '-' ) { tr_snprintf( buf, buflen, "Amazon S3 %c.%c.%c", id[3], id[5], id[7] ); } else if( !memcmp( id, "OP", 2 ) ) { tr_snprintf( buf, buflen, "Opera (Build %c%c%c%c)", id[2], id[3], id[4], id[5] ); } else if( !memcmp( id, "-ML", 3 ) ) { tr_snprintf( buf, buflen, "MLDonkey %c%c%c%c%c", id[3], id[4], id[5], id[6], id[7] ); } else if( !memcmp( id, "DNA", 3 ) ) { tr_snprintf( buf, buflen, "BitTorrent DNA %d.%d.%d", strint(id+3,2), strint(id+5,2), strint(id+7,2) ); } else if( !memcmp( id, "Plus", 4 ) ) { tr_snprintf( buf, buflen, "Plus! v2 %c.%c%c", id[4], id[5], id[6] ); } else if( !memcmp( id, "XBT", 3 ) ) { tr_snprintf( buf, buflen, "XBT Client %c.%c.%c%s", id[3], id[4], id[5], getMnemonicEnd(id[6]) ); } else if( !memcmp( id, "Mbrst", 5 ) ) { tr_snprintf( buf, buflen, "burst! %c.%c.%c", id[5], id[7], id[9] ); } else if( !memcmp( id, "btpd", 4 ) ) { tr_snprintf( buf, buflen, "BT Protocol Daemon %c%c%c", id[5], id[6], id[7] ); } else if( !memcmp( id, "BLZ", 3 ) ) { tr_snprintf( buf, buflen, "Blizzard Downloader %d.%d", id[3]+1, id[4] ); } else if( '\0' == id[0] && !memcmp( &id[1], "BS", 2 ) ) { tr_snprintf( buf, buflen, "BitSpirit %u", ( id[1] == 0 ? 1 : id[1] ) ); } else if( !memcmp( id, "QVOD", 4 ) ) { four_digits( buf, buflen, "QVOD", id+4 ); } else if( !memcmp( id, "-NE", 3 ) ) { four_digits( buf, buflen, "BT Next Evolution", id+3 ); } /* Shad0w-style */ { int a, b, c; if( strchr( "AOQRSTU", id[0] ) && getShadowInt( id[1], &a ) && getShadowInt( id[2], &b ) && getShadowInt( id[3], &c ) ) { const char * name = NULL; switch( id[0] ) { case 'A': name = "ABC"; break; case 'O': name = "Osprey"; break; case 'Q': name = "BTQueue"; break; case 'R': name = "Tribler"; break; case 'S': name = "Shad0w"; break; case 'T': name = "BitTornado"; break; case 'U': name = "UPnP NAT Bit Torrent"; break; } if( name ) { tr_snprintf( buf, buflen, "%s %d.%d.%d", name, a, b, c ); return; } } } /* No match */ if( !*buf ) { struct evbuffer * out = evbuffer_new( ); const char *in, *in_end; for( in=(const char*)id, in_end=in+8; in!=in_end; ++in ) { if( isprint( *in ) ) evbuffer_add_printf( out, "%c", *in ); else evbuffer_add_printf( out, "%%%02X", (unsigned int)*in ); } tr_strlcpy( buf, EVBUFFER_DATA( out ), buflen ); evbuffer_free( out ); } }
static int testInt( void ) { uint8_t buf[128]; int64_t val; int err; const uint8_t * end; /* good int string */ tr_snprintf( (char*)buf, sizeof( buf ), "i64e" ); err = tr_bencParseInt( buf, buf + 4, &end, &val ); check( err == 0 ); check( val == 64 ); check( end == buf + 4 ); /* missing 'e' */ end = NULL; val = 888; err = tr_bencParseInt( buf, buf + 3, &end, &val ); check( err == EILSEQ ); check( val == 888 ); check( end == NULL ); /* empty buffer */ err = tr_bencParseInt( buf, buf + 0, &end, &val ); check( err == EILSEQ ); check( val == 888 ); check( end == NULL ); /* bad number */ tr_snprintf( (char*)buf, sizeof( buf ), "i6z4e" ); err = tr_bencParseInt( buf, buf + 5, &end, &val ); check( err == EILSEQ ); check( val == 888 ); check( end == NULL ); /* negative number */ tr_snprintf( (char*)buf, sizeof( buf ), "i-3e" ); err = tr_bencParseInt( buf, buf + 4, &end, &val ); check( err == 0 ); check( val == -3 ); check( end == buf + 4 ); /* zero */ tr_snprintf( (char*)buf, sizeof( buf ), "i0e" ); err = tr_bencParseInt( buf, buf + 4, &end, &val ); check( err == 0 ); check( val == 0 ); check( end == buf + 3 ); /* no leading zeroes allowed */ val = 0; end = NULL; tr_snprintf( (char*)buf, sizeof( buf ), "i04e" ); err = tr_bencParseInt( buf, buf + 4, &end, &val ); check( err == EILSEQ ); check( val == 0 ); check( end == NULL ); return 0; }
int main( int argc, char ** argv ) { int c; const char * optarg; tr_benc settings; tr_bool boolVal; tr_bool loaded; tr_bool foreground = FALSE; tr_bool dumpSettings = FALSE; const char * configDir = NULL; const char * pid_filename; dtr_watchdir * watchdir = NULL; FILE * logfile = NULL; tr_bool pidfile_created = FALSE; signal( SIGINT, gotsig ); signal( SIGTERM, gotsig ); #ifndef WIN32 signal( SIGHUP, gotsig ); #endif /* load settings from defaults + config file */ tr_bencInitDict( &settings, 0 ); tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, TRUE ); configDir = getConfigDir( argc, (const char**)argv ); loaded = tr_sessionLoadSettings( &settings, configDir, MY_NAME ); /* overwrite settings from the comamndline */ tr_optind = 1; while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) { switch( c ) { case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg ); tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, TRUE ); break; case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE ); break; case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE ); break; case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg ); tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, TRUE ); break; case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, FALSE ); break; case 941: tr_bencDictAddStr( &settings, TR_PREFS_KEY_INCOMPLETE_DIR, optarg ); tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE ); break; case 942: tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE ); break; case 'd': dumpSettings = TRUE; break; case 'e': logfile = fopen( optarg, "a+" ); if( logfile == NULL ) fprintf( stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror( errno ) ); break; case 'f': foreground = TRUE; break; case 'g': /* handled above */ break; case 'V': /* version */ fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING); exit( 0 ); case 'o': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, TRUE ); break; case 'O': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, FALSE ); break; case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) ); break; case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, TRUE ); break; case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE ); break; case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg ); break; case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg ); break; case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg ); break; case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) ); break; case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, TRUE ); break; case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, FALSE ); break; case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) ); break; case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) ); break; case 800: paused = TRUE; break; case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED ); break; case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED ); break; case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED ); break; case 'i': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg ); break; case 'I': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg ); break; case 'r': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg ); break; case 953: tr_bencDictAddReal( &settings, TR_PREFS_KEY_RATIO, atof(optarg) ); tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, TRUE ); break; case 954: tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, FALSE ); break; case 'x': tr_bencDictAddStr( &settings, PREF_KEY_PIDFILE, optarg ); break; case 'y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, TRUE ); break; case 'Y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, FALSE ); break; case 810: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_ERR ); break; case 811: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF ); break; case 812: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_DBG ); break; default: showUsage( ); break; } } if( foreground && !logfile ) logfile = stderr; if( !loaded ) { printMessage( logfile, TR_MSG_ERR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__ ); return -1; } if( dumpSettings ) { char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL ); fprintf( stderr, "%s", str ); tr_free( str ); return 0; } if( !foreground && tr_daemon( TRUE, FALSE ) < 0 ) { char buf[256]; tr_snprintf( buf, sizeof( buf ), "Failed to daemonize: %s", tr_strerror( errno ) ); printMessage( logfile, TR_MSG_ERR, MY_NAME, buf, __FILE__, __LINE__ ); exit( 1 ); } /* start the session */ tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR ); tr_formatter_size_init( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR ); tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR ); mySession = tr_sessionInit( "daemon", configDir, TRUE, &settings ); tr_ninf( NULL, "Using settings from \"%s\"", configDir ); tr_sessionSaveSettings( mySession, configDir, &settings ); pid_filename = NULL; tr_bencDictFindStr( &settings, PREF_KEY_PIDFILE, &pid_filename ); if( pid_filename && *pid_filename ) { FILE * fp = fopen( pid_filename, "w+" ); if( fp != NULL ) { fprintf( fp, "%d", (int)getpid() ); fclose( fp ); tr_inf( "Saved pidfile \"%s\"", pid_filename ); pidfile_created = TRUE; } else tr_err( "Unable to save pidfile \"%s\": %s", pid_filename, strerror( errno ) ); } if( tr_bencDictFindBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal ) && boolVal ) tr_ninf( MY_NAME, "requiring authentication" ); /* maybe add a watchdir */ { const char * dir; if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal ) && boolVal && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir ) && dir && *dir ) { tr_inf( "Watching \"%s\" for new .torrent files", dir ); watchdir = dtr_watchdir_new( mySession, dir, onFileAdded ); } } /* load the torrents */ { tr_torrent ** torrents; tr_ctor * ctor = tr_ctorNew( mySession ); if( paused ) tr_ctorSetPaused( ctor, TR_FORCE, TRUE ); torrents = tr_sessionLoadTorrents( mySession, ctor, NULL ); tr_free( torrents ); tr_ctorFree( ctor ); } #ifdef HAVE_SYSLOG if( !foreground ) openlog( MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON ); #endif while( !closing ) { tr_wait_msec( 1000 ); /* sleep one second */ dtr_watchdir_update( watchdir ); pumpLogMessages( logfile ); } /* shutdown */ #if HAVE_SYSLOG if( !foreground ) { syslog( LOG_INFO, "%s", "Closing session" ); closelog( ); } #endif printf( "Closing transmission session..." ); tr_sessionSaveSettings( mySession, configDir, &settings ); dtr_watchdir_free( watchdir ); tr_sessionClose( mySession ); printf( " done.\n" ); /* cleanup */ if( pidfile_created ) remove( pid_filename ); tr_bencFree( &settings ); return 0; }
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; }