source: trunk/libtransmission/platform.c @ 3773

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

on Darwin, use NSCParameterAssert() instead of assert().

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