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 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; }