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

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

add libnatpmp by Thomas Bernard

File size: 6.9 KB
Line 
1/* $Id: natpmp.c,v 1.4 2007/12/02 00:12:47 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007, Thomas BERNARD <miniupnp@free.fr>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
16#include <errno.h>
17#include <string.h>
18#include <unistd.h>
19#include <fcntl.h>
20#include <time.h>
21#include <sys/time.h>
22#include <sys/types.h>
23#include <sys/socket.h>
24#include "natpmp.h"
25#include "getgateway.h"
26
27int initnatpmp(natpmp_t * p)
28{
29        int flags; 
30        struct sockaddr_in addr;
31        if(!p)
32                return NATPMP_ERR_INVALIDARGS;
33        memset(p, 0, sizeof(natpmp_t));
34        p->s = socket(PF_INET, SOCK_DGRAM, 0);
35        if(p->s < 0)
36                return NATPMP_ERR_SOCKETERROR;
37        if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
38                return NATPMP_ERR_FCNTLERROR;
39        if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
40                return NATPMP_ERR_FCNTLERROR;
41
42        if(getdefaultgateway(&(p->gateway)) < 0)
43                return NATPMP_ERR_CANNOTGETGATEWAY;
44       
45        memset(&addr, 0, sizeof(addr));
46        addr.sin_family = AF_INET;
47        addr.sin_port = htons(NATPMP_PORT);
48        addr.sin_addr.s_addr = p->gateway;
49        if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
50                return NATPMP_ERR_CONNECTERR;
51        return 0;
52}
53
54int closenatpmp(natpmp_t * p)
55{
56        if(!p)
57                return NATPMP_ERR_INVALIDARGS;
58        if(close(p->s) < 0)
59                return NATPMP_ERR_CLOSEERR;
60        return 0;
61}
62
63int sendpendingrequest(natpmp_t * p)
64{
65        int r;
66/*      struct sockaddr_in addr;*/
67        if(!p)
68                return NATPMP_ERR_INVALIDARGS;
69/*      memset(&addr, 0, sizeof(addr));
70        addr.sin_family = AF_INET;
71        addr.sin_port = htons(NATPMP_PORT);
72        addr.sin_addr.s_addr = p->gateway;
73        r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
74                           (struct sockaddr *)&addr, sizeof(addr));*/
75        r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
76        return (r<0) ? NATPMP_ERR_SENDERR : r;
77}
78
79int sendnatpmprequest(natpmp_t * p)
80{
81        int n;
82        if(!p)
83                return NATPMP_ERR_INVALIDARGS;
84        /* TODO : check if no request is allready pending */
85        p->has_pending_request = 1;
86        p->try_number = 1;
87        n = sendpendingrequest(p);
88        gettimeofday(&p->retry_time, NULL);     // check errors !
89        p->retry_time.tv_usec += 250000;        /* add 250ms */
90        if(p->retry_time.tv_usec >= 1000000) {
91                p->retry_time.tv_usec -= 1000000;
92                p->retry_time.tv_sec++;
93        }
94        return n;
95}
96
97int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
98{
99        struct timeval now;
100        if(!p || !timeout)
101                return NATPMP_ERR_INVALIDARGS;
102        if(!p->has_pending_request)
103                return NATPMP_ERR_NOPENDINGREQ;
104        if(gettimeofday(&now, NULL) < 0)
105                return NATPMP_ERR_GETTIMEOFDAYERR;
106        timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
107        timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
108        if(timeout->tv_usec < 0) {
109                timeout->tv_usec += 1000000;
110                timeout->tv_sec--;
111        }
112        return 0;
113}
114
115int sendpublicaddressrequest(natpmp_t * p)
116{
117        if(!p)
118                return NATPMP_ERR_INVALIDARGS;
119        //static const unsigned char request[] = { 0, 0 };
120        p->pending_request[0] = 0;
121        p->pending_request[1] = 0;
122        p->pending_request_len = 2;
123        // TODO: return 0 instead of sizeof(request) ??
124        return sendnatpmprequest(p);
125}
126
127int sendnewportmappingrequest(natpmp_t * p, int protocol,
128                              uint16_t privateport, uint16_t publicport,
129                                                          uint32_t lifetime)
130{
131        if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
132                return NATPMP_ERR_INVALIDARGS;
133        p->pending_request[0] = 0;
134        p->pending_request[1] = protocol;
135        p->pending_request[2] = 0;
136        p->pending_request[3] = 0;
137        *((uint16_t *)(p->pending_request + 4)) = htons(privateport);
138        *((uint16_t *)(p->pending_request + 6)) = htons(publicport);
139        *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
140        p->pending_request_len = 12;
141        return sendnatpmprequest(p);
142}
143
144int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
145{
146        unsigned char buf[16];
147        struct sockaddr_in addr;
148        socklen_t addrlen = sizeof(addr);
149        int n;
150        if(!p)
151                return NATPMP_ERR_INVALIDARGS;
152        n = recvfrom(p->s, buf, sizeof(buf), 0,
153                     (struct sockaddr *)&addr, &addrlen);
154        if(n<0)
155                switch(errno) {
156                case EAGAIN:
157                        n = NATPMP_TRYAGAIN;
158                        break;
159                case ECONNREFUSED:
160                        n = NATPMP_ERR_NOGATEWAYSUPPORT;
161                        break;
162                default:
163                        n = NATPMP_ERR_RECVFROM;
164                }
165        /* check that addr is correct (= gateway) */
166        else if(addr.sin_addr.s_addr != p->gateway)
167                n = NATPMP_ERR_WRONGPACKETSOURCE;
168        else {
169                response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
170                response->epoch = ntohl(*((uint32_t *)(buf + 4)));
171                if(buf[0] != 0)
172                        n = NATPMP_ERR_UNSUPPORTEDVERSION;
173                else if(buf[1] < 128 || buf[1] > 130)
174                        n = NATPMP_ERR_UNSUPPORTEDOPCODE;
175                else if(response->resultcode != 0) {
176                        switch(response->resultcode) {
177                        case 1:
178                                n = NATPMP_ERR_UNSUPPORTEDVERSION;
179                                break;
180                        case 2:
181                                n = NATPMP_ERR_NOTAUTHORIZED;
182                                break;
183                        case 3:
184                                n = NATPMP_ERR_NETWORKFAILURE;
185                                break;
186                        case 4:
187                                n = NATPMP_ERR_OUTOFRESOURCES;
188                                break;
189                        case 5:
190                                n = NATPMP_ERR_UNSUPPORTEDOPCODE;
191                                break;
192                        default:
193                                n = NATPMP_ERR_UNDEFINEDERROR;
194                        }
195                } else {
196                        response->type = buf[1] & 0x7f;
197                        if(buf[1] == 128)
198                                //response->publicaddress.addr = *((uint32_t *)(buf + 8));
199                                response->publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
200                        else {
201                                response->newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
202                                response->newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
203                                response->newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
204                        }
205                        n = 0;
206                }
207        }
208        return n;
209}
210
211int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
212{
213        int n;
214        if(!p || !response)
215                return NATPMP_ERR_INVALIDARGS;
216        if(!p->has_pending_request)
217                return NATPMP_ERR_NOPENDINGREQ;
218        n = readnatpmpresponse(p, response);
219        if(n<0) {
220                if(n==NATPMP_TRYAGAIN) {
221                        struct timeval now;
222                        gettimeofday(&now, NULL);       // check errors !
223                        if(timercmp(&now, &p->retry_time, >=)) {
224                                int delay, r;
225                                if(p->try_number >= 9) {
226                                        return NATPMP_ERR_NOGATEWAYSUPPORT;
227                                }
228                                /*printf("retry! %d\n", p->try_number);*/
229                                delay = 250 * (1<<p->try_number);       // ms
230                                /*for(i=0; i<p->try_number; i++)
231                                        delay += delay;*/
232                                p->retry_time.tv_sec += (delay / 1000);
233                                p->retry_time.tv_usec += (delay % 1000) * 1000;
234                                if(p->retry_time.tv_usec >= 1000000) {
235                                        p->retry_time.tv_usec -= 1000000;
236                                        p->retry_time.tv_sec++;
237                                }
238                                p->try_number++;
239                                r = sendpendingrequest(p);
240                                if(r<0)
241                                        return r;
242                        }
243                }
244        } else {
245                p->has_pending_request = 0;
246        }
247        return n;
248}
249
Note: See TracBrowser for help on using the repository browser.