source: trunk/libtransmission/natpmp.c @ 3731

Last change on this file since 3731 was 3731, checked in by charles, 15 years ago

use Thomas Bernard's miniupnp library instead of rolling our own.

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