Ignore:
Timestamp:
Nov 9, 2009, 5:45:16 AM (12 years ago)
Author:
charles
Message:

(trunk libT) #2502: announce own IPv6 address to peers

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/net.c

    r9485 r9499  
    11/******************************************************************************
     2 *
    23 * $Id$
    34 *
     
    463464    tr_fdSocketClose( session, s );
    464465}
     466
     467/*
     468   get_source_address(), get_name_source_address(), and
     469   global_unicast_address() were written by Juliusz Chroboczek,
     470   and are covered under the same license as dht.c.
     471   Please feel free to copy them into your software
     472   if it can help unbreaking the double-stack Internet. */
     473
     474/* Get the source address used for a given destination address.  Since
     475   there is no official interface to get this information, we create
     476   a connected UDP socket (connected UDP... hmm...) and check its source
     477   address. */
     478static int
     479get_source_address( const struct sockaddr  * dst,
     480                    socklen_t                dst_len,
     481                    struct sockaddr        * src,
     482                    socklen_t              * src_len )
     483{
     484    int s, rc, save;
     485
     486    s = socket(dst->sa_family, SOCK_DGRAM, 0);
     487    if(s < 0)
     488        goto fail;
     489
     490    /* Since it's a UDP socket, this doesn't actually send any packets. */
     491    rc = connect(s, dst, dst_len);
     492    if(rc < 0)
     493        goto fail;
     494
     495    rc = getsockname(s, src, src_len);
     496    if(rc < 0)
     497        goto fail;
     498
     499    EVUTIL_CLOSESOCKET( s );
     500
     501    return rc;
     502
     503 fail:
     504    save = errno;
     505    EVUTIL_CLOSESOCKET( s );
     506    errno = save;
     507    return -1;
     508}
     509
     510/* Like above, but for a given DNS name. */
     511static int
     512get_name_source_address(int af, const char *name,
     513                        struct sockaddr *src, socklen_t *src_len)
     514{
     515    struct addrinfo hints, *info, *infop;
     516    int rc;
     517
     518    memset(&hints, 0, sizeof(hints));
     519    hints.ai_family = af;
     520    hints.ai_socktype = SOCK_DGRAM;
     521
     522    rc = getaddrinfo(name, NULL, &hints, &info);
     523    if(rc != 0) {
     524        errno = ENOENT;
     525        return -1;
     526    }
     527
     528    rc = -1;
     529    errno = ENOENT;
     530    infop = info;
     531    while(infop) {
     532        if(infop->ai_addr->sa_family == af) {
     533            rc = get_source_address(infop->ai_addr, infop->ai_addrlen,
     534                                    src, src_len);
     535            if(rc >= 0)
     536                break;
     537        }
     538        infop = infop->ai_next;
     539    }
     540
     541    freeaddrinfo(info);
     542    return rc;
     543}
     544
     545/* We all hate NATs. */
     546static int
     547global_unicast_address(struct sockaddr *sa)
     548{
     549    if(sa->sa_family == AF_INET) {
     550        const unsigned char *a =
     551            (unsigned char*)&((struct sockaddr_in*)sa)->sin_addr;
     552        if(a[0] == 0 || a[0] == 127 || a[0] >= 224 ||
     553           a[0] == 10 || (a[0] == 172 && a[1] >= 16 && a[1] <= 31) ||
     554           (a[0] == 192 && a[1] == 168))
     555            return 0;
     556        return 1;
     557    } else if(sa->sa_family == AF_INET6) {
     558        const unsigned char *a =
     559            (unsigned char*)&((struct sockaddr_in6*)sa)->sin6_addr;
     560        /* 2000::/3 */
     561        return (a[0] & 0xE0) == 0x20;
     562    } else {
     563        errno = EAFNOSUPPORT;
     564        return -1;
     565    }
     566}
     567
     568int
     569tr_globalAddress( int af, void *addr, int *addr_len )
     570{
     571    struct sockaddr_storage ss;
     572    socklen_t ss_len = sizeof(ss);
     573    int rc;
     574
     575    /* This should be a name with both IPv4 and IPv6 addresses. */
     576    rc = get_name_source_address( af, "www.transmissionbt.com",
     577                                  (struct sockaddr*)&ss, &ss_len );
     578    /* In case Charles removes IPv6 from his website. */
     579    if( rc < 0 )
     580        rc = get_name_source_address(  af, "www.ietf.org",
     581                                      (struct sockaddr*)&ss, &ss_len );
     582
     583    if( rc < 0 )
     584        return -1;
     585
     586    if( !global_unicast_address( (struct sockaddr*)&ss) )
     587        return -1;
     588
     589    switch(af) {
     590    case AF_INET:
     591        if(*addr_len < 4)
     592            return -1;
     593        memcpy(addr, &((struct sockaddr_in*)&ss)->sin_addr, 4);
     594        *addr_len = 4;
     595        return 1;
     596    case AF_INET6:
     597        if(*addr_len < 16)
     598            return -1;
     599        memcpy(addr, &((struct sockaddr_in6*)&ss)->sin6_addr, 16);
     600        *addr_len = 16;
     601        return 1;
     602    default:
     603        return -1;
     604    }
     605}
Note: See TracChangeset for help on using the changeset viewer.