gnunet-service-nat_mini.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2011-2014, 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_mini.c
  18. * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
  19. * @author Christian Grothoff
  20. */
  21. #include "platform.h"
  22. #include "gnunet_util_lib.h"
  23. #include "gnunet_nat_service.h"
  24. #include "gnunet-service-nat_mini.h"
  25. #include "nat.h"
  26. #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
  27. /**
  28. * How long do we give upnpc to create a mapping?
  29. */
  30. #define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
  31. /**
  32. * How long do we give upnpc to remove a mapping?
  33. */
  34. #define UNMAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
  35. /**
  36. * How often do we check for changes in the mapping?
  37. */
  38. #define MAP_REFRESH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
  39. /* ************************* external-ip calling ************************ */
  40. /**
  41. * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
  42. */
  43. struct GNUNET_NAT_ExternalHandle
  44. {
  45. /**
  46. * Function to call with the result.
  47. */
  48. GNUNET_NAT_IPCallback cb;
  49. /**
  50. * Closure for @e cb.
  51. */
  52. void *cb_cls;
  53. /**
  54. * Read task.
  55. */
  56. struct GNUNET_SCHEDULER_Task *task;
  57. /**
  58. * Handle to `external-ip` process.
  59. */
  60. struct GNUNET_OS_Process *eip;
  61. /**
  62. * Handle to stdout pipe of `external-ip`.
  63. */
  64. struct GNUNET_DISK_PipeHandle *opipe;
  65. /**
  66. * Read handle of @e opipe.
  67. */
  68. const struct GNUNET_DISK_FileHandle *r;
  69. /**
  70. * Number of bytes in @e buf that are valid.
  71. */
  72. size_t off;
  73. /**
  74. * Destination of our read operation (output of 'external-ip').
  75. */
  76. char buf[17];
  77. /**
  78. * Error code for better debugging and user feedback
  79. */
  80. enum GNUNET_NAT_StatusCode ret;
  81. };
  82. /**
  83. * Read the output of `external-ip` into `buf`. When complete, parse
  84. * the address and call our callback.
  85. *
  86. * @param cls the `struct GNUNET_NAT_ExternalHandle`
  87. */
  88. static void
  89. read_external_ipv4 (void *cls)
  90. {
  91. struct GNUNET_NAT_ExternalHandle *eh = cls;
  92. ssize_t ret;
  93. struct in_addr addr;
  94. eh->task = NULL;
  95. ret = GNUNET_DISK_file_read (eh->r,
  96. &eh->buf[eh->off],
  97. sizeof (eh->buf) - eh->off);
  98. if (ret > 0)
  99. {
  100. /* try to read more */
  101. eh->off += ret;
  102. eh->task
  103. = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  104. eh->r,
  105. &read_external_ipv4,
  106. eh);
  107. return;
  108. }
  109. eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID;
  110. if ( (eh->off > 7) &&
  111. (eh->buf[eh->off - 1] == '\n') )
  112. {
  113. eh->buf[eh->off - 1] = '\0';
  114. if (1 == inet_pton (AF_INET,
  115. eh->buf,
  116. &addr))
  117. {
  118. if (0 == addr.s_addr)
  119. eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */
  120. else
  121. eh->ret = GNUNET_NAT_ERROR_SUCCESS;
  122. }
  123. }
  124. eh->cb (eh->cb_cls,
  125. (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
  126. eh->ret);
  127. GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh);
  128. }
  129. /**
  130. * (Asynchronously) signal error invoking `external-ip` to client.
  131. *
  132. * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed)
  133. */
  134. static void
  135. signal_external_ip_error (void *cls)
  136. {
  137. struct GNUNET_NAT_ExternalHandle *eh = cls;
  138. eh->task = NULL;
  139. eh->cb (eh->cb_cls,
  140. NULL,
  141. eh->ret);
  142. GNUNET_free (eh);
  143. }
  144. /**
  145. * Try to get the external IPv4 address of this peer.
  146. *
  147. * @param cb function to call with result
  148. * @param cb_cls closure for @a cb
  149. * @return handle for cancellation (can only be used until @a cb is called), never NULL
  150. */
  151. struct GNUNET_NAT_ExternalHandle *
  152. GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
  153. void *cb_cls)
  154. {
  155. struct GNUNET_NAT_ExternalHandle *eh;
  156. eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle);
  157. eh->cb = cb;
  158. eh->cb_cls = cb_cls;
  159. eh->ret = GNUNET_NAT_ERROR_SUCCESS;
  160. if (GNUNET_SYSERR ==
  161. GNUNET_OS_check_helper_binary ("external-ip",
  162. GNUNET_NO,
  163. NULL))
  164. {
  165. LOG (GNUNET_ERROR_TYPE_INFO,
  166. _("`external-ip' command not found\n"));
  167. eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND;
  168. eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
  169. eh);
  170. return eh;
  171. }
  172. LOG (GNUNET_ERROR_TYPE_DEBUG,
  173. "Running `external-ip' to determine our external IP\n");
  174. eh->opipe = GNUNET_DISK_pipe (GNUNET_YES,
  175. GNUNET_YES,
  176. GNUNET_NO,
  177. GNUNET_YES);
  178. if (NULL == eh->opipe)
  179. {
  180. eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
  181. eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
  182. eh);
  183. return eh;
  184. }
  185. eh->eip =
  186. GNUNET_OS_start_process (GNUNET_NO,
  187. 0,
  188. NULL,
  189. eh->opipe,
  190. NULL,
  191. "external-ip",
  192. "external-ip",
  193. NULL);
  194. if (NULL == eh->eip)
  195. {
  196. GNUNET_DISK_pipe_close (eh->opipe);
  197. eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED;
  198. eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
  199. eh);
  200. return eh;
  201. }
  202. GNUNET_DISK_pipe_close_end (eh->opipe,
  203. GNUNET_DISK_PIPE_END_WRITE);
  204. eh->r = GNUNET_DISK_pipe_handle (eh->opipe,
  205. GNUNET_DISK_PIPE_END_READ);
  206. eh->task
  207. = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
  208. eh->r,
  209. &read_external_ipv4,
  210. eh);
  211. return eh;
  212. }
  213. /**
  214. * Cancel operation.
  215. *
  216. * @param eh operation to cancel
  217. */
  218. void
  219. GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh)
  220. {
  221. if (NULL != eh->eip)
  222. {
  223. (void) GNUNET_OS_process_kill (eh->eip,
  224. SIGKILL);
  225. GNUNET_break (GNUNET_OK ==
  226. GNUNET_OS_process_wait (eh->eip));
  227. GNUNET_OS_process_destroy (eh->eip);
  228. }
  229. if (NULL != eh->opipe)
  230. {
  231. GNUNET_DISK_pipe_close (eh->opipe);
  232. eh->opipe = NULL;
  233. }
  234. if (NULL != eh->task)
  235. {
  236. GNUNET_SCHEDULER_cancel (eh->task);
  237. eh->task = NULL;
  238. }
  239. GNUNET_free (eh);
  240. }
  241. /* ************************* upnpc calling ************************ */
  242. /**
  243. * Handle to a mapping created with upnpc.
  244. */
  245. struct GNUNET_NAT_MiniHandle
  246. {
  247. /**
  248. * Function to call on mapping changes.
  249. */
  250. GNUNET_NAT_MiniAddressCallback ac;
  251. /**
  252. * Closure for @e ac.
  253. */
  254. void *ac_cls;
  255. /**
  256. * Command used to install the map.
  257. */
  258. struct GNUNET_OS_CommandHandle *map_cmd;
  259. /**
  260. * Command used to refresh our map information.
  261. */
  262. struct GNUNET_OS_CommandHandle *refresh_cmd;
  263. /**
  264. * Command used to remove the mapping.
  265. */
  266. struct GNUNET_OS_CommandHandle *unmap_cmd;
  267. /**
  268. * Our current external mapping (if we have one).
  269. */
  270. struct sockaddr_in current_addr;
  271. /**
  272. * We check the mapping periodically to see if it
  273. * still works. This task triggers the check.
  274. */
  275. struct GNUNET_SCHEDULER_Task *refresh_task;
  276. /**
  277. * Are we mapping TCP or UDP?
  278. */
  279. int is_tcp;
  280. /**
  281. * Did we succeed with creating a mapping?
  282. */
  283. int did_map;
  284. /**
  285. * Did we find our mapping during refresh scan?
  286. */
  287. int found;
  288. /**
  289. * Which port are we mapping?
  290. */
  291. uint16_t port;
  292. };
  293. /**
  294. * Run "upnpc -l" to find out if our mapping changed.
  295. *
  296. * @param cls the `struct GNUNET_NAT_MiniHandle`
  297. */
  298. static void
  299. do_refresh (void *cls);
  300. /**
  301. * Process the output from the "upnpc -r" command.
  302. *
  303. * @param cls the `struct GNUNET_NAT_MiniHandle`
  304. * @param line line of output, NULL at the end
  305. */
  306. static void
  307. process_map_output (void *cls,
  308. const char *line);
  309. /**
  310. * Run "upnpc -r" to map our internal port.
  311. *
  312. * @param mini our handle
  313. */
  314. static void
  315. run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini)
  316. {
  317. char pstr[6];
  318. GNUNET_snprintf (pstr,
  319. sizeof (pstr),
  320. "%u",
  321. (unsigned int) mini->port);
  322. mini->map_cmd
  323. = GNUNET_OS_command_run (&process_map_output,
  324. mini,
  325. MAP_TIMEOUT,
  326. "upnpc",
  327. "upnpc",
  328. "-r",
  329. pstr,
  330. mini->is_tcp ? "tcp" : "udp",
  331. NULL);
  332. if (NULL == mini->map_cmd)
  333. {
  334. mini->ac (mini->ac_cls,
  335. GNUNET_SYSERR,
  336. NULL,
  337. 0,
  338. GNUNET_NAT_ERROR_UPNPC_FAILED);
  339. return;
  340. }
  341. }
  342. /**
  343. * Process the output from "upnpc -l" to see if our
  344. * external mapping changed. If so, do the notifications.
  345. *
  346. * @param cls the `struct GNUNET_NAT_MiniHandle`
  347. * @param line line of output, NULL at the end
  348. */
  349. static void
  350. process_refresh_output (void *cls,
  351. const char *line)
  352. {
  353. struct GNUNET_NAT_MiniHandle *mini = cls;
  354. char pstr[9];
  355. const char *s;
  356. unsigned int nport;
  357. struct in_addr exip;
  358. if (NULL == line)
  359. {
  360. GNUNET_OS_command_stop (mini->refresh_cmd);
  361. mini->refresh_cmd = NULL;
  362. if (GNUNET_NO == mini->found)
  363. {
  364. /* mapping disappeared, try to re-create */
  365. if (GNUNET_YES == mini->did_map)
  366. {
  367. mini->ac (mini->ac_cls,
  368. GNUNET_NO,
  369. (const struct sockaddr *) &mini->current_addr,
  370. sizeof (mini->current_addr),
  371. GNUNET_NAT_ERROR_SUCCESS);
  372. mini->did_map = GNUNET_NO;
  373. }
  374. run_upnpc_r (mini);
  375. }
  376. return;
  377. }
  378. if (! mini->did_map)
  379. return; /* never mapped, won't find our mapping anyway */
  380. /* we're looking for output of the form:
  381. * "ExternalIPAddress = 12.134.41.124" */
  382. s = strstr (line,
  383. "ExternalIPAddress = ");
  384. if (NULL != s)
  385. {
  386. s += strlen ("ExternalIPAddress = ");
  387. if (1 != inet_pton (AF_INET,
  388. s,
  389. &exip))
  390. return; /* skip */
  391. if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
  392. return; /* no change */
  393. /* update mapping */
  394. mini->ac (mini->ac_cls,
  395. GNUNET_NO,
  396. (const struct sockaddr *) &mini->current_addr,
  397. sizeof (mini->current_addr),
  398. GNUNET_NAT_ERROR_SUCCESS);
  399. mini->current_addr.sin_addr = exip;
  400. mini->ac (mini->ac_cls,
  401. GNUNET_YES,
  402. (const struct sockaddr *) &mini->current_addr,
  403. sizeof (mini->current_addr),
  404. GNUNET_NAT_ERROR_SUCCESS);
  405. return;
  406. }
  407. /*
  408. * we're looking for output of the form:
  409. *
  410. * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
  411. * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
  412. *
  413. * the pattern we look for is:
  414. *
  415. * "%s TCP PORT->STRING:OURPORT *" or
  416. * "%s UDP PORT->STRING:OURPORT *"
  417. */
  418. GNUNET_snprintf (pstr,
  419. sizeof (pstr),
  420. ":%u ",
  421. mini->port);
  422. if (NULL == (s = strstr (line, "->")))
  423. return; /* skip */
  424. if (NULL == strstr (s, pstr))
  425. return; /* skip */
  426. if (1 !=
  427. SSCANF (line,
  428. (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" :
  429. "%*u UDP %u->%*s:%*u %*s", &nport))
  430. return; /* skip */
  431. mini->found = GNUNET_YES;
  432. if (nport == ntohs (mini->current_addr.sin_port))
  433. return; /* no change */
  434. /* external port changed, update mapping */
  435. mini->ac (mini->ac_cls,
  436. GNUNET_NO,
  437. (const struct sockaddr *) &mini->current_addr,
  438. sizeof (mini->current_addr),
  439. GNUNET_NAT_ERROR_SUCCESS);
  440. mini->current_addr.sin_port = htons ((uint16_t) nport);
  441. mini->ac (mini->ac_cls,
  442. GNUNET_YES,
  443. (const struct sockaddr *) &mini->current_addr,
  444. sizeof (mini->current_addr),
  445. GNUNET_NAT_ERROR_SUCCESS);
  446. }
  447. /**
  448. * Run "upnpc -l" to find out if our mapping changed.
  449. *
  450. * @param cls the 'struct GNUNET_NAT_MiniHandle'
  451. */
  452. static void
  453. do_refresh (void *cls)
  454. {
  455. struct GNUNET_NAT_MiniHandle *mini = cls;
  456. int ac;
  457. mini->refresh_task
  458. = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
  459. &do_refresh,
  460. mini);
  461. LOG (GNUNET_ERROR_TYPE_DEBUG,
  462. "Running `upnpc' to check if our mapping still exists\n");
  463. mini->found = GNUNET_NO;
  464. ac = GNUNET_NO;
  465. if (NULL != mini->map_cmd)
  466. {
  467. /* took way too long, abort it! */
  468. GNUNET_OS_command_stop (mini->map_cmd);
  469. mini->map_cmd = NULL;
  470. ac = GNUNET_YES;
  471. }
  472. if (NULL != mini->refresh_cmd)
  473. {
  474. /* took way too long, abort it! */
  475. GNUNET_OS_command_stop (mini->refresh_cmd);
  476. mini->refresh_cmd = NULL;
  477. ac = GNUNET_YES;
  478. }
  479. mini->refresh_cmd
  480. = GNUNET_OS_command_run (&process_refresh_output,
  481. mini,
  482. MAP_TIMEOUT,
  483. "upnpc",
  484. "upnpc",
  485. "-l",
  486. NULL);
  487. if (GNUNET_YES == ac)
  488. mini->ac (mini->ac_cls,
  489. GNUNET_SYSERR,
  490. NULL,
  491. 0,
  492. GNUNET_NAT_ERROR_UPNPC_TIMEOUT);
  493. }
  494. /**
  495. * Process the output from the 'upnpc -r' command.
  496. *
  497. * @param cls the `struct GNUNET_NAT_MiniHandle`
  498. * @param line line of output, NULL at the end
  499. */
  500. static void
  501. process_map_output (void *cls,
  502. const char *line)
  503. {
  504. struct GNUNET_NAT_MiniHandle *mini = cls;
  505. const char *ipaddr;
  506. char *ipa;
  507. const char *pstr;
  508. unsigned int port;
  509. if (NULL == line)
  510. {
  511. GNUNET_OS_command_stop (mini->map_cmd);
  512. mini->map_cmd = NULL;
  513. if (GNUNET_YES != mini->did_map)
  514. mini->ac (mini->ac_cls,
  515. GNUNET_SYSERR,
  516. NULL,
  517. 0,
  518. GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
  519. if (NULL == mini->refresh_task)
  520. mini->refresh_task
  521. = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
  522. &do_refresh,
  523. mini);
  524. return;
  525. }
  526. /*
  527. * The upnpc output we're after looks like this:
  528. *
  529. * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
  530. */
  531. if ((NULL == (ipaddr = strstr (line, " "))) ||
  532. (NULL == (pstr = strstr (ipaddr, ":"))) ||
  533. (1 != SSCANF (pstr + 1, "%u", &port)))
  534. {
  535. return; /* skip line */
  536. }
  537. ipa = GNUNET_strdup (ipaddr + 1);
  538. strstr (ipa, ":")[0] = '\0';
  539. if (1 != inet_pton (AF_INET,
  540. ipa,
  541. &mini->current_addr.sin_addr))
  542. {
  543. GNUNET_free (ipa);
  544. return; /* skip line */
  545. }
  546. GNUNET_free (ipa);
  547. mini->current_addr.sin_port = htons (port);
  548. mini->current_addr.sin_family = AF_INET;
  549. #if HAVE_SOCKADDR_IN_SIN_LEN
  550. mini->current_addr.sin_len = sizeof (struct sockaddr_in);
  551. #endif
  552. mini->did_map = GNUNET_YES;
  553. mini->ac (mini->ac_cls,
  554. GNUNET_YES,
  555. (const struct sockaddr *) &mini->current_addr,
  556. sizeof (mini->current_addr),
  557. GNUNET_NAT_ERROR_SUCCESS);
  558. }
  559. /**
  560. * Start mapping the given port using (mini)upnpc. This function
  561. * should typically not be used directly (it is used within the
  562. * general-purpose #GNUNET_NAT_register() code). However, it can be
  563. * used if specifically UPnP-based NAT traversal is to be used or
  564. * tested.
  565. *
  566. * @param port port to map
  567. * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
  568. * @param ac function to call with mapping result
  569. * @param ac_cls closure for @a ac
  570. * @return NULL on error (no 'upnpc' installed)
  571. */
  572. struct GNUNET_NAT_MiniHandle *
  573. GNUNET_NAT_mini_map_start (uint16_t port,
  574. int is_tcp,
  575. GNUNET_NAT_MiniAddressCallback ac,
  576. void *ac_cls)
  577. {
  578. struct GNUNET_NAT_MiniHandle *ret;
  579. if (GNUNET_SYSERR ==
  580. GNUNET_OS_check_helper_binary ("upnpc",
  581. GNUNET_NO,
  582. NULL))
  583. {
  584. LOG (GNUNET_ERROR_TYPE_INFO,
  585. _("`upnpc' command not found\n"));
  586. ac (ac_cls,
  587. GNUNET_SYSERR,
  588. NULL, 0,
  589. GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
  590. return NULL;
  591. }
  592. LOG (GNUNET_ERROR_TYPE_DEBUG,
  593. "Running `upnpc' to install mapping\n");
  594. ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
  595. ret->ac = ac;
  596. ret->ac_cls = ac_cls;
  597. ret->is_tcp = is_tcp;
  598. ret->port = port;
  599. ret->refresh_task =
  600. GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
  601. &do_refresh,
  602. ret);
  603. run_upnpc_r (ret);
  604. return ret;
  605. }
  606. /**
  607. * Process output from our 'unmap' command.
  608. *
  609. * @param cls the `struct GNUNET_NAT_MiniHandle`
  610. * @param line line of output, NULL at the end
  611. */
  612. static void
  613. process_unmap_output (void *cls,
  614. const char *line)
  615. {
  616. struct GNUNET_NAT_MiniHandle *mini = cls;
  617. if (NULL == line)
  618. {
  619. LOG (GNUNET_ERROR_TYPE_DEBUG,
  620. "UPnP unmap done\n");
  621. GNUNET_OS_command_stop (mini->unmap_cmd);
  622. mini->unmap_cmd = NULL;
  623. GNUNET_free (mini);
  624. return;
  625. }
  626. /* we don't really care about the output... */
  627. }
  628. /**
  629. * Remove a mapping created with (mini)upnpc. Calling
  630. * this function will give 'upnpc' 1s to remove tha mapping,
  631. * so while this function is non-blocking, a task will be
  632. * left with the scheduler for up to 1s past this call.
  633. *
  634. * @param mini the handle
  635. */
  636. void
  637. GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
  638. {
  639. char pstr[6];
  640. if (NULL != mini->refresh_task)
  641. {
  642. GNUNET_SCHEDULER_cancel (mini->refresh_task);
  643. mini->refresh_task = NULL;
  644. }
  645. if (NULL != mini->refresh_cmd)
  646. {
  647. GNUNET_OS_command_stop (mini->refresh_cmd);
  648. mini->refresh_cmd = NULL;
  649. }
  650. if (NULL != mini->map_cmd)
  651. {
  652. GNUNET_OS_command_stop (mini->map_cmd);
  653. mini->map_cmd = NULL;
  654. }
  655. if (GNUNET_NO == mini->did_map)
  656. {
  657. GNUNET_free (mini);
  658. return;
  659. }
  660. mini->ac (mini->ac_cls,
  661. GNUNET_NO,
  662. (const struct sockaddr *) &mini->current_addr,
  663. sizeof (mini->current_addr),
  664. GNUNET_NAT_ERROR_SUCCESS);
  665. /* Note: oddly enough, deletion uses the external port whereas
  666. * addition uses the internal port; this rarely matters since they
  667. * often are the same, but it might... */
  668. GNUNET_snprintf (pstr,
  669. sizeof (pstr),
  670. "%u",
  671. (unsigned int) ntohs (mini->current_addr.sin_port));
  672. LOG (GNUNET_ERROR_TYPE_DEBUG,
  673. "Unmapping port %u with UPnP\n",
  674. ntohs (mini->current_addr.sin_port));
  675. mini->unmap_cmd
  676. = GNUNET_OS_command_run (&process_unmap_output,
  677. mini,
  678. UNMAP_TIMEOUT,
  679. "upnpc",
  680. "upnpc",
  681. "-d",
  682. pstr,
  683. mini->is_tcp ? "tcp" : "udp",
  684. NULL);
  685. }
  686. /* end of gnunet-service-nat_mini.c */