tor-fw-helper-upnp.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
  2. * Copyright (c) 2010-2011, The Tor Project, Inc. */
  3. /* See LICENSE for licensing information */
  4. /**
  5. * \file tor-fw-helper-upnp.c
  6. * \brief The implementation of our UPnP firewall helper.
  7. **/
  8. #include "orconfig.h"
  9. #ifdef MINIUPNPC
  10. #ifdef _WIN32
  11. #define STATICLIB
  12. #endif
  13. #include <stdint.h>
  14. #include <string.h>
  15. #include <stdio.h>
  16. #include <assert.h>
  17. #include "compat.h"
  18. #include "tor-fw-helper.h"
  19. #include "tor-fw-helper-upnp.h"
  20. /** UPnP timeout value. */
  21. #define UPNP_DISCOVER_TIMEOUT 2000
  22. /** Description of the port mapping in the UPnP table. */
  23. #define UPNP_DESC "Tor relay"
  24. /* XXX TODO: We should print these as a useful user string when we return the
  25. * number to a user */
  26. /** Magic numbers as miniupnpc return codes. */
  27. #define UPNP_ERR_SUCCESS 0
  28. #define UPNP_ERR_NODEVICESFOUND 1
  29. #define UPNP_ERR_NOIGDFOUND 2
  30. #define UPNP_ERR_ADDPORTMAPPING 3
  31. #define UPNP_ERR_GETPORTMAPPING 4
  32. #define UPNP_ERR_DELPORTMAPPING 5
  33. #define UPNP_ERR_GETEXTERNALIP 6
  34. #define UPNP_ERR_INVAL 7
  35. #define UPNP_ERR_OTHER 8
  36. #define UPNP_SUCCESS 1
  37. /** This hooks miniupnpc into our multi-backend API. */
  38. static tor_fw_backend_t tor_miniupnp_backend = {
  39. "miniupnp",
  40. sizeof(struct miniupnpc_state_t),
  41. tor_upnp_init,
  42. tor_upnp_cleanup,
  43. tor_upnp_fetch_public_ip,
  44. tor_upnp_add_tcp_mapping
  45. };
  46. /** Return the backend for miniupnp. */
  47. const tor_fw_backend_t *
  48. tor_fw_get_miniupnp_backend(void)
  49. {
  50. return &tor_miniupnp_backend;
  51. }
  52. /** Initialize the UPnP backend and store the results in
  53. * <b>backend_state</b>.*/
  54. int
  55. tor_upnp_init(tor_fw_options_t *options, void *backend_state)
  56. {
  57. /*
  58. This leaks the user agent from the client to the router - perhaps we don't
  59. want to do that? eg:
  60. User-Agent: Ubuntu/10.04, UPnP/1.0, MiniUPnPc/1.4
  61. */
  62. miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
  63. struct UPNPDev *devlist;
  64. int r;
  65. memset(&(state->urls), 0, sizeof(struct UPNPUrls));
  66. memset(&(state->data), 0, sizeof(struct IGDdatas));
  67. state->init = 0;
  68. devlist = upnpDiscover(UPNP_DISCOVER_TIMEOUT, NULL, NULL, 0);
  69. if (NULL == devlist) {
  70. fprintf(stderr, "E: upnpDiscover returned: NULL\n");
  71. return UPNP_ERR_NODEVICESFOUND;
  72. }
  73. assert(options);
  74. r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data),
  75. state->lanaddr, UPNP_LANADDR_SZ);
  76. fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r,
  77. r==UPNP_SUCCESS?"SUCCESS":"FAILED");
  78. freeUPNPDevlist(devlist);
  79. if (r != 1 && r != 2)
  80. return UPNP_ERR_NOIGDFOUND;
  81. state->init = 1;
  82. return UPNP_ERR_SUCCESS;
  83. }
  84. /** Tear down the UPnP connection stored in <b>backend_state</b>.*/
  85. int
  86. tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state)
  87. {
  88. miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
  89. assert(options);
  90. if (state->init)
  91. FreeUPNPUrls(&(state->urls));
  92. state->init = 0;
  93. return UPNP_ERR_SUCCESS;
  94. }
  95. /** Fetch our likely public IP from our upstream UPnP IGD enabled NAT device.
  96. * Use the connection context stored in <b>backend_state</b>. */
  97. int
  98. tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state)
  99. {
  100. miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
  101. int r;
  102. char externalIPAddress[16];
  103. if (!state->init) {
  104. r = tor_upnp_init(options, state);
  105. if (r != UPNP_ERR_SUCCESS)
  106. return r;
  107. }
  108. r = UPNP_GetExternalIPAddress(state->urls.controlURL,
  109. state->data.first.servicetype,
  110. externalIPAddress);
  111. if (r != UPNPCOMMAND_SUCCESS)
  112. goto err;
  113. if (externalIPAddress[0]) {
  114. fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n",
  115. externalIPAddress); tor_upnp_cleanup(options, state);
  116. options->public_ip_status = 1;
  117. return UPNP_ERR_SUCCESS;
  118. } else {
  119. goto err;
  120. }
  121. err:
  122. tor_upnp_cleanup(options, state);
  123. return UPNP_ERR_GETEXTERNALIP;
  124. }
  125. /** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b>
  126. * and store the results in <b>backend_state</b>. */
  127. int
  128. tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state)
  129. {
  130. miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
  131. int r;
  132. char internal_port_str[6];
  133. char external_port_str[6];
  134. if (!state->init) {
  135. r = tor_upnp_init(options, state);
  136. if (r != UPNP_ERR_SUCCESS)
  137. return r;
  138. }
  139. if (options->verbose)
  140. fprintf(stdout, "V: internal port: %d, external port: %d\n",
  141. (int)options->internal_port, (int)options->external_port);
  142. tor_snprintf(internal_port_str, sizeof(internal_port_str),
  143. "%d", (int)options->internal_port);
  144. tor_snprintf(external_port_str, sizeof(external_port_str),
  145. "%d", (int)options->external_port);
  146. r = UPNP_AddPortMapping(state->urls.controlURL,
  147. state->data.first.servicetype,
  148. external_port_str, internal_port_str,
  149. state->lanaddr, UPNP_DESC, "TCP", 0);
  150. if (r != UPNPCOMMAND_SUCCESS)
  151. return UPNP_ERR_ADDPORTMAPPING;
  152. options->upnp_status = 1;
  153. return UPNP_ERR_SUCCESS;
  154. }
  155. #endif