source: trunk/libtransmission/net.c @ 7436

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

(trunk libT) Refactor ipv6 bind socket check

This will let the RPC server use the same test mechanism eventually. Networking code belongs in net.c anyway.

  • Property svn:keywords set to Date Rev Author Id
File size: 11.9 KB
Line 
1/******************************************************************************
2 * $Id: net.c 7436 2008-12-18 05:55:22Z 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        tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
406               s, tr_ntop_non_ts( addr ), (int)port, sockerrno, tr_strerror( sockerrno ) );
407        tmperrno = sockerrno;
408        tr_netClose( s );
409        s = -tmperrno;
410    }
411
412    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
413               s, tr_peerIoAddrStr( addr, port ) );
414
415    return s;
416}
417
418int
419tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
420{
421    int                     s;
422    struct sockaddr_storage sock;
423    const int               type = SOCK_STREAM;
424    int                     addrlen;
425
426#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
427    int                optval;
428#endif
429
430    assert( tr_isAddress( addr ) );
431
432    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
433                            type ) ) < 0 )
434        return s;
435
436#ifdef SO_REUSEADDR
437    optval = 1;
438    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
439#endif
440
441    addrlen = setup_sockaddr( addr, htons( port ), &sock );
442
443    if( bind( s, (struct sockaddr *) &sock,
444             addrlen ) )
445    {
446        int tmperrno;
447        if( !suppressMsgs )
448            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
449                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
450        tmperrno = sockerrno;
451        tr_netClose( s );
452        return -tmperrno;
453    }
454    if( !suppressMsgs )
455        tr_dbg(  "Bound socket %d to port %d on %s",
456                 s, port, tr_ntop_non_ts( addr ) );
457    return s;
458}
459
460int
461tr_netAccept( tr_session  * session,
462              int           b,
463              tr_address  * addr,
464              tr_port     * port )
465{
466    int fd;
467
468    fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
469    setSndBuf( session, fd );
470    return fd;
471}
472
473void
474tr_netClose( int s )
475{
476    tr_fdSocketClose( s );
477}
Note: See TracBrowser for help on using the repository browser.