natpmp.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. #ifdef __linux__
  18. #ifndef _DEFAULT_SOURCE
  19. #define _DEFAULT_SOURCE
  20. #endif
  21. #define _BSD_SOURCE 1
  22. #endif
  23. #include <string.h>
  24. #include <time.h>
  25. #ifndef _MSC_VER
  26. #include <sys/time.h>
  27. #endif
  28. #ifdef WIN32
  29. #include <winsock2.h>
  30. #include <Ws2tcpip.h>
  31. #include <io.h>
  32. #ifndef EWOULDBLOCK
  33. #define EWOULDBLOCK WSAEWOULDBLOCK
  34. #endif
  35. #ifndef ECONNREFUSED
  36. #define ECONNREFUSED WSAECONNREFUSED
  37. #endif
  38. static int gettimeofday(struct timeval* p, void* tz /* IGNORED */) {
  39. union {
  40. long long ns100; /*time since 1 Jan 1601 in 100ns units */
  41. FILETIME ft;
  42. } _now;
  43. GetSystemTimeAsFileTime( &(_now.ft) );
  44. p->tv_usec=(long)((_now.ns100 / 10LL) % 1000000LL );
  45. p->tv_sec= (long)((_now.ns100-(116444736000000000LL))/10000000LL);
  46. return 0;
  47. }
  48. #else
  49. #include <errno.h>
  50. #include <unistd.h>
  51. #include <fcntl.h>
  52. #include <sys/types.h>
  53. #include <sys/socket.h>
  54. #define closesocket close
  55. #endif
  56. #include "natpmp.h"
  57. #include "getgateway.h"
  58. int initnatpmp(natpmp_t * p)
  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(getdefaultgateway(&(p->gateway)) < 0)
  82. return NATPMP_ERR_CANNOTGETGATEWAY;
  83. memset(&addr, 0, sizeof(addr));
  84. addr.sin_family = AF_INET;
  85. addr.sin_port = htons(NATPMP_PORT);
  86. addr.sin_addr.s_addr = p->gateway;
  87. if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
  88. return NATPMP_ERR_CONNECTERR;
  89. return 0;
  90. }
  91. int closenatpmp(natpmp_t * p)
  92. {
  93. if(!p)
  94. return NATPMP_ERR_INVALIDARGS;
  95. if(closesocket(p->s) < 0)
  96. return NATPMP_ERR_CLOSEERR;
  97. return 0;
  98. }
  99. int sendpendingrequest(natpmp_t * p)
  100. {
  101. int r;
  102. /* struct sockaddr_in addr;*/
  103. if(!p)
  104. return NATPMP_ERR_INVALIDARGS;
  105. /* memset(&addr, 0, sizeof(addr));
  106. addr.sin_family = AF_INET;
  107. addr.sin_port = htons(NATPMP_PORT);
  108. addr.sin_addr.s_addr = p->gateway;
  109. r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
  110. (struct sockaddr *)&addr, sizeof(addr));*/
  111. r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
  112. return (r<0) ? NATPMP_ERR_SENDERR : r;
  113. }
  114. int sendnatpmprequest(natpmp_t * p)
  115. {
  116. int n;
  117. if(!p)
  118. return NATPMP_ERR_INVALIDARGS;
  119. /* TODO : check if no request is allready pending */
  120. p->has_pending_request = 1;
  121. p->try_number = 1;
  122. n = sendpendingrequest(p);
  123. gettimeofday(&p->retry_time, NULL); // check errors !
  124. p->retry_time.tv_usec += 250000; /* add 250ms */
  125. if(p->retry_time.tv_usec >= 1000000) {
  126. p->retry_time.tv_usec -= 1000000;
  127. p->retry_time.tv_sec++;
  128. }
  129. return n;
  130. }
  131. int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
  132. {
  133. struct timeval now;
  134. if(!p || !timeout)
  135. return NATPMP_ERR_INVALIDARGS;
  136. if(!p->has_pending_request)
  137. return NATPMP_ERR_NOPENDINGREQ;
  138. if(gettimeofday(&now, NULL) < 0)
  139. return NATPMP_ERR_GETTIMEOFDAYERR;
  140. timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
  141. timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
  142. if(timeout->tv_usec < 0) {
  143. timeout->tv_usec += 1000000;
  144. timeout->tv_sec--;
  145. }
  146. return 0;
  147. }
  148. int sendpublicaddressrequest(natpmp_t * p)
  149. {
  150. if(!p)
  151. return NATPMP_ERR_INVALIDARGS;
  152. //static const unsigned char request[] = { 0, 0 };
  153. p->pending_request[0] = 0;
  154. p->pending_request[1] = 0;
  155. p->pending_request_len = 2;
  156. // TODO: return 0 instead of sizeof(request) ??
  157. return sendnatpmprequest(p);
  158. }
  159. int sendnewportmappingrequest(natpmp_t * p, int protocol,
  160. uint16_t privateport, uint16_t publicport,
  161. uint32_t lifetime)
  162. {
  163. uint16_t *n;
  164. uint32_t *m;
  165. if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
  166. return NATPMP_ERR_INVALIDARGS;
  167. p->pending_request[0] = 0;
  168. p->pending_request[1] = (char)protocol;
  169. p->pending_request[2] = 0;
  170. p->pending_request[3] = 0;
  171. n = (uint16_t *)(p->pending_request + 4); *n = htons(privateport);
  172. n = (uint16_t *)(p->pending_request + 6); *n = htons(publicport);
  173. m = (uint32_t *)(p->pending_request + 8); *m = htonl(lifetime);
  174. //*((uint16_t *)(p->pending_request + 4)) = htons(privateport);
  175. //*((uint16_t *)(p->pending_request + 6)) = htons(publicport);
  176. //*((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
  177. p->pending_request_len = 12;
  178. return sendnatpmprequest(p);
  179. }
  180. int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
  181. {
  182. unsigned char buf[16];
  183. struct sockaddr_in addr = {0};
  184. socklen_t addrlen = sizeof(addr);
  185. int n;
  186. if(!p)
  187. return NATPMP_ERR_INVALIDARGS;
  188. n = recvfrom(p->s, (char *)buf, sizeof(buf), 0,
  189. (struct sockaddr *)&addr, &addrlen);
  190. if(n<0)
  191. switch(errno) {
  192. /*case EAGAIN:*/
  193. case EWOULDBLOCK:
  194. n = NATPMP_TRYAGAIN;
  195. break;
  196. case ECONNREFUSED:
  197. n = NATPMP_ERR_NOGATEWAYSUPPORT;
  198. break;
  199. default:
  200. n = NATPMP_ERR_RECVFROM;
  201. }
  202. /* check that addr is correct (= gateway) */
  203. else if(addr.sin_addr.s_addr != p->gateway)
  204. n = NATPMP_ERR_WRONGPACKETSOURCE;
  205. else {
  206. response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
  207. response->epoch = ntohl(*((uint32_t *)(buf + 4)));
  208. if(buf[0] != 0)
  209. n = NATPMP_ERR_UNSUPPORTEDVERSION;
  210. else if(buf[1] < 128 || buf[1] > 130)
  211. n = NATPMP_ERR_UNSUPPORTEDOPCODE;
  212. else if(response->resultcode != 0) {
  213. switch(response->resultcode) {
  214. case 1:
  215. n = NATPMP_ERR_UNSUPPORTEDVERSION;
  216. break;
  217. case 2:
  218. n = NATPMP_ERR_NOTAUTHORIZED;
  219. break;
  220. case 3:
  221. n = NATPMP_ERR_NETWORKFAILURE;
  222. break;
  223. case 4:
  224. n = NATPMP_ERR_OUTOFRESOURCES;
  225. break;
  226. case 5:
  227. n = NATPMP_ERR_UNSUPPORTEDOPCODE;
  228. break;
  229. default:
  230. n = NATPMP_ERR_UNDEFINEDERROR;
  231. }
  232. } else {
  233. response->type = buf[1] & 0x7f;
  234. if(buf[1] == 128)
  235. //response->publicaddress.addr = *((uint32_t *)(buf + 8));
  236. response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
  237. else {
  238. response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
  239. response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
  240. response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
  241. }
  242. n = 0;
  243. }
  244. }
  245. return n;
  246. }
  247. int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
  248. {
  249. int n;
  250. if(!p || !response)
  251. return NATPMP_ERR_INVALIDARGS;
  252. if(!p->has_pending_request)
  253. return NATPMP_ERR_NOPENDINGREQ;
  254. n = readnatpmpresponse(p, response);
  255. if(n<0) {
  256. if(n==NATPMP_TRYAGAIN) {
  257. struct timeval now;
  258. gettimeofday(&now, NULL); // check errors !
  259. if(timercmp(&now, &p->retry_time, >=)) {
  260. int delay, r;
  261. if(p->try_number >= 9) {
  262. return NATPMP_ERR_NOGATEWAYSUPPORT;
  263. }
  264. /*printf("retry! %d\n", p->try_number);*/
  265. delay = 250 * (1<<p->try_number); // ms
  266. /*for(i=0; i<p->try_number; i++)
  267. delay += delay;*/
  268. p->retry_time.tv_sec += (delay / 1000);
  269. p->retry_time.tv_usec += (delay % 1000) * 1000;
  270. if(p->retry_time.tv_usec >= 1000000) {
  271. p->retry_time.tv_usec -= 1000000;
  272. p->retry_time.tv_sec++;
  273. }
  274. p->try_number++;
  275. r = sendpendingrequest(p);
  276. if(r<0)
  277. return r;
  278. }
  279. }
  280. } else {
  281. p->has_pending_request = 0;
  282. }
  283. return n;
  284. }
  285. #ifdef ENABLE_STRNATPMPERR
  286. const char * strnatpmperr(int r)
  287. {
  288. const char * s;
  289. switch(r) {
  290. case NATPMP_ERR_INVALIDARGS:
  291. s = "invalid arguments";
  292. break;
  293. case NATPMP_ERR_SOCKETERROR:
  294. s = "socket() failed";
  295. break;
  296. case NATPMP_ERR_CANNOTGETGATEWAY:
  297. s = "cannot get default gateway ip address";
  298. break;
  299. case NATPMP_ERR_CLOSEERR:
  300. #ifdef WIN32
  301. s = "closesocket() failed";
  302. #else
  303. s = "close() failed";
  304. #endif
  305. break;
  306. case NATPMP_ERR_RECVFROM:
  307. s = "recvfrom() failed";
  308. break;
  309. case NATPMP_ERR_NOPENDINGREQ:
  310. s = "no pending request";
  311. break;
  312. case NATPMP_ERR_NOGATEWAYSUPPORT:
  313. s = "the gateway does not support nat-pmp";
  314. break;
  315. case NATPMP_ERR_CONNECTERR:
  316. s = "connect() failed";
  317. break;
  318. case NATPMP_ERR_WRONGPACKETSOURCE:
  319. s = "packet not received from the default gateway";
  320. break;
  321. case NATPMP_ERR_SENDERR:
  322. s = "send() failed";
  323. break;
  324. case NATPMP_ERR_FCNTLERROR:
  325. s = "fcntl() failed";
  326. break;
  327. case NATPMP_ERR_GETTIMEOFDAYERR:
  328. s = "gettimeofday() failed";
  329. break;
  330. case NATPMP_ERR_UNSUPPORTEDVERSION:
  331. s = "unsupported nat-pmp version error from server";
  332. break;
  333. case NATPMP_ERR_UNSUPPORTEDOPCODE:
  334. s = "unsupported nat-pmp opcode error from server";
  335. break;
  336. case NATPMP_ERR_UNDEFINEDERROR:
  337. s = "undefined nat-pmp server error";
  338. break;
  339. case NATPMP_ERR_NOTAUTHORIZED:
  340. s = "not authorized";
  341. break;
  342. case NATPMP_ERR_NETWORKFAILURE:
  343. s = "network failure";
  344. break;
  345. case NATPMP_ERR_OUTOFRESOURCES:
  346. s = "nat-pmp server out of resources";
  347. break;
  348. default:
  349. s = "Unknown libnatpmp error";
  350. }
  351. return s;
  352. }
  353. #endif