1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: natpmp.c 13263 2012-04-07 00:12:57Z jordan $ 11 */ 12 13#include <errno.h> 14#include <time.h> 15#include <inttypes.h> 16 17#include <event2/util.h> /* evutil_inet_ntop() */ 18 19#define ENABLE_STRNATPMPERR 20#include "natpmp.h" 21 22#include "transmission.h" 23#include "natpmp_local.h" 24#include "net.h" /* tr_netCloseSocket */ 25#include "port-forwarding.h" 26#include "utils.h" 27 28#define LIFETIME_SECS 3600 29#define COMMAND_WAIT_SECS 8 30 31static const char * 32getKey( void ) { return _( "Port Forwarding (NAT-PMP)" ); } 33 34typedef enum 35{ 36 TR_NATPMP_IDLE, 37 TR_NATPMP_ERR, 38 TR_NATPMP_DISCOVER, 39 TR_NATPMP_RECV_PUB, 40 TR_NATPMP_SEND_MAP, 41 TR_NATPMP_RECV_MAP, 42 TR_NATPMP_SEND_UNMAP, 43 TR_NATPMP_RECV_UNMAP 44} 45tr_natpmp_state; 46 47struct tr_natpmp 48{ 49 bool has_discovered; 50 bool is_mapped; 51 52 tr_port public_port; 53 tr_port private_port; 54 55 time_t renew_time; 56 time_t command_time; 57 tr_natpmp_state state; 58 natpmp_t natpmp; 59}; 60 61/** 62*** 63**/ 64 65static void 66logVal( const char * func, 67 int ret ) 68{ 69 if( ret == NATPMP_TRYAGAIN ) 70 return; 71 if( ret >= 0 ) 72 tr_ninf( getKey( ), _( "%s succeeded (%d)" ), func, ret ); 73 else 74 tr_ndbg( 75 getKey( ), 76 "%s failed. Natpmp returned %d (%s); errno is %d (%s)", 77 func, ret, strnatpmperr( ret ), errno, tr_strerror( errno ) ); 78} 79 80struct tr_natpmp* 81tr_natpmpInit( void ) 82{ 83 struct tr_natpmp * nat; 84 85 nat = tr_new0( struct tr_natpmp, 1 ); 86 nat->state = TR_NATPMP_DISCOVER; 87 nat->public_port = 0; 88 nat->private_port = 0; 89 nat->natpmp.s = -1; /* socket */ 90 return nat; 91} 92 93void 94tr_natpmpClose( tr_natpmp * nat ) 95{ 96 if( nat ) 97 { 98 if( nat->natpmp.s >= 0 ) 99 tr_netCloseSocket( nat->natpmp.s ); 100 tr_free( nat ); 101 } 102} 103 104static int 105canSendCommand( const struct tr_natpmp * nat ) 106{ 107 return tr_time( ) >= nat->command_time; 108} 109 110static void 111setCommandTime( struct tr_natpmp * nat ) 112{ 113 nat->command_time = tr_time( ) + COMMAND_WAIT_SECS; 114} 115 116int 117tr_natpmpPulse( struct tr_natpmp * nat, tr_port private_port, bool is_enabled, tr_port * public_port ) 118{ 119 int ret; 120 121 if( is_enabled && ( nat->state == TR_NATPMP_DISCOVER ) ) 122 { 123 int val = initnatpmp( &nat->natpmp, 0, 0 ); 124 logVal( "initnatpmp", val ); 125 val = sendpublicaddressrequest( &nat->natpmp ); 126 logVal( "sendpublicaddressrequest", val ); 127 nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_PUB; 128 nat->has_discovered = true; 129 setCommandTime( nat ); 130 } 131 132 if( ( nat->state == TR_NATPMP_RECV_PUB ) && canSendCommand( nat ) ) 133 { 134 natpmpresp_t response; 135 const int val = readnatpmpresponseorretry( &nat->natpmp, &response ); 136 logVal( "readnatpmpresponseorretry", val ); 137 if( val >= 0 ) 138 { 139 char str[128]; 140 evutil_inet_ntop( AF_INET, &response.pnu.publicaddress.addr, str, sizeof( str ) ); 141 tr_ninf( getKey( ), _( "Found public address \"%s\"" ), str ); 142 nat->state = TR_NATPMP_IDLE; 143 } 144 else if( val != NATPMP_TRYAGAIN ) 145 { 146 nat->state = TR_NATPMP_ERR; 147 } 148 } 149 150 if( ( nat->state == TR_NATPMP_IDLE ) || ( nat->state == TR_NATPMP_ERR ) ) 151 { 152 if( nat->is_mapped && ( !is_enabled || ( nat->private_port != private_port ) ) ) 153 nat->state = TR_NATPMP_SEND_UNMAP; 154 } 155 156 if( ( nat->state == TR_NATPMP_SEND_UNMAP ) && canSendCommand( nat ) ) 157 { 158 const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, 159 nat->private_port, 160 nat->public_port, 161 0 ); 162 logVal( "sendnewportmappingrequest", val ); 163 nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_UNMAP; 164 setCommandTime( nat ); 165 } 166 167 if( nat->state == TR_NATPMP_RECV_UNMAP ) 168 { 169 natpmpresp_t resp; 170 const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); 171 logVal( "readnatpmpresponseorretry", val ); 172 if( val >= 0 ) 173 { 174 const int private_port = resp.pnu.newportmapping.privateport; 175 176 tr_ninf( getKey( ), _( "no longer forwarding port %d" ), private_port ); 177 178 if( nat->private_port == private_port ) 179 { 180 nat->private_port = 0; 181 nat->public_port = 0; 182 nat->state = TR_NATPMP_IDLE; 183 nat->is_mapped = false; 184 } 185 } 186 else if( val != NATPMP_TRYAGAIN ) 187 { 188 nat->state = TR_NATPMP_ERR; 189 } 190 } 191 192 if( nat->state == TR_NATPMP_IDLE ) 193 { 194 if( is_enabled && !nat->is_mapped && nat->has_discovered ) 195 nat->state = TR_NATPMP_SEND_MAP; 196 197 else if( nat->is_mapped && tr_time( ) >= nat->renew_time ) 198 nat->state = TR_NATPMP_SEND_MAP; 199 } 200 201 if( ( nat->state == TR_NATPMP_SEND_MAP ) && canSendCommand( nat ) ) 202 { 203 const int val = sendnewportmappingrequest( &nat->natpmp, NATPMP_PROTOCOL_TCP, private_port, private_port, LIFETIME_SECS ); 204 logVal( "sendnewportmappingrequest", val ); 205 nat->state = val < 0 ? TR_NATPMP_ERR : TR_NATPMP_RECV_MAP; 206 setCommandTime( nat ); 207 } 208 209 if( nat->state == TR_NATPMP_RECV_MAP ) 210 { 211 natpmpresp_t resp; 212 const int val = readnatpmpresponseorretry( &nat->natpmp, &resp ); 213 logVal( "readnatpmpresponseorretry", val ); 214 if( val >= 0 ) 215 { 216 nat->state = TR_NATPMP_IDLE; 217 nat->is_mapped = true; 218 nat->renew_time = tr_time( ) + ( resp.pnu.newportmapping.lifetime / 2 ); 219 nat->private_port = resp.pnu.newportmapping.privateport; 220 nat->public_port = resp.pnu.newportmapping.mappedpublicport; 221 tr_ninf( getKey( ), _( "Port %d forwarded successfully" ), nat->private_port ); 222 } 223 else if( val != NATPMP_TRYAGAIN ) 224 { 225 nat->state = TR_NATPMP_ERR; 226 } 227 } 228 229 switch( nat->state ) 230 { 231 case TR_NATPMP_IDLE: 232 *public_port = nat->public_port; 233 return nat->is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED; 234 break; 235 236 case TR_NATPMP_DISCOVER: 237 ret = TR_PORT_UNMAPPED; break; 238 239 case TR_NATPMP_RECV_PUB: 240 case TR_NATPMP_SEND_MAP: 241 case TR_NATPMP_RECV_MAP: 242 ret = TR_PORT_MAPPING; break; 243 244 case TR_NATPMP_SEND_UNMAP: 245 case TR_NATPMP_RECV_UNMAP: 246 ret = TR_PORT_UNMAPPING; break; 247 248 default: 249 ret = TR_PORT_ERROR; break; 250 } 251 return ret; 252} 253 254