source: trunk/libtransmission/platform.c @ 2596

Last change on this file since 2596 was 2596, checked in by charles, 14 years ago

More win32 portability fixes. we now talk to tracker & peers; hash checks work, downloads seem to work. w00t

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