source: trunk/libtransmission/net.c @ 7439

Last change on this file since 7439 was 7439, checked in by jhujhiti, 12 years ago

(trunk libT) Don't log two "errors" we expect to see from time-to-time.

EAFNOSUPPORT: On OS X, socket() incorrectly throws EAFNOSUPPORT for certain
IP/Port combinations. There is nothing we can do about this.

ENETUNREACH: #1606 is evidence that some trackers return IPv6 peers when
contacted over IPv4. This code will silently ignore "network unreachable"
errors for IPv6 connections.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.0 KB
Line 
1/******************************************************************************
2 * $Id: net.c 7439 2008-12-20 08:51:32Z jhujhiti $
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#else
36 #include <arpa/inet.h> /* inet_addr */
37 #include <netdb.h>
38 #include <fcntl.h>
39#endif
40
41#include <evutil.h>
42
43#include "transmission.h"
44#include "fdlimit.h"
45#include "natpmp.h"
46#include "net.h"
47#include "peer-io.h"
48#include "platform.h"
49#include "utils.h"
50
51const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } }; 
52const tr_address tr_inaddr_any = { TR_AF_INET, 
53    { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } }; 
54
55
56void
57tr_netInit( void )
58{
59    static int initialized = FALSE;
60
61    if( !initialized )
62    {
63#ifdef WIN32
64        WSADATA wsaData;
65        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
66#endif
67        initialized = TRUE;
68    }
69}
70
71tr_bool
72tr_isAddress( const tr_address * a )
73{
74    return a && ( a->type==TR_AF_INET || a->type==TR_AF_INET6 );
75}
76
77const char * 
78tr_ntop( const tr_address * src, char * dst, int size ) 
79{
80    assert( tr_isAddress( src ) );
81
82    if( src->type == TR_AF_INET ) 
83        return inet_ntop( AF_INET, &src->addr, dst, size ); 
84    else 
85        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
86} 
87
88/*
89 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
90 * This function is suitable to be called from libTransmission's networking code,
91 * which is single-threaded.
92 */ 
93const char * 
94tr_ntop_non_ts( const tr_address * src ) 
95{ 
96    static char buf[INET6_ADDRSTRLEN]; 
97    return tr_ntop( src, buf, sizeof( buf ) ); 
98} 
99
100tr_address * 
101tr_pton( const char * src, tr_address * dst ) 
102{ 
103    int retval = inet_pton( AF_INET, src, &dst->addr ); 
104    if( retval < 0 ) 
105        return NULL; 
106    else if( retval == 0 ) 
107        retval = inet_pton( AF_INET6, src, &dst->addr ); 
108    else
109    { 
110        dst->type = TR_AF_INET; 
111        return dst; 
112    } 
113
114    if( retval < 1 ) 
115        return NULL; 
116    dst->type = TR_AF_INET6; 
117    return dst; 
118}
119
120void
121tr_normalizeV4Mapped( tr_address * const addr )
122{
123    assert( tr_isAddress( addr ) );
124
125    if( addr->type == TR_AF_INET6 && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) )
126    {
127        addr->type = TR_AF_INET;
128        memcpy( &addr->addr.addr4.s_addr, addr->addr.addr6.s6_addr + 12, 4 );
129    }
130}
131
132/*
133 * Compare two tr_address structures.
134 * Returns:
135 * <0 if a < b
136 * >0 if a > b
137 * 0  if a == b
138 */ 
139int
140tr_compareAddresses( const tr_address * a, const tr_address * b)
141{
142    int addrlen;
143
144    assert( tr_isAddress( a ) );
145    assert( tr_isAddress( b ) );
146
147    /* IPv6 addresses are always "greater than" IPv4 */ 
148    if( a->type != b->type )
149        return a->type == TR_AF_INET ? 1 : -1;
150
151    if( a->type == TR_AF_INET ) 
152        addrlen = sizeof( struct in_addr ); 
153    else 
154        addrlen = sizeof( struct in6_addr ); 
155    return memcmp( &a->addr, &b->addr, addrlen );
156} 
157
158tr_net_af_support
159tr_net_getAFSupport( tr_port port )
160{
161    /* Do we care if an address is in use? Probably not, since it will be
162     * caught later. This will only set up the list of sockets to bind. */
163    static tr_bool alreadyDone       = FALSE;
164    static tr_net_af_support support = { FALSE, FALSE };
165    int s4, s6;
166    if( alreadyDone )
167        return support;
168    s6 = tr_netBindTCP( &tr_in6addr_any, port, TRUE );
169    if( s6 >= 0 || -s6 != EAFNOSUPPORT ) /* we support ipv6 */
170    {
171        listen( s6, 1 );
172        support.has_inet6 = TRUE;
173    }
174    s4 = tr_netBindTCP( &tr_inaddr_any, port, TRUE );
175    if( s4 >= 0 ) /* we bound *with* the ipv6 socket bound (need both)
176                   * or only have ipv4 */
177    {
178        tr_netClose( s4 );
179        support.needs_inet4 = TRUE;
180    }
181    if( s6 >= 0 )
182        tr_netClose( s6 );
183    alreadyDone = TRUE;
184    return support;
185}
186
187/***********************************************************************
188 * Socket list housekeeping
189 **********************************************************************/
190struct tr_socketList
191{
192    int             socket;
193    tr_address      addr;
194    tr_socketList * next;
195};
196
197tr_socketList *
198tr_socketListAppend( tr_socketList * const head,
199                     const tr_address * const addr )
200{
201    tr_socketList * tmp;
202
203    assert( head );
204    assert( tr_isAddress( addr ) );
205
206    for( tmp = head; tmp->next; tmp = tmp->next );
207    tmp->next = tr_socketListNew( addr );
208    return tmp->next;
209}
210
211tr_socketList *
212tr_socketListNew( const tr_address * const addr )
213{
214    tr_socketList * tmp;
215
216    assert( tr_isAddress( addr ) );
217
218    tmp = tr_new( tr_socketList, 1 );
219    tmp->socket = -1;
220    tmp->addr = *addr;
221    tmp->next = NULL;
222    return tmp;
223}
224
225void
226tr_socketListFree( tr_socketList * const head )
227{
228    assert( head );
229
230    if( head->next )
231        tr_socketListFree( head->next );
232    tr_free( head );
233}
234
235void
236tr_socketListRemove( tr_socketList * const head,
237                     tr_socketList * const el)
238{
239    tr_socketList * tmp;
240
241    assert( head );
242    assert( el );
243
244    for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
245    tmp->next = el->next;
246    el->next = NULL;
247    tr_socketListFree(el);
248}
249
250void
251tr_socketListTruncate( tr_socketList * const head,
252                       tr_socketList * const start )
253{
254    tr_socketList * tmp;
255
256    assert( head );
257    assert( start );
258
259    for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
260    tr_socketListFree( start );
261    tmp->next = NULL;
262}
263
264#if 0
265int
266tr_socketListGetSocket( const tr_socketList * const el )
267{
268    assert( el );
269
270    return el->socket;
271}
272
273const tr_address *
274tr_socketListGetAddress( const tr_socketList * const el )
275{
276    assert( el );
277    return &el->addr;
278}
279#endif
280
281void
282tr_socketListForEach( tr_socketList * const head,
283                      void ( * cb ) ( int * const,
284                                      tr_address * const,
285                                      void * const),
286                      void * const userData )
287{
288    tr_socketList * tmp;
289    for( tmp = head; tmp; tmp = tmp->next )
290        cb( &tmp->socket, &tmp->addr, userData );
291}
292
293/***********************************************************************
294 * TCP sockets
295 **********************************************************************/
296
297int
298tr_netSetTOS( int s, int tos )
299{
300#ifdef IP_TOS
301    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
302#else
303    return 0;
304#endif
305}
306
307static int
308makeSocketNonBlocking( int fd )
309{
310    if( fd >= 0 )
311    {
312        if( evutil_make_socket_nonblocking( fd ) )
313        {
314            int tmperrno;
315            tr_err( _( "Couldn't create socket: %s" ),
316                   tr_strerror( sockerrno ) );
317            tmperrno = sockerrno;
318            tr_netClose( fd );
319            fd = -tmperrno;
320        }
321    }
322
323    return fd;
324}
325
326static int
327createSocket( int domain, int type )
328{
329    return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
330}
331
332static void
333setSndBuf( tr_session * session UNUSED, int fd UNUSED )
334{
335#if 0
336    if( fd >= 0 )
337    {
338        const int sndbuf = session->so_sndbuf;
339        const int rcvbuf = session->so_rcvbuf;
340        setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
341        setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
342    }
343#endif
344}
345
346static socklen_t
347setup_sockaddr( const tr_address        * addr,
348                tr_port                   port,
349                struct sockaddr_storage * sockaddr)
350{
351    struct sockaddr_in  sock4;
352    struct sockaddr_in6 sock6;
353
354    assert( tr_isAddress( addr ) );
355
356    if( addr->type == TR_AF_INET )
357    {
358        memset( &sock4, 0, sizeof( sock4 ) );
359        sock4.sin_family      = AF_INET;
360        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
361        sock4.sin_port        = port;
362        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
363        return sizeof( struct sockaddr_in );
364    }
365    else
366    {
367        memset( &sock6, 0, sizeof( sock6 ) );
368        sock6.sin6_family = AF_INET6;
369        sock6.sin6_port = port;
370        sock6.sin6_flowinfo = 0;
371        sock6.sin6_addr = addr->addr.addr6;
372        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
373        return sizeof( struct sockaddr_in6 );
374    }
375}
376
377int
378tr_netOpenTCP( tr_session        * session,
379               const tr_address  * addr,
380               tr_port             port )
381{
382    int                     s;
383    struct sockaddr_storage sock;
384    const int               type = SOCK_STREAM;
385    socklen_t               addrlen;
386
387    assert( tr_isAddress( addr ) );
388
389    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
390                            type ) ) < 0 )
391        return s;
392
393    setSndBuf( session, s );
394
395    addrlen = setup_sockaddr( addr, port, &sock );
396
397    if( ( connect( s, (struct sockaddr *) &sock,
398                  addrlen ) < 0 )
399#ifdef WIN32
400      && ( sockerrno != WSAEWOULDBLOCK )
401#endif
402      && ( sockerrno != EINPROGRESS ) )
403    {
404        int tmperrno;
405        tmperrno = sockerrno;
406        if( tmperrno != ENETUNREACH || addr->type == TR_AF_INET )
407            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
408                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
409                    tr_strerror( tmperrno ) );
410        tr_netClose( s );
411        s = -tmperrno;
412    }
413
414    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
415               s, tr_peerIoAddrStr( addr, port ) );
416
417    return s;
418}
419
420int
421tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
422{
423    int                     s;
424    struct sockaddr_storage sock;
425    const int               type = SOCK_STREAM;
426    int                     addrlen;
427
428#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
429    int                optval;
430#endif
431
432    assert( tr_isAddress( addr ) );
433
434    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
435                            type ) ) < 0 )
436        return s;
437
438#ifdef SO_REUSEADDR
439    optval = 1;
440    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
441#endif
442
443    addrlen = setup_sockaddr( addr, htons( port ), &sock );
444
445    if( bind( s, (struct sockaddr *) &sock,
446             addrlen ) )
447    {
448        int tmperrno;
449        if( !suppressMsgs )
450            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
451                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
452        tmperrno = sockerrno;
453        tr_netClose( s );
454        return -tmperrno;
455    }
456    if( !suppressMsgs )
457        tr_dbg(  "Bound socket %d to port %d on %s",
458                 s, port, tr_ntop_non_ts( addr ) );
459    return s;
460}
461
462int
463tr_netAccept( tr_session  * session,
464              int           b,
465              tr_address  * addr,
466              tr_port     * port )
467{
468    int fd;
469
470    fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
471    setSndBuf( session, fd );
472    return fd;
473}
474
475void
476tr_netClose( int s )
477{
478    tr_fdSocketClose( s );
479}
Note: See TracBrowser for help on using the repository browser.