source: trunk/libtransmission/net.c @ 2614

Last change on this file since 2614 was 2614, checked in by joshe, 14 years ago

Use BEOS to test for beos instead of relying on the build to set SYS_BEOS.
Add missing headers and other miscellaneous fixes for beos.

  • Property svn:keywords set to Date Rev Author Id
File size: 12.5 KB
Line 
1/******************************************************************************
2 * $Id: net.c 2614 2007-08-04 01:17:39Z joshe $
3 *
4 * Copyright (c) 2005-2006 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
30#include <sys/types.h>
31
32#ifndef WIN32
33#include <netdb.h>
34#include <fcntl.h>
35#endif
36
37#include "transmission.h"
38#include "fdlimit.h"
39#include "net.h"
40#include "platform.h"
41#include "utils.h"
42
43
44void
45tr_netInit( void )
46{
47    static int initialized = FALSE;
48    if( !initialized )
49    {
50#ifdef WIN32
51        WSADATA wsaData;
52        WSAStartup(MAKEWORD(2,2), &wsaData);
53#endif
54        initialized = TRUE;
55    }
56}
57
58/***********************************************************************
59 * DNS resolution
60 **********************************************************************/
61
62/***********************************************************************
63 * tr_netResolve
64 ***********************************************************************
65 * Synchronous "resolution": only works with character strings
66 * representing numbers expressed in the Internet standard `.' notation.
67 * Returns a non-zero value if an error occurs.
68 **********************************************************************/
69int tr_netResolve( const char * address, struct in_addr * addr )
70{
71    addr->s_addr = inet_addr( address );
72    return ( addr->s_addr == 0xFFFFFFFF );
73}
74
75static tr_thread_t  * resolveThread;
76static tr_lock_t    * resolveLock;
77static tr_cond_t    * resolveCond;
78static volatile int   resolveDie;
79static tr_resolve_t * resolveQueue;
80
81static void resolveRelease ( tr_resolve_t * );
82static void resolveFunc    ( void * );
83
84struct tr_resolve_s
85{
86    tr_tristate_t  status;
87    char           * address;
88    struct in_addr addr;
89
90    int            refcount;
91    tr_resolve_t   * next;
92};
93
94/***********************************************************************
95 * tr_netResolveThreadInit
96 ***********************************************************************
97 * Initializes the static variables used for resolution and launch the
98 * gethostbyname thread.
99 **********************************************************************/
100void tr_netResolveThreadInit( void )
101{
102    resolveDie   = 0;
103    resolveQueue = NULL;
104    resolveLock = tr_lockNew( );
105    resolveCond = tr_condNew( );
106    resolveThread = tr_threadNew( resolveFunc, NULL, "resolve" );
107}
108
109/***********************************************************************
110 * tr_netResolveThreadClose
111 ***********************************************************************
112 * Notices the gethostbyname thread that is should terminate. Doesn't
113 * wait until it does, in case it is stuck in a resolution: we let it
114 * die and clean itself up.
115 **********************************************************************/
116void tr_netResolveThreadClose( void )
117{
118    tr_lockLock( resolveLock );
119    resolveDie = 1;
120    tr_lockUnlock( resolveLock );
121    tr_condSignal( resolveCond );
122    tr_wait( 200 );
123}
124
125/***********************************************************************
126 * tr_netResolveInit
127 ***********************************************************************
128 * Adds an address to the resolution queue.
129 **********************************************************************/
130tr_resolve_t * tr_netResolveInit( const char * address )
131{
132    tr_resolve_t * r = tr_new0( tr_resolve_t, 1 );
133    r->status   = TR_NET_WAIT;
134    r->address  = strdup( address );
135    r->refcount = 2;
136    r->next     = NULL;
137
138    tr_lockLock( resolveLock );
139    if( !resolveQueue )
140    {
141        resolveQueue = r;
142    }
143    else
144    {
145        tr_resolve_t * iter;
146        for( iter = resolveQueue; iter->next; iter = iter->next );
147        iter->next = r;
148    }
149    tr_lockUnlock( resolveLock );
150    tr_condSignal( resolveCond );
151
152    return r;
153}
154
155/***********************************************************************
156 * tr_netResolvePulse
157 ***********************************************************************
158 * Checks the current status of a resolution.
159 **********************************************************************/
160tr_tristate_t tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr )
161{
162    tr_tristate_t ret;
163
164    tr_lockLock( resolveLock );
165    ret = r->status;
166    if( ret == TR_NET_OK )
167    {
168        *addr = r->addr;
169    }
170    tr_lockUnlock( resolveLock );
171
172    return ret;
173}
174
175/***********************************************************************
176 * tr_netResolveClose
177 ***********************************************************************
178 *
179 **********************************************************************/
180void tr_netResolveClose( tr_resolve_t * r )
181{
182    resolveRelease( r );
183}
184
185/***********************************************************************
186 * resolveRelease
187 ***********************************************************************
188 * The allocated tr_resolve_t structures should be freed when
189 * tr_netResolveClose was called *and* it was removed from the queue.
190 * This can happen in any order, so we use a refcount to know we can
191 * take it out.
192 **********************************************************************/
193static void resolveRelease( tr_resolve_t * r )
194{
195    if( --r->refcount < 1 )
196    {
197        free( r->address );
198        free( r );
199    }
200}
201
202/***********************************************************************
203 * resolveFunc
204 ***********************************************************************
205 * Keeps waiting for addresses to resolve, and removes them from the
206 * queue once resolution is done.
207 **********************************************************************/
208static void resolveFunc( void * arg UNUSED )
209{
210    tr_resolve_t * r;
211    struct hostent * host;
212
213    tr_lockLock( resolveLock );
214
215    while( !resolveDie )
216    {
217        if( !( r = resolveQueue ) )
218        {
219            tr_condWait( resolveCond, resolveLock );
220            continue;
221        }
222
223        /* Blocking resolution */
224        tr_lockUnlock( resolveLock );
225        host = gethostbyname( r->address );
226        tr_lockLock( resolveLock );
227
228        if( host )
229        {
230            memcpy( &r->addr, host->h_addr, host->h_length );
231            r->status = TR_NET_OK;
232        }
233        else
234        {
235            r->status = TR_NET_ERROR;
236        }
237       
238        resolveQueue = r->next;
239        resolveRelease( r );
240    }
241
242    /* Clean up  */
243    tr_lockUnlock( resolveLock );
244    tr_lockFree( resolveLock );
245    resolveLock = NULL;
246    while( ( r = resolveQueue ) )
247    {
248        resolveQueue = r->next;
249        resolveRelease( r );
250    }
251}
252
253
254/***********************************************************************
255 * TCP/UDP sockets
256 **********************************************************************/
257
258static int makeSocketNonBlocking( int s )
259{
260#ifdef WIN32
261    unsigned long flags = 1;
262    if( ioctlsocket( s, FIONBIO, &flags) == SOCKET_ERROR )
263#elif defined(__BEOS__)
264    int flags = 1;
265    if( setsockopt( s, SOL_SOCKET, SO_NONBLOCK,
266                    &flags, sizeof( int ) ) < 0 )
267#else
268    int flags = 1;
269    if( ( flags = fcntl( s, F_GETFL, 0 ) ) < 0 ||
270        fcntl( s, F_SETFL, flags | O_NONBLOCK ) < 0 )
271#endif
272    {
273        tr_err( "Couldn't set socket to non-blocking mode (%s)",
274                strerror( sockerrno ) );
275        tr_netClose( s );
276        return -1;
277    }
278
279    return s;
280}
281
282static int createSocket( int type, int priority )
283{
284    int s;
285    if( ( s = tr_fdSocketCreate( type, priority ) ) < 0 )
286    {
287        return -1;
288    }
289    return makeSocketNonBlocking( s );
290}
291
292static int
293tr_netOpen( const struct in_addr * addr, tr_port_t port,
294            int type, int priority )
295{
296    int s;
297    struct sockaddr_in sock;
298
299    if( ( s = createSocket( type, priority ) ) < 0 )
300    {
301        return -1;
302    }
303
304    memset( &sock, 0, sizeof( sock ) );
305    sock.sin_family      = AF_INET;
306    sock.sin_addr.s_addr = addr->s_addr;
307    sock.sin_port        = port;
308
309    if( ( connect( s, (struct sockaddr *) &sock,
310                   sizeof( struct sockaddr_in ) ) < 0 )
311#ifdef WIN32
312        && ( sockerrno != WSAEWOULDBLOCK )
313#endif
314        && ( sockerrno != EINPROGRESS ) )
315    {
316        tr_err( "Couldn't connect socket (%s)", strerror( sockerrno ) );
317        tr_netClose( s );
318        return -1;
319    }
320
321    return s;
322}
323 
324int
325tr_netOpenTCP( const struct in_addr * addr, tr_port_t port, int priority )
326{
327    return tr_netOpen( addr, port, SOCK_STREAM, priority );
328}
329
330int
331tr_netOpenUDP( const struct in_addr * addr, tr_port_t port, int priority )
332{
333    return tr_netOpen( addr, port, SOCK_DGRAM, priority );
334}
335
336#ifdef IP_ADD_MEMBERSHIP
337int tr_netMcastOpen( int port, const struct in_addr * addr )
338{
339    int fd;
340    struct ip_mreq req;
341
342    fd = tr_netBindUDP( port );
343    if( 0 > fd )
344    {
345        return -1;
346    }
347
348    memset( &req, 0, sizeof( req ) );
349    req.imr_multiaddr.s_addr = addr->s_addr;
350    req.imr_interface.s_addr = htonl( INADDR_ANY );
351    if( setsockopt( fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof ( req ) ) )
352    {
353        tr_err( "Couldn't join multicast group (%s)", strerror( sockerrno ) );
354        tr_netClose( fd );
355        return -1;
356    }
357
358    return fd;
359}
360#else /* IP_ADD_MEMBERSHIP */
361int tr_netMcastOpen( int port UNUSED, const struct in_addr * addr UNUSED )
362{
363    return -1;
364}
365#endif /* IP_ADD_MEMBERSHIP */
366
367static int
368tr_netBind( int port, int type )
369{
370    int s;
371    struct sockaddr_in sock;
372#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
373    int optval;
374#endif
375
376    if( ( s = createSocket( type, 1 ) ) < 0 )
377    {
378        return -1;
379    }
380
381#ifdef SO_REUSEADDR
382    optval = 1;
383    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof( optval ) );
384#endif
385
386#ifdef SO_REUSEPORT
387    if( SOCK_DGRAM == type )
388    {
389        optval = 1;
390        setsockopt( s, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof( optval ) );
391    }
392#endif
393
394    memset( &sock, 0, sizeof( sock ) );
395    sock.sin_family      = AF_INET;
396    sock.sin_addr.s_addr = INADDR_ANY;
397    sock.sin_port        = htons( port );
398
399    if( bind( s, (struct sockaddr *) &sock,
400               sizeof( struct sockaddr_in ) ) )
401    {
402        tr_err( "Couldn't bind port %d", port );
403        tr_netClose( s );
404        return -1;
405    }
406
407    return s;
408}
409
410int
411tr_netBindTCP( int port )
412{
413    return tr_netBind( port, SOCK_STREAM );
414}
415
416int
417tr_netBindUDP( int port )
418{
419    return tr_netBind( port, SOCK_DGRAM );
420}
421
422
423int tr_netAccept( int b, struct in_addr * addr, tr_port_t * port )
424{
425    int s;
426    if( ( s = tr_fdSocketAccept( b, addr, port ) ) < 0 )
427    {
428        return -1;
429    }
430    return makeSocketNonBlocking( s );
431}
432
433int
434tr_netSend( int s, const void * buf, int size )
435{
436    const int ret = send( s, buf, size, 0 );
437    if( ret >= 0 )
438        return ret;
439
440    if( sockerrno == ENOTCONN || sockerrno == EAGAIN || sockerrno == EWOULDBLOCK )
441        return TR_NET_BLOCK;
442
443    return TR_NET_CLOSE;
444}
445
446int tr_netRecvFrom( int s, uint8_t * buf, int size, struct sockaddr_in * addr )
447{
448    socklen_t len;
449    int       ret;
450
451    len = ( NULL == addr ? 0 : sizeof( *addr ) );
452    ret = recvfrom( s, buf, size, 0, ( struct sockaddr * ) addr, &len );
453    if( ret < 0 )
454    {
455        if( sockerrno == EAGAIN || sockerrno == EWOULDBLOCK )
456        {
457            ret = TR_NET_BLOCK;
458        }
459        else
460        {
461            ret = TR_NET_CLOSE;
462        }
463    }
464    if( !ret )
465    {
466        ret = TR_NET_CLOSE;
467    }
468
469    return ret;
470}
471
472void tr_netClose( int s )
473{
474    tr_fdSocketClose( s );
475}
476
477void tr_netNtop( const struct in_addr * addr, char * buf, int len )
478{
479    const uint8_t * cast;
480
481    cast = (const uint8_t *)addr;
482    snprintf( buf, len, "%hhu.%hhu.%hhu.%hhu",
483              cast[0], cast[1], cast[2], cast[3] );
484}
Note: See TracBrowser for help on using the repository browser.