boost::optional<HostAddress> NATPMPInterface::getPublicIP() { if (sendpublicaddressrequest(&p->natpmp) < 0) { SWIFT_LOG(debug) << "Failed to send NAT-PMP public address request!" << std::endl; return boost::optional<HostAddress>(); } int r = 0; natpmpresp_t response; do { fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(p->natpmp.s, &fds); getnatpmprequesttimeout(&p->natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&p->natpmp, &response); } while (r == NATPMP_TRYAGAIN); if (r == 0) { return boost::optional<HostAddress>(HostAddress(reinterpret_cast<const unsigned char*>(&(response.pnu.publicaddress.addr)), 4)); } else { SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl; return boost::optional<HostAddress>(); } }
/** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device. * Use the connection context stored in <b>backend_state</b>. */ int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, void *backend_state) { int r = 0; int x = 0; int sav_errno; natpmp_state_t *state = (natpmp_state_t *) backend_state; struct timeval timeout; r = sendpublicaddressrequest(&(state->natpmp)); fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" " %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); do { getnatpmprequesttimeout(&(state->natpmp), &timeout); x = wait_until_fd_readable(state->natpmp.s, &timeout); if (x == -1) return -1; if (tor_fw_options->verbose) fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = errno; if (tor_fw_options->verbose) fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned" " %d\n", r); if ( r < 0 && r != NATPMP_TRYAGAIN) { fprintf(stderr, "E: NAT-PMP readnatpmpresponseorretry failed %d\n", r); fprintf(stderr, "E: NAT-PMP errno=%d '%s'\n", sav_errno, strerror(sav_errno)); } } while (r == NATPMP_TRYAGAIN ); if (r != 0) { fprintf(stderr, "E: NAT-PMP It appears that something went wrong:" " %d\n", r); return r; } fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", inet_ntoa((state->response).pnu.publicaddress.addr)); tor_fw_options->public_ip_status = 1; if (tor_fw_options->verbose) { fprintf(stdout, "V: result = %u\n", r); fprintf(stdout, "V: type = %u\n", (state->response).type); fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode); fprintf(stdout, "V: epoch = %u\n", (state->response).epoch); } return r; }
static int get_pmp_pubaddr(char *pub_addr) { int r = 0, i = 0, max = 5; natpmpresp_t response; char *pubaddr = NULL; fd_set fds; natpmp_t natpmp; const char *err = NULL; if ((r = initnatpmp(&natpmp)) < 0) { err = "init failed"; goto end; } if ((r = sendpublicaddressrequest(&natpmp)) < 0) { err = "pub addr req failed"; goto end; } do { struct timeval timeout = { 1, 0 }; i++; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for PMP %d/%d\n", i, max); FD_ZERO(&fds); FD_SET(natpmp.s, &fds); if ((r = getnatpmprequesttimeout(&natpmp, &timeout)) < 0) { err = "get timeout failed"; goto end; } if ((r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout)) < 0) { err = "select failed"; goto end; } r = readnatpmpresponseorretry(&natpmp, &response); } while (r == NATPMP_TRYAGAIN && i < max); if (r < 0) { err = "general error"; goto end; } pubaddr = inet_ntoa(response.pnu.publicaddress.addr); switch_copy_string(pub_addr, pubaddr, IP_LEN); nat_globals.nat_type = SWITCH_NAT_TYPE_PMP; closenatpmp(&natpmp); end: if (err) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error checking for PMP [%s]\n", err); } return r; }
NATPMP() { if (::initnatpmp(&_natpmp, 0, 0)) throw std::runtime_error("couldn't initialize natpmp library"); int res = sendpublicaddressrequest(&_natpmp); if (res != 2) throw std::runtime_error("couldn't request public address"); printf("Retreive external ip address\n"); natpmpresp_t response; res = _wait_response(&response); if (res != 0) throw std::runtime_error( "couln't retreive the public address: " + _get_error_string(res) ); }
/* sample code for using libnatpmp */ int main(int argc, char * * argv) { natpmp_t natpmp; natpmpresp_t response; int r; int sav_errno; struct timeval timeout; fd_set fds; int i; int protocol = 0; uint16_t privateport = 0; uint16_t publicport = 0; uint32_t lifetime = 3600; int command = 0; int forcegw = 0; in_addr_t gateway = 0; struct in_addr gateway_in_use; #ifdef WIN32 WSADATA wsaData; int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); if(nResult != NO_ERROR) { fprintf(stderr, "WSAStartup() failed.\n"); return -1; } #endif /* argument parsing */ for(i=1; i<argc; i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 'h': usage(stdout, argv[0]); return 0; case 'g': forcegw = 1; if(argc < i + 1) { fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]); return 1; } gateway = inet_addr(argv[++i]); break; case 'a': command = 'a'; if(argc < i + 4) { fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]); return 1; } i++; if(1 != sscanf(argv[i], "%hu", &publicport)) { fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]); return 1; } i++; if(1 != sscanf(argv[i], "%hu", &privateport)) { fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]); return 1; } i++; if(0 == strcasecmp(argv[i], "tcp")) protocol = NATPMP_PROTOCOL_TCP; else if(0 == strcasecmp(argv[i], "udp")) protocol = NATPMP_PROTOCOL_UDP; else { fprintf(stderr, "%s is not a valid protocol\n", argv[i]); return 1; } if(argc > i + 1) { if(1 != sscanf(argv[i+1], "%u", &lifetime)) { fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]); } else { i++; } } break; default: fprintf(stderr, "Unknown option %s\n", argv[i]); usage(stderr, argv[0]); return 1; } } else { fprintf(stderr, "Unknown option %s\n", argv[i]); usage(stderr, argv[0]); return 1; } } /* initnatpmp() */ r = initnatpmp(&natpmp, forcegw, gateway); printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS"); if(r<0) return 1; gateway_in_use.s_addr = natpmp.gateway; printf("using gateway : %s\n", inet_ntoa(gateway_in_use)); /* sendpublicaddressrequest() */ r = sendpublicaddressrequest(&natpmp); printf("sendpublicaddressrequest returned %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); if(r<0) return 1; do { FD_ZERO(&fds); FD_SET(natpmp.s, &fds); getnatpmprequesttimeout(&natpmp, &timeout); r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); if(r<0) { fprintf(stderr, "select()"); return 1; } r = readnatpmpresponseorretry(&natpmp, &response); sav_errno = errno; printf("readnatpmpresponseorretry returned %d (%s)\n", r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); if(r<0 && r!=NATPMP_TRYAGAIN) { #ifdef ENABLE_STRNATPMPERR fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", strnatpmperr(r)); #endif fprintf(stderr, " errno=%d '%s'\n", sav_errno, strerror(sav_errno)); } } while(r==NATPMP_TRYAGAIN); if(r<0) return 1; /* TODO : check that response.type == 0 */ printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr)); printf("epoch = %u\n", response.epoch); if(command == 'a') { /* sendnewportmappingrequest() */ r = sendnewportmappingrequest(&natpmp, protocol, privateport, publicport, lifetime); printf("sendnewportmappingrequest returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); if(r < 0) return 1; do { FD_ZERO(&fds); FD_SET(natpmp.s, &fds); getnatpmprequesttimeout(&natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&natpmp, &response); printf("readnatpmpresponseorretry returned %d (%s)\n", r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); } while(r==NATPMP_TRYAGAIN); if(r<0) { #ifdef ENABLE_STRNATPMPERR fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", strnatpmperr(r)); #endif return 1; } printf("Mapped public port %hu protocol %s to local port %hu " "liftime %u\n", response.pnu.newportmapping.mappedpublicport, response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" : (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" : "UNKNOWN"), response.pnu.newportmapping.privateport, response.pnu.newportmapping.lifetime); printf("epoch = %u\n", response.epoch); } r = closenatpmp(&natpmp); printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED"); if(r<0) return 1; return 0; }
int tr_natpmpPulse( struct tr_natpmp * nat, tr_port private_port, bool is_enabled, tr_port * public_port ) { int ret; if( is_enabled && ( nat->state == TR_NATPMP_DISCOVER ) ) { int val = initnatpmp( &nat->natpmp ); logVal( "initnatpmp", val ); val = sendpublicaddressrequest( &nat->natpmp ); logVal( "sendpublicaddressrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB; nat->has_discovered = true; setCommandTime( nat ); } if( ( nat->state == TR_NATPMP_RECV_PUB ) && canSendCommand( nat ) ) { natpmpresp_t response; const int val = readnatpmpresponseorretry( &nat->natpmp, &response ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { char str[128]; evutil_inet_ntop( AF_INET, &response.pnu.publicaddress.addr, str, sizeof( str ) ); tr_ninf( getKey( ), _( "Found public address \"%s\"" ), str ); nat->state = TR_NATPMP_IDLE; } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) ) { if( nat->is_mapped && ( !is_enabled || ( nat->private_port != private_port ) ) ) nat->state = TR_NATPMP_SEND_UNMAP; } if( ( nat->state == TR_NATPMP_SEND_UNMAP ) && canSendCommand( nat ) ) { const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, nat->private_port, nat->public_port, 0 ); logVal( "sendnewportmappingrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP; setCommandTime( nat ); } if( nat->state == TR_NATPMP_RECV_UNMAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { const int private_port = resp.pnu.newportmapping.privateport; tr_ninf( getKey( ), _( "no longer forwarding port %d" ), private_port ); if( nat->private_port == private_port ) { nat->private_port = 0; nat->public_port = 0; nat->state = TR_NATPMP_IDLE; nat->is_mapped = false; } } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } if( nat->state == TR_NATPMP_IDLE ) { if( is_enabled && !nat->is_mapped && nat->has_discovered ) nat->state = TR_NATPMP_SEND_MAP; else if( nat->is_mapped && tr_time( ) >= nat->renew_time ) nat->state = TR_NATPMP_SEND_MAP; } if( ( nat->state == TR_NATPMP_SEND_MAP ) && canSendCommand( nat ) ) { const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, private_port, private_port, LIFETIME_SECS ); logVal( "sendnewportmappingrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP; setCommandTime( nat ); } if( nat->state == TR_NATPMP_RECV_MAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { nat->state = TR_NATPMP_IDLE; nat->is_mapped = true; nat->renew_time = tr_time( ) + ( resp.pnu.newportmapping.lifetime / 2 ); nat->private_port = resp.pnu.newportmapping.privateport; nat->public_port = resp.pnu.newportmapping.mappedpublicport; tr_ninf( getKey( ), _( "Port %d forwarded successfully" ), nat->private_port ); } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } switch( nat->state ) { case TR_NATPMP_IDLE: *public_port = nat->public_port; return nat->is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED; break; case TR_NATPMP_DISCOVER: ret = TR_PORT_UNMAPPED; break; case TR_NATPMP_RECV_PUB: case TR_NATPMP_SEND_MAP: case TR_NATPMP_RECV_MAP: ret = TR_PORT_MAPPING; break; case TR_NATPMP_SEND_UNMAP: case TR_NATPMP_RECV_UNMAP: ret = TR_PORT_UNMAPPING; break; default: ret = TR_PORT_ERROR; break; } return ret; }
int natpmp_handler(struct natpmp_handle_t *handle, uint16_t port, time_t lifespan, time_t now) { natpmpresp_t response; // Retry later if we want to wait longer if (handle->retry > now) { return PF_RETRY; } #ifdef DEBUG log_debug("NAT-PMP: Handle port: %hu, lifespan: %ld, state: %s", port, lifespan, natpmp_statestr(handle->state)); #endif // Initialize data structure / socket if (handle->state == NATPMP_STATE_INIT) { int rc = initnatpmp(&handle->natpmp, 0, 0); if (rc >= 0) { handle->state = NATPMP_STATE_REQUEST_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method initnatpmp returned %d.", rc); goto error; } } // Request gateway address if (handle->state == NATPMP_STATE_REQUEST_GATEWAY) { int rc = sendpublicaddressrequest(&handle->natpmp); if (rc >= 0) { handle->retry = now + 8; handle->state = NATPMP_STATE_RECEIVE_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method sendpublicaddressrequest returned %d.", rc); goto error; } } // Read public gateway address if (handle->state == NATPMP_STATE_RECEIVE_GATEWAY) { int rc = readnatpmpresponseorretry(&handle->natpmp, &response); if (rc >= 0) { char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &response.pnu.publicaddress.addr, str, sizeof (str)); log_info("NAT-PMP: Found public address \"%s\".", str); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_RETRY; } else if (rc == NATPMP_TRYAGAIN) { handle->retry = now + (10 * 60); handle->state = NATPMP_STATE_REQUEST_GATEWAY; return PF_RETRY; } else { log_debug("NAT-PMP: Method readnatpmpresponseorretry returned %d.", rc); goto error; } } // Add/Remove port mappings if (handle->state == NATPMP_STATE_REQUEST_PORTMAPPING) { int rc_udp = sendnewportmappingrequest(&handle->natpmp, NATPMP_PROTOCOL_UDP, port, port, lifespan); int rc_tcp = sendnewportmappingrequest(&handle->natpmp, NATPMP_PROTOCOL_TCP, port, port, lifespan); if (rc_udp >= 0 && rc_tcp >= 0) { handle->retry = now + 2; handle->state = NATPMP_STATE_RECEIVE_PORTMAPPING; return PF_RETRY; } else { log_debug("NAT-PMP: Method sendnewportmappingrequest returned an error: %s", strnatpmperr((rc_udp >= 0) ? rc_tcp : rc_udp)); goto error; } } // Check port mapping if (handle->state == NATPMP_STATE_RECEIVE_PORTMAPPING) { int rc = readnatpmpresponseorretry(&handle->natpmp, &response); if (rc >= 0) { int private_port = response.pnu.newportmapping.privateport; int public_port = response.pnu.newportmapping.mappedpublicport; time_t lifetime = response.pnu.newportmapping.lifetime; if (lifetime > 0) { log_info("NAT-PMP: Port forwarding added for port %d (to private port %d) for %ld seconds.", public_port, private_port, lifetime); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_DONE; } else { log_debug("NAT-PMP: Port forwarding removed for public port %d (to private port %d) for %ld seconds.", public_port, private_port, lifetime); handle->state = NATPMP_STATE_REQUEST_PORTMAPPING; return PF_DONE; } } else if (rc == NATPMP_TRYAGAIN) { handle->state = NATPMP_STATE_RECEIVE_PORTMAPPING; return PF_RETRY; } else { log_debug("NAT-PMP: Port forwarding failed for port %d.", port); goto error; } } error:; handle->retry = now + 60; handle->state = NATPMP_STATE_ERROR; return PF_ERROR; }
static int natpmpPulse( ml_upnpmp_t * map ) { int ret; if( map->enabled && ( map->natpmpState == ML_NATPMP_DISCOVER ) ) { int val = initnatpmp( &map->natpmp, 0, 0 ); dbg_printf( "initnatpmp = %d\n", val ); val = sendpublicaddressrequest( &map->natpmp ); dbg_printf( "sendpublicaddressrequest = %d\n", val ); map->natpmpState = val < 0 ? ML_NATPMP_ERR : ML_NATPMP_RECV_PUB; map->natpmpDiscovered = 1; setCommandTime( map ); } if( ( map->natpmpState == ML_NATPMP_RECV_PUB ) && canSendCommand( map ) ) { natpmpresp_t response; const int val = readnatpmpresponseorretry( &map->natpmp, &response ); dbg_printf( "readnatpmpresponseorretry = %d\n", val ); if( val >= 0 ) { dbg_printf( "Found public address \"%s\"\n", inet_ntoa( response.pnu.publicaddress.addr ) ); map->natpmpState = ML_NATPMP_IDLE; } else if( val != NATPMP_TRYAGAIN ) { map->natpmpState = ML_NATPMP_ERR; } } if( ( map->natpmpState == ML_NATPMP_IDLE ) || ( map->natpmpState == ML_NATPMP_ERR ) ) { if( map->natpmpMapped && ( ! map->enabled ) ) map->natpmpState = ML_NATPMP_SEND_UNMAP; } if( ( map->natpmpState == ML_NATPMP_SEND_UNMAP ) && canSendCommand( map ) ) { const int val = sendnewportmappingrequest( &map->natpmp, NATPMP_PROTOCOL_TCP, map->intPort, map->extPort, 0 ); dbg_printf( "sendnewportmappingrequest = %d\n", val ); map->natpmpState = val < 0 ? ML_NATPMP_ERR : ML_NATPMP_RECV_UNMAP; setCommandTime( map ); } if( map->natpmpState == ML_NATPMP_RECV_UNMAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &map->natpmp, &resp ); dbg_printf( "readnatpmpresponseorretry = %d\n", val ); if( val >= 0 ) { const int p = resp.pnu.newportmapping.privateport; dbg_printf( "no longer forwarding port %d\n", p ); if( map->extPort == p ) { map->extPort = 0; map->natpmpState = ML_NATPMP_IDLE; map->natpmpMapped = 0; } } else if( val != NATPMP_TRYAGAIN ) { map->natpmpState = ML_NATPMP_ERR; } } if( map->natpmpState == ML_NATPMP_IDLE ) { if( map->enabled && !map->natpmpMapped && map->natpmpDiscovered ) map->natpmpState = ML_NATPMP_SEND_MAP; else if( map->natpmpMapped && time(NULL) >= map->renewTime ) map->natpmpState = ML_NATPMP_SEND_MAP; } if( ( map->natpmpState == ML_NATPMP_SEND_MAP ) && canSendCommand( map ) ) { const int val = sendnewportmappingrequest( &map->natpmp, NATPMP_PROTOCOL_TCP, map->intPort, map->extPort, LIFETIME_SECS ); dbg_printf( "sendnewportmappingrequest = %d\n", val ); map->natpmpState = val < 0 ? ML_NATPMP_ERR : ML_NATPMP_RECV_MAP; setCommandTime( map ); } if( map->natpmpState == ML_NATPMP_RECV_MAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &map->natpmp, &resp ); dbg_printf( "readnatpmpresponseorretry = %d\n", val ); if( val >= 0 ) { map->natpmpState = ML_NATPMP_IDLE; map->natpmpMapped = 1; map->renewTime = time(NULL) + LIFETIME_SECS; map->extPort = resp.pnu.newportmapping.privateport; dbg_printf( "Port %d forwarded successfully\n", map->extPort ); } else if( val != NATPMP_TRYAGAIN ) { map->natpmpState = ML_NATPMP_ERR; } } switch( map->natpmpState ) { case ML_NATPMP_IDLE: ret = map->natpmpMapped ? ML_PORT_MAPPED : ML_PORT_UNMAPPED; break; case ML_NATPMP_DISCOVER: ret = ML_PORT_UNMAPPED; break; case ML_NATPMP_RECV_PUB: case ML_NATPMP_SEND_MAP: case ML_NATPMP_RECV_MAP: ret = ML_PORT_MAPPING; break; case ML_NATPMP_SEND_UNMAP: case ML_NATPMP_RECV_UNMAP: ret = ML_PORT_UNMAPPING; break; default: ret = ML_PORT_ERROR; break; } return ret; }
int tr_natpmpPulse( struct tr_natpmp * nat, int port, int isEnabled ) { int ret; if( isEnabled && ( nat->state == TR_NATPMP_DISCOVER ) ) { int val = initnatpmp( &nat->natpmp ); logVal( "initnatpmp", val ); val = sendpublicaddressrequest( &nat->natpmp ); logVal( "sendpublicaddressrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB; nat->hasDiscovered = 1; setCommandTime( nat ); } if( ( nat->state == TR_NATPMP_RECV_PUB ) && canSendCommand( nat ) ) { natpmpresp_t response; const int val = readnatpmpresponseorretry( &nat->natpmp, &response ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { tr_ninf( getKey( ), _( "Found public address \"%s\"" ), inet_ntoa( response.pnu.publicaddress.addr ) ); nat->state = TR_NATPMP_IDLE; } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) ) { if( nat->isMapped && ( !isEnabled || ( nat->port != port ) ) ) nat->state = TR_NATPMP_SEND_UNMAP; } if( ( nat->state == TR_NATPMP_SEND_UNMAP ) && canSendCommand( nat ) ) { const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, nat->port, nat->port, 0 ); logVal( "sendnewportmappingrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP; setCommandTime( nat ); } if( nat->state == TR_NATPMP_RECV_UNMAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { const int p = resp.pnu.newportmapping.privateport; tr_ninf( getKey( ), _( "no longer forwarding port %d" ), p ); if( nat->port == p ) { nat->port = -1; nat->state = TR_NATPMP_IDLE; nat->isMapped = 0; } } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } if( nat->state == TR_NATPMP_IDLE ) { if( isEnabled && !nat->isMapped && nat->hasDiscovered ) nat->state = TR_NATPMP_SEND_MAP; else if( nat->isMapped && tr_time( ) >= nat->renewTime ) nat->state = TR_NATPMP_SEND_MAP; } if( ( nat->state == TR_NATPMP_SEND_MAP ) && canSendCommand( nat ) ) { const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, port, port, LIFETIME_SECS ); logVal( "sendnewportmappingrequest", val ); nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP; setCommandTime( nat ); } if( nat->state == TR_NATPMP_RECV_MAP ) { natpmpresp_t resp; const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); logVal( "readnatpmpresponseorretry", val ); if( val >= 0 ) { nat->state = TR_NATPMP_IDLE; nat->isMapped = 1; nat->renewTime = tr_time( ) + LIFETIME_SECS; nat->port = resp.pnu.newportmapping.privateport; tr_ninf( getKey( ), _( "Port %d forwarded successfully" ), nat->port ); } else if( val != NATPMP_TRYAGAIN ) { nat->state = TR_NATPMP_ERR; } } switch( nat->state ) { case TR_NATPMP_IDLE: ret = nat->isMapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED; break; case TR_NATPMP_DISCOVER: ret = TR_PORT_UNMAPPED; break; case TR_NATPMP_RECV_PUB: case TR_NATPMP_SEND_MAP: case TR_NATPMP_RECV_MAP: ret = TR_PORT_MAPPING; break; case TR_NATPMP_SEND_UNMAP: case TR_NATPMP_RECV_UNMAP: ret = TR_PORT_UNMAPPING; break; default: ret = TR_PORT_ERROR; break; } return ret; }