source: trunk/libtransmission/natpmp.c @ 2614

Last change on this file since 2614 was 2614, checked in by joshe, 14 years ago

Use BEOS to test for beos instead of relying on the build to set SYS_BEOS.
Add missing headers and other miscellaneous fixes for beos.

  • Property svn:keywords set to Date Rev Author Id
File size: 22.8 KB
Line 
1/******************************************************************************
2 * $Id: natpmp.c 2614 2007-08-04 01:17:39Z 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 <assert.h>
26#include <errno.h>
27#include <netdb.h>
28#include <stdlib.h>
29#include <string.h>
30#include <time.h>
31
32#include <sys/types.h>
33
34#include "transmission.h"
35#include "natpmp.h"
36#include "net.h"
37#include "platform.h" /* tr_getDefaultRoute() */
38#include "utils.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 == sockerrno || ECONNREFUSED == sockerrno )
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( sockerrno ) );
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 == sockerrno )
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( sockerrno ) );
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.