123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /* $Id: natpmp.c,v 1.20 2015/05/27 12:43:15 nanard Exp $ */
- /* libnatpmp
- Copyright (c) 2007-2015, Thomas BERNARD
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
- #ifdef __linux__
- #define _BSD_SOURCE 1
- #endif
- #include <string.h>
- #include <time.h>
- #if !defined(_MSC_VER)
- #include <sys/time.h>
- #endif
- #ifdef WIN32
- #include <errno.h>
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <io.h>
- #ifndef EWOULDBLOCK
- #define EWOULDBLOCK WSAEWOULDBLOCK
- #endif
- #ifndef ECONNREFUSED
- #define ECONNREFUSED WSAECONNREFUSED
- #endif
- #include "wingettimeofday.h"
- #define gettimeofday natpmp_gettimeofday
- #else
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #define closesocket close
- #endif
- #include "natpmp.h"
- #include "getgateway.h"
- #include <stdio.h>
- LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
- {
- #ifdef WIN32
- u_long ioctlArg = 1;
- #else
- int flags;
- #endif
- struct sockaddr_in addr;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- memset(p, 0, sizeof(natpmp_t));
- p->s = socket(PF_INET, SOCK_DGRAM, 0);
- if(p->s < 0)
- return NATPMP_ERR_SOCKETERROR;
- #ifdef WIN32
- if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
- return NATPMP_ERR_FCNTLERROR;
- #else
- if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
- return NATPMP_ERR_FCNTLERROR;
- if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
- return NATPMP_ERR_FCNTLERROR;
- #endif
- if(forcegw) {
- p->gateway = forcedgw;
- } else {
- if(getdefaultgateway(&(p->gateway)) < 0)
- return NATPMP_ERR_CANNOTGETGATEWAY;
- }
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(NATPMP_PORT);
- addr.sin_addr.s_addr = p->gateway;
- if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
- return NATPMP_ERR_CONNECTERR;
- return 0;
- }
- LIBSPEC int closenatpmp(natpmp_t * p)
- {
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- if(closesocket(p->s) < 0)
- return NATPMP_ERR_CLOSEERR;
- return 0;
- }
- int sendpendingrequest(natpmp_t * p)
- {
- int r;
- /* struct sockaddr_in addr;*/
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- /* memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(NATPMP_PORT);
- addr.sin_addr.s_addr = p->gateway;
- r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
- (struct sockaddr *)&addr, sizeof(addr));*/
- r = (int)send(p->s, (const char *)p->pending_request, p->pending_request_len, 0);
- return (r<0) ? NATPMP_ERR_SENDERR : r;
- }
- int sendnatpmprequest(natpmp_t * p)
- {
- int n;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- /* TODO : check if no request is already pending */
- p->has_pending_request = 1;
- p->try_number = 1;
- n = sendpendingrequest(p);
- gettimeofday(&p->retry_time, NULL); // check errors !
- p->retry_time.tv_usec += 250000; /* add 250ms */
- if(p->retry_time.tv_usec >= 1000000) {
- p->retry_time.tv_usec -= 1000000;
- p->retry_time.tv_sec++;
- }
- return n;
- }
- LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
- {
- struct timeval now;
- if(!p || !timeout)
- return NATPMP_ERR_INVALIDARGS;
- if(!p->has_pending_request)
- return NATPMP_ERR_NOPENDINGREQ;
- if(gettimeofday(&now, NULL) < 0)
- return NATPMP_ERR_GETTIMEOFDAYERR;
- timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
- timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
- if(timeout->tv_usec < 0) {
- timeout->tv_usec += 1000000;
- timeout->tv_sec--;
- }
- return 0;
- }
- LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
- {
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- //static const unsigned char request[] = { 0, 0 };
- p->pending_request[0] = 0;
- p->pending_request[1] = 0;
- p->pending_request_len = 2;
- // TODO: return 0 instead of sizeof(request) ??
- return sendnatpmprequest(p);
- }
- LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
- uint16_t privateport, uint16_t publicport,
- uint32_t lifetime)
- {
- if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
- return NATPMP_ERR_INVALIDARGS;
- p->pending_request[0] = 0;
- p->pending_request[1] = protocol;
- p->pending_request[2] = 0;
- p->pending_request[3] = 0;
- /* break strict-aliasing rules :
- *((uint16_t *)(p->pending_request + 4)) = htons(privateport); */
- p->pending_request[4] = (privateport >> 8) & 0xff;
- p->pending_request[5] = privateport & 0xff;
- /* break stric-aliasing rules :
- *((uint16_t *)(p->pending_request + 6)) = htons(publicport); */
- p->pending_request[6] = (publicport >> 8) & 0xff;
- p->pending_request[7] = publicport & 0xff;
- /* break stric-aliasing rules :
- *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); */
- p->pending_request[8] = (lifetime >> 24) & 0xff;
- p->pending_request[9] = (lifetime >> 16) & 0xff;
- p->pending_request[10] = (lifetime >> 8) & 0xff;
- p->pending_request[11] = lifetime & 0xff;
- p->pending_request_len = 12;
- return sendnatpmprequest(p);
- }
- LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
- {
- unsigned char buf[16];
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
- int n;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- n = recvfrom(p->s, (char *)buf, sizeof(buf), 0,
- (struct sockaddr *)&addr, &addrlen);
- if(n<0)
- #ifdef WIN32
- switch(WSAGetLastError()) {
- #else
- switch(errno) {
- #endif
- /*case EAGAIN:*/
- case EWOULDBLOCK:
- n = NATPMP_TRYAGAIN;
- break;
- case ECONNREFUSED:
- n = NATPMP_ERR_NOGATEWAYSUPPORT;
- break;
- default:
- n = NATPMP_ERR_RECVFROM;
- }
- /* check that addr is correct (= gateway) */
- else if(addr.sin_addr.s_addr != p->gateway)
- n = NATPMP_ERR_WRONGPACKETSOURCE;
- else {
- response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
- response->epoch = ntohl(*((uint32_t *)(buf + 4)));
- if(buf[0] != 0)
- n = NATPMP_ERR_UNSUPPORTEDVERSION;
- else if(buf[1] < 128 || buf[1] > 130)
- n = NATPMP_ERR_UNSUPPORTEDOPCODE;
- else if(response->resultcode != 0) {
- switch(response->resultcode) {
- case 1:
- n = NATPMP_ERR_UNSUPPORTEDVERSION;
- break;
- case 2:
- n = NATPMP_ERR_NOTAUTHORIZED;
- break;
- case 3:
- n = NATPMP_ERR_NETWORKFAILURE;
- break;
- case 4:
- n = NATPMP_ERR_OUTOFRESOURCES;
- break;
- case 5:
- n = NATPMP_ERR_UNSUPPORTEDOPCODE;
- break;
- default:
- n = NATPMP_ERR_UNDEFINEDERROR;
- }
- } else {
- response->type = buf[1] & 0x7f;
- if(buf[1] == 128)
- //response->publicaddress.addr = *((uint32_t *)(buf + 8));
- response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
- else {
- response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
- response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
- response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
- }
- n = 0;
- }
- }
- return n;
- }
- int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
- {
- int n;
- if(!p || !response)
- return NATPMP_ERR_INVALIDARGS;
- if(!p->has_pending_request)
- return NATPMP_ERR_NOPENDINGREQ;
- n = readnatpmpresponse(p, response);
- if(n<0) {
- if(n==NATPMP_TRYAGAIN) {
- struct timeval now;
- gettimeofday(&now, NULL); // check errors !
- if(timercmp(&now, &p->retry_time, >=)) {
- int delay, r;
- if(p->try_number >= 9) {
- return NATPMP_ERR_NOGATEWAYSUPPORT;
- }
- /*printf("retry! %d\n", p->try_number);*/
- delay = 250 * (1<<p->try_number); // ms
- /*for(i=0; i<p->try_number; i++)
- delay += delay;*/
- p->retry_time.tv_sec += (delay / 1000);
- p->retry_time.tv_usec += (delay % 1000) * 1000;
- if(p->retry_time.tv_usec >= 1000000) {
- p->retry_time.tv_usec -= 1000000;
- p->retry_time.tv_sec++;
- }
- p->try_number++;
- r = sendpendingrequest(p);
- if(r<0)
- return r;
- }
- }
- } else {
- p->has_pending_request = 0;
- }
- return n;
- }
- #ifdef ENABLE_STRNATPMPERR
- LIBSPEC const char * strnatpmperr(int r)
- {
- const char * s;
- switch(r) {
- case NATPMP_ERR_INVALIDARGS:
- s = "invalid arguments";
- break;
- case NATPMP_ERR_SOCKETERROR:
- s = "socket() failed";
- break;
- case NATPMP_ERR_CANNOTGETGATEWAY:
- s = "cannot get default gateway ip address";
- break;
- case NATPMP_ERR_CLOSEERR:
- #ifdef WIN32
- s = "closesocket() failed";
- #else
- s = "close() failed";
- #endif
- break;
- case NATPMP_ERR_RECVFROM:
- s = "recvfrom() failed";
- break;
- case NATPMP_ERR_NOPENDINGREQ:
- s = "no pending request";
- break;
- case NATPMP_ERR_NOGATEWAYSUPPORT:
- s = "the gateway does not support nat-pmp";
- break;
- case NATPMP_ERR_CONNECTERR:
- s = "connect() failed";
- break;
- case NATPMP_ERR_WRONGPACKETSOURCE:
- s = "packet not received from the default gateway";
- break;
- case NATPMP_ERR_SENDERR:
- s = "send() failed";
- break;
- case NATPMP_ERR_FCNTLERROR:
- s = "fcntl() failed";
- break;
- case NATPMP_ERR_GETTIMEOFDAYERR:
- s = "gettimeofday() failed";
- break;
- case NATPMP_ERR_UNSUPPORTEDVERSION:
- s = "unsupported nat-pmp version error from server";
- break;
- case NATPMP_ERR_UNSUPPORTEDOPCODE:
- s = "unsupported nat-pmp opcode error from server";
- break;
- case NATPMP_ERR_UNDEFINEDERROR:
- s = "undefined nat-pmp server error";
- break;
- case NATPMP_ERR_NOTAUTHORIZED:
- s = "not authorized";
- break;
- case NATPMP_ERR_NETWORKFAILURE:
- s = "network failure";
- break;
- case NATPMP_ERR_OUTOFRESOURCES:
- s = "nat-pmp server out of resources";
- break;
- default:
- s = "Unknown libnatpmp error";
- }
- return s;
- }
- #endif
|