#include "trevent.h" /* tr_runInEventThread() */
#include "utils.h"
-static int dht_socket = -1, dht6_socket = -1;
-static struct event * dht_event = NULL;
-static struct event * dht6_event = NULL;
-static tr_port dht_port;
+static struct event dht_event, dht6_event;
static unsigned char myid[20];
static tr_session *session = NULL;
tr_ndbg( "DHT", "Finished bootstrapping" );
}
-/* BEP-32 has a rather nice explanation of why we need to bind to one
- IPv6 address, if I may say so myself. */
-
-static int
-rebind_ipv6(tr_bool force)
-{
- struct sockaddr_in6 sin6;
- const unsigned char *ipv6 = tr_globalIPv6();
- static unsigned char *last_bound = NULL;
- int s, rc;
- int one = 1;
-
- /* We currently have no way to enable or disable IPv6 once the DHT has
- been initialised. Oh, well. */
- if(ipv6 == NULL || (!force && dht6_socket < 0)) {
- if(last_bound) {
- free(last_bound);
- last_bound = NULL;
- }
- return 0;
- }
-
- if(last_bound != NULL && memcmp(ipv6, last_bound, 16) == 0)
- return 0;
-
- s = socket(PF_INET6, SOCK_DGRAM, 0);
- if(s < 0) {
- tr_ndbg( "DHT", "dht6_socket: %s", tr_strerror( errno ) );
- return -1;
- }
-
-#ifdef IPV6_V6ONLY
- /* Since we always open an IPv4 socket on the same port, this
- shouldn't matter. But I'm superstitious. */
- setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
-#endif
-
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- if(ipv6)
- memcpy(&sin6.sin6_addr, ipv6, 16);
- sin6.sin6_port = htons(dht_port);
- rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
-
- if(rc < 0) {
- tr_nerr( "DHT", "bind(dht6_socket): %s", tr_strerror( errno ) );
- close(s);
- return -1;
- }
-
- if(dht6_socket < 0) {
- dht6_socket = s;
- } else {
- rc = dup2(s, dht6_socket);
- close(s);
- if(rc < 0)
- return -1;
- }
-
- if(last_bound == NULL)
- last_bound = malloc(16);
- if(last_bound)
- memcpy(last_bound, ipv6, 16);
-
- return 1;
-}
-
int
-tr_dhtInit(tr_session *ss, const tr_address * tr_addr)
+tr_dhtInit(tr_session *ss)
{
- struct sockaddr_in sin;
tr_benc benc;
int rc;
tr_bool have_id = FALSE;
struct bootstrap_closure * cl;
if( session ) /* already initialized */
- return -1;
-
- dht_port = tr_sessionGetPeerPort(ss);
- if(dht_port <= 0)
- return -1;
+ return -1;
tr_ndbg( "DHT", "Initializing DHT" );
- dht_socket = socket(PF_INET, SOCK_DGRAM, 0);
- if(dht_socket < 0)
- goto fail;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- memcpy(&sin.sin_addr, &tr_addr->addr.addr4, sizeof (struct in_addr));
- sin.sin_port = htons(dht_port);
- rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin));
- if(rc < 0)
- goto fail;
-
- if(tr_globalIPv6())
- rebind_ipv6(TRUE);
-
if( getenv( "TR_DHT_VERBOSE" ) != NULL )
dht_debug = stderr;
have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len);
if( have_id && len==20 )
memcpy( myid, raw, len );
- if( dht_socket >= 0 &&
+ if( ss->udp_socket >= 0 &&
tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) {
nodes = tr_memdup( raw, len );
}
- if( dht6_socket > 0 &&
+ if( ss->udp6_socket > 0 &&
tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) {
nodes6 = tr_memdup( raw, len6 );
}
tr_cryptoRandBuf( myid, 20 );
}
- rc = dht_init( dht_socket, dht6_socket, myid, NULL );
+ rc = dht_init( ss->udp_socket, ss->udp6_socket, myid, NULL );
if( rc < 0 )
goto fail;
cl->len6 = len6;
tr_threadNew( dht_bootstrap, cl );
- dht_event = event_new( session->event_base, dht_socket, EV_READ, event_callback, NULL );
- tr_timerAdd( dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
+ event_set( &dht_event, ss->udp_socket, EV_READ, event_callback, NULL );
+ tr_timerAdd( &dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
- if( dht6_socket >= 0 )
+ if( ss->udp6_socket >= 0 )
{
- dht6_event = event_new( session->event_base, dht6_socket, EV_READ, event_callback, NULL );
- tr_timerAdd( dht6_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
+ event_set( &dht6_event, ss->udp6_socket, EV_READ, event_callback, NULL );
+ tr_timerAdd( &dht6_event, 0, tr_cryptoWeakRandInt( 1000000 ) );
}
tr_ndbg( "DHT", "DHT initialized" );
return 1;
- fail:
- {
- const int save = errno;
- close(dht_socket);
- if( dht6_socket >= 0 )
- close(dht6_socket);
- dht_socket = dht6_socket = -1;
- session = NULL;
- tr_ndbg( "DHT", "DHT initialization failed: %s", tr_strerror( save ) );
- errno = save;
- }
-
+ fail:
+ tr_ndbg( "DHT", "DHT initialization failed (errno = %d)", errno );
+ session = NULL;
return -1;
}
event_free( dht_event );
dht_event = NULL;
- if( dht6_event ) {
- event_free( dht6_event );
- dht6_event = NULL;
- }
+ if( ss->udp6_socket >= 0 )
+ event_del( &dht6_event );
/* Since we only save known good nodes, avoid erasing older data if we
don't know enough nodes. */
}
dht_uninit( 1 );
- tr_netCloseSocket( dht_socket );
- dht_socket = -1;
- if(dht6_socket > 0) {
- tr_netCloseSocket( dht6_socket );
- dht6_socket = -1;
- }
-
tr_ndbg("DHT", "Done uninitializing DHT");
session = NULL;
struct getstatus_closure closure = { af, -1, -1 };
if( !tr_dhtEnabled( session ) ||
- (af == AF_INET && dht_socket < 0) ||
- (af == AF_INET6 && dht6_socket < 0) ) {
+ (af == AF_INET && session->udp_socket < 0) ||
+ (af == AF_INET6 && session->udp6_socket < 0) ) {
if( nodes_return )
*nodes_return = 0;
return TR_DHT_STOPPED;
tr_port
tr_dhtPort( tr_session *ss )
{
- return tr_dhtEnabled( ss ) ? dht_port : 0;
+ return tr_dhtEnabled( ss ) ? ss->udp_port : 0;
}
int
static void
event_callback(int s, short type, void *ignore UNUSED )
{
- struct event *event = (s == dht_socket) ? dht_event : dht6_event;
+ struct event *event;
time_t tosleep;
- static int count = 0;
+
+ if (s == session->udp_socket)
+ event = &dht_event;
+ else if(s == session->udp6_socket)
+ event = &dht6_event;
+ else {
+ tr_nerr("DHT", "Event on unexpected socket");
+ event = NULL;
+ }
if( dht_periodic( type == EV_READ, &tosleep, callback, NULL) < 0 ) {
if(errno == EINTR) {
}
}
- /* Only do this once in a while. Counting rather than measuring time
+#ifdef NOTYET
+ /* Only do this once in a while. Counting rather than measuring time
avoids a system call. */
count++;
if(count >= 20) {
rebind_ipv6(FALSE);
count = 0;
}
+#endif
- /* Being slightly late is fine,
- and has the added benefit of adding some jitter. */
- tr_timerAdd( event, tosleep, tr_cryptoWeakRandInt( 1000000 ) );
+ if(event) {
+ /* Being slightly late is fine,
+ and has the added benefit of adding some jitter. */
+ tr_timerAdd( event, tosleep, tr_cryptoWeakRandInt( 1000000 ) );
+ }
}
void
*/
+#include <unistd.h>
+#include <assert.h>
+
#include "transmission.h"
#include "net.h"
#include "session.h"
#include "tr-dht.h"
#include "tr-udp.h"
+/* BEP-32 has a rather nice explanation of why we need to bind to one
+ IPv6 address, if I may say so myself. */
+
+static void
+rebind_ipv6(tr_session *ss, tr_bool force)
+{
+ struct sockaddr_in6 sin6;
+ const unsigned char *ipv6 = tr_globalIPv6();
+ int s = -1, rc;
+ int one = 1;
+
+ /* We currently have no way to enable or disable IPv6 after initialisation.
+ No way to fix that without some surgery to the DHT code itself. */
+ if(ipv6 == NULL || (!force && ss->udp6_socket < 0)) {
+ if(ss->udp6_bound) {
+ free(ss->udp6_bound);
+ ss->udp6_bound = NULL;
+ }
+ return;
+ }
+
+ if(ss->udp6_bound != NULL && memcmp(ipv6, ss->udp6_bound, 16) == 0)
+ return;
+
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if(s < 0)
+ goto fail;
+
+#ifdef IPV6_V6ONLY
+ /* Since we always open an IPv4 socket on the same port, this
+ shouldn't matter. But I'm superstitious. */
+ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
+#endif
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ if(ipv6)
+ memcpy(&sin6.sin6_addr, ipv6, 16);
+ sin6.sin6_port = htons(ss->udp_port);
+ rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ goto fail;
+
+ if(ss->udp6_socket < 0) {
+ ss->udp6_socket = s;
+ s = -1;
+ } else {
+ rc = dup2(s, ss->udp6_socket);
+ if(rc < 0)
+ goto fail;
+ close(s);
+ s = -1;
+ }
+
+ if(ss->udp6_bound == NULL)
+ ss->udp6_bound = malloc(16);
+ if(ss->udp6_bound)
+ memcpy(ss->udp6_bound, ipv6, 16);
+
+ return;
+
+ fail:
+ /* Something went wrong. It's difficult to recover, so let's simply
+ set things up so that we try again next time. */
+ tr_nerr("UDP", "Couldn't rebind IPv6 socket");
+ if(s >= 0)
+ close(s);
+ if(ss->udp6_bound) {
+ free(ss->udp6_bound);
+ ss->udp6_bound = NULL;
+ }
+}
void
tr_udpInit(tr_session *ss, const tr_address * addr)
{
- if( ss->isDHTEnabled )
- tr_dhtInit(ss, addr);
+ struct sockaddr_in sin;
+ int rc;
+
+ assert(ss->udp_socket < 0);
+ assert(ss->udp6_socket < 0);
+
+ ss->udp_port = tr_sessionGetPeerPort(ss);
+ if(ss->udp_port <= 0)
+ return;
+
+ ss->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if(ss->udp_socket < 0) {
+ tr_nerr("UDP", "Couldn't create IPv4 socket");
+ goto ipv6;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, &addr->addr.addr4, sizeof (struct in_addr));
+ sin.sin_port = htons(ss->udp_port);
+ rc = bind(ss->udp_socket, (struct sockaddr*)&sin, sizeof(sin));
+ if(rc < 0) {
+ tr_nerr("UDP", "Couldn't bind IPv4 socket");
+ close(ss->udp_socket);
+ ss->udp_socket = -1;
+ goto ipv6;
+ }
+
+ ipv6:
+ if(tr_globalIPv6())
+ rebind_ipv6(ss, TRUE);
+
+ if(ss->isDHTEnabled)
+ tr_dhtInit(ss);
}
void
tr_udpUninit(tr_session *ss)
{
- return tr_dhtUninit(ss);
+ tr_dhtUninit(ss);
+
+ if(ss->udp_socket >= 0) {
+ tr_netCloseSocket( ss->udp_socket );
+ ss->udp_socket = -1;
+ }
+
+ if(ss->udp6_socket >= 0) {
+ tr_netCloseSocket( ss->udp6_socket );
+ ss->udp6_socket = -1;
+ }
+
+ if(ss->udp6_bound) {
+ free(ss->udp6_bound);
+ ss->udp6_bound = NULL;
+ }
}