source: trunk/third-party/libnatpmp/natpmp.c @ 6303

Last change on this file since 6303 was 6303, checked in by charles, 14 years ago

Update to the 20080707 version of libnatpmp and the 20080703 version of miniupnpc

File size: 8.8 KB
Line 
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 initnatpmp(natpmp_t * p)
38{
39#ifdef WIN32
40        u_long ioctlArg = 1;
41#else
42        int flags; 
43#endif
44        struct sockaddr_in addr;
45        if(!p)
46                return NATPMP_ERR_INVALIDARGS;
47        memset(p, 0, sizeof(natpmp_t));
48        p->s = socket(PF_INET, SOCK_DGRAM, 0);
49        if(p->s < 0)
50                return NATPMP_ERR_SOCKETERROR;
51#ifdef WIN32
52        if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
53                return NATPMP_ERR_FCNTLERROR;
54#else
55        if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
56                return NATPMP_ERR_FCNTLERROR;
57        if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
58                return NATPMP_ERR_FCNTLERROR;
59#endif
60
61        if(getdefaultgateway(&(p->gateway)) < 0)
62                return NATPMP_ERR_CANNOTGETGATEWAY;
63       
64        memset(&addr, 0, sizeof(addr));
65        addr.sin_family = AF_INET;
66        addr.sin_port = htons(NATPMP_PORT);
67        addr.sin_addr.s_addr = p->gateway;
68        if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
69                return NATPMP_ERR_CONNECTERR;
70        return 0;
71}
72
73int closenatpmp(natpmp_t * p)
74{
75        if(!p)
76                return NATPMP_ERR_INVALIDARGS;
77        if(closesocket(p->s) < 0)
78                return NATPMP_ERR_CLOSEERR;
79        return 0;
80}
81
82int sendpendingrequest(natpmp_t * p)
83{
84        int r;
85/*      struct sockaddr_in addr;*/
86        if(!p)
87                return NATPMP_ERR_INVALIDARGS;
88/*      memset(&addr, 0, sizeof(addr));
89        addr.sin_family = AF_INET;
90        addr.sin_port = htons(NATPMP_PORT);
91        addr.sin_addr.s_addr = p->gateway;
92        r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
93                           (struct sockaddr *)&addr, sizeof(addr));*/
94        r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
95        return (r<0) ? NATPMP_ERR_SENDERR : r;
96}
97
98int sendnatpmprequest(natpmp_t * p)
99{
100        int n;
101        if(!p)
102                return NATPMP_ERR_INVALIDARGS;
103        /* TODO : check if no request is allready pending */
104        p->has_pending_request = 1;
105        p->try_number = 1;
106        n = sendpendingrequest(p);
107        gettimeofday(&p->retry_time, NULL);     // check errors !
108        p->retry_time.tv_usec += 250000;        /* add 250ms */
109        if(p->retry_time.tv_usec >= 1000000) {
110                p->retry_time.tv_usec -= 1000000;
111                p->retry_time.tv_sec++;
112        }
113        return n;
114}
115
116int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
117{
118        struct timeval now;
119        if(!p || !timeout)
120                return NATPMP_ERR_INVALIDARGS;
121        if(!p->has_pending_request)
122                return NATPMP_ERR_NOPENDINGREQ;
123        if(gettimeofday(&now, NULL) < 0)
124                return NATPMP_ERR_GETTIMEOFDAYERR;
125        timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
126        timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
127        if(timeout->tv_usec < 0) {
128                timeout->tv_usec += 1000000;
129                timeout->tv_sec--;
130        }
131        return 0;
132}
133
134int sendpublicaddressrequest(natpmp_t * p)
135{
136        if(!p)
137                return NATPMP_ERR_INVALIDARGS;
138        //static const unsigned char request[] = { 0, 0 };
139        p->pending_request[0] = 0;
140        p->pending_request[1] = 0;
141        p->pending_request_len = 2;
142        // TODO: return 0 instead of sizeof(request) ??
143        return sendnatpmprequest(p);
144}
145
146int sendnewportmappingrequest(natpmp_t * p, int protocol,
147                              uint16_t privateport, uint16_t publicport,
148                                                          uint32_t lifetime)
149{
150        if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
151                return NATPMP_ERR_INVALIDARGS;
152        p->pending_request[0] = 0;
153        p->pending_request[1] = protocol;
154        p->pending_request[2] = 0;
155        p->pending_request[3] = 0;
156        *((uint16_t *)(p->pending_request + 4)) = htons(privateport);
157        *((uint16_t *)(p->pending_request + 6)) = htons(publicport);
158        *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
159        p->pending_request_len = 12;
160        return sendnatpmprequest(p);
161}
162
163int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
164{
165        unsigned char buf[16];
166        struct sockaddr_in addr;
167        socklen_t addrlen = sizeof(addr);
168        int n;
169        if(!p)
170                return NATPMP_ERR_INVALIDARGS;
171        n = recvfrom(p->s, buf, sizeof(buf), 0,
172                     (struct sockaddr *)&addr, &addrlen);
173        if(n<0)
174                switch(errno) {
175                /*case EAGAIN:*/
176                case EWOULDBLOCK:
177                        n = NATPMP_TRYAGAIN;
178                        break;
179                case ECONNREFUSED:
180                        n = NATPMP_ERR_NOGATEWAYSUPPORT;
181                        break;
182                default:
183                        n = NATPMP_ERR_RECVFROM;
184                }
185        /* check that addr is correct (= gateway) */
186        else if(addr.sin_addr.s_addr != p->gateway)
187                n = NATPMP_ERR_WRONGPACKETSOURCE;
188        else {
189                response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
190                response->epoch = ntohl(*((uint32_t *)(buf + 4)));
191                if(buf[0] != 0)
192                        n = NATPMP_ERR_UNSUPPORTEDVERSION;
193                else if(buf[1] < 128 || buf[1] > 130)
194                        n = NATPMP_ERR_UNSUPPORTEDOPCODE;
195                else if(response->resultcode != 0) {
196                        switch(response->resultcode) {
197                        case 1:
198                                n = NATPMP_ERR_UNSUPPORTEDVERSION;
199                                break;
200                        case 2:
201                                n = NATPMP_ERR_NOTAUTHORIZED;
202                                break;
203                        case 3:
204                                n = NATPMP_ERR_NETWORKFAILURE;
205                                break;
206                        case 4:
207                                n = NATPMP_ERR_OUTOFRESOURCES;
208                                break;
209                        case 5:
210                                n = NATPMP_ERR_UNSUPPORTEDOPCODE;
211                                break;
212                        default:
213                                n = NATPMP_ERR_UNDEFINEDERROR;
214                        }
215                } else {
216                        response->type = buf[1] & 0x7f;
217                        if(buf[1] == 128)
218                                //response->publicaddress.addr = *((uint32_t *)(buf + 8));
219                                response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
220                        else {
221                                response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
222                                response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
223                                response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
224                        }
225                        n = 0;
226                }
227        }
228        return n;
229}
230
231int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
232{
233        int n;
234        if(!p || !response)
235                return NATPMP_ERR_INVALIDARGS;
236        if(!p->has_pending_request)
237                return NATPMP_ERR_NOPENDINGREQ;
238        n = readnatpmpresponse(p, response);
239        if(n<0) {
240                if(n==NATPMP_TRYAGAIN) {
241                        struct timeval now;
242                        gettimeofday(&now, NULL);       // check errors !
243                        if(timercmp(&now, &p->retry_time, >=)) {
244                                int delay, r;
245                                if(p->try_number >= 9) {
246                                        return NATPMP_ERR_NOGATEWAYSUPPORT;
247                                }
248                                /*printf("retry! %d\n", p->try_number);*/
249                                delay = 250 * (1<<p->try_number);       // ms
250                                /*for(i=0; i<p->try_number; i++)
251                                        delay += delay;*/
252                                p->retry_time.tv_sec += (delay / 1000);
253                                p->retry_time.tv_usec += (delay % 1000) * 1000;
254                                if(p->retry_time.tv_usec >= 1000000) {
255                                        p->retry_time.tv_usec -= 1000000;
256                                        p->retry_time.tv_sec++;
257                                }
258                                p->try_number++;
259                                r = sendpendingrequest(p);
260                                if(r<0)
261                                        return r;
262                        }
263                }
264        } else {
265                p->has_pending_request = 0;
266        }
267        return n;
268}
269
270#ifdef ENABLE_STRNATPMPERR
271const char * strnatpmperr(int r)
272{
273        const char * s;
274        switch(r) {
275        case NATPMP_ERR_INVALIDARGS:
276                s = "invalid arguments";
277                break;
278        case NATPMP_ERR_SOCKETERROR:
279                s = "socket() failed";
280                break;
281        case NATPMP_ERR_CANNOTGETGATEWAY:
282                s = "cannot get default gateway ip address";
283                break;
284        case NATPMP_ERR_CLOSEERR:
285#ifdef WIN32
286                s = "closesocket() failed";
287#else
288                s = "close() failed";
289#endif
290                break;
291        case NATPMP_ERR_RECVFROM:
292                s = "recvfrom() failed";
293                break;
294        case NATPMP_ERR_NOPENDINGREQ:
295                s = "no pending request";
296                break;
297        case NATPMP_ERR_NOGATEWAYSUPPORT:
298                s = "the gateway does not support nat-pmp";
299                break;
300        case NATPMP_ERR_CONNECTERR:
301                s = "connect() failed";
302                break;
303        case NATPMP_ERR_WRONGPACKETSOURCE:
304                s = "packet not received from the default gateway";
305                break;
306        case NATPMP_ERR_SENDERR:
307                s = "send() failed";
308                break;
309        case NATPMP_ERR_FCNTLERROR:
310                s = "fcntl() failed";
311                break;
312        case NATPMP_ERR_GETTIMEOFDAYERR:
313                s = "gettimeofday() failed";
314                break;
315        case NATPMP_ERR_UNSUPPORTEDVERSION:
316                s = "unsupported nat-pmp version error from server";
317                break;
318        case NATPMP_ERR_UNSUPPORTEDOPCODE:
319                s = "unsupported nat-pmp opcode error from server";
320                break;
321        case NATPMP_ERR_UNDEFINEDERROR:
322                s = "undefined nat-pmp server error";
323                break;
324        case NATPMP_ERR_NOTAUTHORIZED:
325                s = "not authorized";
326                break;
327        case NATPMP_ERR_NETWORKFAILURE:
328                s = "network failure";
329                break;
330        case NATPMP_ERR_OUTOFRESOURCES:
331                s = "nat-pmp server out of resources";
332                break;
333        default:
334                s = "Unknown libnatpmp error";
335        }
336        return s;
337}
338#endif
339
Note: See TracBrowser for help on using the repository browser.