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

Last change on this file since 10284 was 10284, checked in by livings124, 12 years ago

update libnatpmp to 20100202

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