123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- /*
- * This file is part of the Sofia-SIP package
- *
- * Copyright (C) 2005,2006 Nokia Corporation.
- *
- * Contact: Pekka Pessi <pekka.pessi@nokia.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
- /**
- * STUN test client
- *
- * @author Pekka Pessi <Pekka.Pessi@nokia.com>
- * @author Martti Mela <Martti.Mela@nokia.com>
- * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
- *
- * @date Created: Thu Jul 24 17:21:00 2003 ppessi
- */
- /**@page stunc STUN test client.
- *
- * @section stunc_synopsis Synopsis
- * <tt>stunc [OPTIONS] \<stun-server-address\></tt>
- *
- * @section stunc_description Description
- * The @em stunc utility can be used to gather information about possible
- * NAT devices that are located between the client and STUN server.
- *
- * @em stunc can provide the following information: the IP address and
- * port as seen by the STUN server, detecting presence of NATs, and
- * hints on the type of address translation done. It should be noted
- * that the results of NAT type and life-time detection should be
- * considered as hints. There is no guarantee that NAT(s) will handle
- * future packets in the same way.
- *
- * @section stunc_options Command Line Options
- * The @em stunc utility accepts following command line options:
- *
- * <dl>
- *
- * <dt>-b</dt>
- * <dd>Perform a STUN binding discovery. @em stunc will report the
- * client transport address (IP:port) as seen by the STUN server. In
- * the presence of NATs, this address is allocated by the NAT closest
- * to the STUN server.
- * </dd>
- *
- * <dt>-l</dt>
- * <dd>Perform a STUN binding life-time check.
- * </dd>
- *
- * <dt>-n</dt>
- * <dd>Perform a STUN binding type check. Notice that the results
- * are only hints. Nondeterministic behaviour, resource exhaustion,
- * or reboots of network elements can cause changes in NAT behaviour
- * between successive runs of stunc.
- * </dd>
- *
- * <dt>-r</dt>
- * <dd>Randomize the local port. Otherwise @em stunc let's the
- * operating system select a free port.
- * </dd>
- *
- * <dt>-s</dt>
- * <dd>Request a shared-secret over TLS. Tests whether the STUN server
- * supports the shared-secret mechanism (needed to protect message
- * integrity). Can be combined with @em -b, @em -l and @em -n.
- * </dd>
- *
- * </dl>
- *
- * @section stunc_return Return Codes
- * <table>
- * <tr><td>0</td><td>when successful</td></tr>
- * <tr><td>1</td><td>when any errors detected</td></tr>
- * </table>
- *
- * @section stunc_examples Examples
- *
- * Discover the NAT binding, use a random local port:
- * @code
- * $ stunc stunserver.org -b -r
- * @endcode
- *
- * @section stunc_environment Environment
- * #STUN_DEBUG
- *
- * @section stunc_bugs Reporting Bugs
- * Report bugs to <sofia-sip-devel@lists.sourceforge.net>.
- *
- * @section stunc_author Authors
- * - Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
- * - Martti Mela <martti -dot mela -at- nokia -dot- com>
- * - Kai Vehmanen <kai -dot vehmanen -at- nokia -dot- com>
- *
- * @section stunc_copyright Copyright
- * Copyright (C) 2005,2006 Nokia Corporation.
- *
- * This program is free software; see the source for copying conditions.
- * There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE.
- */
- #include "config.h"
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <time.h>
- typedef struct stunc_s stunc_t;
- #define SU_ROOT_MAGIC stunc_t
- #define STUN_MAGIC_T stunc_t
- #define STUN_DISCOVERY_MAGIC_T stunc_t
- #include "sofia-sip/stun.h"
- #include "sofia-sip/stun_tag.h"
- #include "sofia-sip/sofia_features.h"
- #include <sofia-sip/su.h>
- enum {
- do_secret = 1,
- do_bind = 2,
- do_nat_check = 4,
- do_life_check = 8,
- do_randomize_port = 16
- };
- #if HAVE_FUNC
- #elif HAVE_FUNCTION
- #define __func__ __FUNCTION__
- #else
- static char const __func__[] = "stunc";
- #endif
- #ifndef SU_DEBUG
- #define SU_DEBUG 0
- #endif
- #define SU_LOG (stun_log)
- #include <sofia-sip/su_debug.h>
- void usage(char *name)
- {
- fprintf(stderr,
- "stunc (%s)\n"
- "usage: %s <server> [-b] [-n] [-l] [-r] [-s]\n"
- " -b\tmake a binding request\n"
- " -l\tperform NAT lifetime check\n"
- " -n\tperform NAT type check\n"
- " -r\trandomize the local port\n",
- " -s\trequest shared-secret over TLS (combined with -[bln])\n"
- SOFIA_SIP_NAME_VERSION, name);
- exit(1);
- }
- struct stunc_s {
- su_socket_t sc_socket;
- int sc_flags;
- };
- static
- void stunc_lifetime_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event);
- static
- void stunc_nattype_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event);
- static
- void stunc_bind_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event);
- static
- void stunc_ss_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event)
- {
- int err;
- SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
- stunc->sc_flags &= ~do_secret;
- if (!stunc->sc_flags)
- su_root_break(stun_root(sh));
- switch (event) {
- case stun_tls_done:
- if (stunc->sc_flags & do_bind) {
- err = stun_bind(sh, stunc_bind_cb, stunc,
- STUNTAG_SOCKET(stunc->sc_socket),
- STUNTAG_REGISTER_EVENTS(1),
- TAG_NULL());
- if (err < 0) {
- SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_handle_bind()"));
- su_root_break(stun_root(sh));
- }
- }
- break;
- case stun_tls_connection_failed:
- SU_DEBUG_0(("%s: Obtaining shared secret failed.\n",
- __func__));
- stunc->sc_flags &= ~do_bind;
- if (!stunc->sc_flags)
- su_root_break(stun_root(sh));
- break;
- case stun_tls_connection_timeout:
- SU_DEBUG_0(("%s: Timeout when obtaining shared secret.\n",
- __func__));
- stunc->sc_flags &= ~do_bind;
- break;
- default:
- break;
- }
- return;
- }
- static
- void stunc_bind_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event)
- {
- su_sockaddr_t sa[1];
- char ipaddr[48];
- socklen_t addrlen;
- SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
- stunc->sc_flags &= ~do_bind;
- if (!stunc->sc_flags)
- su_root_break(stun_root(sh));
- switch (event) {
- case stun_discovery_done:
- addrlen = sizeof(*sa);
- memset(sa, 0, addrlen);
- if (stun_discovery_get_address(sd, sa, &addrlen) < 0) {
- SU_DEBUG_0(("%s: stun_discovery_get_address() failed", __func__));
- return;
- }
- SU_DEBUG_0(("%s: local address NATed as %s:%u\n", __func__,
- su_inet_ntop(sa->su_family, SU_ADDR(sa),
- ipaddr, sizeof(ipaddr)),
- (unsigned) ntohs(sa->su_port)));
- break;
- case stun_discovery_timeout:
- case stun_discovery_error:
- case stun_error:
- default:
- break;
- }
- return;
- }
- static
- void stunc_nattype_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event)
- {
- SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
- stunc->sc_flags &= ~do_nat_check;
- if (!stunc->sc_flags)
- su_root_break(stun_root(sh));
- switch (event) {
- case stun_discovery_timeout:
- SU_DEBUG_3(("%s: NAT type determination timeout.\n", __func__));
- break;
- case stun_discovery_done:
- SU_DEBUG_3(("%s: NAT type determined to be '%s' (%d).\n",
- __func__, stun_nattype_str(sd), (int)stun_nattype(sd)));
- break;
- case stun_error:
- default:
- break;
- }
- return;
- }
- static
- void stunc_lifetime_cb(stunc_t *stunc,
- stun_handle_t *sh,
- stun_discovery_t *sd,
- stun_action_t action,
- stun_state_t event)
- {
- SU_DEBUG_3(("%s: %s\n", __func__, stun_str_state(event)));
- stunc->sc_flags &= ~do_life_check;
- if (!stunc->sc_flags)
- su_root_break(stun_root(sh));
- switch (event) {
- case stun_discovery_timeout:
- SU_DEBUG_3(("%s: Lifetime determination timeout.\n", __func__));
- break;
- case stun_discovery_done:
- SU_DEBUG_3(("%s: Lifetime determined to be %d.\n", __func__, stun_lifetime(sd)));
- break;
- case stun_error:
- default:
- break;
- }
- return;
- }
- int main(int argc, char *argv[])
- {
- int err = 0, i, sflags = 0;
- stunc_t stunc[1];
- su_root_t *root;
- stun_handle_t *sh;
- su_socket_t s;
- if (su_init() != 0)
- return -1;
- root = su_root_create(stunc);
- if (argc < 3)
- usage(argv[0]);
- for (i = 2; argv[i]; i++) {
- if (strcmp(argv[i], "-s") == 0)
- sflags |= do_secret;
- else if (strcmp(argv[i], "-b") == 0)
- sflags |= do_bind;
- else if (strcmp(argv[i], "-n") == 0)
- sflags |= do_nat_check;
- else if (strcmp(argv[i], "-l") == 0)
- sflags |= do_life_check;
- else if (strcmp(argv[i], "-r") == 0)
- sflags |= do_randomize_port;
- else {
- fprintf(stderr, "Unable to parse option %s.\n", argv[i]);
- usage(argv[0]);
- }
- }
- /* Running this test requires a local STUN server on default port */
- sh = stun_handle_init(root,
- STUNTAG_SERVER(argv[1]),
- STUNTAG_REQUIRE_INTEGRITY(sflags & do_secret),
- TAG_NULL());
- if (!sh) {
- SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_handle_init()"));
- return -1;
- }
- s = su_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (s == -1) {
- SU_DEBUG_0(("%s: %s failed: %s\n", __func__,
- "su_socket()", su_gli_strerror(errno)));
- return -1;
- }
- stunc->sc_socket = s;
- stunc->sc_flags = sflags;
- if (sflags & do_randomize_port) {
- su_sockaddr_t sockaddr;
- char ipaddr[SU_ADDRSIZE + 2] = { 0 };
- socklen_t socklen = sizeof(sockaddr);
- srand((unsigned int)time((time_t *)NULL));
- memset(&sockaddr, 0, sizeof(su_sockaddr_t));
- sockaddr.su_port = htons((rand() % (65536 - 1024)) + 1024);
- sockaddr.su_family = AF_INET;
- SU_DEBUG_3(("stunc: Binding to local port %u.\n", ntohs(sockaddr.su_port)));
- err = bind(s, (struct sockaddr *)&sockaddr, socklen);
- if (err < 0) {
- SU_DEBUG_1(("%s: Error %d binding to %s:%u\n", __func__, err,
- su_inet_ntop(sockaddr.su_family, SU_ADDR(&sockaddr),
- ipaddr, sizeof(ipaddr)),
- (unsigned) ntohs(sockaddr.su_port)));
- return -1;
- }
- stunc->sc_flags &= ~do_randomize_port;
- }
- if (sflags & do_secret) {
- if (stun_obtain_shared_secret(sh, stunc_ss_cb, stunc, TAG_NULL()) < 0) {
- SU_DEBUG_3(("%s: %s failed\n", __func__,
- "stun_handle_request_shared_secret()"));
- return -1;
- }
- }
- /* If we want to bind and no integrity required */
- if ((sflags & do_bind) && !(sflags & do_secret)) {
- err = stun_bind(sh, stunc_bind_cb, stunc,
- STUNTAG_SOCKET(s),
- STUNTAG_REGISTER_EVENTS(1),
- TAG_NULL());
- if (err < 0) {
- SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_bind()"));
- return -1;
- }
- }
- if (sflags & do_nat_check) {
- err = stun_test_nattype(sh, stunc_nattype_cb, stunc,
- STUNTAG_REGISTER_EVENTS(1),
- STUNTAG_SOCKET(stunc->sc_socket),
- TAG_NULL());
- if (err < 0) {
- SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_test_nattype()"));
- su_root_break(stun_root(sh));
- }
- }
- if (sflags & do_life_check) {
- err = stun_test_lifetime(sh, stunc_lifetime_cb, stunc,
- STUNTAG_REGISTER_EVENTS(1),
- STUNTAG_SOCKET(stunc->sc_socket),
- TAG_NULL());
- if (err < 0) {
- SU_DEBUG_0(("%s: %s failed\n", __func__, "stun_test_lifetime()"));
- su_root_break(stun_root(sh));
- }
- }
- if (err == 0)
- su_root_run(root);
- stun_handle_destroy(sh);
- su_root_destroy(root);
- return 0;
- }
|