1/* $Id: natpmp.c,v 1.14 2011/07/15 08:30:11 nanard Exp $ */ 2/* libnatpmp 3Copyright (c) 2007-2011, Thomas BERNARD 4All rights reserved. 5 6Redistribution and use in source and binary forms, with or without 7modification, are permitted provided that the following conditions are met: 8 9 * Redistributions of source code must retain the above copyright notice, 10 this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above copyright notice, 12 this list of conditions and the following disclaimer in the documentation 13 and/or other materials provided with the distribution. 14 * The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27POSSIBILITY OF SUCH DAMAGE. 28*/ 29#ifdef __linux__ 30#define _BSD_SOURCE 1 31#endif 32#include <string.h> 33#include <time.h> 34#if !defined(_MSC_VER) 35#include <sys/time.h> 36#endif 37#ifdef WIN32 38#include <errno.h> 39#include <winsock2.h> 40#include <ws2tcpip.h> 41#include <io.h> 42#define EWOULDBLOCK WSAEWOULDBLOCK 43#define ECONNREFUSED WSAECONNREFUSED 44#include "wingettimeofday.h" 45#else 46#include <errno.h> 47#include <unistd.h> 48#include <fcntl.h> 49#include <sys/types.h> 50#include <sys/socket.h> 51#define closesocket close 52#endif 53#include "natpmp.h" 54#include "getgateway.h" 55 56LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw) 57{ 58#ifdef WIN32 59 u_long ioctlArg = 1; 60#else 61 int flags; 62#endif 63 struct sockaddr_in addr; 64 if(!p) 65 return NATPMP_ERR_INVALIDARGS; 66 memset(p, 0, sizeof(natpmp_t)); 67 p->s = socket(PF_INET, SOCK_DGRAM, 0); 68 if(p->s < 0) 69 return NATPMP_ERR_SOCKETERROR; 70#ifdef WIN32 71 if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR) 72 return NATPMP_ERR_FCNTLERROR; 73#else 74 if((flags = fcntl(p->s, F_GETFL, 0)) < 0) 75 return NATPMP_ERR_FCNTLERROR; 76 if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0) 77 return NATPMP_ERR_FCNTLERROR; 78#endif 79 80 if(forcegw) { 81 p->gateway = forcedgw; 82 } else { 83 if(getdefaultgateway(&(p->gateway)) < 0) 84 return NATPMP_ERR_CANNOTGETGATEWAY; 85 } 86 87 memset(&addr, 0, sizeof(addr)); 88 addr.sin_family = AF_INET; 89 addr.sin_port = htons(NATPMP_PORT); 90 addr.sin_addr.s_addr = p->gateway; 91 if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) 92 return NATPMP_ERR_CONNECTERR; 93 return 0; 94} 95 96LIBSPEC int closenatpmp(natpmp_t * p) 97{ 98 if(!p) 99 return NATPMP_ERR_INVALIDARGS; 100 if(closesocket(p->s) < 0) 101 return NATPMP_ERR_CLOSEERR; 102 return 0; 103} 104 105int sendpendingrequest(natpmp_t * p) 106{ 107 int r; 108/* struct sockaddr_in addr;*/ 109 if(!p) 110 return NATPMP_ERR_INVALIDARGS; 111/* memset(&addr, 0, sizeof(addr)); 112 addr.sin_family = AF_INET; 113 addr.sin_port = htons(NATPMP_PORT); 114 addr.sin_addr.s_addr = p->gateway; 115 r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0, 116 (struct sockaddr *)&addr, sizeof(addr));*/ 117 r = (int)send(p->s, p->pending_request, p->pending_request_len, 0); 118 return (r<0) ? NATPMP_ERR_SENDERR : r; 119} 120 121int sendnatpmprequest(natpmp_t * p) 122{ 123 int n; 124 if(!p) 125 return NATPMP_ERR_INVALIDARGS; 126 /* TODO : check if no request is allready pending */ 127 p->has_pending_request = 1; 128 p->try_number = 1; 129 n = sendpendingrequest(p); 130 gettimeofday(&p->retry_time, NULL); // check errors ! 131 p->retry_time.tv_usec += 250000; /* add 250ms */ 132 if(p->retry_time.tv_usec >= 1000000) { 133 p->retry_time.tv_usec -= 1000000; 134 p->retry_time.tv_sec++; 135 } 136 return n; 137} 138 139LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout) 140{ 141 struct timeval now; 142 if(!p || !timeout) 143 return NATPMP_ERR_INVALIDARGS; 144 if(!p->has_pending_request) 145 return NATPMP_ERR_NOPENDINGREQ; 146 if(gettimeofday(&now, NULL) < 0) 147 return NATPMP_ERR_GETTIMEOFDAYERR; 148 timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec; 149 timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec; 150 if(timeout->tv_usec < 0) { 151 timeout->tv_usec += 1000000; 152 timeout->tv_sec--; 153 } 154 return 0; 155} 156 157LIBSPEC int sendpublicaddressrequest(natpmp_t * p) 158{ 159 if(!p) 160 return NATPMP_ERR_INVALIDARGS; 161 //static const unsigned char request[] = { 0, 0 }; 162 p->pending_request[0] = 0; 163 p->pending_request[1] = 0; 164 p->pending_request_len = 2; 165 // TODO: return 0 instead of sizeof(request) ?? 166 return sendnatpmprequest(p); 167} 168 169LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, 170 uint16_t privateport, uint16_t publicport, 171 uint32_t lifetime) 172{ 173 if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP)) 174 return NATPMP_ERR_INVALIDARGS; 175 p->pending_request[0] = 0; 176 p->pending_request[1] = protocol; 177 p->pending_request[2] = 0; 178 p->pending_request[3] = 0; 179 *((uint16_t *)(p->pending_request + 4)) = htons(privateport); 180 *((uint16_t *)(p->pending_request + 6)) = htons(publicport); 181 *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); 182 p->pending_request_len = 12; 183 return sendnatpmprequest(p); 184} 185 186LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response) 187{ 188 unsigned char buf[16]; 189 struct sockaddr_in addr; 190 socklen_t addrlen = sizeof(addr); 191 int n; 192 if(!p) 193 return NATPMP_ERR_INVALIDARGS; 194 n = recvfrom(p->s, buf, sizeof(buf), 0, 195 (struct sockaddr *)&addr, &addrlen); 196 if(n<0) 197 switch(errno) { 198 /*case EAGAIN:*/ 199 case EWOULDBLOCK: 200 n = NATPMP_TRYAGAIN; 201 break; 202 case ECONNREFUSED: 203 n = NATPMP_ERR_NOGATEWAYSUPPORT; 204 break; 205 default: 206 n = NATPMP_ERR_RECVFROM; 207 } 208 /* check that addr is correct (= gateway) */ 209 else if(addr.sin_addr.s_addr != p->gateway) 210 n = NATPMP_ERR_WRONGPACKETSOURCE; 211 else { 212 response->resultcode = ntohs(*((uint16_t *)(buf + 2))); 213 response->epoch = ntohl(*((uint32_t *)(buf + 4))); 214 if(buf[0] != 0) 215 n = NATPMP_ERR_UNSUPPORTEDVERSION; 216 else if(buf[1] < 128 || buf[1] > 130) 217 n = NATPMP_ERR_UNSUPPORTEDOPCODE; 218 else if(response->resultcode != 0) { 219 switch(response->resultcode) { 220 case 1: 221 n = NATPMP_ERR_UNSUPPORTEDVERSION; 222 break; 223 case 2: 224 n = NATPMP_ERR_NOTAUTHORIZED; 225 break; 226 case 3: 227 n = NATPMP_ERR_NETWORKFAILURE; 228 break; 229 case 4: 230 n = NATPMP_ERR_OUTOFRESOURCES; 231 break; 232 case 5: 233 n = NATPMP_ERR_UNSUPPORTEDOPCODE; 234 break; 235 default: 236 n = NATPMP_ERR_UNDEFINEDERROR; 237 } 238 } else { 239 response->type = buf[1] & 0x7f; 240 if(buf[1] == 128) 241 //response->publicaddress.addr = *((uint32_t *)(buf + 8)); 242 response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8)); 243 else { 244 response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8))); 245 response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10))); 246 response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12))); 247 } 248 n = 0; 249 } 250 } 251 return n; 252} 253 254int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response) 255{ 256 int n; 257 if(!p || !response) 258 return NATPMP_ERR_INVALIDARGS; 259 if(!p->has_pending_request) 260 return NATPMP_ERR_NOPENDINGREQ; 261 n = readnatpmpresponse(p, response); 262 if(n<0) { 263 if(n==NATPMP_TRYAGAIN) { 264 struct timeval now; 265 gettimeofday(&now, NULL); // check errors ! 266 if(timercmp(&now, &p->retry_time, >=)) { 267 int delay, r; 268 if(p->try_number >= 9) { 269 return NATPMP_ERR_NOGATEWAYSUPPORT; 270 } 271 /*printf("retry! %d\n", p->try_number);*/ 272 delay = 250 * (1<<p->try_number); // ms 273 /*for(i=0; i<p->try_number; i++) 274 delay += delay;*/ 275 p->retry_time.tv_sec += (delay / 1000); 276 p->retry_time.tv_usec += (delay % 1000) * 1000; 277 if(p->retry_time.tv_usec >= 1000000) { 278 p->retry_time.tv_usec -= 1000000; 279 p->retry_time.tv_sec++; 280 } 281 p->try_number++; 282 r = sendpendingrequest(p); 283 if(r<0) 284 return r; 285 } 286 } 287 } else { 288 p->has_pending_request = 0; 289 } 290 return n; 291} 292 293#ifdef ENABLE_STRNATPMPERR 294LIBSPEC const char * strnatpmperr(int r) 295{ 296 const char * s; 297 switch(r) { 298 case NATPMP_ERR_INVALIDARGS: 299 s = "invalid arguments"; 300 break; 301 case NATPMP_ERR_SOCKETERROR: 302 s = "socket() failed"; 303 break; 304 case NATPMP_ERR_CANNOTGETGATEWAY: 305 s = "cannot get default gateway ip address"; 306 break; 307 case NATPMP_ERR_CLOSEERR: 308#ifdef WIN32 309 s = "closesocket() failed"; 310#else 311 s = "close() failed"; 312#endif 313 break; 314 case NATPMP_ERR_RECVFROM: 315 s = "recvfrom() failed"; 316 break; 317 case NATPMP_ERR_NOPENDINGREQ: 318 s = "no pending request"; 319 break; 320 case NATPMP_ERR_NOGATEWAYSUPPORT: 321 s = "the gateway does not support nat-pmp"; 322 break; 323 case NATPMP_ERR_CONNECTERR: 324 s = "connect() failed"; 325 break; 326 case NATPMP_ERR_WRONGPACKETSOURCE: 327 s = "packet not received from the default gateway"; 328 break; 329 case NATPMP_ERR_SENDERR: 330 s = "send() failed"; 331 break; 332 case NATPMP_ERR_FCNTLERROR: 333 s = "fcntl() failed"; 334 break; 335 case NATPMP_ERR_GETTIMEOFDAYERR: 336 s = "gettimeofday() failed"; 337 break; 338 case NATPMP_ERR_UNSUPPORTEDVERSION: 339 s = "unsupported nat-pmp version error from server"; 340 break; 341 case NATPMP_ERR_UNSUPPORTEDOPCODE: 342 s = "unsupported nat-pmp opcode error from server"; 343 break; 344 case NATPMP_ERR_UNDEFINEDERROR: 345 s = "undefined nat-pmp server error"; 346 break; 347 case NATPMP_ERR_NOTAUTHORIZED: 348 s = "not authorized"; 349 break; 350 case NATPMP_ERR_NETWORKFAILURE: 351 s = "network failure"; 352 break; 353 case NATPMP_ERR_OUTOFRESOURCES: 354 s = "nat-pmp server out of resources"; 355 break; 356 default: 357 s = "Unknown libnatpmp error"; 358 } 359 return s; 360} 361#endif 362 363