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

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

(win32) Fix case of included header filenames

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
270const char * strnatpmperr(int r)
271{
272        const char * s;
273        switch(r) {
274        case NATPMP_ERR_INVALIDARGS:
275                s = "invalid arguments";
276                break;
277        case NATPMP_ERR_SOCKETERROR:
278                s = "socket() failed";
279                break;
280        case NATPMP_ERR_CANNOTGETGATEWAY:
281                s = "cannot get default gateway ip address";
282                break;
283        case NATPMP_ERR_CLOSEERR:
284#ifdef WIN32
285                s = "closesocket() failed";
286#else
287                s = "close() failed";
288#endif
289                break;
290        case NATPMP_ERR_RECVFROM:
291                s = "recvfrom() failed";
292                break;
293        case NATPMP_ERR_NOPENDINGREQ:
294                s = "no pending request";
295                break;
296        case NATPMP_ERR_NOGATEWAYSUPPORT:
297                s = "the gateway does not support nat-pmp";
298                break;
299        case NATPMP_ERR_CONNECTERR:
300                s = "connect() failed";
301                break;
302        case NATPMP_ERR_WRONGPACKETSOURCE:
303                s = "packet not received from the default gateway";
304                break;
305        case NATPMP_ERR_SENDERR:
306                s = "send() failed";
307                break;
308        case NATPMP_ERR_FCNTLERROR:
309                s = "fcntl() failed";
310                break;
311        case NATPMP_ERR_GETTIMEOFDAYERR:
312                s = "gettimeofday() failed";
313                break;
314        case NATPMP_ERR_UNSUPPORTEDVERSION:
315                s = "unsupported nat-pmp version error from server";
316                break;
317        case NATPMP_ERR_UNSUPPORTEDOPCODE:
318                s = "unsupported nat-pmp opcode error from server";
319                break;
320        case NATPMP_ERR_UNDEFINEDERROR:
321                s = "undefined nat-pmp server error";
322                break;
323        case NATPMP_ERR_NOTAUTHORIZED:
324                s = "not authorized";
325                break;
326        case NATPMP_ERR_NETWORKFAILURE:
327                s = "network failure";
328                break;
329        case NATPMP_ERR_OUTOFRESOURCES:
330                s = "nat-pmp server out of resources";
331                break;
332        default:
333                s = "Unknown libnatpmp error";
334        }
335        return s;
336}
337
Note: See TracBrowser for help on using the repository browser.