source: trunk/libtransmission/natpmp.c @ 1443

Last change on this file since 1443 was 1443, checked in by joshe, 15 years ago

When sending a nat-pmp packet, treat EHOSTDOWN as a temporary error.

  • Property svn:keywords set to Date Rev Author Id
File size: 22.0 KB
Line 
1/******************************************************************************
2 * $Id: natpmp.c 1443 2007-01-28 08:54:19Z joshe $
3 *
4 * Copyright (c) 2006 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 "transmission.h"
26
27#define PMP_PORT                5351
28#define PMP_MCAST_ADDR          "224.0.0.1"
29#define PMP_INITIAL_DELAY       250     /* ms, 1/4 second */
30#define PMP_TOTAL_DELAY         120000  /* ms, 2 minutes */
31#define PMP_VERSION             0
32#define PMP_OPCODE_GETIP        0
33#define PMP_OPCODE_ADDUDP       1
34#define PMP_OPCODE_ADDTCP       2
35#define PMP_LIFETIME            3600    /* secs, one hour */
36#define PMP_RESPCODE_OK         0
37#define PMP_RESPCODE_BADVERS    1
38#define PMP_RESPCODE_REFUSED    2
39#define PMP_RESPCODE_NETDOWN    3
40#define PMP_RESPCODE_NOMEM      4
41#define PMP_RESPCODE_BADOPCODE  5
42
43#define PMP_OPCODE_FROM_RESPONSE( op )  ( 0x80 ^ (op) )
44#define PMP_OPCODE_TO_RESPONSE( op )    ( 0x80 | (op) )
45#define PMP_OPCODE_IS_RESPONSE( op )    ( 0x80 & (op) )
46#define PMP_TOBUF16( buf, num ) ( *( (uint16_t *) (buf) ) = htons( (num) ) )
47#define PMP_TOBUF32( buf, num ) ( *( (uint32_t *) (buf) ) = htonl( (num) ) )
48#define PMP_FROMBUF16( buf )    ( htons( *( (uint16_t *) (buf) ) ) )
49#define PMP_FROMBUF32( buf )    ( htonl( *( (uint32_t *) (buf) ) ) )
50
51typedef struct tr_natpmp_uptime_s
52{
53    time_t   when;
54    uint32_t uptime;
55} tr_natpmp_uptime_t;
56
57typedef struct tr_natpmp_req_s
58{
59    unsigned int         adding : 1;
60    unsigned int         nobodyhome : 1;
61    unsigned int         tmpfail : 1;
62    int                  fd;
63    int                  delay;
64    uint64_t             retry;
65    uint64_t             timeout;
66    int                  port;
67    tr_natpmp_uptime_t * uptime;
68} tr_natpmp_req_t;
69
70struct tr_natpmp_s
71{
72#define PMP_STATE_IDLE          1
73#define PMP_STATE_ADDING        2
74#define PMP_STATE_DELETING      3
75#define PMP_STATE_MAPPED        4
76#define PMP_STATE_FAILED        5
77#define PMP_STATE_NOBODYHOME    6
78#define PMP_STATE_TMPFAIL       7
79    char               state;
80    unsigned int       active : 1;
81    unsigned int       mapped : 1;
82    struct in_addr     dest;
83    int                newport;
84    int                mappedport;
85    tr_lock_t          lock;
86    uint64_t           renew;
87    tr_natpmp_req_t *  req;
88    tr_natpmp_uptime_t uptime;
89    int                mcastfd;
90};
91
92static int
93checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
94static void
95killsock( int * fd );
96static tr_natpmp_req_t *
97newreq( int adding, struct in_addr addr, int port,
98        tr_natpmp_uptime_t * uptime );
99static tr_tristate_t
100pulsereq( tr_natpmp_req_t * req, uint64_t * renew );
101static int
102mcastsetup();
103static void
104mcastpulse( tr_natpmp_t * pmp );
105static void
106killreq( tr_natpmp_req_t ** req );
107static int
108sendrequest( int adding, int fd, int port );
109static tr_tristate_t
110readrequest( uint8_t * buf, int len, int adding, int port,
111             tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail );
112
113tr_natpmp_t *
114tr_natpmpInit()
115{
116    tr_natpmp_t * pmp;
117
118    pmp = calloc( 1, sizeof( *pmp ) );
119    if( NULL == pmp )
120    {
121        return NULL;
122    }
123
124    pmp->state       = PMP_STATE_IDLE;
125    pmp->mcastfd     = -1;
126
127    if( tr_getDefaultRoute( &pmp->dest ) || INADDR_ANY == pmp->dest.s_addr )
128    {
129        pmp->dest.s_addr = INADDR_NONE;
130    }
131
132    if( INADDR_NONE == pmp->dest.s_addr )
133    {
134        tr_dbg( "nat-pmp device is unknown" );
135    }
136    else
137    {
138        char addrstr[INET_ADDRSTRLEN];
139        tr_netNtop( &pmp->dest, addrstr, sizeof( addrstr ) );
140        tr_dbg( "nat-pmp device is %s", addrstr );
141    }
142
143    tr_lockInit( &pmp->lock );
144
145    return pmp;
146}
147
148void
149tr_natpmpStart( tr_natpmp_t * pmp )
150{
151    tr_lockLock( &pmp->lock );
152
153    if( !pmp->active )
154    {
155        tr_inf( "starting nat-pmp" );
156        pmp->active = 1;
157        if( 0 > pmp->mcastfd )
158        {
159            pmp->mcastfd = mcastsetup();
160        }
161        /* XXX should I change state? */
162    }
163
164    tr_lockUnlock( &pmp->lock );
165}
166
167void
168tr_natpmpStop( tr_natpmp_t * pmp )
169{
170    tr_lockLock( &pmp->lock );
171
172    if( pmp->active )
173    {
174        tr_inf( "stopping nat-pmp" );
175        pmp->active = 0;
176        killsock( &pmp->mcastfd );
177        switch( pmp->state )
178        {
179            case PMP_STATE_IDLE:
180                break;
181            case PMP_STATE_ADDING:
182                pmp->state = PMP_STATE_IDLE;
183                tr_dbg( "nat-pmp state add -> idle" );
184                if( NULL != pmp->req )
185                {
186                    pmp->mappedport = pmp->req->port;
187                    killreq( &pmp->req );
188                    pmp->state = PMP_STATE_DELETING;
189                    tr_dbg( "nat-pmp state idle -> del" );
190                }
191                break;
192            case PMP_STATE_DELETING:
193                break;
194            case PMP_STATE_MAPPED:
195                pmp->state = PMP_STATE_DELETING;
196                tr_dbg( "nat-pmp state mapped -> del" );
197                break;
198            case PMP_STATE_FAILED:
199            case PMP_STATE_NOBODYHOME:
200            case PMP_STATE_TMPFAIL:
201                break;
202            default:
203                assert( 0 );
204                break;
205        }
206    }
207
208    tr_lockUnlock( &pmp->lock );
209}
210
211int
212tr_natpmpStatus( tr_natpmp_t * pmp )
213{
214    int ret;
215
216    tr_lockLock( &pmp->lock );
217
218   
219    if( !pmp->active )
220    {
221        ret = ( PMP_STATE_DELETING == pmp->state ?
222                TR_NAT_TRAVERSAL_UNMAPPING : TR_NAT_TRAVERSAL_DISABLED );
223    }
224    else if( pmp->mapped )
225    {
226        ret = TR_NAT_TRAVERSAL_MAPPED;
227    }
228    else
229    {
230        switch( pmp->state )
231        {
232            case PMP_STATE_IDLE:
233            case PMP_STATE_ADDING:
234            case PMP_STATE_DELETING:
235                ret = TR_NAT_TRAVERSAL_MAPPING;
236                break;
237            case PMP_STATE_FAILED:
238            case PMP_STATE_TMPFAIL:
239                ret = TR_NAT_TRAVERSAL_ERROR;
240                break;
241            case PMP_STATE_NOBODYHOME:
242                ret = TR_NAT_TRAVERSAL_NOTFOUND;
243                break;
244            case PMP_STATE_MAPPED:
245            default:
246                /* if pmp->state is PMP_STATE_MAPPED then pmp->mapped
247                   should be true */
248                assert( 0 );
249                ret = TR_NAT_TRAVERSAL_ERROR;
250                break;
251        }
252    }
253
254    tr_lockUnlock( &pmp->lock );
255
256    return ret;
257}
258
259void
260tr_natpmpForwardPort( tr_natpmp_t * pmp, int port )
261{
262    tr_lockLock( &pmp->lock );
263    tr_inf( "nat-pmp set port %i", port );
264    pmp->newport = port;
265    tr_lockUnlock( &pmp->lock );
266}
267
268void
269tr_natpmpClose( tr_natpmp_t * pmp )
270{
271    /* try to send at least one delete request if we have a port mapping */
272    tr_natpmpStop( pmp );
273    tr_natpmpPulse( pmp );
274
275    tr_lockLock( &pmp->lock );
276    killreq( &pmp->req );
277    tr_lockClose( &pmp->lock );
278    free( pmp );
279}
280
281void
282tr_natpmpPulse( tr_natpmp_t * pmp )
283{
284    tr_lockLock( &pmp->lock );
285
286    if( 0 <= pmp->mcastfd )
287    {
288        mcastpulse( pmp );
289    }
290
291    if( pmp->active || PMP_STATE_DELETING == pmp->state )
292    {
293        switch( pmp->state )
294        {
295            case PMP_STATE_IDLE:
296            case PMP_STATE_TMPFAIL:
297                if( 0 < pmp->newport )
298                {
299                    tr_dbg( "nat-pmp state %s -> add with port %i",
300                            ( PMP_STATE_IDLE == pmp->state ? "idle" : "err" ),
301                            pmp->newport );
302                    pmp->state = PMP_STATE_ADDING;
303                }
304                break;
305
306            case PMP_STATE_ADDING:
307                if( NULL == pmp->req )
308                {
309                    if( 0 >= pmp->newport )
310                    {
311                        tr_dbg( "nat-pmp state add -> idle, no port" );
312                        pmp->state = PMP_STATE_IDLE;
313                    }
314                    else if( INADDR_NONE == pmp->dest.s_addr )
315                    {
316                        tr_dbg( "nat-pmp state add -> fail, no default route" );
317                        pmp->state = PMP_STATE_FAILED;
318                    }
319                    else
320                    {
321                        pmp->req = newreq( 1, pmp->dest, pmp->newport,
322                                           &pmp->uptime );
323                        if( NULL == pmp->req )
324                        {
325                            pmp->state = PMP_STATE_FAILED;
326                            tr_dbg( "nat-pmp state add -> fail on req init" );
327                        }
328                    }
329                }
330                if( PMP_STATE_ADDING == pmp->state )
331                {
332                    switch( pulsereq( pmp->req, &pmp->renew ) )
333                    {
334                        case TR_NET_ERROR:
335                            if( pmp->req->nobodyhome )
336                            {
337                                pmp->state = PMP_STATE_NOBODYHOME;
338                                tr_dbg( "nat-pmp state add -> nobodyhome on pulse" );
339                            }
340                            else if( pmp->req->tmpfail )
341                            {
342                                pmp->state = PMP_STATE_TMPFAIL;
343                                tr_dbg( "nat-pmp state add -> err on pulse" );
344                                if( pmp->req->port == pmp->newport )
345                                {
346                                    pmp->newport = 0;
347                                }
348                            }
349                            else
350                            {
351                                pmp->state = PMP_STATE_FAILED;
352                                tr_dbg( "nat-pmp state add -> fail on pulse" );
353                            }
354                            killreq( &pmp->req );
355                            break;
356                        case TR_NET_OK:
357                            pmp->mappedport = pmp->req->port;
358                            killreq( &pmp->req );
359                            pmp->state = PMP_STATE_MAPPED;
360                            pmp->mapped = 1;
361                            tr_dbg( "nat-pmp state add -> mapped with port %i",
362                                    pmp->mappedport);
363                            tr_inf( "nat-pmp mapped port %i", pmp->mappedport );
364                            break;
365                        case TR_NET_WAIT:
366                            break;
367                    }
368                }
369                break;
370
371            case PMP_STATE_DELETING:
372                if( NULL == pmp->req )
373                {
374                    assert( 0 < pmp->mappedport );
375                    pmp->req = newreq( 0, pmp->dest, pmp->mappedport,
376                                       &pmp->uptime );
377                    if( NULL == pmp->req )
378                    {
379                        pmp->state = PMP_STATE_FAILED;
380                        tr_dbg( "nat-pmp state del -> fail on req init" );
381                    }
382                }
383                if( PMP_STATE_DELETING == pmp->state )
384                {
385                    switch( pulsereq( pmp->req, &pmp->renew ) )
386                    {
387                        case TR_NET_ERROR:
388                            if( pmp->req->nobodyhome )
389                            {
390                                pmp->mapped = 0;
391                                pmp->state = PMP_STATE_NOBODYHOME;
392                                tr_dbg( "nat-pmp state del -> nobodyhome on pulse" );
393                            }
394                            else if( pmp->req->tmpfail )
395                            {
396                                pmp->mapped = 0;
397                                pmp->state = PMP_STATE_TMPFAIL;
398                                tr_dbg( "nat-pmp state del -> err on pulse" );
399                                pmp->mappedport = -1;
400                            }
401                            else
402                            {
403                                pmp->state = PMP_STATE_FAILED;
404                                tr_dbg( "nat-pmp state del -> fail on pulse" );
405                            }
406                            killreq( &pmp->req );
407                            break;
408                        case TR_NET_OK:
409                            tr_dbg( "nat-pmp state del -> idle with port %i",
410                                    pmp->req->port);
411                            tr_inf( "nat-pmp unmapped port %i", pmp->req->port );
412                            pmp->mapped = 0;
413                            pmp->mappedport = -1;
414                            killreq( &pmp->req );
415                            pmp->state = PMP_STATE_IDLE;
416                            break;
417                        case TR_NET_WAIT:
418                            break;
419                    }
420                }
421                break;
422
423            case PMP_STATE_MAPPED:
424                if( pmp->newport != pmp->mappedport )
425                {
426                    tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
427                            pmp->mappedport, pmp->newport );
428                    pmp->state = PMP_STATE_DELETING;
429                }
430                else if( tr_date() > pmp->renew )
431                {
432                    pmp->state = PMP_STATE_ADDING;
433                    tr_dbg( "nat-pmp state mapped -> add for renewal" );
434                }
435                break;
436
437            case PMP_STATE_FAILED:
438            case PMP_STATE_NOBODYHOME:
439                break;
440
441            default:
442                assert( 0 );
443                break;
444        }
445    }
446
447    tr_lockUnlock( &pmp->lock );
448}
449
450static int
451checktime( tr_natpmp_uptime_t * uptime, uint32_t cursecs )
452{
453    time_t   now;
454    int      ret;
455    uint32_t estimated;
456
457    now = time( NULL );
458    ret = 0;
459    if( 0 < uptime->when )
460    {
461        estimated = ( ( now - uptime->when ) * 7 / 8 ) + uptime->uptime;
462        if( estimated > cursecs )
463        {
464            ret = 1;
465        }
466    }
467
468    uptime->when   = now;
469    uptime->uptime = cursecs;
470
471    return ret;
472}
473
474static void
475killsock( int * fd )
476{
477    if( 0 <= *fd )
478    {
479        tr_netClose( *fd );
480        *fd = -1;
481    }
482}
483
484static tr_natpmp_req_t *
485newreq( int adding, struct in_addr addr, int port,
486        tr_natpmp_uptime_t * uptime )
487{
488    tr_natpmp_req_t * ret;
489    uint64_t          now;
490
491    ret = calloc( 1, sizeof( *ret ) );
492    if( NULL == ret )
493    {
494        goto err;
495    }
496    ret->fd = -1;
497    ret->fd = tr_netOpenUDP( addr, htons( PMP_PORT ), 1 );
498    if( 0 > ret->fd )
499    {
500        goto err;
501    }
502    if( sendrequest( adding, ret->fd, port ) )
503    {
504        goto err;
505    }
506
507    now          = tr_date();
508    ret->adding  = adding;
509    ret->delay   = PMP_INITIAL_DELAY;
510    ret->retry   = now + PMP_INITIAL_DELAY;
511    ret->timeout = now + PMP_TOTAL_DELAY;
512    ret->port    = port;
513    ret->uptime  = uptime;
514
515    return ret;
516
517  err:
518    if( NULL != ret )
519    {
520        killsock( &ret->fd );
521    }
522    free( ret );
523
524    return NULL;
525}
526
527static tr_tristate_t
528pulsereq( tr_natpmp_req_t * req, uint64_t * renew )
529{
530    struct sockaddr_in sin;
531    uint8_t            buf[16];
532    int                res, tmpfail;
533    uint64_t           now;
534    tr_tristate_t      ret;
535
536    now = tr_date();
537
538    if( now >= req->timeout )
539    {
540        tr_dbg( "nat-pmp request timed out" );
541        req->nobodyhome = 1;
542        return TR_NET_ERROR;
543    }
544
545    if( now >= req->retry )
546    {
547        if( sendrequest( req->adding, req->fd, req->port ) )
548        {
549            return TR_NET_ERROR;
550        }
551        req->delay *= 2;
552        req->timeout = now + req->delay;
553    }
554
555    res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
556    if( TR_NET_BLOCK & res )
557    {
558        return TR_NET_WAIT;
559    }
560    else if( TR_NET_CLOSE & res )
561    {
562        if( ECONNRESET == errno || ECONNREFUSED == errno )
563        {
564            tr_dbg( "nat-pmp not supported by device" );
565            req->nobodyhome = 1;
566        }
567        else
568        {
569            tr_inf( "error reading nat-pmp response (%s)", strerror( errno ) );
570        }
571        return TR_NET_ERROR;
572    }
573
574    tr_dbg( "nat-pmp read %i byte response", res );
575
576    ret = readrequest( buf, res, req->adding, req->port, req->uptime, renew,
577                       &tmpfail );
578    req->tmpfail = ( tmpfail ? 1 : 0 );
579    return ret;
580}
581
582static int
583mcastsetup()
584{
585    int fd;
586    struct in_addr addr;
587
588    addr.s_addr = inet_addr( PMP_MCAST_ADDR );
589    fd = tr_netMcastOpen( PMP_PORT, addr );
590    if( 0 > fd )
591    {
592        return -1;
593    }
594
595    tr_dbg( "nat-pmp create multicast socket %i", fd );
596
597    return fd;
598}
599
600static void
601mcastpulse( tr_natpmp_t * pmp )
602{
603    struct sockaddr_in sin;
604    uint8_t            buf[16];
605    int                res;
606    char               dbgstr[INET_ADDRSTRLEN];
607
608    res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
609    if( TR_NET_BLOCK & res )
610    {
611        return;
612    }
613    else if( TR_NET_CLOSE & res )
614    {
615        tr_err( "error reading nat-pmp multicast message" );
616        killsock( &pmp->mcastfd );
617        return;
618    }
619
620    tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
621    tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );
622
623    if( pmp->dest.s_addr != sin.sin_addr.s_addr )
624    {
625        tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
626                dbgstr );
627        return;
628    }
629
630    if( TR_NET_OK == readrequest( buf, res, 0, -1, &pmp->uptime, &pmp->renew, NULL ) &&
631        PMP_STATE_NOBODYHOME == pmp->state )
632    {
633        tr_dbg( "nat-pmp state notfound -> idle" );
634        pmp->state = PMP_STATE_IDLE;
635    }
636}
637
638static void
639killreq( tr_natpmp_req_t ** req )
640{
641    if( NULL != *req )
642    {
643        killsock( &(*req)->fd );
644        free( *req );
645        *req = NULL;
646    }
647}
648
649static int
650sendrequest( int adding, int fd, int port )
651{
652    uint8_t buf[12];
653    int res;
654
655    buf[0] = PMP_VERSION;
656    buf[1] = PMP_OPCODE_ADDTCP;
657    buf[2] = 0;
658    buf[3] = 0;
659    PMP_TOBUF16( buf + 4, port );
660    if( adding )
661    {
662        PMP_TOBUF16( buf + 6, port );
663        PMP_TOBUF32( buf + 8, PMP_LIFETIME );
664    }
665    else
666    {
667        PMP_TOBUF16( buf + 6, 0 );
668        PMP_TOBUF32( buf + 8, 0 );
669    }
670
671    res = tr_netSend( fd, buf, sizeof( buf ) );
672    if( TR_NET_CLOSE & res && EHOSTUNREACH == errno )
673    {
674        res = TR_NET_BLOCK;
675    }
676    /* XXX is it all right to assume the entire thing is written? */
677
678    /* XXX I should handle blocking here */
679
680    return ( ( TR_NET_CLOSE | TR_NET_BLOCK ) & res  ? 1 : 0 );
681}
682
683static tr_tristate_t
684readrequest( uint8_t * buf, int len, int adding, int port,
685             tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail )
686{
687    uint8_t            version, opcode, wantedopcode;
688    uint16_t           rescode, privport, pubport;
689    uint32_t           seconds, lifetime;
690
691    assert( !adding || NULL != tmpfail );
692    if( NULL != tmpfail )
693    {
694        *tmpfail = 0;
695    }
696    if( 4 > len )
697    {
698        tr_err( "read truncated %i byte nat-pmp response packet", len );
699        return TR_NET_ERROR;
700    }
701    version      = buf[0];
702    opcode       = buf[1];
703    rescode      = PMP_FROMBUF16( buf + 2 );
704    wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
705
706    if( !PMP_OPCODE_IS_RESPONSE( opcode ) )
707    {
708        tr_dbg( "nat-pmp ignoring request packet" );
709        return TR_NET_WAIT;
710    }
711    opcode = PMP_OPCODE_FROM_RESPONSE( opcode );
712
713    if( PMP_VERSION != version )
714    {
715        tr_err( "bad nat-pmp version %hhu", buf[0] );
716        return TR_NET_ERROR;
717    }
718    if( wantedopcode != opcode )
719    {
720        tr_err( "bad nat-pmp opcode %hhu", opcode );
721        return TR_NET_ERROR;
722    }
723    switch( rescode )
724    {
725        case PMP_RESPCODE_OK:
726            break;
727        case PMP_RESPCODE_REFUSED:
728        case PMP_RESPCODE_NETDOWN:
729        case PMP_RESPCODE_NOMEM:
730            if( NULL != tmpfail )
731            {
732                *tmpfail = 1;
733            }
734            /* fallthrough */
735        default:
736            tr_err( "bad nat-pmp result code %hu", rescode );
737            return TR_NET_ERROR;
738    }
739
740    if( 8 > len )
741    {
742        tr_err( "read truncated %i byte nat-pmp response packet", len );
743        return TR_NET_ERROR;
744    }
745    seconds = PMP_FROMBUF32( buf + 4 );
746
747    if( checktime( uptime, seconds ) )
748    {
749        *renew = 0;
750        tr_inf( "detected nat-pmp device reset" );
751        /* XXX should reset retry counter here */
752        return TR_NET_WAIT;
753    }
754
755    if( 0 <= port )
756    {
757        assert( PMP_OPCODE_ADDTCP == wantedopcode );
758        if( 16 > len )
759        {
760            tr_err( "read truncated %i byte nat-pmp response packet", len );
761            return TR_NET_ERROR;
762        }
763        privport = PMP_FROMBUF16( buf + 8 );
764        pubport  = PMP_FROMBUF16( buf + 10 );
765        lifetime = PMP_FROMBUF32( buf + 12 );
766
767        if( port != privport )
768        {
769            /* private port doesn't match, ignore it */
770            tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
771                    privport, port );
772            return TR_NET_WAIT;
773        }
774
775        if( adding )
776        {
777            if( port != pubport )
778            {
779                *tmpfail = 1;
780                /* XXX should just start announcing the pub port we're given */
781                return TR_NET_ERROR;
782            }
783            tr_dbg( "nat-pmp set renew to half of %u", lifetime );
784            *renew = tr_date() + ( lifetime / 2 * 1000 );
785        }
786    }
787
788    return TR_NET_OK;
789}
Note: See TracBrowser for help on using the repository browser.