source: branches/1.5x/libtransmission/net.c @ 8204

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

(1.5x libT) various backports for 1.52:
(1) recognize Aria2 as a client
(2) remove jhujhiti's tr_suspectAddress(), since he removed it from trunka
(3) on Mac, better detection of where the Web UI files are located
(4) reintroduce the web task queue
(5) various minor formatting changes to reduce the diffs between 1.52 and trunk

  • Property svn:keywords set to Date Rev Author Id
File size: 14.0 KB
Line 
1/******************************************************************************
2 * $Id: net.c 8204 2009-04-10 17:34:25Z 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#ifdef WIN32
61static const char *
62inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
63{
64    if (af == AF_INET)
65    {
66        struct sockaddr_in in;
67        memset(&in, 0, sizeof(in));
68        in.sin_family = AF_INET;
69        memcpy(&in.sin_addr, src, sizeof(struct in_addr));
70        getnameinfo((struct sockaddr *)&in, sizeof(struct
71            sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
72        return dst;
73    }
74    else if (af == AF_INET6)
75    {
76        struct sockaddr_in6 in;
77        memset(&in, 0, sizeof(in));
78        in.sin6_family = AF_INET6;
79        memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
80        getnameinfo((struct sockaddr *)&in, sizeof(struct
81            sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
82        return dst;
83    }
84    return NULL;
85}
86
87static int
88inet_pton(int af, const char *src, void *dst)
89{
90    struct addrinfo hints;
91    struct addrinfo *res;
92    struct addrinfo *ressave;
93
94    memset(&hints, 0, sizeof(struct addrinfo));
95    hints.ai_family = af;
96
97    if (getaddrinfo(src, NULL, &hints, &res) != 0)
98        return -1;
99
100    ressave = res;
101
102    while (res)
103    {
104        memcpy(dst, res->ai_addr, res->ai_addrlen);
105        res = res->ai_next;
106    }
107
108    freeaddrinfo(ressave);
109    return 0;
110}
111
112#endif
113
114
115void
116tr_netInit( void )
117{
118    static int initialized = FALSE;
119
120    if( !initialized )
121    {
122#ifdef WIN32
123        WSADATA wsaData;
124        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
125#endif
126        initialized = TRUE;
127    }
128}
129
130const char * 
131tr_ntop( const tr_address * src, char * dst, int size ) 
132{
133    assert( tr_isAddress( src ) );
134
135    if( src->type == TR_AF_INET ) 
136        return inet_ntop( AF_INET, &src->addr, dst, size ); 
137    else 
138        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
139} 
140
141/*
142 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
143 * This function is suitable to be called from libTransmission's networking code,
144 * which is single-threaded.
145 */ 
146const char * 
147tr_ntop_non_ts( const tr_address * src ) 
148{ 
149    static char buf[INET6_ADDRSTRLEN]; 
150    return tr_ntop( src, buf, sizeof( buf ) ); 
151} 
152
153tr_address * 
154tr_pton( const char * src, tr_address * dst ) 
155{ 
156    int retval = inet_pton( AF_INET, src, &dst->addr ); 
157    if( retval < 0 ) 
158        return NULL; 
159    else if( retval == 0 ) 
160        retval = inet_pton( AF_INET6, src, &dst->addr ); 
161    else
162    { 
163        dst->type = TR_AF_INET; 
164        return dst; 
165    } 
166
167    if( retval < 1 ) 
168        return NULL; 
169    dst->type = TR_AF_INET6; 
170    return dst; 
171}
172
173/*
174 * Compare two tr_address structures.
175 * Returns:
176 * <0 if a < b
177 * >0 if a > b
178 * 0  if a == b
179 */ 
180int
181tr_compareAddresses( const tr_address * a, const tr_address * b)
182{
183    int addrlen;
184
185    assert( tr_isAddress( a ) );
186    assert( tr_isAddress( b ) );
187
188    /* IPv6 addresses are always "greater than" IPv4 */ 
189    if( a->type != b->type )
190        return a->type == TR_AF_INET ? 1 : -1;
191
192    if( a->type == TR_AF_INET ) 
193        addrlen = sizeof( struct in_addr ); 
194    else 
195        addrlen = sizeof( struct in6_addr ); 
196    return memcmp( &a->addr, &b->addr, addrlen );
197} 
198
199tr_bool
200tr_net_hasIPv6( tr_port port )
201{
202    static tr_bool alreadyDone = FALSE;
203    static tr_bool result      = FALSE;
204    int s;
205    if( alreadyDone )
206        return result;
207    s = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
208    if( s >= 0 || -s != EAFNOSUPPORT ) /* we support ipv6 */
209    {
210        result = TRUE;
211        tr_netClose( s );
212    }
213    alreadyDone = TRUE;
214    return result;
215}
216
217/***********************************************************************
218 * Socket list housekeeping
219 **********************************************************************/
220struct tr_socketList
221{
222    int             socket;
223    tr_address      addr;
224    tr_socketList * next;
225};
226
227tr_socketList *
228tr_socketListAppend( tr_socketList * const head,
229                     const tr_address * const addr )
230{
231    tr_socketList * tmp;
232
233    assert( head );
234    assert( tr_isAddress( addr ) );
235
236    for( tmp = head; tmp->next; tmp = tmp->next );
237    tmp->next = tr_socketListNew( addr );
238    return tmp->next;
239}
240
241tr_socketList *
242tr_socketListNew( const tr_address * const addr )
243{
244    tr_socketList * tmp;
245
246    assert( tr_isAddress( addr ) );
247
248    tmp = tr_new( tr_socketList, 1 );
249    tmp->socket = -1;
250    tmp->addr = *addr;
251    tmp->next = NULL;
252    return tmp;
253}
254
255void
256tr_socketListFree( tr_socketList * const head )
257{
258    assert( head );
259
260    if( head->next )
261        tr_socketListFree( head->next );
262    tr_free( head );
263}
264
265void
266tr_socketListRemove( tr_socketList * const head,
267                     tr_socketList * const el)
268{
269    tr_socketList * tmp;
270
271    assert( head );
272    assert( el );
273
274    for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
275    tmp->next = el->next;
276    el->next = NULL;
277    tr_socketListFree(el);
278}
279
280void
281tr_socketListTruncate( tr_socketList * const head,
282                       tr_socketList * const start )
283{
284    tr_socketList * tmp;
285
286    assert( head );
287    assert( start );
288
289    for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
290    tr_socketListFree( start );
291    tmp->next = NULL;
292}
293
294#if 0
295int
296tr_socketListGetSocket( const tr_socketList * const el )
297{
298    assert( el );
299
300    return el->socket;
301}
302
303const tr_address *
304tr_socketListGetAddress( const tr_socketList * const el )
305{
306    assert( el );
307    return &el->addr;
308}
309#endif
310
311void
312tr_socketListForEach( tr_socketList * const head,
313                      void ( * cb ) ( int * const,
314                                      tr_address * const,
315                                      void * const),
316                      void * const userData )
317{
318    tr_socketList * tmp;
319    for( tmp = head; tmp; tmp = tmp->next )
320        cb( &tmp->socket, &tmp->addr, userData );
321}
322
323/***********************************************************************
324 * TCP sockets
325 **********************************************************************/
326
327int
328tr_netSetTOS( int s, int tos )
329{
330#ifdef IP_TOS
331    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
332#else
333    return 0;
334#endif
335}
336
337static int
338makeSocketNonBlocking( int fd )
339{
340    if( fd >= 0 )
341    {
342        if( evutil_make_socket_nonblocking( fd ) )
343        {
344            int tmperrno;
345            tr_err( _( "Couldn't create socket: %s" ),
346                   tr_strerror( sockerrno ) );
347            tmperrno = sockerrno;
348            tr_netClose( fd );
349            fd = -tmperrno;
350        }
351    }
352
353    return fd;
354}
355
356static int
357createSocket( int domain, int type )
358{
359    return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
360}
361
362static void
363setSndBuf( tr_session * session UNUSED, int fd UNUSED )
364{
365#if 0
366    if( fd >= 0 )
367    {
368        const int sndbuf = session->so_sndbuf;
369        const int rcvbuf = session->so_rcvbuf;
370        setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
371        setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
372    }
373#endif
374}
375
376static socklen_t
377setup_sockaddr( const tr_address        * addr,
378                tr_port                   port,
379                struct sockaddr_storage * sockaddr)
380{
381    struct sockaddr_in  sock4;
382    struct sockaddr_in6 sock6;
383
384    assert( tr_isAddress( addr ) );
385
386    if( addr->type == TR_AF_INET )
387    {
388        memset( &sock4, 0, sizeof( sock4 ) );
389        sock4.sin_family      = AF_INET;
390        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
391        sock4.sin_port        = port;
392        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
393        return sizeof( struct sockaddr_in );
394    }
395    else
396    {
397        memset( &sock6, 0, sizeof( sock6 ) );
398        sock6.sin6_family = AF_INET6;
399        sock6.sin6_port = port;
400        sock6.sin6_flowinfo = 0;
401        sock6.sin6_addr = addr->addr.addr6;
402        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
403        return sizeof( struct sockaddr_in6 );
404    }
405}
406
407static tr_bool
408isMulticastAddress( const tr_address * addr )
409{
410    if( addr->type == TR_AF_INET && IN_MULTICAST( htonl( addr->addr.addr4.s_addr ) ) )
411        return TRUE;
412
413    if( addr->type == TR_AF_INET6 && ( addr->addr.addr6.s6_addr[0] == 0xff ) )
414        return TRUE;
415
416    return FALSE;
417}
418
419static TR_INLINE tr_bool
420isIPv4MappedOrCompatAddress( const tr_address * addr )
421{
422    if( addr->type == TR_AF_INET6 )
423    {
424        if( IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) ||
425            IN6_IS_ADDR_V4COMPAT( &addr->addr.addr6 ) )
426            return TRUE;
427    }
428    return FALSE;
429}
430
431static TR_INLINE tr_bool
432isIPv6LinkLocalAddress( const tr_address * addr )
433{
434    if( addr->type == TR_AF_INET6 &&
435        IN6_IS_ADDR_LINKLOCAL( &addr->addr.addr6 ) )
436        return TRUE;
437    return FALSE;
438}
439
440tr_bool
441tr_isValidPeerAddress( const tr_address * addr, tr_port port )
442{
443    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) ||
444        isIPv4MappedOrCompatAddress( addr ) )
445        return FALSE;
446
447    if( port == 0 )
448        return FALSE;
449
450    return TRUE;
451}
452
453int
454tr_netOpenTCP( tr_session        * session,
455               const tr_address  * addr,
456               tr_port             port )
457{
458    int                     s;
459    struct sockaddr_storage sock;
460    const int               type = SOCK_STREAM;
461    socklen_t               addrlen;
462
463    assert( tr_isAddress( addr ) );
464
465    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) )
466        return -EINVAL;
467
468    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ), type ) ) < 0 )
469        return s;
470
471    setSndBuf( session, s );
472
473    addrlen = setup_sockaddr( addr, port, &sock );
474
475    if( ( connect( s, (struct sockaddr *) &sock,
476                  addrlen ) < 0 )
477#ifdef WIN32
478      && ( sockerrno != WSAEWOULDBLOCK )
479#endif
480      && ( sockerrno != EINPROGRESS ) )
481    {
482        int tmperrno;
483        tmperrno = sockerrno;
484        if( ( tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH )
485                || addr->type == TR_AF_INET )
486            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
487                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
488                    tr_strerror( tmperrno ) );
489        tr_netClose( s );
490        s = -tmperrno;
491    }
492
493    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
494               s, tr_peerIoAddrStr( addr, port ) );
495
496    return s;
497}
498
499int
500tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
501{
502    int                     s;
503    struct sockaddr_storage sock;
504    const int               type = SOCK_STREAM;
505    int                     addrlen;
506    int                     retval;
507
508#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT ) || defined( IPV6_V6ONLY )
509    int                optval = 1;
510#endif
511
512    assert( tr_isAddress( addr ) );
513
514    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
515                            type ) ) < 0 )
516        return s;
517
518#ifdef SO_REUSEADDR
519    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
520#endif
521
522#ifdef IPV6_V6ONLY
523    if( addr->type == TR_AF_INET6 && 
524        ( retval = setsockopt( s, IPPROTO_IPV6, IPV6_V6ONLY, &optval,
525                             sizeof( optval ) ) ) == -1 ) {
526        /* the kernel may not support this. if not, ignore it */
527        if( errno != ENOPROTOOPT )
528            return -errno;
529    }
530#endif
531
532    addrlen = setup_sockaddr( addr, htons( port ), &sock );
533
534    if( bind( s, (struct sockaddr *) &sock,
535             addrlen ) )
536    {
537        int tmperrno;
538        if( !suppressMsgs )
539            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
540                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
541        tmperrno = sockerrno;
542        tr_netClose( s );
543        return -tmperrno;
544    }
545    if( !suppressMsgs )
546        tr_dbg(  "Bound socket %d to port %d on %s",
547                 s, port, tr_ntop_non_ts( addr ) );
548    return s;
549}
550
551int
552tr_netAccept( tr_session  * session,
553              int           b,
554              tr_address  * addr,
555              tr_port     * port )
556{
557    int fd;
558
559    fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
560    setSndBuf( session, fd );
561    return fd;
562}
563
564void
565tr_netClose( int s )
566{
567    tr_fdSocketClose( s );
568}
Note: See TracBrowser for help on using the repository browser.