• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/transmission/transmission-2.73/third-party/libnatpmp/
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