source: trunk/libtransmission/platform.c @ 3775

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

undoing the r3773-r3774 experiment.

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