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

Last change on this file since 6706 was 6706, checked in by muks, 13 years ago

(win32) Add includes for natpmp

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#include <errno.h>
21#ifdef WIN32
22#include <winsock2.h>
23#include <ws2tcpip.h>
24#include <io.h>
25#define EWOULDBLOCK WSAEWOULDBLOCK
26#define ECONNREFUSED WSAECONNREFUSED
27#else
28#include <errno.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#define closesocket close
34#endif
35#include "natpmp.h"
36#include "getgateway.h"
37
38int initnatpmp(natpmp_t * p)
39{
40#ifdef WIN32
41        u_long ioctlArg = 1;
42#else
43        int flags; 
44#endif
45        struct sockaddr_in addr;
46        if(!p)
47                return NATPMP_ERR_INVALIDARGS;
48        memset(p, 0, sizeof(natpmp_t));
49        p->s = socket(PF_INET, SOCK_DGRAM, 0);
50        if(p->s < 0)
51                return NATPMP_ERR_SOCKETERROR;
52#ifdef WIN32
53        if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
54                return NATPMP_ERR_FCNTLERROR;
55#else
56        if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
57                return NATPMP_ERR_FCNTLERROR;
58        if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
59                return NATPMP_ERR_FCNTLERROR;
60#endif
61
62        if(getdefaultgateway(&(p->gateway)) < 0)
63                return NATPMP_ERR_CANNOTGETGATEWAY;
64       
65        memset(&addr, 0, sizeof(addr));
66        addr.sin_family = AF_INET;
67        addr.sin_port = htons(NATPMP_PORT);
68        addr.sin_addr.s_addr = p->gateway;
69        if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
70                return NATPMP_ERR_CONNECTERR;
71        return 0;
72}
73
74int closenatpmp(natpmp_t * p)
75{
76        if(!p)
77                return NATPMP_ERR_INVALIDARGS;
78        if(closesocket(p->s) < 0)
79                return NATPMP_ERR_CLOSEERR;
80        return 0;
81}
82
83int sendpendingrequest(natpmp_t * p)
84{
85        int r;
86/*      struct sockaddr_in addr;*/
87        if(!p)
88                return NATPMP_ERR_INVALIDARGS;
89/*      memset(&addr, 0, sizeof(addr));
90        addr.sin_family = AF_INET;
91        addr.sin_port = htons(NATPMP_PORT);
92        addr.sin_addr.s_addr = p->gateway;
93        r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
94                           (struct sockaddr *)&addr, sizeof(addr));*/
95        r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
96        return (r<0) ? NATPMP_ERR_SENDERR : r;
97}
98
99int sendnatpmprequest(natpmp_t * p)
100{
101        int n;
102        if(!p)
103                return NATPMP_ERR_INVALIDARGS;
104        /* TODO : check if no request is allready pending */
105        p->has_pending_request = 1;
106        p->try_number = 1;
107        n = sendpendingrequest(p);
108        gettimeofday(&p->retry_time, NULL);     // check errors !
109        p->retry_time.tv_usec += 250000;        /* add 250ms */
110        if(p->retry_time.tv_usec >= 1000000) {
111                p->retry_time.tv_usec -= 1000000;
112                p->retry_time.tv_sec++;
113        }
114        return n;
115}
116
117int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
118{
119        struct timeval now;
120        if(!p || !timeout)
121                return NATPMP_ERR_INVALIDARGS;
122        if(!p->has_pending_request)
123                return NATPMP_ERR_NOPENDINGREQ;
124        if(gettimeofday(&now, NULL) < 0)
125                return NATPMP_ERR_GETTIMEOFDAYERR;
126        timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
127        timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
128        if(timeout->tv_usec < 0) {
129                timeout->tv_usec += 1000000;
130                timeout->tv_sec--;
131        }
132        return 0;
133}
134
135int sendpublicaddressrequest(natpmp_t * p)
136{
137        if(!p)
138                return NATPMP_ERR_INVALIDARGS;
139        //static const unsigned char request[] = { 0, 0 };
140        p->pending_request[0] = 0;
141        p->pending_request[1] = 0;
142        p->pending_request_len = 2;
143        // TODO: return 0 instead of sizeof(request) ??
144        return sendnatpmprequest(p);
145}
146
147int sendnewportmappingrequest(natpmp_t * p, int protocol,
148                              uint16_t privateport, uint16_t publicport,
149                                                          uint32_t lifetime)
150{
151        if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
152                return NATPMP_ERR_INVALIDARGS;
153        p->pending_request[0] = 0;
154        p->pending_request[1] = protocol;
155        p->pending_request[2] = 0;
156        p->pending_request[3] = 0;
157        *((uint16_t *)(p->pending_request + 4)) = htons(privateport);
158        *((uint16_t *)(p->pending_request + 6)) = htons(publicport);
159        *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
160        p->pending_request_len = 12;
161        return sendnatpmprequest(p);
162}
163
164int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
165{
166        unsigned char buf[16];
167        struct sockaddr_in addr;
168        socklen_t addrlen = sizeof(addr);
169        int n;
170        if(!p)
171                return NATPMP_ERR_INVALIDARGS;
172        n = recvfrom(p->s, buf, sizeof(buf), 0,
173                     (struct sockaddr *)&addr, &addrlen);
174        if(n<0)
175                switch(errno) {
176                /*case EAGAIN:*/
177                case EWOULDBLOCK:
178                        n = NATPMP_TRYAGAIN;
179                        break;
180                case ECONNREFUSED:
181                        n = NATPMP_ERR_NOGATEWAYSUPPORT;
182                        break;
183                default:
184                        n = NATPMP_ERR_RECVFROM;
185                }
186        /* check that addr is correct (= gateway) */
187        else if(addr.sin_addr.s_addr != p->gateway)
188                n = NATPMP_ERR_WRONGPACKETSOURCE;
189        else {
190                response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
191                response->epoch = ntohl(*((uint32_t *)(buf + 4)));
192                if(buf[0] != 0)
193                        n = NATPMP_ERR_UNSUPPORTEDVERSION;
194                else if(buf[1] < 128 || buf[1] > 130)
195                        n = NATPMP_ERR_UNSUPPORTEDOPCODE;
196                else if(response->resultcode != 0) {
197                        switch(response->resultcode) {
198                        case 1:
199                                n = NATPMP_ERR_UNSUPPORTEDVERSION;
200                                break;
201                        case 2:
202                                n = NATPMP_ERR_NOTAUTHORIZED;
203                                break;
204                        case 3:
205                                n = NATPMP_ERR_NETWORKFAILURE;
206                                break;
207                        case 4:
208                                n = NATPMP_ERR_OUTOFRESOURCES;
209                                break;
210                        case 5:
211                                n = NATPMP_ERR_UNSUPPORTEDOPCODE;
212                                break;
213                        default:
214                                n = NATPMP_ERR_UNDEFINEDERROR;
215                        }
216                } else {
217                        response->type = buf[1] & 0x7f;
218                        if(buf[1] == 128)
219                                //response->publicaddress.addr = *((uint32_t *)(buf + 8));
220                                response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
221                        else {
222                                response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
223                                response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
224                                response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
225                        }
226                        n = 0;
227                }
228        }
229        return n;
230}
231
232int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
233{
234        int n;
235        if(!p || !response)
236                return NATPMP_ERR_INVALIDARGS;
237        if(!p->has_pending_request)
238                return NATPMP_ERR_NOPENDINGREQ;
239        n = readnatpmpresponse(p, response);
240        if(n<0) {
241                if(n==NATPMP_TRYAGAIN) {
242                        struct timeval now;
243                        gettimeofday(&now, NULL);       // check errors !
244                        if(timercmp(&now, &p->retry_time, >=)) {
245                                int delay, r;
246                                if(p->try_number >= 9) {
247                                        return NATPMP_ERR_NOGATEWAYSUPPORT;
248                                }
249                                /*printf("retry! %d\n", p->try_number);*/
250                                delay = 250 * (1<<p->try_number);       // ms
251                                /*for(i=0; i<p->try_number; i++)
252                                        delay += delay;*/
253                                p->retry_time.tv_sec += (delay / 1000);
254                                p->retry_time.tv_usec += (delay % 1000) * 1000;
255                                if(p->retry_time.tv_usec >= 1000000) {
256                                        p->retry_time.tv_usec -= 1000000;
257                                        p->retry_time.tv_sec++;
258                                }
259                                p->try_number++;
260                                r = sendpendingrequest(p);
261                                if(r<0)
262                                        return r;
263                        }
264                }
265        } else {
266                p->has_pending_request = 0;
267        }
268        return n;
269}
270
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
Note: See TracBrowser for help on using the repository browser.