source: branches/nat-traversal/libtransmission/natpmp.c @ 902

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

Treat a couple more NAT-PMP errors as temporary.

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