aboutsummaryrefslogtreecommitdiff
path: root/src/nat/libnatpmp/natpmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nat/libnatpmp/natpmp.c')
-rw-r--r--src/nat/libnatpmp/natpmp.c409
1 files changed, 0 insertions, 409 deletions
diff --git a/src/nat/libnatpmp/natpmp.c b/src/nat/libnatpmp/natpmp.c
deleted file mode 100644
index 12526df8e..000000000
--- a/src/nat/libnatpmp/natpmp.c
+++ /dev/null
@@ -1,409 +0,0 @@
1/* $Id: natpmp.c,v 1.8 2008/07/02 22:33:06 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
4 * http://miniupnp.free.fr/libnatpmp.html
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17#include <string.h>
18#include <time.h>
19#include <sys/time.h>
20#ifdef WIN32
21#include <winsock2.h>
22#include <Ws2tcpip.h>
23#include <io.h>
24#define EWOULDBLOCK WSAEWOULDBLOCK
25#define ECONNREFUSED WSAECONNREFUSED
26#else
27#include <errno.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#define closesocket close
33#endif
34#include "natpmp.h"
35#include "getgateway.h"
36
37int
38initnatpmp (natpmp_t * p)
39{
40#ifdef WIN32
41 u_long ioctlArg = 1;
42#else
43 int flags;
44#endif
45 int domain = AF_INET;
46 int gw_domain;
47 struct sockaddr_in addr;
48 struct sockaddr_in6 addr6;
49
50 if (!p)
51 return NATPMP_ERR_INVALIDARGS;
52
53 if (p->addr)
54 domain = (p->addr->sa_family == AF_INET) ? PF_INET : PF_INET6;
55
56 memset (p, 0, sizeof (natpmp_t));
57 p->s = socket (domain, SOCK_DGRAM, 0);
58 if (p->s < 0)
59 return NATPMP_ERR_SOCKETERROR;
60 /* If addr has been set, use it, else get the default from connect() */
61 if (p->addr && bind (p->s, p->addr, p->addrlen) < 0)
62 return NATPMP_ERR_BINDERROR;
63#ifdef WIN32
64 if (ioctlsocket (p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
65 return NATPMP_ERR_FCNTLERROR;
66#else
67 if ((flags = fcntl (p->s, F_GETFL, 0)) < 0)
68 return NATPMP_ERR_FCNTLERROR;
69 if (fcntl (p->s, F_SETFL, flags | O_NONBLOCK) < 0)
70 return NATPMP_ERR_FCNTLERROR;
71#endif
72
73 if (getdefaultgateway (&gw_domain, p->gateway) < 0)
74 return NATPMP_ERR_CANNOTGETGATEWAY;
75
76 if (domain != gw_domain)
77 return NATPMP_ERR_ADDRERROR;
78
79 if (domain == AF_INET)
80 {
81 memset (&addr, 0, sizeof (addr));
82 addr.sin_family = AF_INET;
83 addr.sin_port = htons (NATPMP_PORT);
84 memcpy (&addr.sin_addr.s_addr, p->gateway, 4 * sizeof (uint8_t));
85#ifdef HAVE_SOCKADDR_IN_SIN_LEN
86 addr.sin_len = sizeof (addr);
87#endif
88 if (connect (p->s, (struct sockaddr *) &addr, sizeof (addr)) < 0)
89 return NATPMP_ERR_CONNECTERR;
90 }
91 else
92 {
93 memset (&addr6, 0, sizeof (addr6));
94 addr6.sin6_family = AF_INET6;
95 addr6.sin6_port = htons (NATPMP_PORT);
96 memcpy (addr6.sin6_addr.s6_addr, p->gateway, 16 * sizeof (uint8_t));
97#ifdef HAVE_SOCKADDR_IN_SIN_LEN
98 addr6.sin6_len = sizeof (addr6);
99#endif
100 if (connect (p->s, (struct sockaddr *) &addr6, sizeof (addr6)) < 0)
101 return NATPMP_ERR_CONNECTERR;
102 }
103
104 return 0;
105}
106
107int
108closenatpmp (natpmp_t * p)
109{
110 if (!p)
111 return NATPMP_ERR_INVALIDARGS;
112 if (closesocket (p->s) < 0)
113 return NATPMP_ERR_CLOSEERR;
114 return 0;
115}
116
117static int
118sendpendingrequest (natpmp_t * p)
119{
120 int r;
121/* struct sockaddr_in addr;*/
122 if (!p)
123 return NATPMP_ERR_INVALIDARGS;
124/* memset(&addr, 0, sizeof(addr));
125 addr.sin_family = AF_INET;
126 addr.sin_port = htons(NATPMP_PORT);
127 addr.sin_addr.s_addr = p->gateway;
128 r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
129 (struct sockaddr *)&addr, sizeof(addr));*/
130 r = (int) send (p->s, p->pending_request, p->pending_request_len, 0);
131 return (r < 0) ? NATPMP_ERR_SENDERR : r;
132}
133
134static int
135sendnatpmprequest (natpmp_t * p)
136{
137 int n;
138 if (!p)
139 return NATPMP_ERR_INVALIDARGS;
140 /* TODO : check if no request is allready pending */
141 p->has_pending_request = 1;
142 p->try_number = 1;
143 n = sendpendingrequest (p);
144 gettimeofday (&p->retry_time, NULL); // check errors !
145 p->retry_time.tv_usec += 250000; /* add 250ms */
146 if (p->retry_time.tv_usec >= 1000000)
147 {
148 p->retry_time.tv_usec -= 1000000;
149 p->retry_time.tv_sec++;
150 }
151 return n;
152}
153
154int
155getnatpmprequesttimeout (natpmp_t * p, struct timeval *timeout)
156{
157 struct timeval now;
158 if (!p || !timeout)
159 return NATPMP_ERR_INVALIDARGS;
160 if (!p->has_pending_request)
161 return NATPMP_ERR_NOPENDINGREQ;
162 if (gettimeofday (&now, NULL) < 0)
163 return NATPMP_ERR_GETTIMEOFDAYERR;
164 timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
165 timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
166 if (timeout->tv_usec < 0)
167 {
168 timeout->tv_usec += 1000000;
169 timeout->tv_sec--;
170 }
171 return 0;
172}
173
174int
175sendpublicaddressrequest (natpmp_t * p)
176{
177 if (!p)
178 return NATPMP_ERR_INVALIDARGS;
179 //static const unsigned char request[] = { 0, 0 };
180 p->pending_request[0] = 0;
181 p->pending_request[1] = 0;
182 p->pending_request_len = 2;
183 // TODO: return 0 instead of sizeof(request) ??
184 return sendnatpmprequest (p);
185}
186
187int
188sendnewportmappingrequest (natpmp_t * p, int protocol,
189 uint16_t privateport, uint16_t publicport,
190 uint32_t lifetime)
191{
192 if (!p
193 || (protocol != NATPMP_PROTOCOL_TCP && protocol != NATPMP_PROTOCOL_UDP))
194 return NATPMP_ERR_INVALIDARGS;
195 p->pending_request[0] = 0;
196 p->pending_request[1] = protocol;
197 p->pending_request[2] = 0;
198 p->pending_request[3] = 0;
199 *((uint16_t *) (p->pending_request + 4)) = htons (privateport);
200 *((uint16_t *) (p->pending_request + 6)) = htons (publicport);
201 *((uint32_t *) (p->pending_request + 8)) = htonl (lifetime);
202 p->pending_request_len = 12;
203 return sendnatpmprequest (p);
204}
205
206static int
207readnatpmpresponse (natpmp_t * p, natpmpresp_t * response)
208{
209 unsigned char buf[16];
210 struct sockaddr_storage addr;
211 socklen_t addrlen = sizeof (addr);
212 int n;
213 if (!p)
214 return NATPMP_ERR_INVALIDARGS;
215 n = recvfrom (p->s, buf, sizeof (buf), 0,
216 (struct sockaddr *) &addr, &addrlen);
217 if (n < 0)
218 switch (errno)
219 {
220 /*case EAGAIN: */
221 case EWOULDBLOCK:
222 n = NATPMP_TRYAGAIN;
223 break;
224 case ECONNREFUSED:
225 n = NATPMP_ERR_NOGATEWAYSUPPORT;
226 break;
227 default:
228 n = NATPMP_ERR_RECVFROM;
229 }
230 /* check that addr is correct (= gateway) */
231 else if (addr.ss_family == AF_INET && memcmp (&((struct sockaddr_in *) &addr)->sin_addr.s_addr, p->gateway, 4 * sizeof (uint8_t)) == 0)
232 n = NATPMP_ERR_WRONGPACKETSOURCE;
233 else if (addr.ss_family == AF_INET6 && memcmp (((struct sockaddr_in6 *) &addr)->sin6_addr.s6_addr, p->gateway, 16 * sizeof (uint8_t)) == 0)
234 n = NATPMP_ERR_WRONGPACKETSOURCE;
235 else
236 {
237 response->resultcode = ntohs (*((uint16_t *) (buf + 2)));
238 response->epoch = ntohl (*((uint32_t *) (buf + 4)));
239 if (buf[0] != 0)
240 n = NATPMP_ERR_UNSUPPORTEDVERSION;
241 else if (buf[1] < 128 || buf[1] > 130)
242 n = NATPMP_ERR_UNSUPPORTEDOPCODE;
243 else if (response->resultcode != 0)
244 {
245 switch (response->resultcode)
246 {
247 case 1:
248 n = NATPMP_ERR_UNSUPPORTEDVERSION;
249 break;
250 case 2:
251 n = NATPMP_ERR_NOTAUTHORIZED;
252 break;
253 case 3:
254 n = NATPMP_ERR_NETWORKFAILURE;
255 break;
256 case 4:
257 n = NATPMP_ERR_OUTOFRESOURCES;
258 break;
259 case 5:
260 n = NATPMP_ERR_UNSUPPORTEDOPCODE;
261 break;
262 default:
263 n = NATPMP_ERR_UNDEFINEDERROR;
264 }
265 }
266 else
267 {
268 response->type = buf[1] & 0x7f;
269 if (buf[1] == 128)
270 {
271 response->pnu.publicaddress.family = AF_INET;
272 memset (&response->pnu.publicaddress.addr6.s6_addr, 0, sizeof (struct in6_addr));
273 response->pnu.publicaddress.addr.s_addr =
274 *((uint32_t *) (buf + 8));
275 /* FIXME: support IPv6 address */
276 }
277 else
278 {
279 response->pnu.newportmapping.privateport =
280 ntohs (*((uint16_t *) (buf + 8)));
281 response->pnu.newportmapping.mappedpublicport =
282 ntohs (*((uint16_t *) (buf + 10)));
283 response->pnu.newportmapping.lifetime =
284 ntohl (*((uint32_t *) (buf + 12)));
285 }
286 n = 0;
287 }
288 }
289 return n;
290}
291
292int
293readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response)
294{
295 int n;
296 if (!p || !response)
297 return NATPMP_ERR_INVALIDARGS;
298 if (!p->has_pending_request)
299 return NATPMP_ERR_NOPENDINGREQ;
300 n = readnatpmpresponse (p, response);
301 if (n < 0)
302 {
303 if (n == NATPMP_TRYAGAIN)
304 {
305 struct timeval now;
306 gettimeofday (&now, NULL); // check errors !
307 if (timercmp (&now, &p->retry_time, >=))
308 {
309 int delay, r;
310 if (p->try_number >= 9)
311 {
312 return NATPMP_ERR_NOGATEWAYSUPPORT;
313 }
314 /*printf("retry! %d\n", p->try_number); */
315 delay = 250 * (1 << p->try_number); // ms
316 /*for(i=0; i<p->try_number; i++)
317 delay += delay; */
318 p->retry_time.tv_sec += (delay / 1000);
319 p->retry_time.tv_usec += (delay % 1000) * 1000;
320 if (p->retry_time.tv_usec >= 1000000)
321 {
322 p->retry_time.tv_usec -= 1000000;
323 p->retry_time.tv_sec++;
324 }
325 p->try_number++;
326 r = sendpendingrequest (p);
327 if (r < 0)
328 return r;
329 }
330 }
331 }
332 else
333 {
334 p->has_pending_request = 0;
335 }
336 return n;
337}
338
339#ifdef ENABLE_STRNATPMPERR
340const char *
341strnatpmperr (int r)
342{
343 const char *s;
344 switch (r)
345 {
346 case NATPMP_ERR_INVALIDARGS:
347 s = "invalid arguments";
348 break;
349 case NATPMP_ERR_SOCKETERROR:
350 s = "socket() failed";
351 break;
352 case NATPMP_ERR_CANNOTGETGATEWAY:
353 s = "cannot get default gateway ip address";
354 break;
355 case NATPMP_ERR_CLOSEERR:
356#ifdef WIN32
357 s = "closesocket() failed";
358#else
359 s = "close() failed";
360#endif
361 break;
362 case NATPMP_ERR_RECVFROM:
363 s = "recvfrom() failed";
364 break;
365 case NATPMP_ERR_NOPENDINGREQ:
366 s = "no pending request";
367 break;
368 case NATPMP_ERR_NOGATEWAYSUPPORT:
369 s = "the gateway does not support nat-pmp";
370 break;
371 case NATPMP_ERR_CONNECTERR:
372 s = "connect() failed";
373 break;
374 case NATPMP_ERR_WRONGPACKETSOURCE:
375 s = "packet not received from the default gateway";
376 break;
377 case NATPMP_ERR_SENDERR:
378 s = "send() failed";
379 break;
380 case NATPMP_ERR_FCNTLERROR:
381 s = "fcntl() failed";
382 break;
383 case NATPMP_ERR_GETTIMEOFDAYERR:
384 s = "gettimeofday() failed";
385 break;
386 case NATPMP_ERR_UNSUPPORTEDVERSION:
387 s = "unsupported nat-pmp version error from server";
388 break;
389 case NATPMP_ERR_UNSUPPORTEDOPCODE:
390 s = "unsupported nat-pmp opcode error from server";
391 break;
392 case NATPMP_ERR_UNDEFINEDERROR:
393 s = "undefined nat-pmp server error";
394 break;
395 case NATPMP_ERR_NOTAUTHORIZED:
396 s = "not authorized";
397 break;
398 case NATPMP_ERR_NETWORKFAILURE:
399 s = "network failure";
400 break;
401 case NATPMP_ERR_OUTOFRESOURCES:
402 s = "nat-pmp server out of resources";
403 break;
404 default:
405 s = "Unknown libnatpmp error";
406 }
407 return s;
408}
409#endif