source: trunk/libtransmission/net.c @ 9513

Last change on this file since 9513 was 9513, checked in by charles, 12 years ago

(trunk libT) #2112: provide "ipv6=" parameter to trackers

  • Property svn:keywords set to Date Rev Author Id
File size: 16.3 KB
Line 
1/******************************************************************************
2 *
3 * $Id: net.c 9513 2009-11-10 17:03:23Z charles $
4 *
5 * Copyright (c) 2005-2008 Transmission authors and contributors
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *****************************************************************************/
25
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <assert.h>
31
32#include <sys/types.h>
33
34#ifdef WIN32
35 #include <winsock2.h> /* inet_addr */
36 #include <WS2tcpip.h>
37#else
38 #include <arpa/inet.h> /* inet_addr */
39 #include <netdb.h>
40 #include <fcntl.h>
41#endif
42#include <unistd.h>
43
44#include <stdarg.h> /* some 1.4.x versions of evutil.h need this */
45#include <evutil.h>
46
47#include "transmission.h"
48#include "fdlimit.h"
49#include "natpmp.h"
50#include "net.h"
51#include "peer-io.h"
52#include "platform.h"
53#include "session.h"
54#include "utils.h"
55
56#ifndef IN_MULTICAST
57#define IN_MULTICAST( a ) ( ( ( a ) & 0xf0000000 ) == 0xe0000000 )
58#endif
59
60const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } };
61const tr_address tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } };
62
63#ifdef WIN32
64static const char *
65inet_ntop( int af, const void *src, char *dst, socklen_t cnt )
66{
67    if (af == AF_INET)
68    {
69        struct sockaddr_in in;
70        memset( &in, 0, sizeof( in ) );
71        in.sin_family = AF_INET;
72        memcpy( &in.sin_addr, src, sizeof( struct in_addr ) );
73        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
74                    dst, cnt, NULL, 0, NI_NUMERICHOST);
75        return dst;
76    }
77    else if (af == AF_INET6)
78    {
79        struct sockaddr_in6 in;
80        memset( &in, 0, sizeof( in ) );
81        in.sin6_family = AF_INET6;
82        memcpy( &in.sin6_addr, src, sizeof( struct in_addr6 ) );
83        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
84                    dst, cnt, NULL, 0, NI_NUMERICHOST);
85        return dst;
86    }
87    return NULL;
88}
89
90static int
91inet_pton(int af, const char *src, void *dst)
92{
93    struct addrinfo hints;
94    struct addrinfo *res;
95    struct addrinfo *ressave;
96
97    memset(&hints, 0, sizeof(struct addrinfo));
98    hints.ai_family = af;
99
100    if (getaddrinfo(src, NULL, &hints, &res) != 0)
101        return -1;
102
103    ressave = res;
104
105    while (res)
106    {
107        memcpy(dst, res->ai_addr, res->ai_addrlen);
108        res = res->ai_next;
109    }
110
111    freeaddrinfo(ressave);
112    return 0;
113}
114
115#endif
116
117
118void
119tr_netInit( void )
120{
121    static int initialized = FALSE;
122
123    if( !initialized )
124    {
125#ifdef WIN32
126        WSADATA wsaData;
127        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
128#endif
129        initialized = TRUE;
130    }
131}
132
133const char *
134tr_ntop( const tr_address * src, char * dst, int size )
135{
136    assert( tr_isAddress( src ) );
137
138    if( src->type == TR_AF_INET )
139        return inet_ntop( AF_INET, &src->addr, dst, size );
140    else
141        return inet_ntop( AF_INET6, &src->addr, dst, size );
142}
143
144/*
145 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
146 * This function is suitable to be called from libTransmission's networking code,
147 * which is single-threaded.
148 */
149const char *
150tr_ntop_non_ts( const tr_address * src )
151{
152    static char buf[INET6_ADDRSTRLEN];
153    return tr_ntop( src, buf, sizeof( buf ) );
154}
155
156tr_address *
157tr_pton( const char * src, tr_address * dst )
158{
159    int retval = inet_pton( AF_INET, src, &dst->addr );
160    assert( dst );
161    if( retval < 0 )
162        return NULL;
163    else if( retval == 0 )
164        retval = inet_pton( AF_INET6, src, &dst->addr );
165    else
166    {
167        dst->type = TR_AF_INET;
168        return dst;
169    }
170
171    if( retval < 1 )
172        return NULL;
173    dst->type = TR_AF_INET6;
174    return dst;
175}
176
177/*
178 * Compare two tr_address structures.
179 * Returns:
180 * <0 if a < b
181 * >0 if a > b
182 * 0  if a == b
183 */
184int
185tr_compareAddresses( const tr_address * a, const tr_address * b)
186{
187    static const int sizes[2] = { sizeof(struct in_addr), sizeof(struct in6_addr) };
188
189    assert( tr_isAddress( a ) );
190    assert( tr_isAddress( b ) );
191
192    /* IPv6 addresses are always "greater than" IPv4 */
193    if( a->type != b->type )
194        return a->type == TR_AF_INET ? 1 : -1;
195
196    return memcmp( &a->addr, &b->addr, sizes[a->type] );
197}
198
199/***********************************************************************
200 * TCP sockets
201 **********************************************************************/
202
203int
204tr_netSetTOS( int s, int tos )
205{
206#ifdef IP_TOS
207    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
208#else
209    return 0;
210#endif
211}
212
213static socklen_t
214setup_sockaddr( const tr_address        * addr,
215                tr_port                   port,
216                struct sockaddr_storage * sockaddr)
217{
218    assert( tr_isAddress( addr ) );
219
220    if( addr->type == TR_AF_INET )
221    {
222        struct sockaddr_in  sock4;
223        memset( &sock4, 0, sizeof( sock4 ) );
224        sock4.sin_family      = AF_INET;
225        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
226        sock4.sin_port        = port;
227        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
228        return sizeof( struct sockaddr_in );
229    }
230    else
231    {
232        struct sockaddr_in6 sock6;
233        memset( &sock6, 0, sizeof( sock6 ) );
234        sock6.sin6_family   = AF_INET6;
235        sock6.sin6_port     = port;
236        sock6.sin6_flowinfo = 0;
237        sock6.sin6_addr     = addr->addr.addr6;
238        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
239        return sizeof( struct sockaddr_in6 );
240    }
241}
242
243static tr_bool
244isMulticastAddress( const tr_address * addr )
245{
246    if( addr->type == TR_AF_INET && IN_MULTICAST( htonl( addr->addr.addr4.s_addr ) ) )
247        return TRUE;
248
249    if( addr->type == TR_AF_INET6 && ( addr->addr.addr6.s6_addr[0] == 0xff ) )
250        return TRUE;
251
252    return FALSE;
253}
254
255static TR_INLINE tr_bool
256isIPv4MappedOrCompatAddress( const tr_address * addr )
257{
258    if( addr->type == TR_AF_INET6 )
259    {
260        if( IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) ||
261            IN6_IS_ADDR_V4COMPAT( &addr->addr.addr6 ) )
262            return TRUE;
263    }
264    return FALSE;
265}
266
267static TR_INLINE tr_bool
268isIPv6LinkLocalAddress( const tr_address * addr )
269{
270    if( addr->type == TR_AF_INET6 &&
271        IN6_IS_ADDR_LINKLOCAL( &addr->addr.addr6 ) )
272        return TRUE;
273    return FALSE;
274}
275
276tr_bool
277tr_isValidPeerAddress( const tr_address * addr, tr_port port )
278{
279    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) ||
280        isIPv4MappedOrCompatAddress( addr ) )
281        return FALSE;
282
283    if( port == 0 )
284        return FALSE;
285
286    return TRUE;
287}
288
289int
290tr_netOpenTCP( tr_session        * session,
291               const tr_address  * addr,
292               tr_port             port )
293{
294    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
295    int                     s;
296    struct sockaddr_storage sock;
297    socklen_t               addrlen;
298    const tr_address      * source_addr;
299    socklen_t               sourcelen;
300    struct sockaddr_storage source_sock;
301
302    assert( tr_isAddress( addr ) );
303
304    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) )
305        return -EINVAL;
306
307    s = tr_fdSocketCreate( session, domains[addr->type], SOCK_STREAM );
308    if( s < 0 )
309        return -1;
310
311    if( evutil_make_socket_nonblocking( s ) < 0 ) {
312        tr_netClose( session, s );
313        return -1;
314    }
315
316    addrlen = setup_sockaddr( addr, port, &sock );
317
318    /* set source address */
319    source_addr = tr_sessionGetPublicAddress( session, addr->type );
320    assert( source_addr );
321    sourcelen = setup_sockaddr( source_addr, 0, &source_sock );
322    if( bind( s, ( struct sockaddr * ) &source_sock, sourcelen ) )
323    {
324        tr_err( _( "Couldn't set source address %s on %d: %s" ),
325                tr_ntop_non_ts( source_addr ), s, tr_strerror( errno ) );
326        return -errno;
327    }
328
329    if( ( connect( s, (struct sockaddr *) &sock,
330                  addrlen ) < 0 )
331#ifdef WIN32
332      && ( sockerrno != WSAEWOULDBLOCK )
333#endif
334      && ( sockerrno != EINPROGRESS ) )
335    {
336        int tmperrno;
337        tmperrno = sockerrno;
338        if( ( tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH )
339                || addr->type == TR_AF_INET )
340            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
341                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
342                    tr_strerror( tmperrno ) );
343        tr_netClose( session, s );
344        s = -tmperrno;
345    }
346
347    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
348               s, tr_peerIoAddrStr( addr, port ) );
349
350    return s;
351}
352
353static int
354tr_netBindTCPImpl( const tr_address * addr, tr_port port, tr_bool suppressMsgs, int * errOut )
355{
356    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
357    struct sockaddr_storage sock;
358    int fd;
359    int addrlen;
360    int optval;
361
362    assert( tr_isAddress( addr ) );
363
364    fd = socket( domains[addr->type], SOCK_STREAM, 0 );
365    if( fd < 0 ) {
366        *errOut = sockerrno;
367        return -1;
368    }
369
370    if( evutil_make_socket_nonblocking( fd ) < 0 ) {
371        *errOut = sockerrno;
372        tr_netCloseSocket( fd );
373        return -1;
374    }
375
376    optval = 1;
377    setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
378    setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
379
380#ifdef IPV6_V6ONLY
381    if( addr->type == TR_AF_INET6 )
382        if( setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof( optval ) ) == -1 )
383            if( sockerrno != ENOPROTOOPT ) { /* if the kernel doesn't support it, ignore it */
384                *errOut = sockerrno;
385                return -1;
386            }
387#endif
388
389    addrlen = setup_sockaddr( addr, htons( port ), &sock );
390    if( bind( fd, (struct sockaddr *) &sock, addrlen ) ) {
391        const int err = sockerrno;
392        if( !suppressMsgs )
393            tr_err( _( "Couldn't bind port %d on %s: %s" ),
394                    port, tr_ntop_non_ts( addr ), tr_strerror( err ) );
395        tr_netCloseSocket( fd );
396        *errOut = err;
397        return -1;
398    }
399
400    if( !suppressMsgs )
401        tr_dbg( "Bound socket %d to port %d on %s", fd, port, tr_ntop_non_ts( addr ) );
402
403    if( listen( fd, 128 ) == -1 ) {
404        *errOut = sockerrno;
405        tr_netCloseSocket( fd );
406        return -1;
407    }
408
409    return fd;
410}
411
412int
413tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
414{
415    int unused;
416    return tr_netBindTCPImpl( addr, port, suppressMsgs, &unused );
417}
418
419tr_bool
420tr_net_hasIPv6( tr_port port )
421{
422    static tr_bool result = FALSE;
423    static tr_bool alreadyDone = FALSE;
424
425    if( !alreadyDone )
426    {
427        int err;
428        int fd = tr_netBindTCPImpl( &tr_in6addr_any, port, TRUE, &err );
429        if( fd >= 0 || err != EAFNOSUPPORT ) /* we support ipv6 */
430            result = TRUE;
431        if( fd >= 0 )
432            tr_netCloseSocket( fd );
433        alreadyDone = TRUE;
434    }
435
436    return result;
437}
438
439int
440tr_netAccept( tr_session  * session,
441              int           b,
442              tr_address  * addr,
443              tr_port     * port )
444{
445    int fd = tr_fdSocketAccept( session, b, addr, port );
446
447    if( fd>=0 && evutil_make_socket_nonblocking(fd)<0 ) {
448        tr_netClose( session, fd );
449        fd = -1;
450    }
451
452    return fd;
453}
454
455void
456tr_netCloseSocket( int fd )
457{
458    EVUTIL_CLOSESOCKET( fd );
459}
460
461void
462tr_netClose( tr_session * session, int s )
463{
464    tr_fdSocketClose( session, s );
465}
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}
606
607/* Return our global IPv6 address, with caching. */
608
609const unsigned char *
610tr_globalIPv6( void )
611{
612    static unsigned char ipv6[16];
613    static time_t last_time = 0;
614    static int have_ipv6 = 0;
615    const time_t now = time( NULL );
616
617    /* Re-check every half hour */
618    if( last_time < now - 1800 )
619    {
620        int addrlen = 16;
621        const int rc = tr_globalAddress( AF_INET6, ipv6, &addrlen );
622        have_ipv6 = ( rc >= 0 ) && ( addrlen == 16 );
623        last_time = now;
624    }
625
626    return have_ipv6 ? ipv6 : NULL;
627}
Note: See TracBrowser for help on using the repository browser.