예제 #1
0
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)
        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)
        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;
}
예제 #2
0
static char*
announce_url_new (const tr_session * session, const tr_announce_request * req)
{
    const char * str;
    const unsigned char * ipv6;
    struct evbuffer * buf = evbuffer_new ();
    char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1];

    tr_http_escape_sha1 (escaped_info_hash, req->info_hash);

    evbuffer_expand (buf, 1024);

    evbuffer_add_printf (buf, "%s"
                              "%c"
                              "info_hash=%s"
                              "&peer_id=%*.*s"
                              "&port=%d"
                              "&uploaded=%" PRIu64
                              "&downloaded=%" PRIu64
                              "&left=%" PRIu64
                              "&numwant=%d"
                              "&key=%x"
                              "&compact=1"
                              "&supportcrypto=1",
                              req->url,
                              strchr (req->url, '?') ? '&' : '?',
                              escaped_info_hash,
                              PEER_ID_LEN, PEER_ID_LEN, req->peer_id,
                              req->port,
                              req->up,
                              req->down,
                              req->leftUntilComplete,
                              req->numwant,
                              req->key);

    if (session->encryptionMode == TR_ENCRYPTION_REQUIRED)
        evbuffer_add_printf (buf, "&requirecrypto=1");

    if (req->corrupt)
        evbuffer_add_printf (buf, "&corrupt=%" PRIu64, req->corrupt);

    str = get_event_string (req);
    if (str && *str)
        evbuffer_add_printf (buf, "&event=%s", str);

    str = req->tracker_id_str;
    if (str && *str)
        evbuffer_add_printf (buf, "&trackerid=%s", str);

    /* There are two incompatible techniques for announcing an IPv6 address.
       BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
       while OpenTracker requires that peers announce twice, once over IPv4
       and once over IPv6.

       To be safe, we should do both: add the "ipv6=" parameter and
       announce twice. At any rate, we're already computing our IPv6
       address (for the LTEP handshake), so this comes for free. */

    ipv6 = tr_globalIPv6 ();
    if (ipv6) {
        char ipv6_readable[INET6_ADDRSTRLEN];
        evutil_inet_ntop (AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN);
        evbuffer_add_printf (buf, "&ipv6=");
        tr_http_escape (buf, ipv6_readable, -1, true);
    }

    return evbuffer_free_to_str (buf);
}
예제 #3
0
void
tr_udpInit (tr_session *ss)
{
    bool is_default;
    const struct tr_address * public_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_logAddNamedError ("UDP", "Couldn't create IPv4 socket");
        goto ipv6;
    }

    memset (&sin, 0, sizeof (sin));
    sin.sin_family = AF_INET;
    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET, &is_default);
    if (public_addr && !is_default)
        memcpy (&sin.sin_addr, &public_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_logAddNamedError ("UDP", "Couldn't bind IPv4 socket");
        close (ss->udp_socket);
        ss->udp_socket = -1;
        goto ipv6;
    }
    ss->udp_event =
        event_new (ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
                  event_callback, ss);
    if (ss->udp_event == NULL)
        tr_logAddNamedError ("UDP", "Couldn't allocate IPv4 event");

 ipv6:
    if (tr_globalIPv6 ())
        rebind_ipv6 (ss, true);
    if (ss->udp6_socket >= 0) {
        ss->udp6_event =
            event_new (ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
                      event_callback, ss);
        if (ss->udp6_event == NULL)
            tr_logAddNamedError ("UDP", "Couldn't allocate IPv6 event");
    }

    tr_udpSetSocketBuffers (ss);

    if (ss->isDHTEnabled)
        tr_dhtInit (ss);

    if (ss->udp_event)
        event_add (ss->udp_event, NULL);
    if (ss->udp6_event)
        event_add (ss->udp6_event, NULL);
}
예제 #4
0
static void
rebind_ipv6 (tr_session *ss, bool force)
{
    bool is_default;
    const struct tr_address * public_addr;
    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);
    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET6, &is_default);
    if (public_addr && !is_default)
        sin6.sin6_addr = public_addr->addr.addr6;

    rc = bind (s, (struct sockaddr*)&sin6, sizeof (sin6));
    if (rc < 0)
        goto fail;

    if (ss->udp6_socket < 0) {
        ss->udp6_socket = s;
    } else {
        rc = dup2 (s, ss->udp6_socket);
        if (rc < 0)
            goto fail;
        close (s);
    }

    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_logAddNamedError ("UDP", "Couldn't rebind IPv6 socket");
    if (s >= 0)
        close (s);
    if (ss->udp6_bound) {
        free (ss->udp6_bound);
        ss->udp6_bound = NULL;
    }
}
예제 #5
0
int
tr_dhtInit(tr_session *ss, const tr_address * tr_addr)
{
    struct sockaddr_in sin;
    tr_benc benc;
    int rc;
    tr_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;

    dht_port = tr_sessionGetPeerPort(ss);
    if(dht_port <= 0)
        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;

    dat_file = tr_buildPath( ss->configDir, "dht.dat", NULL );
    rc = tr_bencLoadFile( &benc, TR_FMT_BENC, dat_file );
    tr_free( dat_file );
    if(rc == 0) {
        have_id = tr_bencDictFindRaw(&benc, "id", &raw, &len);
        if( have_id && len==20 )
            memcpy( myid, raw, len );
        if( dht_socket >= 0 &&
            tr_bencDictFindRaw( &benc, "nodes", &raw, &len ) && !(len%6) ) {
                nodes = tr_memdup( raw, len );
        }
        if( dht6_socket > 0 &&
            tr_bencDictFindRaw( &benc, "nodes6", &raw, &len6 ) && !(len6%18) ) {
            nodes6 = tr_memdup( raw, len6 );
        }
        tr_bencFree( &benc );
    }

    if(nodes == NULL)
        len = 0;
    if(nodes6 == NULL)
        len6 = 0;

    if( have_id )
        tr_ninf( "DHT", "Reusing old id" );
    else {
        /* Note that DHT ids need to be distributed uniformly,
         * so it should be something truly random. */
        tr_ninf( "DHT", "Generating new id" );
        tr_cryptoRandBuf( myid, 20 );
    }

    rc = dht_init( dht_socket, dht6_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_event = event_new( session->event_base, dht_socket, EV_READ, event_callback, NULL );
    tr_timerAdd( dht_event, 0, tr_cryptoWeakRandInt( 1000000 ) );

    if( dht6_socket >= 0 )
    {
        dht6_event = event_new( session->event_base, dht6_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 (errno = %d)", save );
        errno = save;
    }

    return -1;
}