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

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

Don't treat a requested port being in use as a permanent error with NAT-PMP

  • Property svn:keywords set to Date Rev Author Id
File size: 21.5 KB
Line 
1/******************************************************************************
2 * $Id: natpmp.c 901 2006-09-24 05:03:42Z 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         portused : 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_PORTUSED      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 * used );
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_PORTUSED:
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_PORTUSED:
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_PORTUSED:
297                if( 0 < pmp->newport )
298                {
299                    pmp->state = PMP_STATE_ADDING;
300                    tr_dbg( "nat-pmp state %s -> add with port %i",
301                            ( PMP_STATE_IDLE == pmp->state ? "idle" : "used" ),
302                            pmp->newport );
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->portused )
341                            {
342                                pmp->state = PMP_STATE_PORTUSED;
343                                tr_dbg( "nat-pmp state add -> used 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
393                            {
394                                pmp->state = PMP_STATE_FAILED;
395                                tr_dbg( "nat-pmp state del -> fail on pulse" );
396                            }
397                            killreq( &pmp->req );
398                            break;
399                        case TR_OK:
400                            tr_dbg( "nat-pmp state del -> idle with port %i",
401                                    pmp->req->port);
402                            tr_inf( "nat-pmp unmapped port %i", pmp->req->port );
403                            pmp->mappedport = -1;
404                            killreq( &pmp->req );
405                            pmp->state = PMP_STATE_IDLE;
406                            break;
407                        case TR_WAIT:
408                            break;
409                    }
410                }
411                break;
412
413            case PMP_STATE_MAPPED:
414                if( pmp->newport != pmp->mappedport )
415                {
416                    tr_dbg( "nat-pmp state mapped -> del, port from %i to %i",
417                            pmp->mappedport, pmp->newport );
418                    pmp->state = PMP_STATE_DELETING;
419                }
420                else if( tr_date() > pmp->renew )
421                {
422                    pmp->state = PMP_STATE_ADDING;
423                    tr_dbg( "nat-pmp state mapped -> add for renewal" );
424                }
425                break;
426
427            case PMP_STATE_FAILED:
428            case PMP_STATE_NOBODYHOME:
429                break;
430
431            default:
432                assert( 0 );
433                break;
434        }
435    }
436
437    tr_lockUnlock( &pmp->lock );
438}
439
440static int
441checktime( tr_natpmp_uptime_t * uptime, uint32_t cursecs )
442{
443    time_t   now;
444    int      ret;
445    uint32_t estimated;
446
447    now = time( NULL );
448    ret = 0;
449    if( 0 < uptime->when )
450    {
451        estimated = ( ( now - uptime->when ) * 7 / 8 ) + uptime->uptime;
452        if( estimated > cursecs )
453        {
454            ret = 1;
455        }
456    }
457
458    uptime->when   = now;
459    uptime->uptime = cursecs;
460
461    return ret;
462}
463
464static void
465killsock( int * fd, tr_fd_t * fdlimit )
466{
467    if( 0 <= *fd )
468    {
469        tr_netClose( *fd );
470        *fd = -1;
471        tr_fdSocketClosed( fdlimit, 0 );
472    }
473}
474
475static tr_natpmp_req_t *
476newreq( int adding, struct in_addr addr, int port, tr_fd_t * fdlimit,
477        tr_natpmp_uptime_t * uptime )
478{
479    tr_natpmp_req_t * ret;
480    uint64_t          now;
481
482    ret = calloc( 1, sizeof( *ret ) );
483    if( NULL == ret )
484    {
485        goto err;
486    }
487    ret->fd = -1;
488    if( tr_fdSocketWillCreate( fdlimit, 0 ) )
489    {
490        goto err;
491    }
492    ret->fd = tr_netOpenUDP( addr, htons( PMP_PORT ) );
493    if( 0 > ret->fd )
494    {
495        goto err;
496    }
497    if( sendrequest( adding, ret->fd, port ) )
498    {
499        goto err;
500    }
501
502    now          = tr_date();
503    ret->adding  = adding;
504    ret->delay   = PMP_INITIAL_DELAY;
505    ret->retry   = now + PMP_INITIAL_DELAY;
506    ret->timeout = now + PMP_TOTAL_DELAY;
507    ret->port    = port;
508    ret->fdlimit = fdlimit;
509    ret->uptime  = uptime;
510
511    return ret;
512
513  err:
514    if( NULL != ret )
515    {
516        killsock( &ret->fd, fdlimit );
517    }
518    free( ret );
519
520    return NULL;
521}
522
523static tr_tristate_t
524pulsereq( tr_natpmp_req_t * req, uint64_t * renew )
525{
526    struct sockaddr_in sin;
527    uint8_t            buf[16];
528    int                res, used;
529    uint64_t           now;
530    tr_tristate_t      ret;
531
532    now = tr_date();
533
534    if( now >= req->timeout )
535    {
536        tr_dbg( "nat-pmp request timed out" );
537        req->nobodyhome = 1;
538        return TR_ERROR;
539    }
540
541    if( now >= req->retry )
542    {
543        if( sendrequest( req->adding, req->fd, req->port ) )
544        {
545            return TR_ERROR;
546        }
547        req->delay *= 2;
548        req->timeout = now + req->delay;
549    }
550
551    res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
552    if( TR_NET_BLOCK & res )
553    {
554        return TR_WAIT;
555    }
556    else if( TR_NET_CLOSE & res )
557    {
558        if( ECONNRESET == errno || ECONNREFUSED == errno )
559        {
560            tr_dbg( "nat-pmp not supported by device" );
561            req->nobodyhome = 1;
562        }
563        else
564        {
565            tr_inf( "error reading nat-pmp response (%s)", strerror( errno ) );
566        }
567        return TR_ERROR;
568    }
569
570    tr_dbg( "nat-pmp read %i byte response", res );
571
572    ret = readrequest( buf, res, req->adding, req->port, req->uptime, renew,
573                       &used );
574    req->portused = ( used ? 1 : 0 );
575    return ret;
576}
577
578static int
579mcastsetup( tr_fd_t * fdlimit )
580{
581    int fd;
582    struct in_addr addr;
583
584    if( tr_fdSocketWillCreate( fdlimit, 0 ) )
585    {
586        return -1;
587    }
588
589    addr.s_addr = inet_addr( PMP_MCAST_ADDR );
590    fd = tr_netMcastOpen( PMP_PORT, addr );
591    if( 0 > fd )
592    {
593        tr_fdSocketClosed( fdlimit, 0 );
594        return -1;
595    }
596
597    tr_dbg( "nat-pmp create multicast socket %i", fd );
598
599    return fd;
600}
601
602static void
603mcastpulse( tr_natpmp_t * pmp )
604{
605    struct sockaddr_in sin;
606    uint8_t            buf[16];
607    int                res;
608    char               dbgstr[INET_ADDRSTRLEN];
609
610    res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
611    if( TR_NET_BLOCK & res )
612    {
613        return;
614    }
615    else if( TR_NET_CLOSE & res )
616    {
617        tr_err( "error reading nat-pmp multicast message" );
618        killsock( &pmp->mcastfd, pmp->fdlimit );
619        return;
620    }
621
622    tr_netNtop( &sin.sin_addr, dbgstr, sizeof( dbgstr ) );
623    tr_dbg( "nat-pmp read %i byte multicast packet from %s", res, dbgstr );
624
625    if( pmp->dest.s_addr != sin.sin_addr.s_addr )
626    {
627        tr_dbg( "nat-pmp ignoring multicast packet from unknown host %s",
628                dbgstr );
629        return;
630    }
631
632    if( TR_OK == readrequest( buf, res, 0, -1, &pmp->uptime, &pmp->renew, NULL ) &&
633        PMP_STATE_NOBODYHOME == pmp->state )
634    {
635        tr_dbg( "nat-pmp state notfound -> idle" );
636        pmp->state = PMP_STATE_IDLE;
637    }
638}
639
640static void
641killreq( tr_natpmp_req_t ** req )
642{
643    if( NULL != *req )
644    {
645        killsock( &(*req)->fd, (*req)->fdlimit );
646        free( *req );
647        *req = NULL;
648    }
649}
650
651static int
652sendrequest( int adding, int fd, int port )
653{
654    uint8_t buf[12];
655    int res;
656
657    buf[0] = PMP_VERSION;
658    buf[1] = PMP_OPCODE_ADDTCP;
659    buf[2] = 0;
660    buf[3] = 0;
661    PMP_TOBUF16( buf + 4, port );
662    if( adding )
663    {
664        PMP_TOBUF16( buf + 6, port );
665        PMP_TOBUF32( buf + 8, PMP_LIFETIME );
666    }
667    else
668    {
669        PMP_TOBUF16( buf + 6, 0 );
670        PMP_TOBUF32( buf + 8, 0 );
671    }
672
673    res = tr_netSend( fd, buf, sizeof( buf ) );
674    /* XXX is it all right to assume the entire thing is written? */
675
676    /* XXX I should handle blocking here */
677
678    return ( ( TR_NET_CLOSE | TR_NET_BLOCK ) & res  ? 1 : 0 );
679}
680
681static tr_tristate_t
682readrequest( uint8_t * buf, int len, int adding, int port,
683             tr_natpmp_uptime_t * uptime, uint64_t * renew, int * used )
684{
685    uint8_t            version, opcode, wantedopcode;
686    uint16_t           rescode, privport, pubport;
687    uint32_t           seconds, lifetime;
688
689    assert( !adding || NULL != used );
690    if( NULL != used )
691    {
692        *used = 0;
693    }
694    if( 4 > len )
695    {
696        tr_err( "read truncated %i byte nat-pmp response packet", len );
697        return TR_ERROR;
698    }
699    version      = buf[0];
700    opcode       = buf[1];
701    rescode      = PMP_FROMBUF16( buf + 2 );
702    wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
703
704    if( !PMP_OPCODE_IS_RESPONSE( opcode ) )
705    {
706        tr_dbg( "nat-pmp ignoring request packet" );
707        return TR_WAIT;
708    }
709    opcode = PMP_OPCODE_FROM_RESPONSE( opcode );
710
711    if( PMP_VERSION != version )
712    {
713        tr_err( "bad nat-pmp version %hhu", buf[0] );
714        return TR_ERROR;
715    }
716    if( wantedopcode != opcode )
717    {
718        tr_err( "bad nat-pmp opcode %hhu", opcode );
719        return TR_ERROR;
720    }
721    if( PMP_RESPCODE_OK != rescode )
722    {
723        tr_err( "bad nat-pmp result code %hu", rescode );
724        return TR_ERROR;
725    }
726
727    if( 8 > len )
728    {
729        tr_err( "read truncated %i byte nat-pmp response packet", len );
730        return TR_ERROR;
731    }
732    seconds = PMP_FROMBUF32( buf + 4 );
733
734    if( checktime( uptime, seconds ) )
735    {
736        *renew = 0;
737        tr_inf( "detected nat-pmp device reset" );
738        /* XXX should reset retry counter here */
739        return TR_WAIT;
740    }
741
742    if( 0 <= port )
743    {
744        assert( PMP_OPCODE_ADDTCP == wantedopcode );
745        if( 16 > len )
746        {
747            tr_err( "read truncated %i byte nat-pmp response packet", len );
748            return TR_ERROR;
749        }
750        privport = PMP_FROMBUF16( buf + 8 );
751        pubport  = PMP_FROMBUF16( buf + 10 );
752        lifetime = PMP_FROMBUF32( buf + 12 );
753
754        if( port != privport )
755        {
756            /* private port doesn't match, ignore it */
757            tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
758                    privport, port );
759            return TR_WAIT;
760        }
761
762        if( adding )
763        {
764            if( port != pubport )
765            {
766                *used = 1;
767                /* XXX should just start announcing the pub port we're given */
768                return TR_ERROR;
769            }
770            tr_dbg( "nat-pmp set renew to half of %u", lifetime );
771            *renew = tr_date() + ( lifetime / 2 * 1000 );
772        }
773    }
774
775    return TR_OK;
776}
Note: See TracBrowser for help on using the repository browser.