gnunet-service-nat_helper.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2009, 2010, 2011, 2016 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 nat/gnunet-service-nat_helper.c
  18. * @brief runs the gnunet-helper-nat-server
  19. * @author Milan Bouchet-Valat
  20. * @author Christian Grothoff
  21. */
  22. #include "platform.h"
  23. #include "gnunet_util_lib.h"
  24. #include "gnunet-service-nat_helper.h"
  25. /**
  26. * Information we keep per NAT helper process.
  27. */
  28. struct HelperContext
  29. {
  30. /**
  31. * IP address we pass to the NAT helper.
  32. */
  33. struct in_addr internal_address;
  34. /**
  35. * Function to call if we receive a reversal request.
  36. */
  37. GN_ReversalCallback cb;
  38. /**
  39. * Closure for @e cb.
  40. */
  41. void *cb_cls;
  42. /**
  43. * How long do we wait for restarting a crashed gnunet-helper-nat-server?
  44. */
  45. struct GNUNET_TIME_Relative server_retry_delay;
  46. /**
  47. * ID of select gnunet-helper-nat-server stdout read task
  48. */
  49. struct GNUNET_SCHEDULER_Task *server_read_task;
  50. /**
  51. * The process id of the server process (if behind NAT)
  52. */
  53. struct GNUNET_OS_Process *server_proc;
  54. /**
  55. * stdout pipe handle for the gnunet-helper-nat-server process
  56. */
  57. struct GNUNET_DISK_PipeHandle *server_stdout;
  58. /**
  59. * stdout file handle (for reading) for the gnunet-helper-nat-server process
  60. */
  61. const struct GNUNET_DISK_FileHandle *server_stdout_handle;
  62. };
  63. /**
  64. * Task that restarts the gnunet-helper-nat-server process after a crash
  65. * after a certain delay.
  66. *
  67. * @param cls a `struct HelperContext`
  68. */
  69. static void
  70. restart_nat_server (void *cls);
  71. /**
  72. * Try again starting the helper later
  73. *
  74. * @param h context of the helper
  75. */
  76. static void
  77. try_again (struct HelperContext *h)
  78. {
  79. GNUNET_assert (NULL == h->server_read_task);
  80. h->server_retry_delay
  81. = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay);
  82. h->server_read_task
  83. = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
  84. &restart_nat_server,
  85. h);
  86. }
  87. /**
  88. * We have been notified that gnunet-helper-nat-server has written
  89. * something to stdout. Handle the output, then reschedule this
  90. * function to be called again once more is available.
  91. *
  92. * @param cls the `struct HelperContext`
  93. */
  94. static void
  95. nat_server_read (void *cls)
  96. {
  97. struct HelperContext *h = cls;
  98. char mybuf[40];
  99. ssize_t bytes;
  100. int port;
  101. const char *port_start;
  102. struct sockaddr_in sin_addr;
  103. h->server_read_task = NULL;
  104. memset (mybuf,
  105. 0,
  106. sizeof (mybuf));
  107. bytes
  108. = GNUNET_DISK_file_read (h->server_stdout_handle,
  109. mybuf,
  110. sizeof (mybuf));
  111. if (bytes < 1)
  112. {
  113. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  114. "Finished reading from server stdout with code: %d\n",
  115. (int) bytes);
  116. if (0 != GNUNET_OS_process_kill (h->server_proc,
  117. GNUNET_TERM_SIG))
  118. GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
  119. "nat",
  120. "kill");
  121. GNUNET_OS_process_wait (h->server_proc);
  122. GNUNET_OS_process_destroy (h->server_proc);
  123. h->server_proc = NULL;
  124. GNUNET_DISK_pipe_close (h->server_stdout);
  125. h->server_stdout = NULL;
  126. h->server_stdout_handle = NULL;
  127. try_again (h);
  128. return;
  129. }
  130. port_start = NULL;
  131. for (size_t i = 0; i < sizeof (mybuf); i++)
  132. {
  133. if (mybuf[i] == '\n')
  134. {
  135. mybuf[i] = '\0';
  136. break;
  137. }
  138. if ((mybuf[i] == ':') && (i + 1 < sizeof (mybuf)))
  139. {
  140. mybuf[i] = '\0';
  141. port_start = &mybuf[i + 1];
  142. }
  143. }
  144. /* construct socket address of sender */
  145. memset (&sin_addr,
  146. 0,
  147. sizeof (sin_addr));
  148. sin_addr.sin_family = AF_INET;
  149. #if HAVE_SOCKADDR_IN_SIN_LEN
  150. sin_addr.sin_len = sizeof (sin_addr);
  151. #endif
  152. if ( (NULL == port_start) ||
  153. (1 != SSCANF (port_start,
  154. "%d",
  155. &port)) ||
  156. (-1 == inet_pton (AF_INET,
  157. mybuf,
  158. &sin_addr.sin_addr)))
  159. {
  160. /* should we restart gnunet-helper-nat-server? */
  161. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  162. _("gnunet-helper-nat-server generated malformed address `%s'\n"),
  163. mybuf);
  164. h->server_read_task
  165. = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  166. h->server_stdout_handle,
  167. &nat_server_read,
  168. h);
  169. return;
  170. }
  171. sin_addr.sin_port = htons ((uint16_t) port);
  172. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  173. "gnunet-helper-nat-server read: %s:%d\n",
  174. mybuf,
  175. port);
  176. h->cb (h->cb_cls,
  177. &sin_addr);
  178. h->server_read_task
  179. = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  180. h->server_stdout_handle,
  181. &nat_server_read,
  182. h);
  183. }
  184. /**
  185. * Task that restarts the gnunet-helper-nat-server process after a crash
  186. * after a certain delay.
  187. *
  188. * @param cls a `struct HelperContext`
  189. */
  190. static void
  191. restart_nat_server (void *cls)
  192. {
  193. struct HelperContext *h = cls;
  194. char *binary;
  195. char ia[INET_ADDRSTRLEN];
  196. h->server_read_task = NULL;
  197. GNUNET_assert (NULL !=
  198. inet_ntop (AF_INET,
  199. &h->internal_address,
  200. ia,
  201. sizeof (ia)));
  202. /* Start the server process */
  203. binary
  204. = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
  205. if (GNUNET_YES !=
  206. GNUNET_OS_check_helper_binary (binary,
  207. GNUNET_YES,
  208. ia))
  209. {
  210. /* move instantly to max delay, as this is unlikely to be fixed */
  211. h->server_retry_delay
  212. = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD;
  213. GNUNET_free (binary);
  214. try_again (h);
  215. return;
  216. }
  217. h->server_stdout
  218. = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES,
  219. GNUNET_NO, GNUNET_YES);
  220. if (NULL == h->server_stdout)
  221. {
  222. GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
  223. "pipe");
  224. GNUNET_free (binary);
  225. try_again (h);
  226. return;
  227. }
  228. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  229. "Starting `%s' at `%s'\n",
  230. "gnunet-helper-nat-server",
  231. ia);
  232. h->server_proc
  233. = GNUNET_OS_start_process (GNUNET_NO,
  234. 0,
  235. NULL,
  236. h->server_stdout,
  237. NULL,
  238. binary,
  239. "gnunet-helper-nat-server",
  240. ia,
  241. NULL);
  242. GNUNET_free (binary);
  243. if (NULL == h->server_proc)
  244. {
  245. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  246. _("Failed to start %s\n"),
  247. "gnunet-helper-nat-server");
  248. GNUNET_DISK_pipe_close (h->server_stdout);
  249. h->server_stdout = NULL;
  250. try_again (h);
  251. return;
  252. }
  253. /* Close the write end of the read pipe */
  254. GNUNET_DISK_pipe_close_end (h->server_stdout,
  255. GNUNET_DISK_PIPE_END_WRITE);
  256. h->server_stdout_handle
  257. = GNUNET_DISK_pipe_handle (h->server_stdout,
  258. GNUNET_DISK_PIPE_END_READ);
  259. h->server_read_task
  260. = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  261. h->server_stdout_handle,
  262. &nat_server_read,
  263. h);
  264. }
  265. /**
  266. * Start the gnunet-helper-nat-server and process incoming
  267. * requests.
  268. *
  269. * @param internal_address
  270. * @param cb function to call if we receive a request
  271. * @param cb_cls closure for @a cb
  272. * @return NULL on error
  273. */
  274. struct HelperContext *
  275. GN_start_gnunet_nat_server_ (const struct in_addr *internal_address,
  276. GN_ReversalCallback cb,
  277. void *cb_cls)
  278. {
  279. struct HelperContext *h;
  280. h = GNUNET_new (struct HelperContext);
  281. h->cb = cb;
  282. h->cb_cls = cb_cls;
  283. h->internal_address = *internal_address;
  284. restart_nat_server (h);
  285. if (NULL == h->server_stdout)
  286. {
  287. GN_stop_gnunet_nat_server_ (h);
  288. return NULL;
  289. }
  290. return h;
  291. }
  292. /**
  293. * Start the gnunet-helper-nat-server and process incoming
  294. * requests.
  295. *
  296. * @param h helper context to stop
  297. */
  298. void
  299. GN_stop_gnunet_nat_server_ (struct HelperContext *h)
  300. {
  301. if (NULL != h->server_read_task)
  302. {
  303. GNUNET_SCHEDULER_cancel (h->server_read_task);
  304. h->server_read_task = NULL;
  305. }
  306. if (NULL != h->server_proc)
  307. {
  308. if (0 != GNUNET_OS_process_kill (h->server_proc,
  309. GNUNET_TERM_SIG))
  310. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
  311. "kill");
  312. GNUNET_OS_process_wait (h->server_proc);
  313. GNUNET_OS_process_destroy (h->server_proc);
  314. h->server_proc = NULL;
  315. GNUNET_DISK_pipe_close (h->server_stdout);
  316. h->server_stdout = NULL;
  317. h->server_stdout_handle = NULL;
  318. }
  319. if (NULL != h->server_stdout)
  320. {
  321. GNUNET_DISK_pipe_close (h->server_stdout);
  322. h->server_stdout = NULL;
  323. h->server_stdout_handle = NULL;
  324. }
  325. GNUNET_free (h);
  326. }
  327. /**
  328. * We want to connect to a peer that is behind NAT. Run the
  329. * gnunet-helper-nat-client to send dummy ICMP responses to cause
  330. * that peer to connect to us (connection reversal).
  331. *
  332. * @param internal_address out internal address to use
  333. * @param internal_port port to use
  334. * @param remote_v4 the address of the peer (IPv4-only)
  335. * @return #GNUNET_SYSERR on error,
  336. * #GNUNET_OK otherwise
  337. */
  338. int
  339. GN_request_connection_reversal (const struct in_addr *internal_address,
  340. uint16_t internal_port,
  341. const struct in_addr *remote_v4)
  342. {
  343. char intv4[INET_ADDRSTRLEN];
  344. char remv4[INET_ADDRSTRLEN];
  345. char port_as_string[6];
  346. struct GNUNET_OS_Process *proc;
  347. char *binary;
  348. if (NULL == inet_ntop (AF_INET,
  349. internal_address,
  350. intv4,
  351. INET_ADDRSTRLEN))
  352. {
  353. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
  354. "inet_ntop");
  355. return GNUNET_SYSERR;
  356. }
  357. if (NULL == inet_ntop (AF_INET,
  358. remote_v4,
  359. remv4,
  360. INET_ADDRSTRLEN))
  361. {
  362. GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
  363. "inet_ntop");
  364. return GNUNET_SYSERR;
  365. }
  366. GNUNET_snprintf (port_as_string,
  367. sizeof (port_as_string),
  368. "%d",
  369. internal_port);
  370. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  371. "Running gnunet-helper-nat-client %s %s %u\n",
  372. intv4,
  373. remv4,
  374. internal_port);
  375. binary
  376. = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
  377. proc
  378. = GNUNET_OS_start_process (GNUNET_NO,
  379. 0,
  380. NULL,
  381. NULL,
  382. NULL,
  383. binary,
  384. "gnunet-helper-nat-client",
  385. intv4,
  386. remv4,
  387. port_as_string,
  388. NULL);
  389. GNUNET_free (binary);
  390. if (NULL == proc)
  391. return GNUNET_SYSERR;
  392. /* we know that the gnunet-helper-nat-client will terminate virtually
  393. * instantly */
  394. GNUNET_OS_process_wait (proc);
  395. GNUNET_OS_process_destroy (proc);
  396. return GNUNET_OK;
  397. }
  398. /* end of gnunet-service-nat_helper.c */