source: trunk/libtransmission/net.c @ 7397

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

(trunk libT) add ipv6 support by jhujhiti. I think this is the largest user-contributed patch we've ever used... thanks jhujhiti :)

  • Property svn:keywords set to Date Rev Author Id
File size: 10.7 KB
Line 
1/******************************************************************************
2 * $Id: net.c 7397 2008-12-15 00:17:08Z 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#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
71
72const char * 
73tr_ntop( const tr_address * src, char * dst, int size ) 
74{ 
75    if( src->type == TR_AF_INET ) 
76        return inet_ntop( AF_INET, &src->addr, dst, size ); 
77    else 
78        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
79} 
80
81/*
82 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
83 * This function is suitable to be called from libTransmission's networking code,
84 * which is single-threaded.
85 */ 
86const char * 
87tr_ntop_non_ts( const tr_address * src ) 
88{ 
89    static char buf[INET6_ADDRSTRLEN]; 
90    return tr_ntop( src, buf, sizeof( buf ) ); 
91} 
92
93tr_address * 
94tr_pton( const char * src, tr_address * dst ) 
95{ 
96    int retval = inet_pton( AF_INET, src, &dst->addr ); 
97    if( retval < 0 ) 
98        return NULL; 
99    else if( retval == 0 ) 
100        retval = inet_pton( AF_INET6, src, &dst->addr ); 
101    else
102    { 
103        dst->type = TR_AF_INET; 
104        return dst; 
105    } 
106
107    if( retval < 1 ) 
108        return NULL; 
109    dst->type = TR_AF_INET6; 
110    return dst; 
111}
112
113void
114tr_normalizeV4Mapped( tr_address * const addr )
115{
116    if( addr->type == TR_AF_INET6 && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) )
117    {
118        addr->type = TR_AF_INET;
119        memcpy( &addr->addr.addr4.s_addr, addr->addr.addr6.s6_addr + 12, 4 );
120    }
121}
122
123/*
124 * Compare two tr_address structures.
125 * Returns:
126 * <0 if a < b
127 * >0 if a > b
128 * 0  if a == b
129 */ 
130int 
131tr_compareAddresses( const tr_address * a, const tr_address * b) 
132{ 
133    int retval; 
134    int addrlen; 
135
136    /* IPv6 addresses are always "greater than" IPv4 */ 
137    if( a->type == TR_AF_INET && b->type == TR_AF_INET6 ) 
138        return 1; 
139    if( a->type == TR_AF_INET6 && b->type == TR_AF_INET ) 
140        return -1; 
141
142    if( a->type == TR_AF_INET ) 
143        addrlen = sizeof( struct in_addr ); 
144    else 
145        addrlen = sizeof( struct in6_addr ); 
146    retval = memcmp( &a->addr, &b->addr, addrlen ); 
147    if( retval == 0 ) 
148        return 0; 
149     
150    return retval; 
151} 
152
153/***********************************************************************
154 * Socket list housekeeping
155 **********************************************************************/
156struct tr_socketList
157{
158    int             socket;
159    tr_address      addr;
160    tr_socketList * next;
161};
162
163tr_socketList *
164tr_socketListAppend( tr_socketList * const head,
165                     const tr_address * const addr )
166{
167    tr_socketList * tmp;
168    assert( head );
169    for( tmp = head; tmp->next; tmp = tmp->next );
170    tmp->next = tr_socketListNew( addr );
171    return tmp->next;
172}
173
174tr_socketList *
175tr_socketListNew( const tr_address * const addr )
176{
177    tr_socketList * tmp;
178    tmp = tr_new( tr_socketList, 1 );
179    tmp->socket = -1;
180    tmp->addr = *addr;
181    tmp->next = NULL;
182    return tmp;
183}
184
185void
186tr_socketListFree( tr_socketList * const head )
187{
188    assert( head );
189    if( head->next )
190        tr_socketListFree( head->next );
191    tr_free( head );
192}
193
194void
195tr_socketListRemove( tr_socketList * const head,
196                     tr_socketList * const el)
197{
198    tr_socketList * tmp;
199    assert( head );
200    assert( el );
201    for( tmp = head; tmp->next && tmp->next != el; tmp = tmp->next );
202    tmp->next = el->next;
203    el->next = NULL;
204    tr_socketListFree(el);
205}
206
207void
208tr_socketListTruncate( tr_socketList * const head,
209                       tr_socketList * const start )
210{
211    tr_socketList * tmp;
212    assert( head );
213    assert( start );
214    for( tmp = head; tmp->next && tmp->next != start; tmp = tmp->next );
215    tr_socketListFree( start );
216    tmp->next = NULL;
217}
218
219int
220tr_socketListGetSocket( const tr_socketList * const el )
221{
222    assert( el );
223    return el->socket;
224}
225
226const tr_address *
227tr_socketListGetAddress( const tr_socketList * const el )
228{
229    assert( el );
230    return &el->addr;
231}
232
233void
234tr_socketListForEach( tr_socketList * const head,
235                      void ( * cb ) ( int * const,
236                                      tr_address * const,
237                                      void * const),
238                      void * const userData )
239{
240    tr_socketList * tmp;
241    for( tmp = head; tmp; tmp = tmp->next )
242        cb( &tmp->socket, &tmp->addr, userData );
243}
244
245/***********************************************************************
246 * TCP sockets
247 **********************************************************************/
248
249int
250tr_netSetTOS( int s,
251              int tos )
252{
253#ifdef IP_TOS
254    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
255#else
256    return 0;
257#endif
258}
259
260static int
261makeSocketNonBlocking( int fd )
262{
263    if( fd >= 0 )
264    {
265        if( evutil_make_socket_nonblocking( fd ) )
266        {
267            int tmperrno;
268            tr_err( _( "Couldn't create socket: %s" ),
269                   tr_strerror( sockerrno ) );
270            tmperrno = sockerrno;
271            tr_netClose( fd );
272            fd = -tmperrno;
273        }
274    }
275
276    return fd;
277}
278
279static int
280createSocket( int domain, int type )
281{
282    return makeSocketNonBlocking( tr_fdSocketCreate( domain, type ) );
283}
284
285static void
286setSndBuf( tr_session * session UNUSED, int fd UNUSED )
287{
288#if 0
289    if( fd >= 0 )
290    {
291        const int sndbuf = session->so_sndbuf;
292        const int rcvbuf = session->so_rcvbuf;
293        setsockopt( fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof( sndbuf ) );
294        setsockopt( fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof( rcvbuf ) );
295    }
296#endif
297}
298
299static socklen_t
300setup_sockaddr( const tr_address        * addr,
301                tr_port                   port,
302                struct sockaddr_storage * sockaddr)
303{
304    struct sockaddr_in  sock4;
305    struct sockaddr_in6 sock6;
306    if( addr->type == TR_AF_INET )
307    {
308        memset( &sock4, 0, sizeof( sock4 ) );
309        sock4.sin_family      = AF_INET;
310        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
311        sock4.sin_port        = port;
312        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
313        return sizeof( struct sockaddr_in );
314    }
315    else
316    {
317        memset( &sock6, 0, sizeof( sock6 ) );
318        sock6.sin6_family = AF_INET6;
319        sock6.sin6_port = port;
320        sock6.sin6_flowinfo = 0;
321        sock6.sin6_addr = addr->addr.addr6;
322        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
323        return sizeof( struct sockaddr_in6 );
324    }
325}
326
327int
328tr_netOpenTCP( tr_session        * session,
329               const tr_address  * addr,
330               tr_port             port )
331{
332    int                     s;
333    struct sockaddr_storage sock;
334    const int               type = SOCK_STREAM;
335    socklen_t               addrlen;
336
337    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
338                            type ) ) < 0 )
339        return s;
340
341    setSndBuf( session, s );
342
343    addrlen = setup_sockaddr( addr, port, &sock );
344
345    if( ( connect( s, (struct sockaddr *) &sock,
346                  addrlen ) < 0 )
347#ifdef WIN32
348      && ( sockerrno != WSAEWOULDBLOCK )
349#endif
350      && ( sockerrno != EINPROGRESS ) )
351    {
352        int tmperrno;
353        tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
354               s, tr_ntop_non_ts( addr ), (int)port, sockerrno, tr_strerror( sockerrno ) );
355        tmperrno = sockerrno;
356        tr_netClose( s );
357        s = -tmperrno;
358    }
359
360    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
361               s, tr_peerIoAddrStr( addr, port ) );
362
363    return s;
364}
365
366int
367tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
368{
369    int                     s;
370    struct sockaddr_storage sock;
371    const int               type = SOCK_STREAM;
372    int                     addrlen;
373
374#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
375    int                optval;
376#endif
377
378    if( ( s = createSocket( ( addr->type == TR_AF_INET ? AF_INET : AF_INET6 ),
379                            type ) ) < 0 )
380        return s;
381
382#ifdef SO_REUSEADDR
383    optval = 1;
384    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
385#endif
386
387    addrlen = setup_sockaddr( addr, htons( port ), &sock );
388
389    if( bind( s, (struct sockaddr *) &sock,
390             addrlen ) )
391    {
392        int tmperrno;
393        if( !suppressMsgs )
394            tr_err( _( "Couldn't bind port %d on %s: %s" ), port,
395                    tr_ntop_non_ts( addr ), tr_strerror( sockerrno ) );
396        tmperrno = sockerrno;
397        tr_netClose( s );
398        return -tmperrno;
399    }
400    if( !suppressMsgs )
401        tr_dbg(  "Bound socket %d to port %d on %s",
402                 s, port, tr_ntop_non_ts( addr ) );
403    return s;
404}
405
406int
407tr_netAccept( tr_session  * session,
408              int           b,
409              tr_address  * addr,
410              tr_port     * port )
411{
412    int fd = makeSocketNonBlocking( tr_fdSocketAccept( b, addr, port ) );
413    setSndBuf( session, fd );
414    return fd;
415}
416
417void
418tr_netClose( int s )
419{
420    tr_fdSocketClose( s );
421}
Note: See TracBrowser for help on using the repository browser.