gnunet-helper-nat-server-windows.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2010 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file src/nat/gnunet-helper-nat-server-windows.c
  18. * @brief Windows tool to help bypass NATs using ICMP method
  19. * This code will work under W32 only
  20. * @author Christian Grothoff
  21. *
  22. * This program will send ONE ICMP message every 500 ms RAW sockets
  23. * to a DUMMY IP address and also listens for ICMP replies. Since
  24. * it uses RAW sockets, it must be run as an administrative user.
  25. * In order to keep the security risk of the resulting binary
  26. * minimal, the program ONLY opens the two RAW sockets with administrative
  27. * privileges, then drops them and only then starts to process
  28. * command line arguments. The code also does not link against
  29. * any shared libraries (except libc) and is strictly minimal
  30. * (except for checking for errors). The following list of people
  31. * have reviewed this code and considered it safe since the last
  32. * modification (if you reviewed it, please have your name added
  33. * to the list):
  34. *
  35. * - Nathan Evans
  36. * - Christian Grothoff
  37. */
  38. #define _GNU_SOURCE
  39. /* Instead of including gnunet_common.h */
  40. #define GNUNET_memcpy(dst,src,n) do { if (0 != n) { (void) memcpy (dst,src,n); } } while (0)
  41. #define FD_SETSIZE 1024
  42. #include <winsock2.h>
  43. #include <ws2tcpip.h>
  44. #include <sys/time.h>
  45. #include <sys/types.h>
  46. #include <unistd.h>
  47. #include <stdio.h>
  48. #include <string.h>
  49. #include <errno.h>
  50. #include <stdlib.h>
  51. #include <stdint.h>
  52. #include <time.h>
  53. /**
  54. * Should we print some debug output?
  55. */
  56. #define VERBOSE 0
  57. /**
  58. * Must match IP given in the client.
  59. */
  60. #define DUMMY_IP "192.0.2.86"
  61. /**
  62. * Default Port
  63. */
  64. #define NAT_TRAV_PORT 22225
  65. /**
  66. * Must match packet ID used by gnunet-helper-nat-client.c
  67. */
  68. #define PACKET_ID 256
  69. /**
  70. * TTL to use for our outgoing messages.
  71. */
  72. #define IPDEFTTL 64
  73. #define ICMP_ECHO 8
  74. #define ICMP_TIME_EXCEEDED 11
  75. /**
  76. * How often do we send our ICMP messages to receive replies?
  77. */
  78. #define ICMP_SEND_FREQUENCY_MS 500
  79. /**
  80. * IPv4 header.
  81. */
  82. struct ip_header
  83. {
  84. /**
  85. * Version (4 bits) + Internet header length (4 bits)
  86. */
  87. uint8_t vers_ihl;
  88. /**
  89. * Type of service
  90. */
  91. uint8_t tos;
  92. /**
  93. * Total length
  94. */
  95. uint16_t pkt_len;
  96. /**
  97. * Identification
  98. */
  99. uint16_t id;
  100. /**
  101. * Flags (3 bits) + Fragment offset (13 bits)
  102. */
  103. uint16_t flags_frag_offset;
  104. /**
  105. * Time to live
  106. */
  107. uint8_t ttl;
  108. /**
  109. * Protocol
  110. */
  111. uint8_t proto;
  112. /**
  113. * Header checksum
  114. */
  115. uint16_t checksum;
  116. /**
  117. * Source address
  118. */
  119. uint32_t src_ip;
  120. /**
  121. * Destination address
  122. */
  123. uint32_t dst_ip;
  124. };
  125. /**
  126. * Format of ICMP packet.
  127. */
  128. struct icmp_ttl_exceeded_header
  129. {
  130. uint8_t type;
  131. uint8_t code;
  132. uint16_t checksum;
  133. uint32_t unused;
  134. /* followed by original payload */
  135. };
  136. struct icmp_echo_header
  137. {
  138. uint8_t type;
  139. uint8_t code;
  140. uint16_t checksum;
  141. uint32_t reserved;
  142. };
  143. /**
  144. * Beginning of UDP packet.
  145. */
  146. struct udp_header
  147. {
  148. uint16_t src_port;
  149. uint16_t dst_port;
  150. uint16_t length;
  151. uint16_t crc;
  152. };
  153. /**
  154. * Will this binary be run in permissions testing mode?
  155. */
  156. static boolean privilege_testing = FALSE;
  157. /**
  158. * Socket we use to receive "fake" ICMP replies.
  159. */
  160. static SOCKET icmpsock;
  161. /**
  162. * Socket we use to send our ICMP requests.
  163. */
  164. static SOCKET rawsock;
  165. /**
  166. * Socket we use to send our UDP requests.
  167. */
  168. static SOCKET udpsock;
  169. /**
  170. * Target "dummy" address.
  171. */
  172. static struct in_addr dummy;
  173. /**
  174. * CRC-16 for IP/ICMP headers.
  175. *
  176. * @param data what to calculate the CRC over
  177. * @param bytes number of bytes in data (must be multiple of 2)
  178. * @return the CRC 16.
  179. */
  180. static uint16_t
  181. calc_checksum (const uint16_t * data, unsigned int bytes)
  182. {
  183. uint32_t sum;
  184. unsigned int i;
  185. sum = 0;
  186. for (i = 0; i < bytes / 2; i++)
  187. sum += data[i];
  188. sum = (sum & 0xffff) + (sum >> 16);
  189. sum = htons (0xffff - sum);
  190. return sum;
  191. }
  192. /**
  193. * Convert IPv4 address from text to binary form.
  194. *
  195. * @param af address family
  196. * @param cp the address to print
  197. * @param buf where to write the address result
  198. * @return 1 on success
  199. */
  200. static int
  201. inet_pton (int af, const char *cp, struct in_addr *buf)
  202. {
  203. buf->s_addr = inet_addr (cp);
  204. if (buf->s_addr == INADDR_NONE)
  205. {
  206. fprintf (stderr, "Error %d handling address %s", WSAGetLastError (), cp);
  207. return 0;
  208. }
  209. return 1;
  210. }
  211. /**
  212. * Send an ICMP message to the dummy IP.
  213. *
  214. * @param my_ip source address (our ip address)
  215. */
  216. static void
  217. send_icmp_echo (const struct in_addr *my_ip)
  218. {
  219. char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
  220. struct icmp_echo_header icmp_echo;
  221. struct ip_header ip_pkt;
  222. struct sockaddr_in dst;
  223. size_t off;
  224. int err;
  225. off = 0;
  226. ip_pkt.vers_ihl = 0x45;
  227. ip_pkt.tos = 0;
  228. ip_pkt.pkt_len = htons (sizeof (packet));
  229. ip_pkt.id = htons (PACKET_ID);
  230. ip_pkt.flags_frag_offset = 0;
  231. ip_pkt.ttl = IPDEFTTL;
  232. ip_pkt.proto = IPPROTO_ICMP;
  233. ip_pkt.checksum = 0;
  234. ip_pkt.src_ip = my_ip->s_addr;
  235. ip_pkt.dst_ip = dummy.s_addr;
  236. ip_pkt.checksum =
  237. htons (calc_checksum ((uint16_t *) & ip_pkt, sizeof (struct ip_header)));
  238. GNUNET_memcpy (&packet[off], &ip_pkt, sizeof (struct ip_header));
  239. off += sizeof (struct ip_header);
  240. icmp_echo.type = ICMP_ECHO;
  241. icmp_echo.code = 0;
  242. icmp_echo.reserved = 0;
  243. icmp_echo.checksum = 0;
  244. icmp_echo.checksum =
  245. htons (calc_checksum
  246. ((uint16_t *) & icmp_echo, sizeof (struct icmp_echo_header)));
  247. GNUNET_memcpy (&packet[off], &icmp_echo, sizeof (struct icmp_echo_header));
  248. off += sizeof (struct icmp_echo_header);
  249. memset (&dst, 0, sizeof (dst));
  250. dst.sin_family = AF_INET;
  251. dst.sin_addr = dummy;
  252. err =
  253. sendto (rawsock, packet, off, 0, (struct sockaddr *) &dst, sizeof (dst));
  254. if (err < 0)
  255. {
  256. #if VERBOSE
  257. fprintf (stderr, "sendto failed: %s\n", strerror (errno));
  258. #endif
  259. }
  260. else if (err != off)
  261. {
  262. fprintf (stderr, "Error: partial send of ICMP message\n");
  263. }
  264. }
  265. /**
  266. * Send a UDP message to the dummy IP.
  267. */
  268. static void
  269. send_udp ()
  270. {
  271. struct sockaddr_in dst;
  272. ssize_t err;
  273. memset (&dst, 0, sizeof (dst));
  274. dst.sin_family = AF_INET;
  275. dst.sin_addr = dummy;
  276. dst.sin_port = htons (NAT_TRAV_PORT);
  277. err = sendto (udpsock, NULL, 0, 0, (struct sockaddr *) &dst, sizeof (dst));
  278. if (err < 0)
  279. {
  280. #if VERBOSE
  281. fprintf (stderr, "sendto failed: %s\n", strerror (errno));
  282. #endif
  283. }
  284. else if (0 != err)
  285. {
  286. fprintf (stderr, "Error: partial send of ICMP message\n");
  287. }
  288. }
  289. /**
  290. * We've received an ICMP response. Process it.
  291. */
  292. static void
  293. process_icmp_response ()
  294. {
  295. char buf[65536];
  296. ssize_t have;
  297. struct in_addr source_ip;
  298. struct ip_header ip_pkt;
  299. struct icmp_ttl_exceeded_header icmp_ttl;
  300. struct icmp_echo_header icmp_echo;
  301. struct udp_header udp_pkt;
  302. size_t off;
  303. uint16_t port;
  304. DWORD ssize;
  305. have = read (icmpsock, buf, sizeof (buf));
  306. if (have == -1)
  307. {
  308. fprintf (stderr, "Error reading raw socket: %s\n", strerror (errno));
  309. return;
  310. }
  311. #if VERBOSE
  312. fprintf (stderr, "Received message of %u bytes\n", (unsigned int) have);
  313. #endif
  314. if (have <
  315. (ssize_t) (sizeof (struct ip_header) +
  316. sizeof (struct icmp_ttl_exceeded_header) +
  317. sizeof (struct ip_header)))
  318. {
  319. /* malformed */
  320. return;
  321. }
  322. off = 0;
  323. GNUNET_memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
  324. off += sizeof (struct ip_header);
  325. GNUNET_memcpy (&source_ip, &ip_pkt.src_ip, sizeof (source_ip));
  326. GNUNET_memcpy (&icmp_ttl, &buf[off], sizeof (struct icmp_ttl_exceeded_header));
  327. off += sizeof (struct icmp_ttl_exceeded_header);
  328. if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
  329. {
  330. /* different type than what we want */
  331. return;
  332. }
  333. /* skip 2nd IP header */
  334. GNUNET_memcpy (&ip_pkt, &buf[off], sizeof (struct ip_header));
  335. off += sizeof (struct ip_header);
  336. switch (ip_pkt.proto)
  337. {
  338. case IPPROTO_ICMP:
  339. if (have !=
  340. (sizeof (struct ip_header) * 2 +
  341. sizeof (struct icmp_ttl_exceeded_header) +
  342. sizeof (struct icmp_echo_header)))
  343. {
  344. /* malformed */
  345. return;
  346. }
  347. /* grab ICMP ECHO content */
  348. GNUNET_memcpy (&icmp_echo, &buf[off], sizeof (struct icmp_echo_header));
  349. port = (uint16_t) ntohl (icmp_echo.reserved);
  350. break;
  351. case IPPROTO_UDP:
  352. if (have !=
  353. (sizeof (struct ip_header) * 2 +
  354. sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct udp_header)))
  355. {
  356. /* malformed */
  357. return;
  358. }
  359. /* grab UDP content */
  360. GNUNET_memcpy (&udp_pkt, &buf[off], sizeof (struct udp_header));
  361. port = ntohs (udp_pkt.length);
  362. break;
  363. default:
  364. /* different type than what we want */
  365. return;
  366. }
  367. ssize = sizeof (buf);
  368. WSAAddressToString ((LPSOCKADDR) & source_ip, sizeof (source_ip), NULL, buf,
  369. &ssize);
  370. if (port == 0)
  371. fprintf (stdout, "%s\n", buf);
  372. else
  373. fprintf (stdout, "%s:%u\n", buf, (unsigned int) port);
  374. fflush (stdout);
  375. }
  376. /**
  377. * Create an ICMP raw socket for reading.
  378. *
  379. * @return INVALID_SOCKET on error
  380. */
  381. static SOCKET
  382. make_icmp_socket ()
  383. {
  384. SOCKET ret;
  385. ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
  386. if (INVALID_SOCKET == ret)
  387. {
  388. fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
  389. return INVALID_SOCKET;
  390. }
  391. return ret;
  392. }
  393. /**
  394. * Create an ICMP raw socket for writing.
  395. *
  396. * @return INVALID_SOCKET on error
  397. */
  398. static SOCKET
  399. make_raw_socket ()
  400. {
  401. DWORD bOptVal = TRUE;
  402. int bOptLen = sizeof (bOptVal);
  403. rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
  404. if (INVALID_SOCKET == rawsock)
  405. {
  406. fprintf (stderr, "Error opening RAW socket: %s\n", strerror (errno));
  407. return INVALID_SOCKET;
  408. }
  409. if (0 !=
  410. setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &bOptVal,
  411. bOptLen))
  412. {
  413. fprintf (stderr, "Error setting SO_BROADCAST to ON: %s\n",
  414. strerror (errno));
  415. closesocket (rawsock);
  416. return INVALID_SOCKET;
  417. }
  418. if (0 !=
  419. setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &bOptVal, bOptLen))
  420. {
  421. fprintf (stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror (errno));
  422. closesocket (rawsock);
  423. return INVALID_SOCKET;
  424. }
  425. return rawsock;
  426. }
  427. /**
  428. * Create a UDP socket for writing.
  429. *
  430. * @param my_ip source address (our ip address)
  431. * @return INVALID_SOCKET on error
  432. */
  433. static SOCKET
  434. make_udp_socket (const struct in_addr *my_ip)
  435. {
  436. SOCKET ret;
  437. struct sockaddr_in addr;
  438. ret = socket (AF_INET, SOCK_DGRAM, 0);
  439. if (INVALID_SOCKET == ret)
  440. {
  441. fprintf (stderr, "Error opening UDP socket: %s\n", strerror (errno));
  442. return INVALID_SOCKET;
  443. }
  444. memset (&addr, 0, sizeof (addr));
  445. addr.sin_family = AF_INET;
  446. addr.sin_addr = *my_ip;
  447. addr.sin_port = htons (NAT_TRAV_PORT);
  448. if (0 != bind (ret, (struct sockaddr *) &addr, sizeof (addr)))
  449. {
  450. fprintf (stderr, "Error binding UDP socket to port %u: %s\n", NAT_TRAV_PORT,
  451. strerror (errno));
  452. /* likely problematic, but not certain, try to continue */
  453. }
  454. return ret;
  455. }
  456. int
  457. main (int argc, char *const *argv)
  458. {
  459. struct in_addr external;
  460. fd_set rs;
  461. struct timeval tv;
  462. WSADATA wsaData;
  463. unsigned int alt = 0;
  464. if ( (argc > 1) && (0 != strcmp (argv[1], "-d")))
  465. {
  466. privilege_testing = TRUE;
  467. fprintf (stderr,
  468. "%s",
  469. "DEBUG: Running binary in privilege testing mode.");
  470. argv++;
  471. argc--;
  472. }
  473. if (2 != argc)
  474. {
  475. fprintf (stderr,
  476. "This program must be started with our (internal NAT) IP as the only argument.\n");
  477. return 1;
  478. }
  479. if (1 != inet_pton (AF_INET, argv[1], &external))
  480. {
  481. fprintf (stderr, "Error parsing IPv4 address: %s, error %s\n", argv[1],
  482. strerror (errno));
  483. return 1;
  484. }
  485. if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
  486. {
  487. fprintf (stderr, "Internal error converting dummy IP to binary.\n");
  488. return 2;
  489. }
  490. if (WSAStartup (MAKEWORD (2, 1), &wsaData) != 0)
  491. {
  492. fprintf (stderr, "Failed to find Winsock 2.1 or better.\n");
  493. return 2;
  494. }
  495. if (INVALID_SOCKET == (icmpsock = make_icmp_socket ()))
  496. {
  497. return 3;
  498. }
  499. if (INVALID_SOCKET == (make_raw_socket ()))
  500. {
  501. closesocket (icmpsock);
  502. return 3;
  503. }
  504. if (INVALID_SOCKET == (udpsock = make_udp_socket (&external)))
  505. {
  506. closesocket (icmpsock);
  507. closesocket (rawsock);
  508. return 3;
  509. }
  510. while ( ! privilege_testing)
  511. {
  512. FD_ZERO (&rs);
  513. FD_SET (icmpsock, &rs);
  514. tv.tv_sec = 0;
  515. tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
  516. if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
  517. {
  518. if (errno == EINTR)
  519. continue;
  520. fprintf (stderr, "select failed: %s\n", strerror (errno));
  521. break;
  522. }
  523. if (FD_ISSET (icmpsock, &rs))
  524. process_icmp_response ();
  525. if (0 == (++alt % 2))
  526. send_icmp_echo (&external);
  527. else
  528. send_udp ();
  529. }
  530. /* select failed (internal error or OS out of resources) */
  531. closesocket (icmpsock);
  532. closesocket (rawsock);
  533. closesocket (udpsock);
  534. WSACleanup ();
  535. if (privilege_testing)
  536. return 0;
  537. return 4;
  538. }
  539. /* end of gnunet-helper-nat-server-windows.c */