source: trunk/libtransmission/platform.c @ 4040

Last change on this file since 4040 was 4040, checked in by charles, 13 years ago

possible fix for the crash reported by sedatg in ticket #519

  • Property svn:keywords set to Date Rev Author Id
File size: 21.8 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 4040 2007-12-02 16:35:44Z charles $
3 *
4 * Copyright (c) 2005 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 <assert.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#ifdef __BEOS__
32  #include <signal.h> 
33  #include <fs_info.h>
34  #include <FindDirectory.h>
35  #include <kernel/OS.h>
36  #define BEOS_MAX_THREADS 256
37#elif defined(WIN32)
38  #include <windows.h>
39  #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
40#else
41  #define _XOPEN_SOURCE 500 /* needed for recursive locks. */
42  #include <pthread.h>
43#endif
44
45#include <sys/types.h>
46#include <dirent.h>
47#include <fcntl.h>
48#include <unistd.h> /* getuid getpid close */
49
50#include "transmission.h"
51#include "list.h"
52#include "net.h"
53#include "platform.h"
54#include "utils.h"
55
56/***
57****  THREADS
58***/
59
60#ifdef __BEOS__
61typedef thread_id tr_thread_id;
62#elif defined(WIN32)
63typedef DWORD tr_thread_id;
64#else
65typedef pthread_t tr_thread_id;
66#endif
67
68static tr_thread_id
69tr_getCurrentThread( void )
70{
71#ifdef __BEOS__
72    return find_thread( NULL );
73#elif defined(WIN32)
74    return GetCurrentThreadId();
75#else
76    return pthread_self( );
77#endif
78}
79
80static int
81tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
82{
83#ifdef __BEOS__
84    return a == b;
85#elif defined(WIN32)
86    return a == b;
87#else
88    return pthread_equal( a, b );
89#endif
90}
91
92struct tr_thread
93{
94    void          (* func ) ( void * );
95    void           * arg;
96    const char     * name;
97
98#ifdef __BEOS__
99    thread_id        thread;
100#elif defined(WIN32)
101    HANDLE           thread;
102    unsigned int     thread_id;
103#else
104    pthread_t        thread;
105#endif
106
107};
108
109int
110tr_amInThread ( const tr_thread * t )
111{
112    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
113}
114
115#ifdef WIN32
116#define ThreadFuncReturnType unsigned WINAPI
117#else
118#define ThreadFuncReturnType void
119#endif
120
121static ThreadFuncReturnType
122ThreadFunc( void * _t )
123{
124    tr_thread * t = _t;
125    const char * name = t->name;
126
127#ifdef __BEOS__
128    /* This is required because on BeOS, SIGINT is sent to each thread,
129       which kills them not nicely */
130    signal( SIGINT, SIG_IGN );
131#endif
132
133    tr_dbg( "Thread '%s' started", name );
134    t->func( t->arg );
135    tr_dbg( "Thread '%s' exited", name );
136
137#ifdef WIN32
138    _endthreadex( 0 );
139    return 0;
140#endif
141}
142
143tr_thread *
144tr_threadNew( void (*func)(void *),
145              void * arg,
146              const char * name )
147{
148    tr_thread * t = tr_new0( tr_thread, 1 );
149    t->func = func;
150    t->arg  = arg;
151    t->name = name;
152
153#ifdef __BEOS__
154    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
155    resume_thread( t->thread );
156#elif defined(WIN32)
157    t->thread = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &t->thread_id );
158#else
159    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
160#endif
161
162    return t;
163}
164   
165void
166tr_threadJoin( tr_thread * t )
167{
168    if( t != NULL )
169    {
170#ifdef __BEOS__
171        long exit;
172        wait_for_thread( t->thread, &exit );
173#elif defined(WIN32)
174        WaitForSingleObject( t->thread, INFINITE );
175        CloseHandle( t->thread );
176#else
177        pthread_join( t->thread, NULL );
178#endif
179
180        tr_dbg( "Thread '%s' joined", t->name );
181        t->name = NULL;
182        t->func = NULL;
183        tr_free( t );
184    }
185}
186
187/***
188****  LOCKS
189***/
190
191struct tr_lock
192{
193    int depth;
194#ifdef __BEOS__
195    sem_id lock;
196    thread_id lockThread;
197#elif defined(WIN32)
198    CRITICAL_SECTION lock;
199    DWORD lockThread;
200#else
201    pthread_mutex_t lock;
202    pthread_t lockThread;
203#endif
204};
205
206tr_lock*
207tr_lockNew( void )
208{
209    tr_lock * l = tr_new0( tr_lock, 1 );
210
211#ifdef __BEOS__
212    l->lock = create_sem( 1, "" );
213#elif defined(WIN32)
214    InitializeCriticalSection( &l->lock ); /* critical sections support recursion */
215#else
216    pthread_mutexattr_t attr;
217    pthread_mutexattr_init( &attr );
218    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
219    pthread_mutex_init( &l->lock, &attr );
220#endif
221
222    return l;
223}
224
225void
226tr_lockFree( tr_lock * l )
227{
228#ifdef __BEOS__
229    delete_sem( l->lock );
230#elif defined(WIN32)
231    DeleteCriticalSection( &l->lock );
232#else
233    pthread_mutex_destroy( &l->lock );
234#endif
235    tr_free( l );
236}
237
238void
239tr_lockLock( tr_lock * l )
240{
241    const tr_thread_id currentThread = tr_getCurrentThread( );
242
243#ifdef __BEOS__
244    acquire_sem( l->lock );
245#elif defined(WIN32)
246    EnterCriticalSection( &l->lock );
247#else
248    pthread_mutex_lock( &l->lock );
249#endif
250    l->lockThread = currentThread;
251    ++l->depth;
252}
253
254int
255tr_lockHave( const tr_lock * l )
256{
257    return ( l->depth > 0 )
258        && ( l->lockThread == tr_getCurrentThread() );
259}
260
261void
262tr_lockUnlock( tr_lock * l )
263{
264    assert( tr_lockHave( l ) );
265
266#ifdef __BEOS__
267    release_sem( l->lock );
268#elif defined(WIN32)
269    LeaveCriticalSection( &l->lock );
270#else
271    pthread_mutex_unlock( &l->lock );
272#endif
273    --l->depth;
274}
275
276/***
277****  COND
278***/
279
280struct tr_cond
281{
282#ifdef __BEOS__
283    sem_id sem;
284    thread_id threads[BEOS_MAX_THREADS];
285    int start, end;
286#elif defined(WIN32)
287    tr_list * events;
288    tr_lock * lock;
289#else
290    pthread_cond_t cond;
291#endif
292};
293
294#ifdef WIN32
295static DWORD getContEventTLS( void )
296{
297    static int inited = FALSE;
298    static DWORD event_tls;
299    if( !inited ) {
300        inited = TRUE;
301        event_tls = TlsAlloc();
302    }
303    return event_tls;
304}
305#endif
306
307tr_cond*
308tr_condNew( void )
309{
310    tr_cond * c = tr_new0( tr_cond, 1 );
311#ifdef __BEOS__
312    c->sem = create_sem( 1, "" );
313    c->start = 0;
314    c->end = 0;
315#elif defined(WIN32)
316    c->events = NULL;
317    c->lock = tr_lockNew( );
318#else
319    pthread_cond_init( &c->cond, NULL );
320#endif
321    return c;
322}
323
324void
325tr_condWait( tr_cond * c, tr_lock * l )
326{
327#ifdef __BEOS__
328
329    /* Keep track of that thread */
330    acquire_sem( c->sem );
331    c->threads[c->end] = find_thread( NULL );
332    c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
333    assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
334    release_sem( c->sem );
335
336    release_sem( l->lock );
337    suspend_thread( find_thread( NULL ) ); /* Wait for signal */
338    acquire_sem( l->lock );
339
340#elif defined(WIN32)
341
342    /* get this thread's cond event */
343    DWORD key = getContEventTLS ( );
344    HANDLE hEvent = TlsGetValue( key );
345    if( !hEvent ) {
346        hEvent = CreateEvent( 0, FALSE, FALSE, 0 );
347        TlsSetValue( key, hEvent );
348    }
349
350    /* add it to the list of events waiting to be signaled */
351    tr_lockLock( c->lock );
352    tr_list_append( &c->events, hEvent );
353    tr_lockUnlock( c->lock );
354
355    /* now wait for it to be signaled */
356    tr_lockUnlock( l );
357    WaitForSingleObject( hEvent, INFINITE );
358    tr_lockLock( l );
359
360    /* remove it from the list of events waiting to be signaled */
361    tr_lockLock( c->lock );
362    tr_list_remove_data( &c->events, hEvent );
363    tr_lockUnlock( c->lock );
364
365#else
366
367    pthread_cond_wait( &c->cond, &l->lock );
368
369#endif
370}
371
372#ifdef __BEOS__
373static int condTrySignal( tr_cond * c )
374{
375    if( c->start == c->end )
376        return 1;
377
378    for( ;; )
379    {
380        thread_info info;
381        get_thread_info( c->threads[c->start], &info );
382        if( info.state == B_THREAD_SUSPENDED )
383        {
384            resume_thread( c->threads[c->start] );
385            c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
386            break;
387        }
388        /* The thread is not suspended yet, which can happen since
389         * tr_condWait does not atomically suspends after releasing
390         * the semaphore. Wait a bit and try again. */
391        snooze( 5000 );
392    }
393    return 0;
394}
395#endif
396void
397tr_condSignal( tr_cond * c )
398{
399#ifdef __BEOS__
400
401    acquire_sem( c->sem );
402    condTrySignal( c );
403    release_sem( c->sem );
404
405#elif defined(WIN32)
406
407    tr_lockLock( c->lock );
408    if( c->events != NULL )
409        SetEvent( (HANDLE)c->events->data );
410    tr_lockUnlock( c->lock );
411
412#else
413
414    pthread_cond_signal( &c->cond );
415
416#endif
417}
418
419void
420tr_condBroadcast( tr_cond * c )
421{
422#ifdef __BEOS__
423
424    acquire_sem( c->sem );
425    while( !condTrySignal( c ) );
426    release_sem( c->sem );
427
428#elif defined(WIN32)
429
430    tr_list * l;
431    tr_lockLock( c->lock );
432    for( l=c->events; l!=NULL; l=l->next )
433        SetEvent( (HANDLE)l->data );
434    tr_lockUnlock( c->lock );
435
436#else
437
438    pthread_cond_broadcast( &c->cond );
439
440#endif
441}
442
443void
444tr_condFree( tr_cond * c )
445{
446#ifdef __BEOS__
447    delete_sem( c->sem );
448#elif defined(WIN32)
449    tr_list_free( &c->events, NULL );
450    tr_lockFree( c->lock );
451#else
452    pthread_cond_destroy( &c->cond );
453#endif
454    tr_free( c );
455}
456
457
458/***
459****  PATHS
460***/
461
462#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
463#include <pwd.h>
464#endif
465
466const char *
467tr_getHomeDirectory( void )
468{
469    static char buf[MAX_PATH_LENGTH];
470    static int init = 0;
471    const char * envHome;
472
473    if( init )
474        return buf;
475
476    envHome = getenv( "HOME" );
477    if( envHome )
478        snprintf( buf, sizeof(buf), "%s", envHome );
479    else {
480#ifdef WIN32
481        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
482#elif defined(__BEOS__) || defined(__AMIGAOS4__)
483        *buf = '\0';
484#else
485        struct passwd * pw = getpwuid( getuid() );
486        endpwent();
487        if( pw != NULL )
488            snprintf( buf, sizeof(buf), "%s", pw->pw_dir );
489#endif
490    }
491
492    init = 1;
493    return buf;
494}
495
496
497static void
498tr_migrateResume( const char *oldDirectory, const char *newDirectory )
499{
500    DIR * dirh = opendir( oldDirectory );
501
502    if( dirh != NULL )
503    {
504        struct dirent * dirp;
505
506        while( ( dirp = readdir( dirh ) ) )
507        {
508            if( !strncmp( "resume.", dirp->d_name, 7 ) )
509            {
510                char o[MAX_PATH_LENGTH];
511                char n[MAX_PATH_LENGTH];
512                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
513                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
514                rename( o, n );
515            }
516        }
517
518        closedir( dirh );
519    }
520}
521
522const char *
523tr_getPrefsDirectory( void )
524{
525    static char   buf[MAX_PATH_LENGTH];
526    static int    init = 0;
527    static size_t buflen = sizeof(buf);
528    const char* h;
529
530    if( init )
531        return buf;
532
533    h = tr_getHomeDirectory();
534#ifdef __BEOS__
535    find_directory( B_USER_SETTINGS_DIRECTORY,
536                    dev_for_path("/boot"), true, buf, buflen );
537    strcat( buf, "/Transmission" );
538#elif defined( SYS_DARWIN )
539    tr_buildPath ( buf, buflen, h,
540                  "Library", "Application Support", "Transmission", NULL );
541#elif defined(__AMIGAOS4__)
542    snprintf( buf, buflen, "PROGDIR:.transmission" );
543#elif defined(WIN32)
544    {
545        char tmp[MAX_PATH_LENGTH];
546        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, tmp );
547        tr_buildPath( buf, sizeof(buf), tmp, "Transmission", NULL );
548        buflen = strlen( buf );
549    }
550#else
551    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
552#endif
553
554    tr_mkdirp( buf, 0700 );
555    init = 1;
556
557#ifdef SYS_DARWIN
558    char old[MAX_PATH_LENGTH];
559    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
560    tr_migrateResume( old, buf );
561    rmdir( old );
562#endif
563
564    return buf;
565}
566
567const char *
568tr_getCacheDirectory( void )
569{
570    static char buf[MAX_PATH_LENGTH];
571    static int  init = 0;
572    static const size_t buflen = sizeof(buf);
573    const char * p;
574
575    if( init )
576        return buf;
577
578    p = tr_getPrefsDirectory();
579#if defined(__BEOS__) || defined(WIN32)
580    tr_buildPath( buf, buflen, p, "Cache", NULL );
581#elif defined( SYS_DARWIN )
582    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
583                  "Library", "Caches", "Transmission", NULL );
584#else
585    tr_buildPath( buf, buflen, p, "cache", NULL );
586#endif
587
588    tr_mkdirp( buf, 0700 );
589    init = 1;
590
591    if( strcmp( p, buf ) )
592        tr_migrateResume( p, buf );
593
594    return buf;
595}
596
597const char *
598tr_getTorrentsDirectory( void )
599{
600    static char buf[MAX_PATH_LENGTH];
601    static int  init = 0;
602    static const size_t buflen = sizeof(buf);
603    const char * p;
604
605    if( init )
606        return buf;
607
608    p = tr_getPrefsDirectory ();
609
610#if defined(__BEOS__) || defined(WIN32)
611    tr_buildPath( buf, buflen, p, "Torrents", NULL );
612#elif defined( SYS_DARWIN )
613    tr_buildPath( buf, buflen, p, "Torrents", NULL );
614#else
615    tr_buildPath( buf, buflen, p, "torrents", NULL );
616#endif
617
618    tr_mkdirp( buf, 0700 );
619    init = 1;
620    return buf;
621}
622
623/***
624****  SOCKETS
625***/
626
627#ifdef BSD
628
629#include <sys/types.h>
630#include <sys/socket.h>
631#include <netinet/in.h>
632#include <arpa/inet.h>
633#include <netinet/in.h> /* struct in_addr */
634#include <sys/sysctl.h>
635#include <net/route.h>
636
637static uint8_t *
638getroute( int * buflen );
639static int
640parseroutes( uint8_t * buf, int len, struct in_addr * addr );
641
642int
643tr_getDefaultRoute( struct in_addr * addr )
644{
645    uint8_t * buf;
646    int len;
647
648    buf = getroute( &len );
649    if( NULL == buf )
650    {
651        tr_err( "failed to get default route (BSD)" );
652        return 1;
653    }
654
655    len = parseroutes( buf, len, addr );
656    free( buf );
657
658    return len;
659}
660
661#ifndef SA_SIZE
662#define ROUNDUP( a, size ) \
663    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
664#define SA_SIZE( sap ) \
665    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
666                    sizeof( u_long ) )
667#endif /* !SA_SIZE */
668#define NEXT_SA( sap ) \
669    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
670
671static uint8_t *
672getroute( int * buflen )
673{
674    int     mib[6];
675    size_t  len;
676    uint8_t * buf;
677
678    mib[0] = CTL_NET;
679    mib[1] = PF_ROUTE;
680    mib[2] = 0;
681    mib[3] = AF_INET;
682    mib[4] = NET_RT_FLAGS;
683    mib[5] = RTF_GATEWAY;
684
685    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
686    {
687        if( ENOENT != errno )
688        {
689            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
690                    strerror( sockerrno ) );
691        }
692        *buflen = 0;
693        return NULL;
694    }
695
696    buf = malloc( len );
697    if( NULL == buf )
698    {
699        *buflen = 0;
700        return NULL;
701    }
702
703    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
704    {
705        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
706                strerror( sockerrno ) );
707        free( buf );
708        *buflen = 0;
709        return NULL;
710    }
711
712    *buflen = len;
713
714    return buf;
715}
716
717static int
718parseroutes( uint8_t * buf, int len, struct in_addr * addr )
719{
720    uint8_t            * end;
721    struct rt_msghdr   * rtm;
722    struct sockaddr    * sa;
723    struct sockaddr_in * sin;
724    int                  ii;
725    struct in_addr       dest, gw;
726
727    end = buf + len;
728    while( end > buf + sizeof( *rtm ) )
729    {
730        rtm = (struct rt_msghdr *) buf;
731        buf += rtm->rtm_msglen;
732        if( end >= buf )
733        {
734            dest.s_addr = INADDR_NONE;
735            gw.s_addr   = INADDR_NONE;
736            sa = (struct sockaddr *) ( rtm + 1 );
737
738            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
739            {
740                if( buf < (uint8_t *) NEXT_SA( sa ) )
741                {
742                    break;
743                }
744
745                if( rtm->rtm_addrs & ( 1 << ii ) )
746                {
747                    if( AF_INET == sa->sa_family )
748                    {
749                        sin = (struct sockaddr_in *) sa;
750                        switch( ii )
751                        {
752                            case RTAX_DST:
753                                dest = sin->sin_addr;
754                                break;
755                            case RTAX_GATEWAY:
756                                gw = sin->sin_addr;
757                                break;
758                        }
759                    }
760                    sa = NEXT_SA( sa );
761                }
762            }
763
764            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
765            {
766                *addr = gw;
767                return 0;
768            }
769        }
770    }
771
772    return 1;
773}
774
775#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
776
777#include <linux/types.h>
778#include <linux/netlink.h>
779#include <linux/rtnetlink.h>
780
781#define SEQNUM 195909
782
783static int
784getsock( void );
785static uint8_t *
786getroute( int fd, unsigned int * buflen );
787static int
788parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
789
790int
791tr_getDefaultRoute( struct in_addr * addr )
792{
793    int fd, ret;
794    unsigned int len;
795    uint8_t * buf;
796
797    ret = 1;
798    fd = getsock();
799    if( 0 <= fd )
800    {
801        while( ret )
802        {
803            buf = getroute( fd, &len );
804            if( NULL == buf )
805            {
806                break;
807            }
808            ret = parseroutes( buf, len, addr );
809            free( buf );
810        }
811        close( fd );
812    }
813
814    if( ret )
815    {
816        tr_err( "failed to get default route (Linux)" );
817    }
818
819    return ret;
820}
821
822static int
823getsock( void )
824{
825    int fd, flags;
826    struct
827    {
828        struct nlmsghdr nlh;
829        struct rtgenmsg rtg;
830    } req;
831    struct sockaddr_nl snl;
832
833    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
834    if( 0 > fd )
835    {
836        tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
837        return -1;
838    }
839
840    flags = fcntl( fd, F_GETFL );
841    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
842    {
843        tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
844        close( fd );
845        return -1;
846    }
847
848    memset( &snl, 0, sizeof(snl) );
849    snl.nl_family = AF_NETLINK;
850
851    memset( &req, 0, sizeof(req) );
852    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
853    req.nlh.nlmsg_type = RTM_GETROUTE;
854    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
855    req.nlh.nlmsg_seq = SEQNUM;
856    req.nlh.nlmsg_pid = 0;
857    req.rtg.rtgen_family = AF_INET;
858
859    if( 0 > sendto( fd, &req, sizeof( req ), 0,
860                    (struct sockaddr *) &snl, sizeof( snl ) ) )
861    {
862        tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
863        close( fd );
864        return -1;
865    }
866
867    return fd;
868}
869
870static uint8_t *
871getroute( int fd, unsigned int * buflen )
872{
873    void             * buf;
874    unsigned int       len;
875    ssize_t            res;
876    struct sockaddr_nl snl;
877    socklen_t          slen;
878
879    len = 8192;
880    buf = calloc( 1, len );
881    if( NULL == buf )
882    {
883        *buflen = 0;
884        return NULL;
885    }
886
887    for( ;; )
888    {
889        slen = sizeof( snl );
890        memset( &snl, 0, slen );
891        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
892        if( 0 > res )
893        {
894            if( EAGAIN != sockerrno )
895            {
896                tr_err( "failed to read from routing socket (%s)",
897                        strerror( sockerrno ) );
898            }
899            free( buf );
900            *buflen = 0;
901            return NULL;
902        }
903        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
904        {
905            tr_err( "bad address" );
906            free( buf );
907            *buflen = 0;
908            return NULL;
909        }
910
911        if( 0 == snl.nl_pid )
912        {
913            break;
914        }
915    }
916
917    *buflen = res;
918
919    return buf;
920}
921
922static int
923parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
924{
925    struct nlmsghdr * nlm;
926    struct nlmsgerr * nle;
927    struct rtmsg    * rtm;
928    struct rtattr   * rta;
929    int               rtalen;
930    struct in_addr    gw, dst;
931
932    nlm = ( struct nlmsghdr * ) buf;
933    while( NLMSG_OK( nlm, len ) )
934    {
935        gw.s_addr = INADDR_ANY;
936        dst.s_addr = INADDR_ANY;
937        if( NLMSG_ERROR == nlm->nlmsg_type )
938        {
939            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
940            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
941                nlm->nlmsg_len )
942            {
943                tr_err( "truncated netlink error" );
944            }
945            else
946            {
947                tr_err( "netlink error (%s)", strerror( nle->error ) );
948            }
949            return 1;
950        }
951        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
952                 getpid() == (pid_t) nlm->nlmsg_pid &&
953                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
954        {
955            rtm = NLMSG_DATA( nlm );
956            rta = RTM_RTA( rtm );
957            rtalen = RTM_PAYLOAD( nlm );
958
959            while( RTA_OK( rta, rtalen ) )
960            {
961                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
962                {
963                    switch( rta->rta_type )
964                    {
965                        case RTA_GATEWAY:
966                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
967                            break;
968                        case RTA_DST:
969                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
970                            break;
971                    }
972                }
973                rta = RTA_NEXT( rta, rtalen );
974            }
975        }
976
977        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
978            INADDR_ANY == dst.s_addr )
979        {
980            *addr = gw;
981            return 0;
982        }
983
984        nlm = NLMSG_NEXT( nlm, len );
985    }
986
987    return 1;
988}
989
990#else /* not BSD or Linux */
991
992int
993tr_getDefaultRoute( struct in_addr * addr UNUSED )
994{
995    tr_inf( "don't know how to get default route on this platform" );
996    return 1;
997}
998
999#endif
Note: See TracBrowser for help on using the repository browser.