source: trunk/libtransmission/net.c @ 7730

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

(trunk libT) fix IPv6 reported & solved by erdgeist. add debugging message to help track down any future problems.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.7 KB
Line 
1/******************************************************************************
2 * $Id: net.c 7730 2009-01-16 23:17:31Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <assert.h>
30
31#include <sys/types.h>
32
33#ifdef WIN32
34 #include <winsock2.h> /* inet_addr */
35 #include <WS2tcpip.h>
36#else
37 #include <arpa/inet.h> /* inet_addr */
38 #include <netdb.h>
39 #include <fcntl.h>
40#endif
41
42#include <evutil.h>
43
44#include "transmission.h"
45#include "fdlimit.h"
46#include "natpmp.h"
47#include "net.h"
48#include "peer-io.h"
49#include "platform.h"
50#include "utils.h"
51
52#ifndef IN_MULTICAST
53#define IN_MULTICAST( a ) ( ( ( a ) & 0xf0000000 ) == 0xe0000000 )
54#endif
55
56const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } }; 
57const tr_address tr_inaddr_any = { TR_AF_INET, 
58    { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } }; 
59
60
61#ifdef WIN32
62static const char *
63inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
64{
65    if (af == AF_INET)
66    {
67        struct sockaddr_in in;
68        memset(&in, 0, sizeof(in));
69        in.sin_family = AF_INET;
70        memcpy(&in.sin_addr, src, sizeof(struct in_addr));
71        getnameinfo((struct sockaddr *)&in, sizeof(struct
72            sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
73        return dst;
74    }
75    else if (af == AF_INET6)
76    {
77        struct sockaddr_in6 in;
78        memset(&in, 0, sizeof(in));
79        in.sin6_family = AF_INET6;
80        memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
81        getnameinfo((struct sockaddr *)&in, sizeof(struct
82            sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
83        return dst;
84    }
85    return NULL;
86}
87
88static int
89inet_pton(int af, const char *src, void *dst)
90{
91    struct addrinfo hints;
92    struct addrinfo *res;
93    struct addrinfo *ressave;
94
95    memset(&hints, 0, sizeof(struct addrinfo));
96    hints.ai_family = af;
97
98    if (getaddrinfo(src, NULL, &hints, &res) != 0)
99        return -1;
100
101    ressave = res;
102
103    while (res)
104    {
105        memcpy(dst, res->ai_addr, res->ai_addrlen);
106        res = res->ai_next;
107    }
108
109    freeaddrinfo(ressave);
110    return 0;
111}
112
113#endif
114
115
116void
117tr_netInit( void )
118{
119    static int initialized = FALSE;
120
121    if( !initialized )
122    {
123#ifdef WIN32
124        WSADATA wsaData;
125        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
126#endif
127        initialized = TRUE;
128    }
129}
130
131void
132tr_suspectAddress( const tr_address * a UNUSED, const char * source UNUSED )
133{
134/* this is overkill for a production environment,
135 * but useful in the nightly builds, so only compile it into the nightlies */
136#ifdef TR_UNSTABLE
137    /* be really aggressive in what we report */
138    if( a->type == TR_AF_INET && !( ntohl( a->addr.addr4.s_addr ) & 0xff000000 ) )
139        tr_err(  "Funny looking address %s from %s", tr_ntop_non_ts( a ), source );
140    /* /16s taken from ipv6 rib on 21 dec, 2008 */
141    /* this is really, really ugly. expedience over quality */
142    if( a->type == TR_AF_INET6 )
143    {
144        uint16_t slash16;
145        uint16_t valid[] = { 0x339, 0x2002, 0x2003, 0x2400, 0x2401, 0x2402,
146            0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2600, 0x2607, 0x2610,
147            0x2620, 0x2800, 0x2801, 0x2a00, 0x2a01, 0x0a02, 0x2001, 0x0000 };
148        uint16_t *p;
149        tr_bool good = FALSE;
150        p = valid;
151        memcpy( &slash16, &a->addr, 2 );
152        slash16 = ntohs( slash16 );
153        while( *p )
154        {
155            if( slash16 == *p )
156                good = TRUE;
157            p++;
158        }
159        if( !good && !IN6_IS_ADDR_V4MAPPED( &a->addr.addr6 ) )
160            tr_err(  "Funny looking address %s from %s", tr_ntop_non_ts( a ), source );
161    }
162#endif
163}
164
165const char * 
166tr_ntop( const tr_address * src, char * dst, int size ) 
167{
168    assert( tr_isAddress( src ) );
169
170    if( src->type == TR_AF_INET ) 
171        return inet_ntop( AF_INET, &src->addr, dst, size ); 
172    else 
173        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
174} 
175
176/*
177 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
178 * This function is suitable to be called from libTransmission's networking code,
179 * which is single-threaded.
180 */ 
181const char * 
182tr_ntop_non_ts( const tr_address * src ) 
183{ 
184    static char buf[INET6_ADDRSTRLEN]; 
185    return tr_ntop( src, buf, sizeof( buf ) ); 
186} 
187
188tr_address * 
189tr_pton( const char * src, tr_address * dst ) 
190{ 
191    int retval = inet_pton( AF_INET, src, &dst->addr ); 
192    if( retval < 0 ) 
193        return NULL; 
194    else if( retval == 0 ) 
195        retval = inet_pton( AF_INET6, src, &dst->addr ); 
196    else
197    { 
198        dst->type = TR_AF_INET; 
199        return dst; 
200    } 
201
202    if( retval < 1 ) 
203        return NULL; 
204    dst->type = TR_AF_INET6; 
205    return dst; 
206}
207
208void
209tr_normalizeV4Mapped( tr_address * const addr )
210{
211    assert( tr_isAddress( addr ) );
212
213    if( addr->type == TR_AF_INET6 && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) )
214    {
215        addr->type = TR_AF_INET;
216        memcpy( &addr->addr.addr4.s_addr, addr->addr.addr6.s6_addr + 12, 4 );
217    }
218}
219
220/*
221 * Compare two tr_address structures.
222 * Returns:
223 * <0 if a < b
224 * >0 if a > b
225 * 0  if a == b
226 */ 
227int
228tr_compareAddresses( const tr_address * a, const tr_address * b)
229{
230    int addrlen;
231
232    assert( tr_isAddress( a ) );
233    assert( tr_isAddress( b ) );
234
235    /* IPv6 addresses are always "greater than" IPv4 */ 
236    if( a->type != b->type )
237        return a->type == TR_AF_INET ? 1 : -1;
238
239    if( a->type == TR_AF_INET ) 
240        addrlen = sizeof( struct in_addr ); 
241    else 
242        addrlen = sizeof( struct in6_addr ); 
243    return memcmp( &a->addr, &b->addr, addrlen );
244} 
245
246tr_net_af_support
247tr_net_getAFSupport( tr_port port )
248{
249    /* Do we care if an address is in use? Probably not, since it will be
250     * caught later. This will only set up the list of sockets to bind. */
251    static tr_bool alreadyDone       = FALSE;
252    static tr_net_af_support support = { FALSE, FALSE };
253    int s4, s6;
254    if( alreadyDone )
255        return support;
256    s6 = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
257    if( s6 >= 0 || -s6 != EAFNOSUPPORT ) /* we support ipv6 */
258    {
259        listen( s6, 1 );
260        support.has_inet6 = TRUE;
261    }
262    s4 = tr_netBindTCP( &tr_inaddr_any, port, TRUE );
263    if( s4 >= 0 ) /* we bound *with* the ipv6 socket bound (need both)
264                   * or only have ipv4 */
265    {
266        tr_netClose( s4 );
267        support.needs_inet4 = TRUE;
268    }
269    if( s6 >= 0 )
270        tr_netClose( s6 );
271    alreadyDone = TRUE;
272    return support;
273}
274
275/***********************************************************************
276 * Socket list housekeeping
277 **********************************************************************/
278struct tr_socketList
279{
280    int             socket;
281    tr_address      addr;
282    tr_socketList * next;
283};
284
285tr_socketList *
286tr_socketListAppend( tr_socketList * const head,
287                     const tr_address * const addr )
288{
289    tr_socketList * tmp;
290
291    assert( head );
292    assert( tr_isAddress( addr ) );
293
294    for( tmp = head; tmp->next; tmp = tmp->next );
295    tmp->next = tr_socketListNew( addr );
296    return tmp->next;
297}
298
299tr_socketList *
300tr_socketListNew( const tr_address * const addr )
301{
302    tr_socketList * tmp;
303
304    assert( tr_isAddress( addr ) );
305
306    tmp = tr_new( tr_socketList, 1 );
307    tmp->socket = -1;
308    tmp->addr = *addr;
309    tmp->next = NULL;
310    return tmp;
311}
312
313void
314tr_socketListFree( tr_socketList * const head )
315{
316    assert( head );
317
318    if( head->next )
319        tr_socketListFree( head->next );
320    tr_free( head );
321}
322
323void
324tr_socketListRemove( tr_socketList * const head,
325                     tr_socketList * const el)
326{
327    tr_socketList * tmp;
328
329    assert( head );
330    assert( el );
331
332    for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
333    tmp->next = el->next;
334    el->next = NULL;
335    tr_socketListFree(el);
336}
337
338void
339tr_socketListTruncate( tr_socketList * const head,
340                       tr_socketList * const start )
341{
342    tr_socketList * tmp;
343
344    assert( head );
345    assert( start );
346
347    for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
348    tr_socketListFree( start );
349    tmp->next = NULL;
350}
351
352#if 0
353int
354tr_socketListGetSocket( const tr_socketList * const el )
355{
356    assert( el );
357
358    return el->socket;
359}
360
361const tr_address *
362tr_socketListGetAddress( const tr_socketList * const el )
363{
364    assert( el );
365    return &el->addr;
366}
367#endif
368
369void
370tr_socketListForEach( tr_socketList * const head,
371                      void ( * cb ) ( int * const,
372                                      tr_address * const,
373                                      void * const),
374                      void * const userData )
375{
376    tr_socketList * tmp;
377    for( tmp = head; tmp; tmp = tmp->next )
378        cb( &tmp->socket, &tmp->addr, userData );
379}
380
381/***********************************************************************
382 * TCP sockets
383 **********************************************************************/
384
385int
386tr_netSetTOS( int s, int tos )
387{
388#ifdef IP_TOS
389    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
390#else
391    return 0;
392#endif
393}
394
395static int
396makeSocketNonBlocking( int fd )
397{
398    if( fd >= 0 )
399    {
400        if( evutil_make_socket_nonblocking( fd ) )
401        {
402            int tmperrno;
403            tr_err( _( "Couldn't create socket: %s" ),
404                   tr_strerror( sockerrno ) );
405            tmperrno = sockerrno;
406            tr_netClose( fd );
407            fd = -tmperrno;
408        }
409    }
410
411    return fd;
412}
413
414static int
415createSocket( int domain, int type )
416{
417    return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
418}
419
420static void
421setSndBuf( tr_session * session UNUSED, int fd UNUSED )
422{
423#if 0
424    if( fd >= 0 )
425    {
426        const int sndbuf = session->so_sndbuf;
427        const int rcvbuf = session->so_rcvbuf;
428        setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
429        setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
430    }
431#endif
432}
433
434static socklen_t
435setup_sockaddr( const tr_address        * addr,
436                tr_port                   port,
437                struct sockaddr_storage * sockaddr)
438{
439    struct sockaddr_in  sock4;
440    struct sockaddr_in6 sock6;
441
442    assert( tr_isAddress( addr ) );
443
444    if( addr->type == TR_AF_INET )
445    {
446        memset( &sock4, 0, sizeof( sock4 ) );
447        sock4.sin_family      = AF_INET;
448        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
449        sock4.sin_port        = port;
450        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
451        return sizeof( struct sockaddr_in );
452    }
453    else
454    {
455        memset( &sock6, 0, sizeof( sock6 ) );
456        sock6.sin6_family = AF_INET6;
457        sock6.sin6_port = port;
458        sock6.sin6_flowinfo = 0;
459        sock6.sin6_addr = addr->addr.addr6;
460        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
461        return sizeof( struct sockaddr_in6 );
462    }
463}
464
465int
466tr_netOpenTCP( tr_session        * session,
467               const tr_address  * addr,
468               tr_port             port )
469{
470    int                     s;
471    struct sockaddr_storage sock;
472    const int               type = SOCK_STREAM;
473    socklen_t               addrlen;
474
475    assert( tr_isAddress( addr ) );
476
477    /* don't try to connect to multicast addresses */
478    if( addr->type == TR_AF_INET && IN_MULTICAST( htonl( addr->addr.addr4.s_addr ) ) )
479        return -EINVAL;
480    if( addr->type == TR_AF_INET6 && ( addr->addr.addr6.s6_addr[0] == 0xff ) )
481        return -EINVAL;
482
483    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ), type ) ) < 0 )
484        return s;
485
486    setSndBuf( session, s );
487
488    addrlen = setup_sockaddr( addr, port, &sock );
489
490    if( ( connect( s, (struct sockaddr *) &sock,
491                  addrlen ) < 0 )
492#ifdef WIN32
493      && ( sockerrno != WSAEWOULDBLOCK )
494#endif
495      && ( sockerrno != EINPROGRESS ) )
496    {
497        int tmperrno;
498        tmperrno = sockerrno;
499        if( tmperrno != ENETUNREACH || addr->type == TR_AF_INET )
500            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
501                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
502                    tr_strerror( tmperrno ) );
503        tr_netClose( s );
504        s = -tmperrno;
505    }
506
507    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
508               s, tr_peerIoAddrStr( addr, port ) );
509
510    return s;
511}
512
513int
514tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
515{
516    int                     s;
517    struct sockaddr_storage sock;
518    const int               type = SOCK_STREAM;
519    int                     addrlen;
520
521#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
522    int                optval;
523#endif
524
525    assert( tr_isAddress( addr ) );
526
527    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
528                            type ) ) < 0 )
529        return s;
530
531#ifdef SO_REUSEADDR
532    optval = 1;
533    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
534#endif
535
536    addrlen = setup_sockaddr( addr, htons( port ), &sock );
537
538    if( bind( s, (struct sockaddr *) &sock,
539             addrlen ) )
540    {
541        int tmperrno;
542        if( !suppressMsgs )
543            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
544                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
545        tmperrno = sockerrno;
546        tr_netClose( s );
547        return -tmperrno;
548    }
549    if( !suppressMsgs )
550        tr_dbg(  "Bound socket %d to port %d on %s",
551                 s, port, tr_ntop_non_ts( addr ) );
552    return s;
553}
554
555int
556tr_netAccept( tr_session  * session,
557              int           b,
558              tr_address  * addr,
559              tr_port     * port )
560{
561    int fd;
562
563    fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
564    setSndBuf( session, fd );
565    return fd;
566}
567
568void
569tr_netClose( int s )
570{
571    tr_fdSocketClose( s );
572}
Note: See TracBrowser for help on using the repository browser.