source: trunk/libtransmission/net.c @ 12094

Last change on this file since 12094 was 12094, checked in by jordan, 11 years ago

(trunk libT) new function: tr_ssToAddr()

This handles converting the data in a sockaddr_storage to a tr_address + port, and removes redundant code from fdlimit.c and tr-udp.c that previously did this work.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.8 KB
Line 
1/******************************************************************************
2 *
3 * $Id: net.c 12094 2011-03-04 21:00:52Z jordan $
4 *
5 * Copyright (c) Transmission authors and contributors
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *****************************************************************************/
25
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <assert.h>
31
32#include <sys/types.h>
33
34#ifdef WIN32
35 #define _WIN32_WINNT   0x0501
36 #include <ws2tcpip.h>
37#else
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <arpa/inet.h> /* inet_addr */
42 #include <netdb.h>
43 #include <fcntl.h>
44#endif
45#include <unistd.h>
46
47#include <event2/util.h>
48#include <libutp/utp.h>
49
50#include "transmission.h"
51#include "fdlimit.h"
52#include "natpmp.h"
53#include "net.h"
54#include "peer-io.h"
55#include "platform.h"
56#include "session.h"
57#include "tr-utp.h"
58#include "utils.h"
59
60#ifndef IN_MULTICAST
61#define IN_MULTICAST( a ) ( ( ( a ) & 0xf0000000 ) == 0xe0000000 )
62#endif
63
64const tr_address tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } };
65const tr_address tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY, 0x00, 0x00, 0x00 } } } } };
66
67#ifdef WIN32
68const char *
69inet_ntop( int af, const void * src, char * dst, socklen_t cnt )
70{
71    if (af == AF_INET)
72    {
73        struct sockaddr_in in;
74        memset( &in, 0, sizeof( in ) );
75        in.sin_family = AF_INET;
76        memcpy( &in.sin_addr, src, sizeof( struct in_addr ) );
77        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in),
78                    dst, cnt, NULL, 0, NI_NUMERICHOST);
79        return dst;
80    }
81    else if (af == AF_INET6)
82    {
83        struct sockaddr_in6 in;
84        memset( &in, 0, sizeof( in ) );
85        in.sin6_family = AF_INET6;
86        memcpy( &in.sin6_addr, src, sizeof( struct in_addr6 ) );
87        getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6),
88                    dst, cnt, NULL, 0, NI_NUMERICHOST);
89        return dst;
90    }
91    return NULL;
92}
93
94int
95inet_pton( int af, const char * src, void * dst )
96{
97    struct addrinfo hints, *res, *ressave;
98    struct sockaddr_in * s4;
99    struct sockaddr_in6 * s6;
100
101    memset( &hints, 0, sizeof( struct addrinfo ));
102    hints.ai_family = af;
103    hints.ai_flags = AI_NUMERICHOST;
104
105    if( getaddrinfo( src, NULL, &hints, &res ) ) {
106        if( WSAGetLastError() == WSAHOST_NOT_FOUND )
107            return 0;
108        else {
109            errno = EAFNOSUPPORT;
110            return -1;
111        }
112    }
113
114    ressave = res;
115    while( res ) {
116        switch (res->ai_family) {
117            case AF_INET:
118                s4 = (struct sockaddr_in *) res->ai_addr;
119                memcpy( dst, &s4->sin_addr, sizeof( struct in_addr ) );
120                break;
121            case AF_INET6:
122                s6 = (struct sockaddr_in6 *) res->ai_addr;
123                memcpy( dst, &s6->sin6_addr, sizeof( struct in6_addr ) );
124                break;
125            default: /* AF_UNSPEC, AF_NETBIOS */
126                break;
127        }
128        res = res->ai_next;
129    }
130
131    freeaddrinfo(ressave);
132    return 1;
133}
134#endif
135
136void
137tr_netInit( void )
138{
139    static int initialized = FALSE;
140
141    if( !initialized )
142    {
143#ifdef WIN32
144        WSADATA wsaData;
145        WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
146#endif
147        initialized = TRUE;
148    }
149}
150
151char *
152tr_net_strerror( char * buf, size_t buflen, int err )
153{
154    *buf = '\0';
155#ifdef WIN32
156    FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, buflen, NULL );
157#else
158    tr_strlcpy( buf, tr_strerror( err ), buflen );
159#endif
160    return buf;
161}
162
163const char *
164tr_ntop( const tr_address * src, char * dst, int size )
165{
166    assert( tr_isAddress( src ) );
167
168    if( src->type == TR_AF_INET )
169        return inet_ntop( AF_INET, &src->addr, dst, size );
170    else
171        return inet_ntop( AF_INET6, &src->addr, dst, size );
172}
173
174/*
175 * Non-threadsafe version of tr_ntop, which uses a static memory area for a buffer.
176 * This function is suitable to be called from libTransmission's networking code,
177 * which is single-threaded.
178 */
179const char *
180tr_ntop_non_ts( const tr_address * src )
181{
182    static char buf[INET6_ADDRSTRLEN];
183    return tr_ntop( src, buf, sizeof( buf ) );
184}
185
186tr_address *
187tr_pton( const char * src, tr_address * dst )
188{
189    int retval = inet_pton( AF_INET, src, &dst->addr );
190    assert( dst );
191    if( retval < 0 )
192        return NULL;
193    else if( retval == 0 )
194        retval = inet_pton( AF_INET6, src, &dst->addr );
195    else
196    {
197        dst->type = TR_AF_INET;
198        return dst;
199    }
200
201    if( retval < 1 )
202        return NULL;
203    dst->type = TR_AF_INET6;
204    return dst;
205}
206
207/*
208 * Compare two tr_address structures.
209 * Returns:
210 * <0 if a < b
211 * >0 if a > b
212 * 0  if a == b
213 */
214int
215tr_compareAddresses( const tr_address * a, const tr_address * b)
216{
217    static const int sizes[2] = { sizeof(struct in_addr), sizeof(struct in6_addr) };
218
219    /* IPv6 addresses are always "greater than" IPv4 */
220    if( a->type != b->type )
221        return a->type == TR_AF_INET ? 1 : -1;
222
223    return memcmp( &a->addr, &b->addr, sizes[a->type] );
224}
225
226/***********************************************************************
227 * TCP sockets
228 **********************************************************************/
229
230int
231tr_netSetTOS( int s, int tos )
232{
233#ifdef IP_TOS
234    return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
235#else
236    return 0;
237#endif
238}
239
240int
241tr_netSetCongestionControl( int s UNUSED, const char *algorithm UNUSED )
242{
243#ifdef TCP_CONGESTION
244    return setsockopt( s, IPPROTO_TCP, TCP_CONGESTION,
245                       algorithm, strlen(algorithm) + 1 );
246#else
247    errno = ENOSYS;
248    return -1;
249#endif
250}
251
252tr_bool
253tr_ssToAddr( tr_address * setme_addr,     
254             tr_port    * setme_port,     
255             const struct sockaddr_storage * from )
256{
257    if( from->ss_family == AF_INET )
258    {
259        struct sockaddr_in * sin = (struct sockaddr_in *)from;
260        setme_addr->type = TR_AF_INET;
261        setme_addr->addr.addr4.s_addr = sin->sin_addr.s_addr;
262        *setme_port = sin->sin_port;
263        return TRUE;
264    }
265
266    if( from->ss_family == AF_INET6 )
267    {
268        struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) from;
269        setme_addr->type = TR_AF_INET6;
270        setme_addr->addr.addr6 = sin6->sin6_addr;
271        *setme_port = sin6->sin6_port;
272        return TRUE;
273    }
274
275    return FALSE;
276}
277
278static socklen_t
279setup_sockaddr( const tr_address        * addr,
280                tr_port                   port,
281                struct sockaddr_storage * sockaddr)
282{
283    assert( tr_isAddress( addr ) );
284
285    if( addr->type == TR_AF_INET )
286    {
287        struct sockaddr_in  sock4;
288        memset( &sock4, 0, sizeof( sock4 ) );
289        sock4.sin_family      = AF_INET;
290        sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
291        sock4.sin_port        = port;
292        memcpy( sockaddr, &sock4, sizeof( sock4 ) );
293        return sizeof( struct sockaddr_in );
294    }
295    else
296    {
297        struct sockaddr_in6 sock6;
298        memset( &sock6, 0, sizeof( sock6 ) );
299        sock6.sin6_family   = AF_INET6;
300        sock6.sin6_port     = port;
301        sock6.sin6_flowinfo = 0;
302        sock6.sin6_addr     = addr->addr.addr6;
303        memcpy( sockaddr, &sock6, sizeof( sock6 ) );
304        return sizeof( struct sockaddr_in6 );
305    }
306}
307
308int
309tr_netOpenPeerSocket( tr_session        * session,
310                      const tr_address  * addr,
311                      tr_port             port,
312                      tr_bool             clientIsSeed )
313{
314    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
315    int                     s;
316    struct sockaddr_storage sock;
317    socklen_t               addrlen;
318    const tr_address      * source_addr;
319    socklen_t               sourcelen;
320    struct sockaddr_storage source_sock;
321
322    assert( tr_isAddress( addr ) );
323
324    if( !tr_isValidPeerAddress( addr, port ) )
325        return -EINVAL;
326
327    s = tr_fdSocketCreate( session, domains[addr->type], SOCK_STREAM );
328    if( s < 0 )
329        return -1;
330
331    /* seeds don't need much of a read buffer... */
332    if( clientIsSeed ) {
333        int n = 8192;
334        if( setsockopt( s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n) ) )
335            tr_inf( "Unable to set SO_RCVBUF on socket %d: %s", s, tr_strerror( sockerrno ) );
336    }
337
338    if( evutil_make_socket_nonblocking( s ) < 0 ) {
339        tr_netClose( session, s );
340        return -1;
341    }
342
343    addrlen = setup_sockaddr( addr, port, &sock );
344
345    /* set source address */
346    source_addr = tr_sessionGetPublicAddress( session, addr->type, NULL );
347    assert( source_addr );
348    sourcelen = setup_sockaddr( source_addr, 0, &source_sock );
349    if( bind( s, ( struct sockaddr * ) &source_sock, sourcelen ) )
350    {
351        tr_err( _( "Couldn't set source address %s on %d: %s" ),
352                tr_ntop_non_ts( source_addr ), s, tr_strerror( errno ) );
353        return -errno;
354    }
355
356    if( ( connect( s, (struct sockaddr *) &sock,
357                  addrlen ) < 0 )
358#ifdef WIN32
359      && ( sockerrno != WSAEWOULDBLOCK )
360#endif
361      && ( sockerrno != EINPROGRESS ) )
362    {
363        int tmperrno;
364        tmperrno = sockerrno;
365        if( ( tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH )
366                || addr->type == TR_AF_INET )
367            tr_err( _( "Couldn't connect socket %d to %s, port %d (errno %d - %s)" ),
368                    s, tr_ntop_non_ts( addr ), (int)ntohs( port ), tmperrno,
369                    tr_strerror( tmperrno ) );
370        tr_netClose( session, s );
371        s = -tmperrno;
372    }
373
374    tr_deepLog( __FILE__, __LINE__, NULL, "New OUTGOING connection %d (%s)",
375               s, tr_peerIoAddrStr( addr, port ) );
376
377    return s;
378}
379
380struct UTPSocket *
381tr_netOpenPeerUTPSocket( tr_session        * session,
382                         const tr_address  * addr,
383                         tr_port             port,
384                         tr_bool             clientIsSeed UNUSED )
385{
386    struct sockaddr_storage ss;
387    socklen_t sslen;
388    sslen = setup_sockaddr( addr, port, &ss );
389
390    return UTP_Create( tr_utpSendTo, (void*)session,
391                       (struct sockaddr*)&ss, sslen );
392}
393
394static int
395tr_netBindTCPImpl( const tr_address * addr, tr_port port, tr_bool suppressMsgs, int * errOut )
396{
397    static const int domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
398    struct sockaddr_storage sock;
399    int fd;
400    int addrlen;
401    int optval;
402
403    assert( tr_isAddress( addr ) );
404
405    fd = socket( domains[addr->type], SOCK_STREAM, 0 );
406    if( fd < 0 ) {
407        *errOut = sockerrno;
408        return -1;
409    }
410
411    if( evutil_make_socket_nonblocking( fd ) < 0 ) {
412        *errOut = sockerrno;
413        tr_netCloseSocket( fd );
414        return -1;
415    }
416
417    optval = 1;
418    setsockopt( fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
419    setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval) );
420
421#ifdef IPV6_V6ONLY
422    if( addr->type == TR_AF_INET6 )
423        if( setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof( optval ) ) == -1 )
424            if( sockerrno != ENOPROTOOPT ) { /* if the kernel doesn't support it, ignore it */
425                *errOut = sockerrno;
426                return -1;
427            }
428#endif
429
430    addrlen = setup_sockaddr( addr, htons( port ), &sock );
431    if( bind( fd, (struct sockaddr *) &sock, addrlen ) ) {
432        const int err = sockerrno;
433        if( !suppressMsgs )
434        {
435            const char * fmt;
436            const char * hint;
437
438            if( err == EADDRINUSE )
439                hint = _( "Is another copy of Transmission already running?" );
440            else
441                hint = NULL;
442
443            if( hint == NULL )
444                fmt = _( "Couldn't bind port %d on %s: %s" );
445            else
446                fmt = _( "Couldn't bind port %d on %s: %s (%s)" );
447
448            tr_err( fmt, port, tr_ntop_non_ts( addr ), tr_strerror( err ), hint );
449        }
450        tr_netCloseSocket( fd );
451        *errOut = err;
452        return -1;
453    }
454
455    if( !suppressMsgs )
456        tr_dbg( "Bound socket %d to port %d on %s", fd, port, tr_ntop_non_ts( addr ) );
457
458    if( listen( fd, 128 ) == -1 ) {
459        *errOut = sockerrno;
460        tr_netCloseSocket( fd );
461        return -1;
462    }
463
464    return fd;
465}
466
467int
468tr_netBindTCP( const tr_address * addr, tr_port port, tr_bool suppressMsgs )
469{
470    int unused;
471    return tr_netBindTCPImpl( addr, port, suppressMsgs, &unused );
472}
473
474tr_bool
475tr_net_hasIPv6( tr_port port )
476{
477    static tr_bool result = FALSE;
478    static tr_bool alreadyDone = FALSE;
479
480    if( !alreadyDone )
481    {
482        int err;
483        int fd = tr_netBindTCPImpl( &tr_in6addr_any, port, TRUE, &err );
484        if( fd >= 0 || err != EAFNOSUPPORT ) /* we support ipv6 */
485            result = TRUE;
486        if( fd >= 0 )
487            tr_netCloseSocket( fd );
488        alreadyDone = TRUE;
489    }
490
491    return result;
492}
493
494int
495tr_netAccept( tr_session  * session,
496              int           b,
497              tr_address  * addr,
498              tr_port     * port )
499{
500    int fd = tr_fdSocketAccept( session, b, addr, port );
501
502    if( fd>=0 && evutil_make_socket_nonblocking(fd)<0 ) {
503        tr_netClose( session, fd );
504        fd = -1;
505    }
506
507    return fd;
508}
509
510void
511tr_netCloseSocket( int fd )
512{
513    EVUTIL_CLOSESOCKET( fd );
514}
515
516void
517tr_netClose( tr_session * session, int s )
518{
519    tr_fdSocketClose( session, s );
520}
521
522/*
523   get_source_address() and global_unicast_address() were written by
524   Juliusz Chroboczek, and are covered under the same license as dht.c.
525   Please feel free to copy them into your software if it can help
526   unbreaking the double-stack Internet. */
527
528/* Get the source address used for a given destination address. Since
529   there is no official interface to get this information, we create
530   a connected UDP socket (connected UDP... hmm...) and check its source
531   address. */
532static int
533get_source_address( const struct sockaddr  * dst,
534                    socklen_t                dst_len,
535                    struct sockaddr        * src,
536                    socklen_t              * src_len )
537{
538    int s, rc, save;
539
540    s = socket(dst->sa_family, SOCK_DGRAM, 0);
541    if(s < 0)
542        goto fail;
543
544    /* Since it's a UDP socket, this doesn't actually send any packets. */
545    rc = connect(s, dst, dst_len);
546    if(rc < 0)
547        goto fail;
548
549    rc = getsockname(s, src, src_len);
550    if(rc < 0)
551        goto fail;
552
553    EVUTIL_CLOSESOCKET( s );
554
555    return rc;
556
557 fail:
558    save = errno;
559    EVUTIL_CLOSESOCKET( s );
560    errno = save;
561    return -1;
562}
563
564/* We all hate NATs. */
565static int
566global_unicast_address(struct sockaddr *sa)
567{
568    if(sa->sa_family == AF_INET) {
569        const unsigned char *a =
570            (unsigned char*)&((struct sockaddr_in*)sa)->sin_addr;
571        if(a[0] == 0 || a[0] == 127 || a[0] >= 224 ||
572           a[0] == 10 || (a[0] == 172 && a[1] >= 16 && a[1] <= 31) ||
573           (a[0] == 192 && a[1] == 168))
574            return 0;
575        return 1;
576    } else if(sa->sa_family == AF_INET6) {
577        const unsigned char *a =
578            (unsigned char*)&((struct sockaddr_in6*)sa)->sin6_addr;
579        /* 2000::/3 */
580        return (a[0] & 0xE0) == 0x20;
581    } else {
582        errno = EAFNOSUPPORT;
583        return -1;
584    }
585}
586
587static int
588tr_globalAddress( int af, void *addr, int *addr_len )
589{
590    struct sockaddr_storage ss;
591    socklen_t sslen = sizeof(ss);
592    struct sockaddr_in sin;
593    struct sockaddr_in6 sin6;
594    struct sockaddr *sa;
595    socklen_t salen;
596    int rc;
597
598    switch(af) {
599    case AF_INET:
600        memset(&sin, 0, sizeof(sin));
601        sin.sin_family = AF_INET;
602        inet_pton(AF_INET, "91.121.74.28", &sin.sin_addr);
603        sin.sin_port = htons(6969);
604        sa = (struct sockaddr*)&sin;
605        salen = sizeof(sin);
606        break;
607    case AF_INET6:
608        memset(&sin6, 0, sizeof(sin6));
609        sin6.sin6_family = AF_INET6;
610        /* In order for address selection to work right, this should be
611           a native IPv6 address, not Teredo or 6to4. */
612        inet_pton(AF_INET6, "2001:1890:1112:1::20", &sin6.sin6_addr);
613        sin6.sin6_port = htons(6969);
614        sa = (struct sockaddr*)&sin6;
615        salen = sizeof(sin6);
616        break;
617    default:
618        return -1;
619    }
620
621    rc = get_source_address( sa, salen, (struct sockaddr*)&ss, &sslen );
622
623    if( rc < 0 )
624        return -1;
625
626    if( !global_unicast_address( (struct sockaddr*)&ss) )
627        return -1;
628
629    switch(af) {
630    case AF_INET:
631        if(*addr_len < 4)
632            return -1;
633        memcpy(addr, &((struct sockaddr_in*)&ss)->sin_addr, 4);
634        *addr_len = 4;
635        return 1;
636    case AF_INET6:
637        if(*addr_len < 16)
638            return -1;
639        memcpy(addr, &((struct sockaddr_in6*)&ss)->sin6_addr, 16);
640        *addr_len = 16;
641        return 1;
642    default:
643        return -1;
644    }
645}
646
647/* Return our global IPv6 address, with caching. */
648
649const unsigned char *
650tr_globalIPv6( void )
651{
652    static unsigned char ipv6[16];
653    static time_t last_time = 0;
654    static int have_ipv6 = 0;
655    const time_t now = tr_time( );
656
657    /* Re-check every half hour */
658    if( last_time < now - 1800 )
659    {
660        int addrlen = 16;
661        const int rc = tr_globalAddress( AF_INET6, ipv6, &addrlen );
662        have_ipv6 = ( rc >= 0 ) && ( addrlen == 16 );
663        last_time = now;
664    }
665
666    return have_ipv6 ? ipv6 : NULL;
667}
668
669/***
670****
671****
672***/
673
674static tr_bool
675isIPv4MappedAddress( const tr_address * addr )
676{
677    return ( addr->type == TR_AF_INET6 ) && IN6_IS_ADDR_V4MAPPED( &addr->addr.addr6 );
678}
679
680static tr_bool
681isIPv6LinkLocalAddress( const tr_address * addr )
682{
683    return ( ( addr->type == TR_AF_INET6 )
684                  && IN6_IS_ADDR_LINKLOCAL( &addr->addr.addr6 ) );
685}
686
687/* isMartianAddr was written by Juliusz Chroboczek,
688   and is covered under the same license as third-party/dht/dht.c. */
689static tr_bool
690isMartianAddr( const struct tr_address * a )
691{
692    static const unsigned char zeroes[16] =
693        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
694
695    assert( tr_isAddress( a ) );
696
697    switch( a->type )
698    {
699        case TR_AF_INET: {
700            const unsigned char * address = (const unsigned char*)&a->addr.addr4;
701            return (address[0] == 0) ||
702                   (address[0] == 127) ||
703                   ((address[0] & 0xE0) == 0xE0);
704            break;
705        }
706
707        case TR_AF_INET6: {
708            const unsigned char * address = (const unsigned char*)&a->addr.addr6;
709            return (address[0] == 0xFF) ||
710                   (memcmp(address, zeroes, 15) == 0 &&
711                    (address[15] == 0 || address[15] == 1)) ||
712                   /* Addresses outside of 2000::/3 are currently reserved,
713                      but might be allocated at some future time. Since
714                      there are a lot of buggy peers pushing around such
715                      addresses over PEX, we reject them until the end of
716                      the 13th Baktun. */
717                   (tr_time() < 1356130800 && (address[0] & 0xE0) != 0x20);
718            break;
719        }
720
721        default:
722            return TRUE;
723    }
724}
725
726tr_bool
727tr_isValidPeerAddress( const tr_address * addr, tr_port port )
728{
729    return ( port != 0 )
730        && ( tr_isAddress( addr ) )
731        && ( !isIPv6LinkLocalAddress( addr ) )
732        && ( !isIPv4MappedAddress( addr ) )
733        && ( !isMartianAddr( addr ) );
734}
Note: See TracBrowser for help on using the repository browser.