source: trunk/libtransmission/net.c @ 8791

Last change on this file since 8791 was 8791, checked in by titer, 13 years ago

Don't assume that errnos are positive (they are not on Haiku), and
pass them on through an additional parameter if needed

  • Property svn:keywords set to Date Rev Author Id
File size: 12.0 KB
Line 
1/******************************************************************************
2 * $Id: net.c 8791 2009-07-09 18:14:33Z titer $
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#include <unistd.h>
42
43#include <stdarg.h> /* 1.4.x versions of evutil.h need this */
44#include <evutil.h>
45
46#include "transmission.h"
47#include "fdlimit.h"
48#include "natpmp.h"
49#include "net.h"
50#include "peer-io.h"
51#include "platform.h"
52#include "session.h"
53#include "utils.h"
54
55#ifndef IN_MULTICAST
56#define IN_MULTICAST( a ) ( ( ( a ) & 0xf0000000 ) == 0xe0000000 )
57#endif
58
59const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } }; 
60const tr_address tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } }; 
61
62#ifdef WIN32
63static const char *
64inet_ntop( int af, const void *src, char *dst, socklen_t cnt )
65{
66    if (af == AF_INET)
67    {
68        struct sockaddr_in in;
69        memset( &in, 0, sizeof( in ) );
70        in.sin_family = AF_INET;
71        memcpy( &in.sin_addr, src, sizeof( struct in_addr ) );
72        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
73                    dst, cnt, NULL, 0, NI_NUMERICHOST);
74        return dst;
75    }
76    else if (af == AF_INET6)
77    {
78        struct sockaddr_in6 in;
79        memset( &in, 0, sizeof( in ) );
80        in.sin6_family = AF_INET6;
81        memcpy( &in.sin6_addr, src, sizeof( struct in_addr6 ) );
82        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
83                    dst, cnt, NULL, 0, NI_NUMERICHOST);
84        return dst;
85    }
86    return NULL;
87}
88
89static int
90inet_pton(int af, const char *src, void *dst)
91{
92    struct addrinfo hints;
93    struct addrinfo *res;
94    struct addrinfo *ressave;
95
96    memset(&hints, 0, sizeof(struct addrinfo));
97    hints.ai_family = af;
98
99    if (getaddrinfo(src, NULL, &hints, &res) != 0)
100        return -1;
101
102    ressave = res;
103
104    while (res)
105    {
106        memcpy(dst, res->ai_addr, res->ai_addrlen);
107        res = res->ai_next;
108    }
109
110    freeaddrinfo(ressave);
111    return 0;
112}
113
114#endif
115
116
117void
118tr_netInit( void )
119{
120    static int initialized = FALSE;
121
122    if( !initialized )
123    {
124#ifdef WIN32
125        WSADATA wsaData;
126        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
127#endif
128        initialized = TRUE;
129    }
130}
131
132const char * 
133tr_ntop( const tr_address * src, char * dst, int size ) 
134{
135    assert( tr_isAddress( src ) );
136
137    if( src->type == TR_AF_INET ) 
138        return inet_ntop( AF_INET, &src->addr, dst, size ); 
139    else 
140        return inet_ntop( AF_INET6, &src->addr, dst, size ); 
141} 
142
143/*
144 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
145 * This function is suitable to be called from libTransmission's networking code,
146 * which is single-threaded.
147 */ 
148const char * 
149tr_ntop_non_ts( const tr_address * src ) 
150{ 
151    static char buf[INET6_ADDRSTRLEN]; 
152    return tr_ntop( src, buf, sizeof( buf ) ); 
153} 
154
155tr_address * 
156tr_pton( const char * src, tr_address * dst ) 
157{ 
158    int retval = inet_pton( AF_INET, src, &dst->addr ); 
159    assert( dst );
160    if( retval < 0 ) 
161        return NULL; 
162    else if( retval == 0 ) 
163        retval = inet_pton( AF_INET6, src, &dst->addr ); 
164    else
165    { 
166        dst->type = TR_AF_INET; 
167        return dst; 
168    } 
169
170    if( retval < 1 ) 
171        return NULL; 
172    dst->type = TR_AF_INET6; 
173    return dst; 
174}
175
176/*
177 * Compare two tr_address structures.
178 * Returns:
179 * <0 if a < b
180 * >0 if a > b
181 * 0  if a == b
182 */ 
183int
184tr_compareAddresses( const tr_address * a, const tr_address * b)
185{
186    static const int sizes[2] = { sizeof(struct in_addr), sizeof(struct in6_addr) };
187
188    assert( tr_isAddress( a ) );
189    assert( tr_isAddress( b ) );
190
191    /* IPv6 addresses are always "greater than" IPv4 */ 
192    if( a->type != b->type )
193        return a->type == TR_AF_INET ? 1 : -1;
194
195    return memcmp( &a->addr, &b->addr, sizes[a->type] );
196} 
197
198/***********************************************************************
199 * TCP sockets
200 **********************************************************************/
201
202int
203tr_netSetTOS( int s, int tos )
204{
205#ifdef IP_TOS
206    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
207#else
208    return 0;
209#endif
210}
211
212static socklen_t
213setup_sockaddr( const tr_address        * addr,
214                tr_port                   port,
215                struct sockaddr_storage * sockaddr)
216{
217    assert( tr_isAddress( addr ) );
218
219    if( addr->type == TR_AF_INET )
220    {
221        struct sockaddr_in  sock4;
222        memset( &sock4, 0, sizeof( sock4 ) );
223        sock4.sin_family      = AF_INET;
224        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
225        sock4.sin_port        = port;
226        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
227        return sizeof( struct sockaddr_in );
228    }
229    else
230    {
231        struct sockaddr_in6 sock6;
232        memset( &sock6, 0, sizeof( sock6 ) );
233        sock6.sin6_family   = AF_INET6;
234        sock6.sin6_port     = port;
235        sock6.sin6_flowinfo = 0;
236        sock6.sin6_addr     = addr->addr.addr6;
237        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
238        return sizeof( struct sockaddr_in6 );
239    }
240}
241
242static tr_bool
243isMulticastAddress( const tr_address * addr )
244{
245    if( addr->type == TR_AF_INET && IN_MULTICAST( htonl( addr->addr.addr4.s_addr ) ) )
246        return TRUE;
247
248    if( addr->type == TR_AF_INET6 && ( addr->addr.addr6.s6_addr[0] == 0xff ) )
249        return TRUE;
250
251    return FALSE;
252}
253
254static TR_INLINE tr_bool
255isIPv4MappedOrCompatAddress( const tr_address * addr )
256{
257    if( addr->type == TR_AF_INET6 )
258    {
259        if( IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 ) ||
260            IN6_IS_ADDR_V4COMPAT( &addr->addr.addr6 ) )
261            return TRUE;
262    }
263    return FALSE;
264}
265
266static TR_INLINE tr_bool
267isIPv6LinkLocalAddress( const tr_address * addr )
268{
269    if( addr->type == TR_AF_INET6 &&
270        IN6_IS_ADDR_LINKLOCAL( &addr->addr.addr6 ) )
271        return TRUE;
272    return FALSE;
273}
274
275tr_bool
276tr_isValidPeerAddress( const tr_address * addr, tr_port port )
277{
278    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) ||
279        isIPv4MappedOrCompatAddress( addr ) )
280        return FALSE;
281
282    if( port == 0 )
283        return FALSE;
284
285    return TRUE;
286}
287
288int
289tr_netOpenTCP( tr_session        * session,
290               const tr_address  * addr,
291               tr_port             port )
292{
293    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
294    int                     s;
295    struct sockaddr_storage sock;
296    socklen_t               addrlen;
297    const tr_address      * source_addr;
298    socklen_t               sourcelen;
299    struct sockaddr_storage source_sock;
300
301    assert( tr_isAddress( addr ) );
302
303    if( isMulticastAddress( addr ) || isIPv6LinkLocalAddress( addr ) )
304        return -EINVAL;
305
306    s = tr_fdSocketCreate( domains[addr->type], SOCK_STREAM );
307    if( s < 0 )
308        return -1;
309
310    if( evutil_make_socket_nonblocking( s ) < 0 ) {
311        tr_netClose( s );
312        return -1;
313    }
314
315    addrlen = setup_sockaddr( addr, port, &sock );
316   
317    /* set source address */
318    source_addr = tr_sessionGetPublicAddress( session, addr->type );
319    assert( source_addr );
320    sourcelen = setup_sockaddr( source_addr, 0, &source_sock );
321    if( bind( s, ( struct sockaddr * ) &source_sock, sourcelen ) )
322    {
323        tr_err( _( "Couldn't set source address %s on %d: %s" ),
324                tr_ntop_non_ts( source_addr ), s, tr_strerror( errno ) );
325        return -errno;
326    }
327
328    if( ( connect( s, (struct sockaddr *) &sock,
329                  addrlen ) < 0 )
330#ifdef WIN32
331      && ( sockerrno != WSAEWOULDBLOCK )
332#endif
333      && ( sockerrno != EINPROGRESS ) )
334    {
335        int tmperrno;
336        tmperrno = sockerrno;
337        if( ( tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH )
338                || addr->type == TR_AF_INET )
339            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
340                    s, tr_ntop_non_ts( addr ), (int)port, tmperrno,
341                    tr_strerror( tmperrno ) );
342        tr_netClose( s );
343        s = -tmperrno;
344    }
345
346    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
347               s, tr_peerIoAddrStr( addr, port ) );
348
349    return s;
350}
351
352static int
353tr_netBindTCPImpl( const tr_address * addr, tr_port port, tr_bool suppressMsgs, int * errOut )
354{
355    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
356    struct sockaddr_storage sock;
357    int fd;
358    int addrlen;
359    int optval;
360
361    assert( tr_isAddress( addr ) );
362
363    fd = socket( domains[addr->type], SOCK_STREAM, 0 );
364    if( fd < 0 ) {
365        *errOut = sockerrno;
366        return -1;
367    }
368
369    if( evutil_make_socket_nonblocking( fd ) < 0 ) {
370        *errOut = sockerrno;
371        EVUTIL_CLOSESOCKET( fd );
372        return -1;
373    }
374
375    optval = 1;
376    setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
377    setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
378
379#ifdef IPV6_V6ONLY
380    if( addr->type == TR_AF_INET6 )
381        if( setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof( optval ) ) == -1 )
382            if( sockerrno != ENOPROTOOPT ) { /* if the kernel doesn't support it, ignore it */
383                *errOut = sockerrno;
384                return -1;
385            }
386#endif
387
388    addrlen = setup_sockaddr( addr, htons( port ), &sock );
389    if( bind( fd, (struct sockaddr *) &sock, addrlen ) ) {
390        const int err = sockerrno;
391        if( !suppressMsgs )
392            tr_err( _( "Couldn't bind port %d on %s: %s" ),
393                    port, tr_ntop_non_ts( addr ), tr_strerror( err ) );
394        EVUTIL_CLOSESOCKET( fd );
395        *errOut = err;
396        return -1;
397    }
398
399    if( !suppressMsgs )
400        tr_dbg( "Bound socket %d to port %d on %s", fd, port, tr_ntop_non_ts( addr ) );
401
402    if( listen( fd, 128 ) == -1 ) {
403        *errOut = sockerrno;
404        EVUTIL_CLOSESOCKET( fd );
405        return -1;
406    }
407
408    return fd;
409}
410
411int
412tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
413{
414    int unused;
415    return tr_netBindTCPImpl( addr, port, suppressMsgs, &unused );
416}
417
418tr_bool
419tr_net_hasIPv6( tr_port port )
420{
421    static tr_bool result = FALSE;
422    static tr_bool alreadyDone = FALSE;
423
424    if( !alreadyDone )
425    {
426        int err;
427        int fd = tr_netBindTCPImpl( &tr_in6addr_any, port, TRUE, &err );
428        if( fd >= 0 || err != EAFNOSUPPORT ) /* we support ipv6 */
429            result = TRUE;
430        if( fd >= 0 )
431            EVUTIL_CLOSESOCKET( fd );
432        alreadyDone = TRUE;
433    }
434
435    return result;
436}
437
438int
439tr_netAccept( tr_session  * session UNUSED,
440              int           b,
441              tr_address  * addr,
442              tr_port     * port )
443{
444    int fd = tr_fdSocketAccept( b, addr, port );
445
446    if( fd>=0 && evutil_make_socket_nonblocking(fd)<0 ) {
447        tr_netClose( fd );
448        fd = -1;
449    }
450
451    return fd;
452}
453
454void
455tr_netClose( int s )
456{
457    tr_fdSocketClose( s );
458}
Note: See TracBrowser for help on using the repository browser.