natpmp.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /* $Id: natpmp.c,v 1.20 2015/05/27 12:43:15 nanard Exp $ */
  2. /* libnatpmp
  3. Copyright (c) 2007-2015, Thomas BERNARD
  4. All rights reserved.
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are met:
  7. * Redistributions of source code must retain the above copyright notice,
  8. this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright notice,
  10. this list of conditions and the following disclaimer in the documentation
  11. and/or other materials provided with the distribution.
  12. * The name of the author may not be used to endorse or promote products
  13. derived from this software without specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  18. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #ifdef __linux__
  27. #define _BSD_SOURCE 1
  28. #endif
  29. #include <string.h>
  30. #include <time.h>
  31. #if !defined(_MSC_VER)
  32. #include <sys/time.h>
  33. #endif
  34. #ifdef WIN32
  35. #include <errno.h>
  36. #include <winsock2.h>
  37. #include <ws2tcpip.h>
  38. #include <io.h>
  39. #ifndef EWOULDBLOCK
  40. #define EWOULDBLOCK WSAEWOULDBLOCK
  41. #endif
  42. #ifndef ECONNREFUSED
  43. #define ECONNREFUSED WSAECONNREFUSED
  44. #endif
  45. #include "wingettimeofday.h"
  46. #define gettimeofday natpmp_gettimeofday
  47. #else
  48. #include <errno.h>
  49. #include <unistd.h>
  50. #include <fcntl.h>
  51. #include <sys/types.h>
  52. #include <sys/socket.h>
  53. #define closesocket close
  54. #endif
  55. #include "natpmp.h"
  56. #include "getgateway.h"
  57. #include <stdio.h>
  58. LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
  59. {
  60. #ifdef WIN32
  61. u_long ioctlArg = 1;
  62. #else
  63. int flags;
  64. #endif
  65. struct sockaddr_in addr;
  66. if(!p)
  67. return NATPMP_ERR_INVALIDARGS;
  68. memset(p, 0, sizeof(natpmp_t));
  69. p->s = socket(PF_INET, SOCK_DGRAM, 0);
  70. if(p->s < 0)
  71. return NATPMP_ERR_SOCKETERROR;
  72. #ifdef WIN32
  73. if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
  74. return NATPMP_ERR_FCNTLERROR;
  75. #else
  76. if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
  77. return NATPMP_ERR_FCNTLERROR;
  78. if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
  79. return NATPMP_ERR_FCNTLERROR;
  80. #endif
  81. if(forcegw) {
  82. p->gateway = forcedgw;
  83. } else {
  84. if(getdefaultgateway(&(p->gateway)) < 0)
  85. return NATPMP_ERR_CANNOTGETGATEWAY;
  86. }
  87. memset(&addr, 0, sizeof(addr));
  88. addr.sin_family = AF_INET;
  89. addr.sin_port = htons(NATPMP_PORT);
  90. addr.sin_addr.s_addr = p->gateway;
  91. if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
  92. return NATPMP_ERR_CONNECTERR;
  93. return 0;
  94. }
  95. LIBSPEC int closenatpmp(natpmp_t * p)
  96. {
  97. if(!p)
  98. return NATPMP_ERR_INVALIDARGS;
  99. if(closesocket(p->s) < 0)
  100. return NATPMP_ERR_CLOSEERR;
  101. return 0;
  102. }
  103. int sendpendingrequest(natpmp_t * p)
  104. {
  105. int r;
  106. /* struct sockaddr_in addr;*/
  107. if(!p)
  108. return NATPMP_ERR_INVALIDARGS;
  109. /* memset(&addr, 0, sizeof(addr));
  110. addr.sin_family = AF_INET;
  111. addr.sin_port = htons(NATPMP_PORT);
  112. addr.sin_addr.s_addr = p->gateway;
  113. r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
  114. (struct sockaddr *)&addr, sizeof(addr));*/
  115. r = (int)send(p->s, (const char *)p->pending_request, p->pending_request_len, 0);
  116. return (r<0) ? NATPMP_ERR_SENDERR : r;
  117. }
  118. int sendnatpmprequest(natpmp_t * p)
  119. {
  120. int n;
  121. if(!p)
  122. return NATPMP_ERR_INVALIDARGS;
  123. /* TODO : check if no request is already pending */
  124. p->has_pending_request = 1;
  125. p->try_number = 1;
  126. n = sendpendingrequest(p);
  127. gettimeofday(&p->retry_time, NULL); // check errors !
  128. p->retry_time.tv_usec += 250000; /* add 250ms */
  129. if(p->retry_time.tv_usec >= 1000000) {
  130. p->retry_time.tv_usec -= 1000000;
  131. p->retry_time.tv_sec++;
  132. }
  133. return n;
  134. }
  135. LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
  136. {
  137. struct timeval now;
  138. if(!p || !timeout)
  139. return NATPMP_ERR_INVALIDARGS;
  140. if(!p->has_pending_request)
  141. return NATPMP_ERR_NOPENDINGREQ;
  142. if(gettimeofday(&now, NULL) < 0)
  143. return NATPMP_ERR_GETTIMEOFDAYERR;
  144. timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
  145. timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
  146. if(timeout->tv_usec < 0) {
  147. timeout->tv_usec += 1000000;
  148. timeout->tv_sec--;
  149. }
  150. return 0;
  151. }
  152. LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
  153. {
  154. if(!p)
  155. return NATPMP_ERR_INVALIDARGS;
  156. //static const unsigned char request[] = { 0, 0 };
  157. p->pending_request[0] = 0;
  158. p->pending_request[1] = 0;
  159. p->pending_request_len = 2;
  160. // TODO: return 0 instead of sizeof(request) ??
  161. return sendnatpmprequest(p);
  162. }
  163. LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
  164. uint16_t privateport, uint16_t publicport,
  165. uint32_t lifetime)
  166. {
  167. if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
  168. return NATPMP_ERR_INVALIDARGS;
  169. p->pending_request[0] = 0;
  170. p->pending_request[1] = protocol;
  171. p->pending_request[2] = 0;
  172. p->pending_request[3] = 0;
  173. /* break strict-aliasing rules :
  174. *((uint16_t *)(p->pending_request + 4)) = htons(privateport); */
  175. p->pending_request[4] = (privateport >> 8) & 0xff;
  176. p->pending_request[5] = privateport & 0xff;
  177. /* break stric-aliasing rules :
  178. *((uint16_t *)(p->pending_request + 6)) = htons(publicport); */
  179. p->pending_request[6] = (publicport >> 8) & 0xff;
  180. p->pending_request[7] = publicport & 0xff;
  181. /* break stric-aliasing rules :
  182. *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); */
  183. p->pending_request[8] = (lifetime >> 24) & 0xff;
  184. p->pending_request[9] = (lifetime >> 16) & 0xff;
  185. p->pending_request[10] = (lifetime >> 8) & 0xff;
  186. p->pending_request[11] = lifetime & 0xff;
  187. p->pending_request_len = 12;
  188. return sendnatpmprequest(p);
  189. }
  190. LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
  191. {
  192. unsigned char buf[16];
  193. struct sockaddr_in addr;
  194. socklen_t addrlen = sizeof(addr);
  195. int n;
  196. if(!p)
  197. return NATPMP_ERR_INVALIDARGS;
  198. n = recvfrom(p->s, (char *)buf, sizeof(buf), 0,
  199. (struct sockaddr *)&addr, &addrlen);
  200. if(n<0)
  201. #ifdef WIN32
  202. switch(WSAGetLastError()) {
  203. #else
  204. switch(errno) {
  205. #endif
  206. /*case EAGAIN:*/
  207. case EWOULDBLOCK:
  208. n = NATPMP_TRYAGAIN;
  209. break;
  210. case ECONNREFUSED:
  211. n = NATPMP_ERR_NOGATEWAYSUPPORT;
  212. break;
  213. default:
  214. n = NATPMP_ERR_RECVFROM;
  215. }
  216. /* check that addr is correct (= gateway) */
  217. else if(addr.sin_addr.s_addr != p->gateway)
  218. n = NATPMP_ERR_WRONGPACKETSOURCE;
  219. else {
  220. response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
  221. response->epoch = ntohl(*((uint32_t *)(buf + 4)));
  222. if(buf[0] != 0)
  223. n = NATPMP_ERR_UNSUPPORTEDVERSION;
  224. else if(buf[1] < 128 || buf[1] > 130)
  225. n = NATPMP_ERR_UNSUPPORTEDOPCODE;
  226. else if(response->resultcode != 0) {
  227. switch(response->resultcode) {
  228. case 1:
  229. n = NATPMP_ERR_UNSUPPORTEDVERSION;
  230. break;
  231. case 2:
  232. n = NATPMP_ERR_NOTAUTHORIZED;
  233. break;
  234. case 3:
  235. n = NATPMP_ERR_NETWORKFAILURE;
  236. break;
  237. case 4:
  238. n = NATPMP_ERR_OUTOFRESOURCES;
  239. break;
  240. case 5:
  241. n = NATPMP_ERR_UNSUPPORTEDOPCODE;
  242. break;
  243. default:
  244. n = NATPMP_ERR_UNDEFINEDERROR;
  245. }
  246. } else {
  247. response->type = buf[1] & 0x7f;
  248. if(buf[1] == 128)
  249. //response->publicaddress.addr = *((uint32_t *)(buf + 8));
  250. response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
  251. else {
  252. response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
  253. response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
  254. response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
  255. }
  256. n = 0;
  257. }
  258. }
  259. return n;
  260. }
  261. int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
  262. {
  263. int n;
  264. if(!p || !response)
  265. return NATPMP_ERR_INVALIDARGS;
  266. if(!p->has_pending_request)
  267. return NATPMP_ERR_NOPENDINGREQ;
  268. n = readnatpmpresponse(p, response);
  269. if(n<0) {
  270. if(n==NATPMP_TRYAGAIN) {
  271. struct timeval now;
  272. gettimeofday(&now, NULL); // check errors !
  273. if(timercmp(&now, &p->retry_time, >=)) {
  274. int delay, r;
  275. if(p->try_number >= 9) {
  276. return NATPMP_ERR_NOGATEWAYSUPPORT;
  277. }
  278. /*printf("retry! %d\n", p->try_number);*/
  279. delay = 250 * (1<<p->try_number); // ms
  280. /*for(i=0; i<p->try_number; i++)
  281. delay += delay;*/
  282. p->retry_time.tv_sec += (delay / 1000);
  283. p->retry_time.tv_usec += (delay % 1000) * 1000;
  284. if(p->retry_time.tv_usec >= 1000000) {
  285. p->retry_time.tv_usec -= 1000000;
  286. p->retry_time.tv_sec++;
  287. }
  288. p->try_number++;
  289. r = sendpendingrequest(p);
  290. if(r<0)
  291. return r;
  292. }
  293. }
  294. } else {
  295. p->has_pending_request = 0;
  296. }
  297. return n;
  298. }
  299. #ifdef ENABLE_STRNATPMPERR
  300. LIBSPEC const char * strnatpmperr(int r)
  301. {
  302. const char * s;
  303. switch(r) {
  304. case NATPMP_ERR_INVALIDARGS:
  305. s = "invalid arguments";
  306. break;
  307. case NATPMP_ERR_SOCKETERROR:
  308. s = "socket() failed";
  309. break;
  310. case NATPMP_ERR_CANNOTGETGATEWAY:
  311. s = "cannot get default gateway ip address";
  312. break;
  313. case NATPMP_ERR_CLOSEERR:
  314. #ifdef WIN32
  315. s = "closesocket() failed";
  316. #else
  317. s = "close() failed";
  318. #endif
  319. break;
  320. case NATPMP_ERR_RECVFROM:
  321. s = "recvfrom() failed";
  322. break;
  323. case NATPMP_ERR_NOPENDINGREQ:
  324. s = "no pending request";
  325. break;
  326. case NATPMP_ERR_NOGATEWAYSUPPORT:
  327. s = "the gateway does not support nat-pmp";
  328. break;
  329. case NATPMP_ERR_CONNECTERR:
  330. s = "connect() failed";
  331. break;
  332. case NATPMP_ERR_WRONGPACKETSOURCE:
  333. s = "packet not received from the default gateway";
  334. break;
  335. case NATPMP_ERR_SENDERR:
  336. s = "send() failed";
  337. break;
  338. case NATPMP_ERR_FCNTLERROR:
  339. s = "fcntl() failed";
  340. break;
  341. case NATPMP_ERR_GETTIMEOFDAYERR:
  342. s = "gettimeofday() failed";
  343. break;
  344. case NATPMP_ERR_UNSUPPORTEDVERSION:
  345. s = "unsupported nat-pmp version error from server";
  346. break;
  347. case NATPMP_ERR_UNSUPPORTEDOPCODE:
  348. s = "unsupported nat-pmp opcode error from server";
  349. break;
  350. case NATPMP_ERR_UNDEFINEDERROR:
  351. s = "undefined nat-pmp server error";
  352. break;
  353. case NATPMP_ERR_NOTAUTHORIZED:
  354. s = "not authorized";
  355. break;
  356. case NATPMP_ERR_NETWORKFAILURE:
  357. s = "network failure";
  358. break;
  359. case NATPMP_ERR_OUTOFRESOURCES:
  360. s = "nat-pmp server out of resources";
  361. break;
  362. default:
  363. s = "Unknown libnatpmp error";
  364. }
  365. return s;
  366. }
  367. #endif