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

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

update to the miniupnpc-20080427 snapshot
update to the libnatpmp-20080428 snapshot

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