source: trunk/libtransmission/net.c @ 237

Last change on this file since 237 was 237, checked in by titer, 16 years ago

Clean up, perform all DNS resolutions in a common thread. This should
fix the remaining issues with many open torrents

File size: 10.5 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005-2006 Transmission authors and contributors
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include "transmission.h"
24
25/***********************************************************************
26 * DNS resolution
27 **********************************************************************/
28
29/***********************************************************************
30 * tr_netResolve
31 ***********************************************************************
32 * Synchronous "resolution": only works with character strings
33 * representing numbers expressed in the Internet standard `.' notation.
34 * Returns a non-zero value if an error occurs.
35 **********************************************************************/
36int tr_netResolve( char * address, struct in_addr * addr )
37{
38    addr->s_addr = inet_addr( address );
39    return ( addr->s_addr == 0xFFFFFFFF );
40}
41
42/* TODO: Make this code reentrant */
43static tr_thread_t  resolveThread;
44static tr_lock_t    resolveLock;
45static volatile int resolveDie;
46static tr_resolve_t * resolveQueue;
47
48static void resolveRelease ( tr_resolve_t * );
49static void resolveFunc    ( void * );
50
51struct tr_resolve_s
52{
53    int            status;
54    char           * address;
55    struct in_addr addr;
56
57    int            refcount;
58    tr_resolve_t   * next;
59};
60
61/***********************************************************************
62 * tr_netResolveThreadInit
63 ***********************************************************************
64 * Initializes the static variables used for resolution and launch the
65 * gethostbyname thread.
66 **********************************************************************/
67void tr_netResolveThreadInit()
68{
69    resolveDie   = 0;
70    resolveQueue = NULL;
71    tr_lockInit( &resolveLock );
72    tr_threadCreate( &resolveThread, resolveFunc, NULL );
73}
74
75/***********************************************************************
76 * tr_netResolveThreadClose
77 ***********************************************************************
78 * Notices the gethostbyname thread that is should terminate. Doesn't
79 * wait until it does, in case it is stuck in a resolution: we let it
80 * die and clean itself up.
81 **********************************************************************/
82void tr_netResolveThreadClose()
83{
84    tr_lockLock( &resolveLock );
85    resolveDie = 1;
86    tr_lockUnlock( &resolveLock );
87    tr_wait( 200 );
88}
89
90/***********************************************************************
91 * tr_netResolveInit
92 ***********************************************************************
93 * Adds an address to the resolution queue.
94 **********************************************************************/
95tr_resolve_t * tr_netResolveInit( char * address )
96{
97    tr_resolve_t * r;
98
99    r           = malloc( sizeof( tr_resolve_t ) );
100    r->status   = TR_RESOLVE_WAIT;
101    r->address  = strdup( address );
102    r->refcount = 2;
103    r->next     = NULL;
104
105    tr_lockLock( &resolveLock );
106    if( !resolveQueue )
107    {
108        resolveQueue = r;
109    }
110    else
111    {
112        tr_resolve_t * iter;
113        for( iter = resolveQueue; iter->next; iter = iter->next );
114        iter->next = r;
115    }
116    tr_lockUnlock( &resolveLock );
117
118    return r;
119}
120
121/***********************************************************************
122 * tr_netResolvePulse
123 ***********************************************************************
124 * Checks the current status of a resolution.
125 **********************************************************************/
126int tr_netResolvePulse( tr_resolve_t * r, struct in_addr * addr )
127{
128    int ret;
129
130    tr_lockLock( &resolveLock );
131    ret = r->status;
132    if( ret == TR_RESOLVE_OK )
133    {
134        *addr = r->addr;
135    }
136    tr_lockUnlock( &resolveLock );
137
138    return ret;
139}
140
141/***********************************************************************
142 * tr_netResolveClose
143 ***********************************************************************
144 *
145 **********************************************************************/
146void tr_netResolveClose( tr_resolve_t * r )
147{
148    resolveRelease( r );
149}
150
151/***********************************************************************
152 * resolveRelease
153 ***********************************************************************
154 * The allocated tr_resolve_t structures should be freed when
155 * tr_netResolveClose was called *and* it was removed from the queue.
156 * This can happen in any order, so we use a refcount to know we can
157 * take it out.
158 **********************************************************************/
159static void resolveRelease( tr_resolve_t * r )
160{
161    if( --r->refcount < 1 )
162    {
163        free( r->address );
164        free( r );
165    }
166}
167
168/***********************************************************************
169 * resolveFunc
170 ***********************************************************************
171 * Keeps waiting for addresses to resolve, and removes them from the
172 * queue once resolution is done.
173 **********************************************************************/
174static void resolveFunc( void * unused )
175{
176    tr_resolve_t * r;
177    struct hostent * host;
178
179    tr_dbg( "Resolve thread started" );
180
181    tr_lockLock( &resolveLock );
182
183    while( !resolveDie )
184    {
185        if( !( r = resolveQueue ) )
186        {
187            /* TODO: Use a condition wait */
188            tr_lockUnlock( &resolveLock );
189            tr_wait( 50 );
190            tr_lockLock( &resolveLock );
191            continue;
192        }
193
194        /* Blocking resolution */
195        tr_lockUnlock( &resolveLock );
196        host = gethostbyname( r->address );
197        tr_lockLock( &resolveLock );
198
199        if( host )
200        {
201            memcpy( &r->addr, host->h_addr, host->h_length );
202            r->status = TR_RESOLVE_OK;
203        }
204        else
205        {
206            r->status = TR_RESOLVE_ERROR;
207        }
208       
209        resolveQueue = r->next;
210        resolveRelease( r );
211    }
212
213    /* Clean up  */
214    tr_lockUnlock( &resolveLock );
215    tr_lockClose( &resolveLock );
216    while( ( r = resolveQueue ) )
217    {
218        resolveQueue = r->next;
219        resolveRelease( r );
220    }
221
222    tr_dbg( "Resolve thread exited" );
223}
224
225
226/***********************************************************************
227 * TCP sockets
228 **********************************************************************/
229
230static int makeSocketNonBlocking( int s )
231{
232    int flags;
233
234#ifdef SYS_BEOS
235    flags = 1;
236    if( setsockopt( s, SOL_SOCKET, SO_NONBLOCK,
237                    &flags, sizeof( int ) ) < 0 )
238#else
239    if( ( flags = fcntl( s, F_GETFL, 0 ) ) < 0 ||
240        fcntl( s, F_SETFL, flags | O_NONBLOCK ) < 0 )
241#endif
242    {
243        tr_err( "Could not set socket to non-blocking mode (%s)",
244                strerror( errno ) );
245        tr_netClose( s );
246        return -1;
247    }
248
249    return s;
250}
251
252static int createSocket()
253{
254    int s;
255
256    s = socket( AF_INET, SOCK_STREAM, 0 );
257    if( s < 0 )
258    {
259        tr_err( "Could not create socket (%s)", strerror( errno ) );
260        return -1;
261    }
262
263    return makeSocketNonBlocking( s );
264}
265
266int tr_netOpen( struct in_addr addr, in_port_t port )
267{
268    int s;
269    struct sockaddr_in sock;
270
271    s = createSocket();
272    if( s < 0 )
273    {
274        return -1;
275    }
276
277    memset( &sock, 0, sizeof( sock ) );
278    sock.sin_family      = AF_INET;
279    sock.sin_addr.s_addr = addr.s_addr;
280    sock.sin_port        = port;
281
282    if( connect( s, (struct sockaddr *) &sock,
283                 sizeof( struct sockaddr_in ) ) < 0 &&
284        errno != EINPROGRESS )
285    {
286        tr_err( "Could not connect socket (%s)", strerror( errno ) );
287        tr_netClose( s );
288        return -1;
289    }
290
291    return s;
292}
293
294int tr_netBind( int port )
295{
296    int s;
297    struct sockaddr_in sock;
298#ifdef SO_REUSEADDR
299    int optval;
300#endif
301
302    s = createSocket();
303    if( s < 0 )
304    {
305        return -1;
306    }
307
308#ifdef SO_REUSEADDR
309    optval = 1;
310    setsockopt( s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof( optval ) );
311#endif
312
313    memset( &sock, 0, sizeof( sock ) );
314    sock.sin_family      = AF_INET;
315    sock.sin_addr.s_addr = INADDR_ANY;
316    sock.sin_port        = htons( port );
317
318    if( bind( s, (struct sockaddr *) &sock,
319               sizeof( struct sockaddr_in ) ) )
320    {
321        tr_err( "Could not bind port %d", port );
322        tr_netClose( s );
323        return -1;
324    }
325   
326    tr_inf( "Binded port %d", port );
327    listen( s, 5 );
328
329    return s;
330}
331
332int tr_netAccept( int s, struct in_addr * addr, in_port_t * port )
333{
334    int t;
335    unsigned len;
336    struct sockaddr_in sock;
337
338    len = sizeof( sock );
339    t   = accept( s, (struct sockaddr *) &sock, &len );
340
341    if( t < 0 )
342    {
343        return -1;
344    }
345   
346    *addr = sock.sin_addr;
347    *port = sock.sin_port;
348
349    return makeSocketNonBlocking( t );
350}
351
352int tr_netSend( int s, uint8_t * buf, int size )
353{
354    int ret;
355
356    ret = send( s, buf, size, 0 );
357    if( ret < 0 )
358    {
359        if( errno == ENOTCONN || errno == EAGAIN || errno == EWOULDBLOCK )
360        {
361            ret = TR_NET_BLOCK;
362        }
363        else
364        {
365            ret = TR_NET_CLOSE;
366        }
367    }
368
369    return ret;
370}
371
372int tr_netRecv( int s, uint8_t * buf, int size )
373{
374    int ret;
375
376    ret = recv( s, buf, size, 0 );
377    if( ret < 0 )
378    {
379        if( errno == EAGAIN || errno == EWOULDBLOCK )
380        {
381            ret = TR_NET_BLOCK;
382        }
383        else
384        {
385            ret = TR_NET_CLOSE;
386        }
387    }
388    if( !ret )
389    {
390        ret = TR_NET_CLOSE;
391    }
392
393    return ret;
394}
395
396void tr_netClose( int s )
397{
398#ifdef BEOS_NETSERVER
399    closesocket( s );
400#else
401    close( s );
402#endif
403}
Note: See TracBrowser for help on using the repository browser.