2
0

stunc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. * This file is part of the Sofia-SIP package
  3. *
  4. * Copyright (C) 2005,2006 Nokia Corporation.
  5. *
  6. * Contact: Pekka Pessi <pekka.pessi@nokia.com>
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * as published by the Free Software Foundation; either version 2.1 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. * 02110-1301 USA
  22. *
  23. */
  24. /**
  25. * STUN test client
  26. *
  27. * @author Pekka Pessi <Pekka.Pessi@nokia.com>
  28. * @author Martti Mela <Martti.Mela@nokia.com>
  29. * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
  30. *
  31. * @date Created: Thu Jul 24 17:21:00 2003 ppessi
  32. */
  33. /**@page stunc STUN test client.
  34. *
  35. * @section stunc_synopsis Synopsis
  36. * <tt>stunc [OPTIONS] \<stun-server-address\></tt>
  37. *
  38. * @section stunc_description Description
  39. * The @em stunc utility can be used to gather information about possible
  40. * NAT devices that are located between the client and STUN server.
  41. *
  42. * @em stunc can provide the following information: the IP address and
  43. * port as seen by the STUN server, detecting presence of NATs, and
  44. * hints on the type of address translation done. It should be noted
  45. * that the results of NAT type and life-time detection should be
  46. * considered as hints. There is no guarantee that NAT(s) will handle
  47. * future packets in the same way.
  48. *
  49. * @section stunc_options Command Line Options
  50. * The @em stunc utility accepts following command line options:
  51. *
  52. * <dl>
  53. *
  54. * <dt>-b</dt>
  55. * <dd>Perform a STUN binding discovery. @em stunc will report the
  56. * client transport address (IP:port) as seen by the STUN server. In
  57. * the presence of NATs, this address is allocated by the NAT closest
  58. * to the STUN server.
  59. * </dd>
  60. *
  61. * <dt>-l</dt>
  62. * <dd>Perform a STUN binding life-time check.
  63. * </dd>
  64. *
  65. * <dt>-n</dt>
  66. * <dd>Perform a STUN binding type check. Notice that the results
  67. * are only hints. Nondeterministic behaviour, resource exhaustion,
  68. * or reboots of network elements can cause changes in NAT behaviour
  69. * between successive runs of stunc.
  70. * </dd>
  71. *
  72. * <dt>-r</dt>
  73. * <dd>Randomize the local port. Otherwise @em stunc let's the
  74. * operating system select a free port.
  75. * </dd>
  76. *
  77. * <dt>-s</dt>
  78. * <dd>Request a shared-secret over TLS. Tests whether the STUN server
  79. * supports the shared-secret mechanism (needed to protect message
  80. * integrity). Can be combined with @em -b, @em -l and @em -n.
  81. * </dd>
  82. *
  83. * </dl>
  84. *
  85. * @section stunc_return Return Codes
  86. * <table>
  87. * <tr><td>0</td><td>when successful</td></tr>
  88. * <tr><td>1</td><td>when any errors detected</td></tr>
  89. * </table>
  90. *
  91. * @section stunc_examples Examples
  92. *
  93. * Discover the NAT binding, use a random local port:
  94. * @code
  95. * $ stunc stunserver.org -b -r
  96. * @endcode
  97. *
  98. * @section stunc_environment Environment
  99. * #STUN_DEBUG
  100. *
  101. * @section stunc_bugs Reporting Bugs
  102. * Report bugs to <sofia-sip-devel@lists.sourceforge.net>.
  103. *
  104. * @section stunc_author Authors
  105. * - Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
  106. * - Martti Mela <martti -dot mela -at- nokia -dot- com>
  107. * - Kai Vehmanen <kai -dot vehmanen -at- nokia -dot- com>
  108. *
  109. * @section stunc_copyright Copyright
  110. * Copyright (C) 2005,2006 Nokia Corporation.
  111. *
  112. * This program is free software; see the source for copying conditions.
  113. * There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
  114. * PARTICULAR PURPOSE.
  115. */
  116. #include "config.h"
  117. #include <stdio.h>
  118. #include <string.h>
  119. #include <stdlib.h>
  120. #include <time.h>
  121. typedef struct stunc_s stunc_t;
  122. #define SU_ROOT_MAGIC stunc_t
  123. #define STUN_MAGIC_T stunc_t
  124. #define STUN_DISCOVERY_MAGIC_T stunc_t
  125. #include "sofia-sip/stun.h"
  126. #include "sofia-sip/stun_tag.h"
  127. #include "sofia-sip/sofia_features.h"
  128. #include <sofia-sip/su.h>
  129. enum {
  130. do_secret = 1,
  131. do_bind = 2,
  132. do_nat_check = 4,
  133. do_life_check = 8,
  134. do_randomize_port = 16
  135. };
  136. #if HAVE_FUNC
  137. #elif HAVE_FUNCTION
  138. #define __func__ __FUNCTION__
  139. #else
  140. static char const __func__[] = "stunc";
  141. #endif
  142. #ifndef SU_DEBUG
  143. #define SU_DEBUG 0
  144. #endif
  145. #define SU_LOG (stun_log)
  146. #include <sofia-sip/su_debug.h>
  147. void usage(char *name)
  148. {
  149. fprintf(stderr,
  150. "stunc (%s)\n"
  151. "usage: %s <server> [-b] [-n] [-l] [-r] [-s]\n"
  152. " -b\tmake a binding request\n"
  153. " -l\tperform NAT lifetime check\n"
  154. " -n\tperform NAT type check\n"
  155. " -r\trandomize the local port\n",
  156. " -s\trequest shared-secret over TLS (combined with -[bln])\n"
  157. SOFIA_SIP_NAME_VERSION, name);
  158. exit(1);
  159. }
  160. struct stunc_s {
  161. su_socket_t sc_socket;
  162. int sc_flags;
  163. };
  164. static
  165. void stunc_lifetime_cb(stunc_t *stunc,
  166. stun_handle_t *sh,
  167. stun_discovery_t *sd,
  168. stun_action_t action,
  169. stun_state_t event);
  170. static
  171. void stunc_nattype_cb(stunc_t *stunc,
  172. stun_handle_t *sh,
  173. stun_discovery_t *sd,
  174. stun_action_t action,
  175. stun_state_t event);
  176. static
  177. void stunc_bind_cb(stunc_t *stunc,
  178. stun_handle_t *sh,
  179. stun_discovery_t *sd,
  180. stun_action_t action,
  181. stun_state_t event);
  182. static
  183. void stunc_ss_cb(stunc_t *stunc,
  184. stun_handle_t *sh,
  185. stun_discovery_t *sd,
  186. stun_action_t action,
  187. stun_state_t event)
  188. {
  189. int err;
  190. SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
  191. stunc->sc_flags &= ~do_secret;
  192. if (!stunc->sc_flags)
  193. su_root_break(stun_root(sh));
  194. switch (event) {
  195. case stun_tls_done:
  196. if (stunc->sc_flags & do_bind) {
  197. err = stun_bind(sh, stunc_bind_cb, stunc,
  198. STUNTAG_SOCKET(stunc->sc_socket),
  199. STUNTAG_REGISTER_EVENTS(1),
  200. TAG_NULL());
  201. if (err < 0) {
  202. SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_handle_bind()"));
  203. su_root_break(stun_root(sh));
  204. }
  205. }
  206. break;
  207. case stun_tls_connection_failed:
  208. SU_DEBUG_0(("%s: Obtaining shared secret failed.\n",
  209. __func__));
  210. stunc->sc_flags &= ~do_bind;
  211. if (!stunc->sc_flags)
  212. su_root_break(stun_root(sh));
  213. break;
  214. case stun_tls_connection_timeout:
  215. SU_DEBUG_0(("%s: Timeout when obtaining shared secret.\n",
  216. __func__));
  217. stunc->sc_flags &= ~do_bind;
  218. break;
  219. default:
  220. break;
  221. }
  222. return;
  223. }
  224. static
  225. void stunc_bind_cb(stunc_t *stunc,
  226. stun_handle_t *sh,
  227. stun_discovery_t *sd,
  228. stun_action_t action,
  229. stun_state_t event)
  230. {
  231. su_sockaddr_t sa[1];
  232. char ipaddr[48];
  233. socklen_t addrlen;
  234. SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
  235. stunc->sc_flags &= ~do_bind;
  236. if (!stunc->sc_flags)
  237. su_root_break(stun_root(sh));
  238. switch (event) {
  239. case stun_discovery_done:
  240. addrlen = sizeof(*sa);
  241. memset(sa, 0, addrlen);
  242. if (stun_discovery_get_address(sd, sa, &addrlen) < 0) {
  243. SU_DEBUG_0(("%s: stun_discovery_get_address() failed", __func__));
  244. return;
  245. }
  246. SU_DEBUG_0(("%s: local address NATed as %s:%u\n", __func__,
  247. su_inet_ntop(sa->su_family, SU_ADDR(sa),
  248. ipaddr, sizeof(ipaddr)),
  249. (unsigned) ntohs(sa->su_port)));
  250. break;
  251. case stun_discovery_timeout:
  252. case stun_discovery_error:
  253. case stun_error:
  254. default:
  255. break;
  256. }
  257. return;
  258. }
  259. static
  260. void stunc_nattype_cb(stunc_t *stunc,
  261. stun_handle_t *sh,
  262. stun_discovery_t *sd,
  263. stun_action_t action,
  264. stun_state_t event)
  265. {
  266. SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
  267. stunc->sc_flags &= ~do_nat_check;
  268. if (!stunc->sc_flags)
  269. su_root_break(stun_root(sh));
  270. switch (event) {
  271. case stun_discovery_timeout:
  272. SU_DEBUG_3(("%s: NAT type determination timeout.\n", __func__));
  273. break;
  274. case stun_discovery_done:
  275. SU_DEBUG_3(("%s: NAT type determined to be '%s' (%d).\n",
  276. __func__, stun_nattype_str(sd), (int)stun_nattype(sd)));
  277. break;
  278. case stun_error:
  279. default:
  280. break;
  281. }
  282. return;
  283. }
  284. static
  285. void stunc_lifetime_cb(stunc_t *stunc,
  286. stun_handle_t *sh,
  287. stun_discovery_t *sd,
  288. stun_action_t action,
  289. stun_state_t event)
  290. {
  291. SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
  292. stunc->sc_flags &= ~do_life_check;
  293. if (!stunc->sc_flags)
  294. su_root_break(stun_root(sh));
  295. switch (event) {
  296. case stun_discovery_timeout:
  297. SU_DEBUG_3(("%s: Lifetime determination timeout.\n", __func__));
  298. break;
  299. case stun_discovery_done:
  300. SU_DEBUG_3(("%s: Lifetime determined to be %d.\n", __func__, stun_lifetime(sd)));
  301. break;
  302. case stun_error:
  303. default:
  304. break;
  305. }
  306. return;
  307. }
  308. int main(int argc, char *argv[])
  309. {
  310. int err = 0, i, sflags = 0;
  311. stunc_t stunc[1];
  312. su_root_t *root;
  313. stun_handle_t *sh;
  314. su_socket_t s;
  315. if (su_init() != 0)
  316. return -1;
  317. root = su_root_create(stunc);
  318. if (argc < 3)
  319. usage(argv[0]);
  320. for (i = 2; argv[i]; i++) {
  321. if (strcmp(argv[i], "-s") == 0)
  322. sflags |= do_secret;
  323. else if (strcmp(argv[i], "-b") == 0)
  324. sflags |= do_bind;
  325. else if (strcmp(argv[i], "-n") == 0)
  326. sflags |= do_nat_check;
  327. else if (strcmp(argv[i], "-l") == 0)
  328. sflags |= do_life_check;
  329. else if (strcmp(argv[i], "-r") == 0)
  330. sflags |= do_randomize_port;
  331. else {
  332. fprintf(stderr, "Unable to parse option %s.\n", argv[i]);
  333. usage(argv[0]);
  334. }
  335. }
  336. /* Running this test requires a local STUN server on default port */
  337. sh = stun_handle_init(root,
  338. STUNTAG_SERVER(argv[1]),
  339. STUNTAG_REQUIRE_INTEGRITY(sflags & do_secret),
  340. TAG_NULL());
  341. if (!sh) {
  342. SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_handle_init()"));
  343. return -1;
  344. }
  345. s = su_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  346. if (s == -1) {
  347. SU_DEBUG_0(("%s: %s failed: %s\n", __func__,
  348. "su_socket()", su_gli_strerror(errno)));
  349. return -1;
  350. }
  351. stunc->sc_socket = s;
  352. stunc->sc_flags = sflags;
  353. if (sflags & do_randomize_port) {
  354. su_sockaddr_t sockaddr;
  355. char ipaddr[SU_ADDRSIZE + 2] = { 0 };
  356. socklen_t socklen = sizeof(sockaddr);
  357. srand((unsigned int)time((time_t *)NULL));
  358. memset(&sockaddr, 0, sizeof(su_sockaddr_t));
  359. sockaddr.su_port = htons((rand() % (65536 - 1024)) + 1024);
  360. sockaddr.su_family = AF_INET;
  361. SU_DEBUG_3(("stunc: Binding to local port %u.\n", ntohs(sockaddr.su_port)));
  362. err = bind(s, (struct sockaddr *)&sockaddr, socklen);
  363. if (err < 0) {
  364. SU_DEBUG_1(("%s: Error %d binding to %s:%u\n", __func__, err,
  365. su_inet_ntop(sockaddr.su_family, SU_ADDR(&sockaddr),
  366. ipaddr, sizeof(ipaddr)),
  367. (unsigned) ntohs(sockaddr.su_port)));
  368. return -1;
  369. }
  370. stunc->sc_flags &= ~do_randomize_port;
  371. }
  372. if (sflags & do_secret) {
  373. if (stun_obtain_shared_secret(sh, stunc_ss_cb, stunc, TAG_NULL()) < 0) {
  374. SU_DEBUG_3(("%s: %s failed\n", __func__,
  375. "stun_handle_request_shared_secret()"));
  376. return -1;
  377. }
  378. }
  379. /* If we want to bind and no integrity required */
  380. if ((sflags & do_bind) && !(sflags & do_secret)) {
  381. err = stun_bind(sh, stunc_bind_cb, stunc,
  382. STUNTAG_SOCKET(s),
  383. STUNTAG_REGISTER_EVENTS(1),
  384. TAG_NULL());
  385. if (err < 0) {
  386. SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_bind()"));
  387. return -1;
  388. }
  389. }
  390. if (sflags & do_nat_check) {
  391. err = stun_test_nattype(sh, stunc_nattype_cb, stunc,
  392. STUNTAG_REGISTER_EVENTS(1),
  393. STUNTAG_SOCKET(stunc->sc_socket),
  394. TAG_NULL());
  395. if (err < 0) {
  396. SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_test_nattype()"));
  397. su_root_break(stun_root(sh));
  398. }
  399. }
  400. if (sflags & do_life_check) {
  401. err = stun_test_lifetime(sh, stunc_lifetime_cb, stunc,
  402. STUNTAG_REGISTER_EVENTS(1),
  403. STUNTAG_SOCKET(stunc->sc_socket),
  404. TAG_NULL());
  405. if (err < 0) {
  406. SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_test_lifetime()"));
  407. su_root_break(stun_root(sh));
  408. }
  409. }
  410. if (err == 0)
  411. su_root_run(root);
  412. stun_handle_destroy(sh);
  413. su_root_destroy(root);
  414. return 0;
  415. }