Changeset 1460 for trunk/libtransmission/natpmp.c
- Timestamp:
- Feb 6, 2007, 3:24:55 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/libtransmission/natpmp.c
r1443 r1460 34 34 #define PMP_OPCODE_ADDTCP 2 35 35 #define PMP_LIFETIME 3600 /* secs, one hour */ 36 #define PMP_RES PCODE_OK037 #define PMP_RES PCODE_BADVERS138 #define PMP_RES PCODE_REFUSED239 #define PMP_RES PCODE_NETDOWN340 #define PMP_RES PCODE_NOMEM441 #define PMP_RES PCODE_BADOPCODE536 #define PMP_RESULT_OK 0 37 #define PMP_RESULT_BADVERS 1 38 #define PMP_RESULT_REFUSED 2 39 #define PMP_RESULT_NETDOWN 3 40 #define PMP_RESULT_NOMEM 4 41 #define PMP_RESULT_BADOPCODE 5 42 42 43 43 #define PMP_OPCODE_FROM_RESPONSE( op ) ( 0x80 ^ (op) ) … … 64 64 uint64_t retry; 65 65 uint64_t timeout; 66 int port;67 tr_natpmp_uptime_t * uptime;66 int askport; 67 int gotport; 68 68 } tr_natpmp_req_t; 69 69 … … 90 90 }; 91 91 92 typedef struct tr_natpmp_parse_s 93 { 94 unsigned int tmpfail : 1; 95 uint32_t seconds; 96 uint16_t port; 97 uint32_t lifetime; 98 } 99 tr_natpmp_parse_t; 100 92 101 static int 93 102 checktime( tr_natpmp_uptime_t * uptime, uint32_t seen ); … … 95 104 killsock( int * fd ); 96 105 static tr_natpmp_req_t * 97 newreq( int adding, struct in_addr addr, int port, 98 tr_natpmp_uptime_t * uptime ); 106 newreq( int adding, struct in_addr addr, int port ); 107 static void 108 killreq( tr_natpmp_req_t ** req ); 109 static void 110 resetreq( tr_natpmp_req_t * req ); 99 111 static tr_tristate_t 100 pulsereq( tr_natpmp_req_t * req, uint64_t * renew ); 112 pulsereq( tr_natpmp_t * req ); 113 static int 114 sendreq( tr_natpmp_req_t * req ); 101 115 static int 102 116 mcastsetup(); 103 117 static void 104 118 mcastpulse( tr_natpmp_t * pmp ); 105 static void106 killreq( tr_natpmp_req_t ** req );107 static int108 sendrequest( int adding, int fd, int port );109 119 static tr_tristate_t 110 readrequest( uint8_t * buf, int len, int adding, int port, 111 tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail ); 120 parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse ); 112 121 113 122 tr_natpmp_t * … … 159 168 pmp->mcastfd = mcastsetup(); 160 169 } 161 /* XXX should I change state? */162 170 } 163 171 … … 184 192 if( NULL != pmp->req ) 185 193 { 186 pmp->mappedport = pmp->req-> port;194 pmp->mappedport = pmp->req->gotport; 187 195 killreq( &pmp->req ); 188 196 pmp->state = PMP_STATE_DELETING; … … 271 279 /* try to send at least one delete request if we have a port mapping */ 272 280 tr_natpmpStop( pmp ); 273 tr_natpmpPulse( pmp );281 tr_natpmpPulse( pmp, NULL ); 274 282 275 283 tr_lockLock( &pmp->lock ); … … 280 288 281 289 void 282 tr_natpmpPulse( tr_natpmp_t * pmp )290 tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort ) 283 291 { 284 292 tr_lockLock( &pmp->lock ); … … 319 327 else 320 328 { 321 pmp->req = newreq( 1, pmp->dest, pmp->newport, 322 &pmp->uptime ); 329 pmp->req = newreq( 1, pmp->dest, pmp->newport ); 323 330 if( NULL == pmp->req ) 324 331 { … … 330 337 if( PMP_STATE_ADDING == pmp->state ) 331 338 { 332 switch( pulsereq( pmp ->req, &pmp->renew) )339 switch( pulsereq( pmp ) ) 333 340 { 334 341 case TR_NET_ERROR: … … 342 349 pmp->state = PMP_STATE_TMPFAIL; 343 350 tr_dbg( "nat-pmp state add -> err on pulse" ); 344 if( pmp->req-> port == pmp->newport )351 if( pmp->req->askport == pmp->newport ) 345 352 { 346 353 pmp->newport = 0; … … 355 362 break; 356 363 case TR_NET_OK: 357 pmp->mappedport = pmp->req->port; 364 pmp->mappedport = pmp->req->gotport; 365 if( pmp->mappedport != pmp->newport && 366 pmp->newport == pmp->req->askport ) 367 { 368 pmp->newport = pmp->req->gotport; 369 } 358 370 killreq( &pmp->req ); 359 371 pmp->state = PMP_STATE_MAPPED; … … 373 385 { 374 386 assert( 0 < pmp->mappedport ); 375 pmp->req = newreq( 0, pmp->dest, pmp->mappedport, 376 &pmp->uptime ); 387 pmp->req = newreq( 0, pmp->dest, pmp->mappedport ); 377 388 if( NULL == pmp->req ) 378 389 { … … 383 394 if( PMP_STATE_DELETING == pmp->state ) 384 395 { 385 switch( pulsereq( pmp ->req, &pmp->renew) )396 switch( pulsereq( pmp ) ) 386 397 { 387 398 case TR_NET_ERROR: … … 408 419 case TR_NET_OK: 409 420 tr_dbg( "nat-pmp state del -> idle with port %i", 410 pmp->req->port); 411 tr_inf( "nat-pmp unmapped port %i", pmp->req->port ); 421 pmp->req->askport); 422 tr_inf( "nat-pmp unmapped port %i", 423 pmp->req->askport ); 412 424 pmp->mapped = 0; 413 425 pmp->mappedport = -1; … … 445 457 } 446 458 459 if( NULL != publicPort ) 460 { 461 *publicPort = -1; 462 } 463 447 464 tr_lockUnlock( &pmp->lock ); 448 465 } … … 483 500 484 501 static tr_natpmp_req_t * 485 newreq( int adding, struct in_addr addr, int port, 486 tr_natpmp_uptime_t * uptime ) 502 newreq( int adding, struct in_addr addr, int port ) 487 503 { 488 504 tr_natpmp_req_t * ret; 489 uint64_t now;490 505 491 506 ret = calloc( 1, sizeof( *ret ) ); 492 507 if( NULL == ret ) 493 508 { 494 goto err;495 } 496 ret->fd = -1; 509 return NULL; 510 } 511 497 512 ret->fd = tr_netOpenUDP( addr, htons( PMP_PORT ), 1 ); 498 513 if( 0 > ret->fd ) 499 514 { 500 goto err; 501 } 502 if( sendrequest( adding, ret->fd, port ) ) 503 { 504 goto err; 505 } 515 free( ret ); 516 return NULL; 517 } 518 519 ret->adding = adding; 520 ret->askport = port; 521 ret->gotport = port; 522 resetreq( ret ); 523 if( sendreq( ret ) ) 524 { 525 killreq( &ret ); 526 return NULL; 527 } 528 529 return ret; 530 } 531 532 static void 533 killreq( tr_natpmp_req_t ** req ) 534 { 535 if( NULL != *req ) 536 { 537 killsock( &(*req)->fd ); 538 free( *req ); 539 *req = NULL; 540 } 541 } 542 543 static void 544 resetreq( tr_natpmp_req_t * req ) 545 { 546 uint64_t now; 506 547 507 548 now = tr_date(); 508 ret->adding = adding; 509 ret->delay = PMP_INITIAL_DELAY; 510 ret->retry = now + PMP_INITIAL_DELAY; 511 ret->timeout = now + PMP_TOTAL_DELAY; 512 ret->port = port; 513 ret->uptime = uptime; 514 515 return ret; 516 517 err: 518 if( NULL != ret ) 519 { 520 killsock( &ret->fd ); 521 } 522 free( ret ); 523 524 return NULL; 549 req->delay = PMP_INITIAL_DELAY; 550 req->retry = now; 551 req->timeout = now + PMP_TOTAL_DELAY; 525 552 } 526 553 527 554 static tr_tristate_t 528 pulsereq( tr_natpmp_req_t * req, uint64_t * renew ) 529 { 555 pulsereq( tr_natpmp_t * pmp ) 556 { 557 tr_natpmp_req_t * req = pmp->req; 530 558 struct sockaddr_in sin; 531 559 uint8_t buf[16]; 532 int res , tmpfail;560 int res; 533 561 uint64_t now; 534 562 tr_tristate_t ret; 563 tr_natpmp_parse_t parse; 535 564 536 565 now = tr_date(); 537 566 /* check for timeout */ 538 567 if( now >= req->timeout ) 539 568 { … … 542 571 return TR_NET_ERROR; 543 572 } 544 545 if( now >= req->retry ) 546 { 547 if( sendrequest( req->adding, req->fd, req->port ) ) 548 { 549 return TR_NET_ERROR; 550 } 551 req->delay *= 2; 552 req->timeout = now + req->delay; 553 } 554 573 /* send another request if it's been long enough */ 574 if( now >= req->retry && sendreq( req ) ) 575 { 576 return TR_NET_ERROR; 577 } 578 579 /* check for incoming packets */ 555 580 res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin ); 556 581 if( TR_NET_BLOCK & res ) … … 572 597 } 573 598 599 /* parse the packet */ 574 600 tr_dbg( "nat-pmp read %i byte response", res ); 575 576 ret = readrequest( buf, res, req->adding, req->port, req->uptime, renew, 577 &tmpfail ); 578 req->tmpfail = ( tmpfail ? 1 : 0 ); 601 ret = parseresponse( buf, res, req->askport, &parse ); 602 req->tmpfail = parse.tmpfail; 603 /* check for device reset */ 604 if( checktime( &pmp->uptime, parse.seconds ) ) 605 { 606 pmp->renew = 0; 607 tr_inf( "detected nat-pmp device reset" ); 608 resetreq( req ); 609 ret = TR_NET_WAIT; 610 } 611 if( TR_NET_OK == ret && req->adding ) 612 { 613 if( req->askport != parse.port ) 614 { 615 tr_dbg( "nat-pmp received %i for public port instead of %i", 616 parse.port, req->askport ); 617 req->gotport = parse.port; 618 } 619 tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime ); 620 pmp->renew = now + ( parse.lifetime / 2 * 1000 ); 621 } 622 579 623 return ret; 624 } 625 626 static int 627 sendreq( tr_natpmp_req_t * req ) 628 { 629 uint8_t buf[12]; 630 int res; 631 632 bzero( buf, sizeof( buf ) ); 633 buf[0] = PMP_VERSION; 634 buf[1] = PMP_OPCODE_ADDTCP; 635 PMP_TOBUF16( buf + 4, req->askport ); 636 if( req->adding ) 637 { 638 PMP_TOBUF16( buf + 6, req->askport ); 639 PMP_TOBUF32( buf + 8, PMP_LIFETIME ); 640 } 641 642 res = tr_netSend( req->fd, buf, sizeof( buf ) ); 643 if( TR_NET_CLOSE & res && EHOSTUNREACH == errno ) 644 { 645 res = TR_NET_BLOCK; 646 } 647 if( TR_NET_CLOSE & res ) 648 { 649 tr_err( "failed to send nat-pmp request (%s)", strerror( errno ) ); 650 return 1; 651 } 652 else if( !( TR_NET_BLOCK & res ) ) 653 { 654 /* XXX is it all right to assume the entire thing is written? */ 655 req->retry = tr_date() + req->delay; 656 req->delay *= 2; 657 } 658 return 0; 580 659 } 581 660 … … 605 684 int res; 606 685 char dbgstr[INET_ADDRSTRLEN]; 686 tr_natpmp_parse_t parse; 607 687 608 688 res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin ); … … 628 708 } 629 709 630 if( TR_NET_OK == readrequest( buf, res, 0, -1, &pmp->uptime, &pmp->renew, NULL ) && 631 PMP_STATE_NOBODYHOME == pmp->state ) 632 { 633 tr_dbg( "nat-pmp state notfound -> idle" ); 634 pmp->state = PMP_STATE_IDLE; 635 } 636 } 637 638 static void 639 killreq( tr_natpmp_req_t ** req ) 640 { 641 if( NULL != *req ) 642 { 643 killsock( &(*req)->fd ); 644 free( *req ); 645 *req = NULL; 646 } 647 } 648 649 static int 650 sendrequest( int adding, int fd, int port ) 651 { 652 uint8_t buf[12]; 653 int res; 654 655 buf[0] = PMP_VERSION; 656 buf[1] = PMP_OPCODE_ADDTCP; 657 buf[2] = 0; 658 buf[3] = 0; 659 PMP_TOBUF16( buf + 4, port ); 660 if( adding ) 661 { 662 PMP_TOBUF16( buf + 6, port ); 663 PMP_TOBUF32( buf + 8, PMP_LIFETIME ); 664 } 665 else 666 { 667 PMP_TOBUF16( buf + 6, 0 ); 668 PMP_TOBUF32( buf + 8, 0 ); 669 } 670 671 res = tr_netSend( fd, buf, sizeof( buf ) ); 672 if( TR_NET_CLOSE & res && EHOSTUNREACH == errno ) 673 { 674 res = TR_NET_BLOCK; 675 } 676 /* XXX is it all right to assume the entire thing is written? */ 677 678 /* XXX I should handle blocking here */ 679 680 return ( ( TR_NET_CLOSE | TR_NET_BLOCK ) & res ? 1 : 0 ); 710 if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) ) 711 { 712 if( checktime( &pmp->uptime, parse.seconds ) ) 713 { 714 pmp->renew = 0; 715 tr_inf( "detected nat-pmp device reset" ); 716 if( NULL != pmp->req ) 717 { 718 resetreq( pmp->req ); 719 } 720 } 721 if( PMP_STATE_NOBODYHOME == pmp->state ) 722 { 723 tr_dbg( "nat-pmp state notfound -> idle" ); 724 pmp->state = PMP_STATE_IDLE; 725 } 726 } 681 727 } 682 728 683 729 static tr_tristate_t 684 readrequest( uint8_t * buf, int len, int adding, int port, 685 tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail ) 686 { 687 uint8_t version, opcode, wantedopcode; 688 uint16_t rescode, privport, pubport; 689 uint32_t seconds, lifetime; 690 691 assert( !adding || NULL != tmpfail ); 692 if( NULL != tmpfail ) 693 { 694 *tmpfail = 0; 695 } 696 if( 4 > len ) 730 parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse ) 731 { 732 int version, respopcode, opcode, wantedopcode, rescode, privport; 733 734 bzero( parse, sizeof( *parse ) ); 735 736 if( 8 > len ) 697 737 { 698 738 tr_err( "read truncated %i byte nat-pmp response packet", len ); 699 739 return TR_NET_ERROR; 700 740 } 741 742 /* parse the first 8 bytes: version, opcode, and result code */ 701 743 version = buf[0]; 702 opcode = buf[1]; 744 respopcode = buf[1]; 745 opcode = PMP_OPCODE_FROM_RESPONSE( respopcode ); 746 wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP ); 703 747 rescode = PMP_FROMBUF16( buf + 2 ); 704 wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP ); 705 706 if( !PMP_OPCODE_IS_RESPONSE( opcode ) ) 748 749 if( PMP_VERSION != version ) 750 { 751 tr_err( "unknown nat-pmp version %hhu", buf[0] ); 752 return TR_NET_ERROR; 753 } 754 if( !PMP_OPCODE_IS_RESPONSE( respopcode ) ) 707 755 { 708 756 tr_dbg( "nat-pmp ignoring request packet" ); 709 757 return TR_NET_WAIT; 710 758 } 711 opcode = PMP_OPCODE_FROM_RESPONSE( opcode ); 712 713 if( PMP_VERSION != version ) 714 { 715 tr_err( "bad nat-pmp version %hhu", buf[0] ); 759 if( wantedopcode != opcode ) 760 { 761 tr_err( "unknown nat-pmp opcode %hhu", opcode ); 716 762 return TR_NET_ERROR; 717 763 } 718 if( wantedopcode != opcode ) 719 { 720 tr_err( "bad nat-pmp opcode %hhu", opcode ); 721 return TR_NET_ERROR; 722 } 764 723 765 switch( rescode ) 724 766 { 725 case PMP_RES PCODE_OK:767 case PMP_RESULT_OK: 726 768 break; 727 case PMP_RESPCODE_REFUSED: 728 case PMP_RESPCODE_NETDOWN: 729 case PMP_RESPCODE_NOMEM: 730 if( NULL != tmpfail ) 731 { 732 *tmpfail = 1; 733 } 734 /* fallthrough */ 769 case PMP_RESULT_REFUSED: 770 tr_err( "nat-pmp mapping failed: refused/unauthorized/disabled" ); 771 parse->tmpfail = 1; 772 return TR_NET_ERROR; 773 case PMP_RESULT_NETDOWN: 774 tr_err( "nat-pmp mapping failed: network down" ); 775 parse->tmpfail = 1; 776 return TR_NET_ERROR; 777 case PMP_RESULT_NOMEM: 778 tr_err( "nat-pmp mapping refused: insufficient resources" ); 779 parse->tmpfail = 1; 780 return TR_NET_ERROR; 735 781 default: 736 tr_err( "bad nat-pmp result code %hu", rescode ); 782 tr_err( "nat-pmp mapping refused: unknown result code: %hu", 783 rescode ); 737 784 return TR_NET_ERROR; 738 785 } 739 786 740 if( 8 > len ) 741 { 742 tr_err( "read truncated %i byte nat-pmp response packet", len ); 743 return TR_NET_ERROR; 744 } 745 seconds = PMP_FROMBUF32( buf + 4 ); 746 747 if( checktime( uptime, seconds ) ) 748 { 749 *renew = 0; 750 tr_inf( "detected nat-pmp device reset" ); 751 /* XXX should reset retry counter here */ 752 return TR_NET_WAIT; 753 } 754 755 if( 0 <= port ) 756 { 757 assert( PMP_OPCODE_ADDTCP == wantedopcode ); 787 parse->seconds = PMP_FROMBUF32( buf + 4 ); 788 if( PMP_OPCODE_ADDTCP == opcode ) 789 { 758 790 if( 16 > len ) 759 791 { … … 761 793 return TR_NET_ERROR; 762 794 } 763 privport = PMP_FROMBUF16( buf + 8 );764 p ubport= PMP_FROMBUF16( buf + 10 );765 lifetime = PMP_FROMBUF32( buf + 12 );795 privport = PMP_FROMBUF16( buf + 8 ); 796 parse->port = PMP_FROMBUF16( buf + 10 ); 797 parse->lifetime = PMP_FROMBUF32( buf + 12 ); 766 798 767 799 if( port != privport ) 768 800 { 769 /* private port doesn't match, ignore it */770 801 tr_dbg( "nat-pmp ignoring message for port %i, expected port %i", 771 802 privport, port ); 772 803 return TR_NET_WAIT; 773 804 } 774 775 if( adding )776 {777 if( port != pubport )778 {779 *tmpfail = 1;780 /* XXX should just start announcing the pub port we're given */781 return TR_NET_ERROR;782 }783 tr_dbg( "nat-pmp set renew to half of %u", lifetime );784 *renew = tr_date() + ( lifetime / 2 * 1000 );785 }786 805 } 787 806
Note: See TracChangeset
for help on using the changeset viewer.