source: trunk/libtransmission/platform.c @ 2578

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

More Win32 portability changes

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