source: trunk/libtransmission/net.c @ 7663

Last change on this file since 7663 was 7663, checked in by charles, 13 years ago

(trunk libT) win32 portability patch from Alexey

  • Property svn:keywords set to Date Rev Author Id
File size: 14.6 KB
Line 
1/******************************************************************************
2 * $Id: net.c 7663 2009-01-11 17:02:04Z 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, const char * source )
133{
134    /* be really aggressive in what we report */
135    if( a->type == TR_AF_INET && !( ntohl( a->addr.addr4.s_addr ) & 0xff000000 ) )
136        tr_err(  "Funny looking address %s from %s", tr_ntop_non_ts( a ), source );
137    /* /16s taken from ipv6 rib on 21 dec, 2008 */
138    /* this is really, really ugly. expedience over quality */
139    if( a->type == TR_AF_INET6 )
140    {
141        uint16_t slash16;
142        uint16_t valid[] = { 0x339, 0x2002, 0x2003, 0x2400, 0x2401, 0x2402,
143            0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2600, 0x2607, 0x2610,
144            0x2620, 0x2800, 0x2801, 0x2a00, 0x2a01, 0x0a02, 0x2001, 0x0000 };
145        uint16_t *p;
146        tr_bool good = FALSE;
147        p = valid;
148        memcpy( &slash16, &a->addr, 2 );
149        slash16 = ntohs( slash16 );
150        while( *p )
151        {
152            if( slash16 == *p )
153                good = TRUE;
154            p++;
155        }
156        if( !good && !IN6_IS_ADDR_V4MAPPED( &a->addr.addr6 ) )
157            tr_err(  "Funny looking address %s from %s", tr_ntop_non_ts( a ), source );
158    }
159}
160
161const char * 
162tr_ntop( const tr_address * src, char * dst, int size ) 
163{
164    assert( tr_isAddress( src ) );
165
166    if( src->type == TR_AF_INET ) 
167        return inet_ntop( AF_INET, &src->addr, dst, size ); 
168    else 
169        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
170} 
171
172/*
173 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
174 * This function is suitable to be called from libTransmission's networking code,
175 * which is single-threaded.
176 */ 
177const char * 
178tr_ntop_non_ts( const tr_address * src ) 
179{ 
180    static char buf[INET6_ADDRSTRLEN]; 
181    return tr_ntop( src, buf, sizeof( buf ) ); 
182} 
183
184tr_address * 
185tr_pton( const char * src, tr_address * dst ) 
186{ 
187    int retval = inet_pton( AF_INET, src, &dst->addr ); 
188    if( retval < 0 ) 
189        return NULL; 
190    else if( retval == 0 ) 
191        retval = inet_pton( AF_INET6, src, &dst->addr ); 
192    else
193    { 
194        dst->type = TR_AF_INET; 
195        return dst; 
196    } 
197
198    if( retval < 1 ) 
199        return NULL; 
200    dst->type = TR_AF_INET6; 
201    return dst; 
202}
203
204void
205tr_normalizeV4Mapped( tr_address * const addr )
206{
207    assert( tr_isAddress( addr ) );
208
209    if( addr->type == TR_AF_INET6 && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) )
210    {
211        addr->type = TR_AF_INET;
212        memcpy( &addr->addr.addr4.s_addr, addr->addr.addr6.s6_addr + 12, 4 );
213    }
214}
215
216/*
217 * Compare two tr_address structures.
218 * Returns:
219 * <0 if a < b
220 * >0 if a > b
221 * 0  if a == b
222 */ 
223int
224tr_compareAddresses( const tr_address * a, const tr_address * b)
225{
226    int addrlen;
227
228    assert( tr_isAddress( a ) );
229    assert( tr_isAddress( b ) );
230
231    /* IPv6 addresses are always "greater than" IPv4 */ 
232    if( a->type != b->type )
233        return a->type == TR_AF_INET ? 1 : -1;
234
235    if( a->type == TR_AF_INET ) 
236        addrlen = sizeof( struct in_addr ); 
237    else 
238        addrlen = sizeof( struct in6_addr ); 
239    return memcmp( &a->addr, &b->addr, addrlen );
240} 
241
242tr_net_af_support
243tr_net_getAFSupport( tr_port port )
244{
245    /* Do we care if an address is in use? Probably not, since it will be
246     * caught later. This will only set up the list of sockets to bind. */
247    static tr_bool alreadyDone       = FALSE;
248    static tr_net_af_support support = { FALSE, FALSE };
249    int s4, s6;
250    if( alreadyDone )
251        return support;
252    s6 = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
253    if( s6 >= 0 || -s6 != EAFNOSUPPORT ) /* we support ipv6 */
254    {
255        listen( s6, 1 );
256        support.has_inet6 = TRUE;
257    }
258    s4 = tr_netBindTCP( &tr_inaddr_any, port, TRUE );
259    if( s4 >= 0 ) /* we bound *with* the ipv6 socket bound (need both)
260                   * or only have ipv4 */
261    {
262        tr_netClose( s4 );
263        support.needs_inet4 = TRUE;
264    }
265    if( s6 >= 0 )
266        tr_netClose( s6 );
267    alreadyDone = TRUE;
268    return support;
269}
270
271/***********************************************************************
272 * Socket list housekeeping
273 **********************************************************************/
274struct tr_socketList
275{
276    int             socket;
277    tr_address      addr;
278    tr_socketList * next;
279};
280
281tr_socketList *
282tr_socketListAppend( tr_socketList * const head,
283                     const tr_address * const addr )
284{
285    tr_socketList * tmp;
286
287    assert( head );
288    assert( tr_isAddress( addr ) );
289
290    for( tmp = head; tmp->next; tmp = tmp->next );
291    tmp->next = tr_socketListNew( addr );
292    return tmp->next;
293}
294
295tr_socketList *
296tr_socketListNew( const tr_address * const addr )
297{
298    tr_socketList * tmp;
299
300    assert( tr_isAddress( addr ) );
301
302    tmp = tr_new( tr_socketList, 1 );
303    tmp->socket = -1;
304    tmp->addr = *addr;
305    tmp->next = NULL;
306    return tmp;
307}
308
309void
310tr_socketListFree( tr_socketList * const head )
311{
312    assert( head );
313
314    if( head->next )
315        tr_socketListFree( head->next );
316    tr_free( head );
317}
318
319void
320tr_socketListRemove( tr_socketList * const head,
321                     tr_socketList * const el)
322{
323    tr_socketList * tmp;
324
325    assert( head );
326    assert( el );
327
328    for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
329    tmp->next = el->next;
330    el->next = NULL;
331    tr_socketListFree(el);
332}
333
334void
335tr_socketListTruncate( tr_socketList * const head,
336                       tr_socketList * const start )
337{
338    tr_socketList * tmp;
339
340    assert( head );
341    assert( start );
342
343    for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
344    tr_socketListFree( start );
345    tmp->next = NULL;
346}
347
348#if 0
349int
350tr_socketListGetSocket( const tr_socketList * const el )
351{
352    assert( el );
353
354    return el->socket;
355}
356
357const tr_address *
358tr_socketListGetAddress( const tr_socketList * const el )
359{
360    assert( el );
361    return &el->addr;
362}
363#endif
364
365void
366tr_socketListForEach( tr_socketList * const head,
367                      void ( * cb ) ( int * const,
368                                      tr_address * const,
369                                      void * const),
370                      void * const userData )
371{
372    tr_socketList * tmp;
373    for( tmp = head; tmp; tmp = tmp->next )
374        cb( &tmp->socket, &tmp->addr, userData );
375}
376
377/***********************************************************************
378 * TCP sockets
379 **********************************************************************/
380
381int
382tr_netSetTOS( int s, int tos )
383{
384#ifdef IP_TOS
385    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
386#else
387    return 0;
388#endif
389}
390
391static int
392makeSocketNonBlocking( int fd )
393{
394    if( fd >= 0 )
395    {
396        if( evutil_make_socket_nonblocking( fd ) )
397        {
398            int tmperrno;
399            tr_err( _( "Couldn't create socket: %s" ),
400                   tr_strerror( sockerrno ) );
401            tmperrno = sockerrno;
402            tr_netClose( fd );
403            fd = -tmperrno;
404        }
405    }
406
407    return fd;
408}
409
410static int
411createSocket( int domain, int type )
412{
413    return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
414}
415
416static void
417setSndBuf( tr_session * session UNUSED, int fd UNUSED )
418{
419#if 0
420    if( fd >= 0 )
421    {
422        const int sndbuf = session->so_sndbuf;
423        const int rcvbuf = session->so_rcvbuf;
424        setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
425        setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
426    }
427#endif
428}
429
430static socklen_t
431setup_sockaddr( const tr_address        * addr,
432                tr_port                   port,
433                struct sockaddr_storage * sockaddr)
434{
435    struct sockaddr_in  sock4;
436    struct sockaddr_in6 sock6;
437
438    assert( tr_isAddress( addr ) );
439
440    if( addr->type == TR_AF_INET )
441    {
442        memset( &sock4, 0, sizeof( sock4 ) );
443        sock4.sin_family      = AF_INET;
444        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
445        sock4.sin_port        = port;
446        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
447        return sizeof( struct sockaddr_in );
448    }
449    else
450    {
451        memset( &sock6, 0, sizeof( sock6 ) );
452        sock6.sin6_family = AF_INET6;
453        sock6.sin6_port = port;
454        sock6.sin6_flowinfo = 0;
455        sock6.sin6_addr = addr->addr.addr6;
456        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
457        return sizeof( struct sockaddr_in6 );
458    }
459}
460
461int
462tr_netOpenTCP( tr_session        * session,
463               const tr_address  * addr,
464               tr_port             port )
465{
466    int                     s;
467    struct sockaddr_storage sock;
468    const int               type = SOCK_STREAM;
469    socklen_t               addrlen;
470
471    assert( tr_isAddress( addr ) );
472
473    /* don't try to connect to multicast addresses */
474    if( addr->type == TR_AF_INET && IN_MULTICAST( htonl( addr->addr.addr4.s_addr ) ) )
475        return -EINVAL;
476    if( addr->type == TR_AF_INET6 && ( addr->addr.addr6.s6_addr[0] & 0xff ) )
477        return -EINVAL;
478
479    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
480                            type ) ) < 0 )
481        return s;
482
483    setSndBuf( session, s );
484
485    addrlen = setup_sockaddr( addr, port, &sock );
486
487    if( ( connect( s, (struct sockaddr *) &sock,
488                  addrlen ) < 0 )
489#ifdef WIN32
490      && ( sockerrno != WSAEWOULDBLOCK )
491#endif
492      && ( sockerrno != EINPROGRESS ) )
493    {
494        int tmperrno;
495        tmperrno = sockerrno;
496        if( tmperrno != ENETUNREACH || addr->type == TR_AF_INET )
497            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
498                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
499                    tr_strerror( tmperrno ) );
500        tr_netClose( s );
501        s = -tmperrno;
502    }
503
504    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
505               s, tr_peerIoAddrStr( addr, port ) );
506
507    return s;
508}
509
510int
511tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
512{
513    int                     s;
514    struct sockaddr_storage sock;
515    const int               type = SOCK_STREAM;
516    int                     addrlen;
517
518#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
519    int                optval;
520#endif
521
522    assert( tr_isAddress( addr ) );
523
524    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
525                            type ) ) < 0 )
526        return s;
527
528#ifdef SO_REUSEADDR
529    optval = 1;
530    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
531#endif
532
533    addrlen = setup_sockaddr( addr, htons( port ), &sock );
534
535    if( bind( s, (struct sockaddr *) &sock,
536             addrlen ) )
537    {
538        int tmperrno;
539        if( !suppressMsgs )
540            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
541                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
542        tmperrno = sockerrno;
543        tr_netClose( s );
544        return -tmperrno;
545    }
546    if( !suppressMsgs )
547        tr_dbg(  "Bound socket %d to port %d on %s",
548                 s, port, tr_ntop_non_ts( addr ) );
549    return s;
550}
551
552int
553tr_netAccept( tr_session  * session,
554              int           b,
555              tr_address  * addr,
556              tr_port     * port )
557{
558    int fd;
559
560    fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
561    setSndBuf( session, fd );
562    return fd;
563}
564
565void
566tr_netClose( int s )
567{
568    tr_fdSocketClose( s );
569}
Note: See TracBrowser for help on using the repository browser.