source: trunk/libtransmission/natpmp.c @ 2544

Last change on this file since 2544 was 2544, checked in by charles, 14 years ago

this looks bug but it's not: just janitorial cleanup, moving #includes from headers into source file

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