/apps/transmission/transmission-2.42/libtransmission/upnp.c

http://snake-os.googlecode.com/ · C · 257 lines · 208 code · 32 blank · 17 comment · 39 complexity · 8ebd945bcc5d324dfb7f24cde956d814 MD5 · raw file

  1. /*
  2. * This file Copyright (C) Mnemosyne LLC
  3. *
  4. * This file is licensed by the GPL version 2. Works owned by the
  5. * Transmission project are granted a special exemption to clause 2(b)
  6. * so that the bulk of its code can remain under the MIT license.
  7. * This exemption does not extend to derived works not owned by
  8. * the Transmission project.
  9. *
  10. * $Id: upnp.c 12593 2011-07-26 01:36:30Z livings124 $
  11. */
  12. #include <assert.h>
  13. #include <errno.h>
  14. #include <miniupnp/miniupnpc.h>
  15. #include <miniupnp/upnpcommands.h>
  16. #include "transmission.h"
  17. #include "port-forwarding.h"
  18. #include "session.h"
  19. #include "upnp.h"
  20. #include "utils.h"
  21. static const char *
  22. getKey( void ) { return _( "Port Forwarding (UPnP)" ); }
  23. typedef enum
  24. {
  25. TR_UPNP_IDLE,
  26. TR_UPNP_ERR,
  27. TR_UPNP_DISCOVER,
  28. TR_UPNP_MAP,
  29. TR_UPNP_UNMAP
  30. }
  31. tr_upnp_state;
  32. struct tr_upnp
  33. {
  34. bool hasDiscovered;
  35. struct UPNPUrls urls;
  36. struct IGDdatas data;
  37. int port;
  38. char lanaddr[16];
  39. unsigned int isMapped;
  40. tr_upnp_state state;
  41. };
  42. /**
  43. ***
  44. **/
  45. tr_upnp*
  46. tr_upnpInit( void )
  47. {
  48. tr_upnp * ret = tr_new0( tr_upnp, 1 );
  49. ret->state = TR_UPNP_DISCOVER;
  50. ret->port = -1;
  51. return ret;
  52. }
  53. void
  54. tr_upnpClose( tr_upnp * handle )
  55. {
  56. assert( !handle->isMapped );
  57. assert( ( handle->state == TR_UPNP_IDLE )
  58. || ( handle->state == TR_UPNP_ERR )
  59. || ( handle->state == TR_UPNP_DISCOVER ) );
  60. if( handle->hasDiscovered )
  61. FreeUPNPUrls( &handle->urls );
  62. tr_free( handle );
  63. }
  64. /**
  65. ***
  66. **/
  67. enum
  68. {
  69. UPNP_IGD_NONE = 0,
  70. UPNP_IGD_VALID_CONNECTED = 1,
  71. UPNP_IGD_VALID_NOT_CONNECTED = 2,
  72. UPNP_IGD_INVALID = 3
  73. };
  74. int
  75. tr_upnpPulse( tr_upnp * handle,
  76. int port,
  77. int isEnabled,
  78. int doPortCheck )
  79. {
  80. int ret;
  81. if( isEnabled && ( handle->state == TR_UPNP_DISCOVER ) )
  82. {
  83. struct UPNPDev * devlist;
  84. errno = 0;
  85. devlist = upnpDiscover( 2000, NULL, NULL, 0, 0, &errno );
  86. if( devlist == NULL )
  87. {
  88. tr_ndbg(
  89. getKey( ), "upnpDiscover failed (errno %d - %s)", errno,
  90. tr_strerror( errno ) );
  91. }
  92. errno = 0;
  93. if( UPNP_GetValidIGD( devlist, &handle->urls, &handle->data,
  94. handle->lanaddr, sizeof( handle->lanaddr ) ) == UPNP_IGD_VALID_CONNECTED )
  95. {
  96. tr_ninf( getKey( ), _(
  97. "Found Internet Gateway Device \"%s\"" ),
  98. handle->urls.controlURL );
  99. tr_ninf( getKey( ), _(
  100. "Local Address is \"%s\"" ), handle->lanaddr );
  101. handle->state = TR_UPNP_IDLE;
  102. handle->hasDiscovered = 1;
  103. }
  104. else
  105. {
  106. handle->state = TR_UPNP_ERR;
  107. tr_ndbg(
  108. getKey( ), "UPNP_GetValidIGD failed (errno %d - %s)",
  109. errno,
  110. tr_strerror( errno ) );
  111. tr_ndbg(
  112. getKey( ),
  113. "If your router supports UPnP, please make sure UPnP is enabled!" );
  114. }
  115. freeUPNPDevlist( devlist );
  116. }
  117. if( handle->state == TR_UPNP_IDLE )
  118. {
  119. if( handle->isMapped && ( !isEnabled || ( handle->port != port ) ) )
  120. handle->state = TR_UPNP_UNMAP;
  121. }
  122. if( isEnabled && handle->isMapped && doPortCheck )
  123. {
  124. char portStr[8];
  125. char intPort[8];
  126. char intClient[16];
  127. tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
  128. if( UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype,
  129. portStr, "TCP", intClient, intPort, NULL, NULL, NULL ) != UPNPCOMMAND_SUCCESS ||
  130. UPNP_GetSpecificPortMappingEntry( handle->urls.controlURL, handle->data.first.servicetype,
  131. portStr, "UDP", intClient, intPort, NULL, NULL, NULL ) != UPNPCOMMAND_SUCCESS )
  132. {
  133. tr_ninf( getKey( ), _( "Port %d isn't forwarded" ), handle->port );
  134. handle->isMapped = false;
  135. }
  136. }
  137. if( handle->state == TR_UPNP_UNMAP )
  138. {
  139. char portStr[16];
  140. tr_snprintf( portStr, sizeof( portStr ), "%d", handle->port );
  141. UPNP_DeletePortMapping( handle->urls.controlURL,
  142. handle->data.first.servicetype,
  143. portStr, "TCP", NULL );
  144. UPNP_DeletePortMapping( handle->urls.controlURL,
  145. handle->data.first.servicetype,
  146. portStr, "UDP", NULL );
  147. tr_ninf( getKey( ),
  148. _(
  149. "Stopping port forwarding through \"%s\", service \"%s\"" ),
  150. handle->urls.controlURL, handle->data.first.servicetype );
  151. handle->isMapped = 0;
  152. handle->state = TR_UPNP_IDLE;
  153. handle->port = -1;
  154. }
  155. if( handle->state == TR_UPNP_IDLE )
  156. {
  157. if( isEnabled && !handle->isMapped )
  158. handle->state = TR_UPNP_MAP;
  159. }
  160. if( handle->state == TR_UPNP_MAP )
  161. {
  162. int err_tcp = -1;
  163. int err_udp = -1;
  164. errno = 0;
  165. if( !handle->urls.controlURL || !handle->data.first.servicetype )
  166. handle->isMapped = 0;
  167. else
  168. {
  169. char portStr[16];
  170. char desc[64];
  171. const int prev_errno = errno;
  172. tr_snprintf( portStr, sizeof( portStr ), "%d", port );
  173. tr_snprintf( desc, sizeof( desc ), "%s at %d", TR_NAME, port );
  174. errno = 0;
  175. err_tcp = UPNP_AddPortMapping( handle->urls.controlURL,
  176. handle->data.first.servicetype,
  177. portStr, portStr, handle->lanaddr,
  178. desc, "TCP", NULL, NULL );
  179. if( err_tcp )
  180. tr_ndbg( getKey( ), "TCP Port forwarding failed with error %d (errno %d - %s)",
  181. err_tcp, errno, tr_strerror( errno ) );
  182. errno = 0;
  183. err_udp = UPNP_AddPortMapping( handle->urls.controlURL,
  184. handle->data.first.servicetype,
  185. portStr, portStr, handle->lanaddr,
  186. desc, "UDP", NULL, NULL );
  187. if( err_udp )
  188. tr_ndbg( getKey( ), "UDP Port forwarding failed with error %d (errno %d - %s)",
  189. err_udp, errno, tr_strerror( errno ) );
  190. errno = prev_errno;
  191. handle->isMapped = !err_tcp | !err_udp;
  192. }
  193. tr_ninf( getKey( ),
  194. _( "Port forwarding through \"%s\", service \"%s\". (local address: %s:%d)" ),
  195. handle->urls.controlURL, handle->data.first.servicetype,
  196. handle->lanaddr, port );
  197. if( handle->isMapped )
  198. {
  199. tr_ninf( getKey( ), "%s", _( "Port forwarding successful!" ) );
  200. handle->port = port;
  201. handle->state = TR_UPNP_IDLE;
  202. }
  203. else
  204. {
  205. tr_ndbg( getKey( ), "If your router supports UPnP, please make sure UPnP is enabled!" );
  206. handle->port = -1;
  207. handle->state = TR_UPNP_ERR;
  208. }
  209. }
  210. switch( handle->state )
  211. {
  212. case TR_UPNP_DISCOVER:
  213. ret = TR_PORT_UNMAPPED; break;
  214. case TR_UPNP_MAP:
  215. ret = TR_PORT_MAPPING; break;
  216. case TR_UPNP_UNMAP:
  217. ret = TR_PORT_UNMAPPING; break;
  218. case TR_UPNP_IDLE:
  219. ret = handle->isMapped ? TR_PORT_MAPPED
  220. : TR_PORT_UNMAPPED; break;
  221. default:
  222. ret = TR_PORT_ERROR; break;
  223. }
  224. return ret;
  225. }