source: trunk/libtransmission/natpmp.c @ 3552

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

fix OpenBSD build error.

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