void tr_dhtUninit (tr_session *ss) { if (session != ss) return; tr_logAddNamedDbg ("DHT", "Uninitializing DHT"); if (dht_timer != NULL) { event_free (dht_timer); dht_timer = NULL; } /* Since we only save known good nodes, avoid erasing older data if we don't know enough nodes. */ if ((tr_dhtStatus (ss, AF_INET, NULL) < TR_DHT_FIREWALLED) && (tr_dhtStatus (ss, AF_INET6, NULL) < TR_DHT_FIREWALLED)) { tr_logAddNamedInfo ("DHT", "Not saving nodes, DHT not ready"); } else { tr_variant benc; struct sockaddr_in sins[300]; struct sockaddr_in6 sins6[300]; char compact[300 * 6], compact6[300 * 18]; char *dat_file; int i, j, num = 300, num6 = 300; int n = dht_get_nodes (sins, &num, sins6, &num6); tr_logAddNamedInfo ("DHT", "Saving %d (%d + %d) nodes", n, num, num6); j = 0; for (i=0; i<num; ++i) { memcpy (compact + j, &sins[i].sin_addr, 4); memcpy (compact + j + 4, &sins[i].sin_port, 2); j += 6; } j = 0; for (i=0; i<num6; ++i) { memcpy (compact6 + j, &sins6[i].sin6_addr, 16); memcpy (compact6 + j + 16, &sins6[i].sin6_port, 2); j += 18; } tr_variantInitDict (&benc, 3); tr_variantDictAddRaw (&benc, TR_KEY_id, myid, 20); if (num > 0) tr_variantDictAddRaw (&benc, TR_KEY_nodes, compact, num * 6); if (num6 > 0) tr_variantDictAddRaw (&benc, TR_KEY_nodes6, compact6, num6 * 18); dat_file = tr_buildPath (ss->configDir, "dht.dat", NULL); tr_variantToFile (&benc, TR_VARIANT_FMT_BENC, dat_file); tr_variantFree (&benc); tr_free (dat_file); } dht_uninit (); tr_logAddNamedDbg ("DHT", "Done uninitializing DHT"); session = NULL; }
static void event_callback (int s, short type UNUSED, void *sv) { int rc; socklen_t fromlen; unsigned char buf[4096]; struct sockaddr_storage from; tr_session *ss = sv; assert (tr_isSession (sv)); assert (type == EV_READ); fromlen = sizeof (from); rc = recvfrom (s, buf, 4096 - 1, 0, (struct sockaddr*)&from, &fromlen); /* Since most packets we receive here are µTP, make quick inline checks for the other protocols. The logic is as follows: - all DHT packets start with 'd'; - all UDP tracker packets start with a 32-bit (!) "action", which is between 0 and 3; - the above cannot be µTP packets, since these start with a 4-bit version number (1). */ if (rc > 0) { if (buf[0] == 'd') { if (tr_sessionAllowsDHT (ss)) { buf[rc] = '\0'; /* required by the DHT code */ tr_dhtCallback (buf, rc, (struct sockaddr*)&from, fromlen, sv); } } else if (rc >= 8 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3) { rc = tau_handle_message (ss, buf, rc); if (!rc) tr_logAddNamedDbg ("UDP", "Couldn't parse UDP tracker packet."); } else { if (tr_sessionIsUTPEnabled (ss)) { rc = tr_utpPacket (buf, rc, (struct sockaddr*)&from, fromlen, ss); if (!rc) tr_logAddNamedDbg ("UDP", "Unexpected UDP packet"); } } } }
int tr_cacheSetLimit (tr_cache * cache, int64_t max_bytes) { char buf[128]; cache->max_bytes = max_bytes; cache->max_blocks = getMaxBlocks (max_bytes); tr_formatter_mem_B (buf, cache->max_bytes, sizeof (buf)); tr_logAddNamedDbg (MY_NAME, "Maximum cache size set to %s (%d blocks)", buf, cache->max_blocks); return cacheTrim (cache); }
static void logVal (const char * func, int ret) { if (ret == NATPMP_TRYAGAIN) return; if (ret >= 0) tr_logAddNamedInfo (getKey (), _("%s succeeded (%d)"), func, ret); else tr_logAddNamedDbg ( getKey (), "%s failed. Natpmp returned %d (%s); errno is %d (%s)", func, ret, strnatpmperr (ret), errno, tr_strerror (errno)); }
static struct UPNPDev * tr_upnpDiscover (int msec) { struct UPNPDev * ret = NULL; #if defined (HAVE_MINIUPNP_16) int err = UPNPDISCOVER_SUCCESS; ret = upnpDiscover (msec, NULL, NULL, 0, 0, &err); if (err != UPNPDISCOVER_SUCCESS) #elif defined (HAVE_MINIUPNP_15) ret = upnpDiscover (msec, NULL, NULL, 0); if (ret == NULL) #endif tr_logAddNamedDbg (getKey (), "upnpDiscover failed (errno %d - %s)", errno, tr_strerror (errno)); return ret; }
static struct UPNPDev * tr_upnpDiscover (int msec) { struct UPNPDev * ret; bool have_err; #if (MINIUPNPC_API_VERSION >= 8) /* adds ipv6 and error args */ int err = UPNPDISCOVER_SUCCESS; ret = upnpDiscover (msec, NULL, NULL, 0, 0, &err); have_err = err != UPNPDISCOVER_SUCCESS; #else ret = upnpDiscover (msec, NULL, NULL, 0); have_err = ret == NULL; #endif if (have_err) tr_logAddNamedDbg (getKey (), "upnpDiscover failed (errno %d - %s)", errno, tr_strerror (errno)); return ret; }
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 pgpipe (int handles[2]) { SOCKET s; struct sockaddr_in serv_addr; int len = sizeof (serv_addr); handles[0] = handles[1] = INVALID_SOCKET; if ((s = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { tr_logAddDebug ("pgpipe failed to create socket: %ui", WSAGetLastError ()); return -1; } memset (&serv_addr, 0, sizeof (serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons (0); serv_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); if (bind (s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) { tr_logAddDebug ("pgpipe failed to bind: %ui", WSAGetLastError ()); closesocket (s); return -1; } if (listen (s, 1) == SOCKET_ERROR) { tr_logAddNamedDbg ("event","pgpipe failed to listen: %ui", WSAGetLastError ()); closesocket (s); return -1; } if (getsockname (s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR) { tr_logAddDebug ("pgpipe failed to getsockname: %ui", WSAGetLastError ()); closesocket (s); return -1; } if ((handles[1] = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { tr_logAddDebug ("pgpipe failed to create socket 2: %ui", WSAGetLastError ()); closesocket (s); return -1; } if (connect (handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) { tr_logAddDebug ("pgpipe failed to connect socket: %ui", WSAGetLastError ()); closesocket (s); return -1; } if ((handles[0] = accept (s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET) { tr_logAddDebug ("pgpipe failed to accept socket: %ui", WSAGetLastError ()); closesocket (handles[1]); handles[1] = INVALID_SOCKET; closesocket (s); return -1; } closesocket (s); return 0; }
int tr_dhtInit (tr_session *ss) { tr_variant benc; int rc; bool have_id = false; char * dat_file; uint8_t * nodes = NULL, * nodes6 = NULL; const uint8_t * raw; size_t len, len6; struct bootstrap_closure * cl; if (session) /* already initialized */ return -1; tr_logAddNamedDbg ("DHT", "Initializing DHT"); if (tr_env_key_exists ("TR_DHT_VERBOSE")) dht_debug = stderr; dat_file = tr_buildPath (ss->configDir, "dht.dat", NULL); rc = tr_variantFromFile (&benc, TR_VARIANT_FMT_BENC, dat_file, NULL) ? 0 : -1; tr_free (dat_file); if (rc == 0) { have_id = tr_variantDictFindRaw (&benc, TR_KEY_id, &raw, &len); if (have_id && len==20) memcpy (myid, raw, len); if (ss->udp_socket != TR_BAD_SOCKET && tr_variantDictFindRaw (&benc, TR_KEY_nodes, &raw, &len) && ! (len%6)) { nodes = tr_memdup (raw, len); } if (ss->udp6_socket != TR_BAD_SOCKET && tr_variantDictFindRaw (&benc, TR_KEY_nodes6, &raw, &len6) && ! (len6%18)) { nodes6 = tr_memdup (raw, len6); } tr_variantFree (&benc); } if (nodes == NULL) len = 0; if (nodes6 == NULL) len6 = 0; if (have_id) tr_logAddNamedInfo ("DHT", "Reusing old id"); else { /* Note that DHT ids need to be distributed uniformly, * so it should be something truly random. */ tr_logAddNamedInfo ("DHT", "Generating new id"); tr_rand_buffer (myid, 20); } rc = dht_init (ss->udp_socket, ss->udp6_socket, myid, NULL); if (rc < 0) goto fail; session = ss; cl = tr_new (struct bootstrap_closure, 1); cl->session = session; cl->nodes = nodes; cl->nodes6 = nodes6; cl->len = len; cl->len6 = len6; tr_threadNew (dht_bootstrap, cl); dht_timer = evtimer_new (session->event_base, timer_callback, session); tr_timerAdd (dht_timer, 0, tr_rand_int_weak (1000000)); tr_logAddNamedDbg ("DHT", "DHT initialized"); return 1; fail: tr_logAddNamedDbg ("DHT", "DHT initialization failed (errno = %d)", errno); session = NULL; return -1; }
static void dht_bootstrap (void *closure) { struct bootstrap_closure *cl = closure; int i; int num = cl->len / 6, num6 = cl->len6 / 18; if (session != cl->session) return; if (cl->len > 0) tr_logAddNamedInfo ("DHT", "Bootstrapping from %d IPv4 nodes", num); if (cl->len6 > 0) tr_logAddNamedInfo ("DHT", "Bootstrapping from %d IPv6 nodes", num6); for (i = 0; i < MAX (num, num6); i++) { if (i < num && !bootstrap_done (cl->session, AF_INET)) { tr_port port; struct tr_address addr; memset (&addr, 0, sizeof (addr)); addr.type = TR_AF_INET; memcpy (&addr.addr.addr4, &cl->nodes[i * 6], 4); memcpy (&port, &cl->nodes[i * 6 + 4], 2); port = ntohs (port); tr_dhtAddNode (cl->session, &addr, port, 1); } if (i < num6 && !bootstrap_done (cl->session, AF_INET6)) { tr_port port; struct tr_address addr; memset (&addr, 0, sizeof (addr)); addr.type = TR_AF_INET6; memcpy (&addr.addr.addr6, &cl->nodes6[i * 18], 16); memcpy (&port, &cl->nodes6[i * 18 + 16], 2); port = ntohs (port); tr_dhtAddNode (cl->session, &addr, port, 1); } /* Our DHT code is able to take up to 9 nodes in a row without dropping any. After that, it takes some time to split buckets. So ping the first 8 nodes quickly, then slow down. */ if (i < 8) nap (2); else nap (15); if (bootstrap_done (session, 0)) break; } if (!bootstrap_done (cl->session, 0)) { char *bootstrap_file; tr_sys_file_t f = TR_BAD_SYS_FILE; bootstrap_file = tr_buildPath (cl->session->configDir, "dht.bootstrap", NULL); if (bootstrap_file) f = tr_sys_file_open (bootstrap_file, TR_SYS_FILE_READ, 0, NULL); if (f != TR_BAD_SYS_FILE) { tr_logAddNamedInfo ("DHT", "Attempting manual bootstrap"); for (;;) { char buf[201]; char *p; int port = 0; if (!tr_sys_file_read_line (f, buf, 200, NULL)) break; p = memchr (buf, ' ', strlen (buf)); if (p != NULL) port = atoi (p + 1); if (p == NULL || port <= 0 || port >= 0x10000) { tr_logAddNamedError ("DHT", "Couldn't parse %s", buf); continue; } *p = '\0'; bootstrap_from_name (buf, port, bootstrap_af (session)); if (bootstrap_done (cl->session, 0)) break; } tr_sys_file_close (f, NULL); } tr_free (bootstrap_file); } if (!bootstrap_done (cl->session, 0)) { for (i = 0; i < 6; i++) { /* We don't want to abuse our bootstrap nodes, so be very slow. The initial wait is to give other nodes a chance to contact us before we attempt to contact a bootstrap node, for example because we've just been restarted. */ nap (40); if (bootstrap_done (cl->session, 0)) break; if (i == 0) tr_logAddNamedInfo ("DHT", "Attempting bootstrap from dht.transmissionbt.com"); bootstrap_from_name ("dht.transmissionbt.com", 6881, bootstrap_af (session)); } } if (cl->nodes) tr_free (cl->nodes); if (cl->nodes6) tr_free (cl->nodes6); tr_free (closure); tr_logAddNamedDbg ("DHT", "Finished bootstrapping"); }
int tr_upnpPulse (tr_upnp * handle, int port, int isEnabled, int doPortCheck) { int ret; if (isEnabled && (handle->state == TR_UPNP_DISCOVER)) { struct UPNPDev * devlist; devlist = tr_upnpDiscover (2000); errno = 0; if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data, handle->lanaddr, sizeof (handle->lanaddr)) == UPNP_IGD_VALID_CONNECTED) { tr_logAddNamedInfo (getKey (), _( "Found Internet Gateway Device \"%s\""), handle->urls.controlURL); tr_logAddNamedInfo (getKey (), _( "Local Address is \"%s\""), handle->lanaddr); handle->state = TR_UPNP_IDLE; handle->hasDiscovered = true; } else { handle->state = TR_UPNP_ERR; tr_logAddNamedDbg ( getKey (), "UPNP_GetValidIGD failed (errno %d - %s)", errno, tr_strerror (errno)); tr_logAddNamedDbg ( 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) { if ((tr_upnpGetSpecificPortMappingEntry (handle, "TCP") != UPNPCOMMAND_SUCCESS) || (tr_upnpGetSpecificPortMappingEntry (handle, "UDP") != UPNPCOMMAND_SUCCESS)) { tr_logAddNamedInfo (getKey (), _("Port %d isn't forwarded"), handle->port); handle->isMapped = false; } } if (handle->state == TR_UPNP_UNMAP) { tr_upnpDeletePortMapping (handle, "TCP", handle->port); tr_upnpDeletePortMapping (handle, "UDP", handle->port); tr_logAddNamedInfo (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 desc[64]; tr_snprintf (desc, sizeof (desc), "%s at %d", TR_NAME, port); err_tcp = tr_upnpAddPortMapping (handle, "TCP", port, desc); err_udp = tr_upnpAddPortMapping (handle, "UDP", port, desc); handle->isMapped = !err_tcp | !err_udp; } tr_logAddNamedInfo (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_logAddNamedInfo (getKey (), "%s", _("Port forwarding successful!")); handle->port = port; handle->state = TR_UPNP_IDLE; } else { tr_logAddNamedDbg (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; }