stun.c 80.4 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3
/*
 * This file is part of the Sofia-SIP package
 *
4
 * Copyright (C) 2005-2006 Nokia Corporation.
Pekka Pessi's avatar
Pekka Pessi committed
5 6 7
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 * 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
 *
 */

/**
 * @file stun.c STUN client module
 *
28
 * See RFC 3489/3489bis for further information.
Pekka Pessi's avatar
Pekka Pessi committed
29
 *
30
 * @author Martti Mela <Martti.Mela@nokia.com>
Martti Mela's avatar
Martti Mela committed
31
 * @author Tat Chan <Tat.Chan@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
32 33 34 35 36 37 38 39
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
 * 
 * @date Created: Thu Jul 24 17:21:00 2003 ppessi
 */

#include "config.h" 

40 41
#include <assert.h>

42
#define SU_ROOT_MAGIC_T struct stun_magic_t
43

44
#include "sofia-sip/stun.h"
45
#include "stun_internal.h"
46
#include "sofia-sip/stun_tag.h"
Pekka Pessi's avatar
Pekka Pessi committed
47

48 49 50 51 52
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_log.h>
#include <sofia-sip/su.h>
#include <sofia-sip/su_localinfo.h>
Pekka Pessi's avatar
Pekka Pessi committed
53

Martti Mela's avatar
Martti Mela committed
54
#if defined(HAVE_OPENSSL)
Pekka Pessi's avatar
Pekka Pessi committed
55
#include <openssl/opensslv.h>
Martti Mela's avatar
Martti Mela committed
56
#endif
Pekka Pessi's avatar
Pekka Pessi committed
57

58 59 60 61
#if !defined(ETIMEDOUT) && defined(_WIN32)
#define ETIMEDOUT WSAETIMEDOUT
#endif

Pekka Pessi's avatar
Pekka Pessi committed
62 63 64 65 66
/* Missing socket symbols */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif

Pekka Pessi's avatar
Pekka Pessi committed
67
/** STUN log. */
68
su_log_t stun_log[] = { SU_LOG_INIT("stun", "STUN_DEBUG", SU_DEBUG) }; 
Pekka Pessi's avatar
Pekka Pessi committed
69

70
enum {
71
  STUN_SENDTO_TIMEOUT = 1000,
Martti Mela's avatar
Martti Mela committed
72
  STUN_TLS_CONNECT_TIMEOUT = 8000,
73 74
};

75

76 77
#if 0 /* XXX: not used at the moment (2006/03) */
static int stun_change_map[4][4] = {
Martti Mela's avatar
Martti Mela committed
78 79 80 81 82
  {0, 1, 2, 3}, /* no change */
  {2, 3, 0, 1}, /* change ip */
  {1, 0, 3, 2}, /* change port */
  {3, 2, 1, 0}  /* change ip and port, Ca:Cp */
};
83
#endif
Martti Mela's avatar
Martti Mela committed
84 85


86 87 88 89 90 91 92 93 94 95 96 97
/* NAT TYPES */
typedef enum stun_nattype_e {
  stun_nat_unknown,
  stun_open_internet,
  stun_udp_blocked,
  stun_sym_udp_fw,
  stun_nat_full_cone,
  stun_nat_sym,
  stun_nat_res_cone,
  stun_nat_port_res_cone,
} stun_nattype_t;

98 99 100
#define CHG_IP		0x001
#define CHG_PORT	0x004

101 102 103 104 105 106 107 108 109 110
#define x_insert(l, n, x) \
 ((l) ? (l)->x##_prev = &(n)->x##_next : 0, \
  (n)->x##_next = (l), (n)->x##_prev = &(l), (l) = (n))

#define x_remove(n, x) \
  ((*(n)->x##_prev = (n)->x##_next) ? \
   (n)->x##_next->x##_prev = (n)->x##_prev : 0)

#define x_is_inserted(n, x) ((n)->x##_prev != NULL)

111
struct stun_discovery_s {
112
  stun_discovery_t   *sd_next, **sd_prev; /**< Linked list */
113

114 115 116
  stun_handle_t          *sd_handle;
  stun_discovery_f        sd_callback;
  stun_discovery_magic_t *sd_magic;
117

118 119 120 121 122 123 124 125 126 127
  su_addrinfo_t    sd_pri_info;      /**< server primary info */
  su_sockaddr_t    sd_pri_addr[1];   /**< server primary address */

  su_addrinfo_t    sd_sec_info;      /**< server secondary info */
  su_sockaddr_t    sd_sec_addr[1];   /**< server secondary address */

  stun_action_t    sd_action;        /**< My action */
  stun_state_t     sd_state;         /**< Progress states */

  su_socket_t      sd_socket;        /**< target socket */
128 129
  su_sockaddr_t    sd_bind_addr[1]; /**< local address */

130
  su_socket_t      sd_socket2;       /**< Alternative socket */
131

132
  int              sd_index;         /**< root_register index */
133

Martti Mela's avatar
Martti Mela committed
134
  /* Binding discovery */
135
  su_sockaddr_t    sd_addr_seen_outside[1];   /**< local address */
Martti Mela's avatar
Martti Mela committed
136

137
  /* NAT type related */
138
  stun_nattype_t   sd_nattype;       /**< Determined NAT type */
139 140 141 142 143
  unsigned         sd_first:1;       /**< These are the requests  */
  unsigned         sd_second:1;
  unsigned         sd_third:1;
  unsigned         sd_fourth:1;
  unsigned         :0;
144 145 146 147 148

  /* Life time related */
  int              sd_lt_cur;
  int              sd_lt;
  int              sd_lt_max;
Martti Mela's avatar
Martti Mela committed
149 150 151 152

  /* Keepalive timeout */
  unsigned int     sd_timeout;
  su_timer_t      *sd_timer;
153 154
};

155 156 157 158 159
struct stun_request_s {
  stun_request_t   *sr_next, **sr_prev; /**< Linked list */
  stun_msg_t       *sr_msg;             /**< STUN message pointer */
  stun_handle_t    *sr_handle;          /**< backpointer, STUN object */

160 161 162
  su_socket_t       sr_socket;          /**< Alternative socket */
  su_localinfo_t    sr_localinfo;       /**< local addrinfo */
  su_sockaddr_t     sr_local_addr[1];   /**< local address */
Martti Mela's avatar
Martti Mela committed
163
  su_sockaddr_t     sr_destination[1];
164

165
  stun_state_t      sr_state;           /**< Progress states */
166 167
  int               sr_retry_count;     /**< current retry number */
  long              sr_timeout;         /**< timeout for next sendto() */
168 169

  int               sr_from_y;
170 171 172
  int               sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
  stun_discovery_t *sr_discovery;
};
173

Martti Mela's avatar
Martti Mela committed
174
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
175
{
176 177 178
  su_home_t       sh_home[1];
  su_root_t      *sh_root;          /**< event loop */
  int             sh_root_index;    /**< object index of su_root_register() */
179

180
  stun_request_t *sh_requests; /**< outgoing requests list */
181
  stun_discovery_t *sh_discoveries; /**< Actions list */
182

183
  int             sh_max_retries;   /**< max resend for sendto() */
184

185 186
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
187

188 189
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
190

191 192
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
193

194 195 196 197 198 199 200
  char           *sh_domain;        /**< domain address for DNS-SRV lookups */

  stun_request_t     *sh_dns_pend_req;
  stun_discovery_t   *sh_dns_pend_sd; 
  stun_action_t       sh_dns_pend_action; 
  stun_dns_lookup_t  *sh_dns_lookup;

Martti Mela's avatar
Martti Mela committed
201
#if defined(HAVE_OPENSSL)
202 203
  SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
  SSL            *sh_ssl;           /**< SSL handle for TLS */
Martti Mela's avatar
Martti Mela committed
204 205 206 207 208
#else
  void           *sh_ctx;           /**< SSL context for TLS */
  void           *sh_ssl;           /**< SSL handle for TLS */
#endif

209 210
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
211
  int             sh_nattype;       /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
212

213 214
  stun_event_f    sh_callback;      /**< callback for calling application */ 
  stun_magic_t   *sh_context;       /**< application context */
215

216 217
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
218

219 220
  int             sh_use_msgint;    /**< try message integrity (TLS) */
  int             sh_req_msgint;    /**< require use of msg-int (TLS) */
Pekka Pessi's avatar
Pekka Pessi committed
221 222
};

Martti Mela's avatar
Martti Mela committed
223

224 225
#define STUN_STATE_STR(x) case x: return #x

226
char const *stun_str_state(stun_state_t state)
227 228 229
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
230
  STUN_STATE_STR(stun_dispose_me);
231 232 233 234 235
  STUN_STATE_STR(stun_tls_connecting);
  STUN_STATE_STR(stun_tls_writing);
  STUN_STATE_STR(stun_tls_closing);
  STUN_STATE_STR(stun_tls_reading);
  STUN_STATE_STR(stun_tls_done);
236 237 238
  STUN_STATE_STR(stun_discovery_init);
  STUN_STATE_STR(stun_discovery_processing);
  STUN_STATE_STR(stun_discovery_done);
239 240 241 242 243 244 245 246 247
  STUN_STATE_STR(stun_bind_init);
  STUN_STATE_STR(stun_bind_processing);
  STUN_STATE_STR(stun_bind_done);
  STUN_STATE_STR(stun_tls_connection_timeout);
  STUN_STATE_STR(stun_tls_connection_failed);
  STUN_STATE_STR(stun_tls_ssl_connect_failed);
  STUN_STATE_STR(stun_request_not_found);
  STUN_STATE_STR(stun_bind_error);
  STUN_STATE_STR(stun_bind_timeout);
248
  STUN_STATE_STR(stun_discovery_timeout);
249
  STUN_STATE_STR(stun_request_timeout);
250 251 252 253 254 255
  
  case stun_error:
  default: return "stun_error";
  }
}

256 257 258
/** 
 * Returns the NAT type attached to STUN discovery handle.
 */
259
char const *stun_nattype(stun_discovery_t *sd)
260
{
Martti Mela's avatar
Martti Mela committed
261 262 263 264 265 266 267 268 269 270 271
  char const *stun_nattype_str[] = {
    "NAT type undetermined",
    "Open Internet",
    "UDP traffic is blocked or server unreachable",
    "Symmetric UDP Firewall",
    "Full-Cone NAT",
    "Symmetric NAT",
    "Restricted Cone NAT",
    "Port Restricted Cone NAT",
  };

Pekka Pessi's avatar
Pekka Pessi committed
272 273 274 275
  if (sd)
    return stun_nattype_str[sd->sd_nattype];
  else
    return stun_nattype_str[stun_nat_unknown];
276 277 278
}


279 280
int stun_lifetime(stun_discovery_t *sd)
{
Pekka Pessi's avatar
Pekka Pessi committed
281
  return sd ? sd->sd_lt_cur : -1;
282 283 284
}


Martti Mela's avatar
Martti Mela committed
285
#if defined(HAVE_OPENSSL)
Pekka Pessi's avatar
Pekka Pessi committed
286
char const stun_version[] = 
Pekka Pessi's avatar
Pekka Pessi committed
287
 "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
Martti Mela's avatar
Martti Mela committed
288 289 290 291
#else
char const stun_version[] = 
 "sofia-sip-stun";
#endif
Pekka Pessi's avatar
Pekka Pessi committed
292

293 294 295 296 297 298 299 300 301 302 303 304 305 306
static int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
static int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
static int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
static stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					       stun_action_t action,
					       stun_discovery_f sdf,
					       stun_discovery_magic_t *magic);
static int stun_discovery_destroy(stun_discovery_t *sd);
static int action_bind(stun_request_t *req, stun_msg_t *binding_response);
static int action_determine_nattype(stun_request_t *req, stun_msg_t *binding_response);
static int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);

static stun_request_t *stun_request_create(stun_discovery_t *sd);
static int stun_send_binding_request(stun_request_t *req,
307
			      su_sockaddr_t *srvr_addr);
308
static int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
309 310

/* timers */
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
static void stun_sendto_timer_cb(su_root_magic_t *magic, 
				 su_timer_t *t,
				 su_timer_arg_t *arg);
static void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
				      su_timer_t *t,
				      su_timer_arg_t *arg);
static void stun_get_lifetime_timer_cb(su_root_magic_t *magic, 
				       su_timer_t *t,
				       su_timer_arg_t *arg);
static void stun_keepalive_timer_cb(su_root_magic_t *magic, 
				    su_timer_t *t,
				    su_timer_arg_t *arg);


static int priv_stun_bind_send(stun_handle_t *sh, stun_request_t *req, stun_discovery_t *sd);
326 327
static int priv_dns_queue_bind(stun_handle_t *sh, stun_request_t *req, stun_discovery_t *sd);
static int priv_dns_queue_shared_secret(stun_handle_t *sh);
328
static int priv_stun_bind_send(stun_handle_t *sh, stun_request_t *req, stun_discovery_t *sd);
Martti Mela's avatar
Martti Mela committed
329

330 331 332 333 334 335
/* Deprecated. Use stun_root(). */
su_root_t *stun_handle_root(stun_handle_t *self)
{
  return stun_root(self);
}

Martti Mela's avatar
Martti Mela committed
336 337
/**
 * Return su_root_t assigned to stun_handle_t.
338
 *
Martti Mela's avatar
Martti Mela committed
339
 * @param self stun_handle_t object
340 341
 * @return su_root_t object, NULL if self not given.
 */
342
su_root_t *stun_root(stun_handle_t *self)
343
{
344
  return self ? self->sh_root : NULL;
345 346 347
}


Pekka Pessi's avatar
Pekka Pessi committed
348
/**
Martti Mela's avatar
Martti Mela committed
349
 * Check if a STUN handle should be created.
Pekka Pessi's avatar
Pekka Pessi committed
350
 *
351 352 353 354 355 356 357
 * Return true if STUNTAG_SERVER() or STUNTAG_DOMAIN() tags have 
 * been specified, or otherwise if STUN_SERVER environment variable 
 * is set.
 *
 * @TAGS
 * @TAG STUNTAG_DOMAIN() domain to use in DNS-SRV based STUN server
 * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address
Pekka Pessi's avatar
Pekka Pessi committed
358 359 360 361 362 363
 *
 * @param tag,value,... tag-value list
 */
int stun_is_requested(tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
364
  tagi_t const *t, *t2;
Pekka Pessi's avatar
Pekka Pessi committed
365 366
  char const *stun_server;

Martti Mela's avatar
Martti Mela committed
367
  enter;
Martti Mela's avatar
Martti Mela committed
368

Pekka Pessi's avatar
Pekka Pessi committed
369 370
  ta_start(ta, tag, value);
  t = tl_find(ta_args(ta), stuntag_server);
371 372 373 374
  t2 = tl_find(ta_args(ta), stuntag_domain);
  if (t && t->t_value) 
    stun_server = (char *)t->t_value;
  else if (t2 && t2->t_value)
375
    stun_server = (char *)t2->t_value;
376 377
  else
    stun_server = getenv("STUN_SERVER");
Pekka Pessi's avatar
Pekka Pessi committed
378 379 380 381 382
  ta_end(ta);

  return stun_server != NULL;
}

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

/** 
 * Creates a STUN handle.
 *
 * The created handles can be used for STUN binding discovery, 
 * keepalives, and other STUN usages.
 *
 * @param root eventloop to used by the stun state machine
 * @param tag,value,... tag-value list 
 *
 * @TAGS
 * @TAG STUNTAG_DOMAIN() domain to use in DNS-SRV based STUN server
 * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address
 * @TAG STUNTAG_REQUIRE_INTEGRITY() true if msg integrity should be
 * used enforced
 *
 */
stun_handle_t *stun_handle_init(su_root_t *root,
				tag_type_t tag, tag_value_t value, ...)
{
  stun_handle_t *stun = NULL;
  char const *server = NULL, *domain = NULL;
  int req_msg_integrity = 1;
  int err;
  ta_list ta;
  
  enter;

  ta_start(ta, tag, value);

  tl_gets(ta_args(ta),
	  STUNTAG_SERVER_REF(server),
	  STUNTAG_DOMAIN_REF(domain),
	  STUNTAG_REQUIRE_INTEGRITY_REF(req_msg_integrity),
	  TAG_END());

  stun = su_home_clone(NULL, sizeof(*stun));

  if (!stun) {
    SU_DEBUG_3(("%s: %s failed\n", __func__, "su_home_clone()"));
    return NULL;
  }

  /* Enviroment overrides */
  if (getenv("STUN_SERVER")) {
    server = getenv("STUN_SERVER");
    SU_DEBUG_5(("%s: using STUN_SERVER=%s\n", __func__, server));
  }

  SU_DEBUG_5(("%s(\"%s\"): called\n", 
	      __func__, server));

  /* fail, if no server or a domain for a DNS-SRV lookup is specified */
  if (!server && !domain)
    return NULL;
  
  stun->sh_pri_info.ai_addrlen = 16;
  stun->sh_pri_info.ai_addr = &stun->sh_pri_addr->su_sa;

  stun->sh_sec_info.ai_addrlen = 16;
  stun->sh_sec_info.ai_addr = &stun->sh_sec_addr->su_sa;

  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;

  stun->sh_domain = su_strdup(stun->sh_home, domain);
  stun->sh_dns_lookup = NULL;
  
  if (server) {
    err = stun_atoaddr(AF_INET, &stun->sh_pri_info, server);

    if (err < 0)
      return NULL;
  }

  stun->sh_nattype = stun_nat_unknown;

  stun->sh_root     = root;
  /* always try TLS: */
  stun->sh_use_msgint = 1;
  /* whether use of shared-secret msgint is required */
  stun->sh_req_msgint = req_msg_integrity;

  stun->sh_max_retries = STUN_MAX_RETRX;

  /* initialize username and password */
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
  
  stun->sh_nattype = stun_nat_unknown;
  
  /* initialize random number generator */
  srand(time(NULL));
  
  ta_end(ta);

  return stun;
}


483
/** 
484
 * Creates a STUN handle.
485
 *
486 487 488 489 490 491
 * The created handles can be used for STUN binding discovery, 
 * keepalives, and other STUN usages.
 *
 * @param context self pointer for callback 'cb'
 * @param root eventloop to used by the stun state machine
 * @param cb callback to signal state machine events
492 493
 * @param tag,value,... tag-value list 
 *
Pekka Pessi's avatar
Pekka Pessi committed
494
 * @TAGS
495
 * @TAG STUNTAG_DOMAIN() domain to use in DNS-SRV based STUN server
496
 * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address
497 498
 * @TAG STUNTAG_REQUIRE_INTEGRITY() true if msg integrity should be
 * used enforced
499 500
 *
 */
501 502

/* Deprecated. Use stun_agent_create() */
Pekka Pessi's avatar
Pekka Pessi committed
503 504 505 506
stun_handle_t *stun_handle_create(stun_magic_t *context,
				  su_root_t *root,
				  stun_event_f cb,
				  tag_type_t tag, tag_value_t value, ...)
Pekka Pessi's avatar
Pekka Pessi committed
507
{
Martti Mela's avatar
Martti Mela committed
508
  stun_handle_t *stun = NULL;
509
  char const *server = NULL, *domain = NULL;
510
  int req_msg_integrity = 1;
511
  int err;
512
  ta_list ta;
513
  
Martti Mela's avatar
Martti Mela committed
514
  enter;
Martti Mela's avatar
Martti Mela committed
515

516 517 518 519
  ta_start(ta, tag, value);

  tl_gets(ta_args(ta),
	  STUNTAG_SERVER_REF(server),
520
	  STUNTAG_DOMAIN_REF(domain),
521
	  STUNTAG_REQUIRE_INTEGRITY_REF(req_msg_integrity),
522 523
	  TAG_END());

524 525 526 527 528 529 530
  stun = su_home_clone(NULL, sizeof(*stun));

  if (!stun) {
    SU_DEBUG_3(("%s: %s failed\n", __func__, "su_home_clone()"));
    return NULL;
  }

531 532 533
  /* Enviroment overrides */
  if (getenv("STUN_SERVER")) {
    server = getenv("STUN_SERVER");
534
    SU_DEBUG_5(("%s: using STUN_SERVER=%s\n", __func__, server));
535
  }
536

537
  SU_DEBUG_5(("%s(\"%s\"): called\n", 
Martti Mela's avatar
Martti Mela committed
538
	      "stun_handle_tcreate", server));
539

540 541
  /* fail, if no server or a domain for a DNS-SRV lookup is specified */
  if (!server && !domain)
542
    return NULL;
543
  
544 545
  stun->sh_pri_info.ai_addrlen = 16;
  stun->sh_pri_info.ai_addr = &stun->sh_pri_addr->su_sa;
Pekka Pessi's avatar
Pekka Pessi committed
546

547 548
  stun->sh_sec_info.ai_addrlen = 16;
  stun->sh_sec_info.ai_addr = &stun->sh_sec_addr->su_sa;
549

550 551
  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;
552

553 554 555 556 557
  stun->sh_domain = su_strdup(stun->sh_home, domain);
  stun->sh_dns_lookup = NULL;
  
  if (server) {
    err = stun_atoaddr(AF_INET, &stun->sh_pri_info, server);
558

559 560 561
    if (err < 0)
      return NULL;
  }
562

563
  stun->sh_nattype = stun_nat_unknown;
564

565 566 567
  stun->sh_root     = root;
  stun->sh_context  = context;
  stun->sh_callback = cb;
568
  /* always try TLS: */
Kai Vehmanen's avatar
Kai Vehmanen committed
569
  stun->sh_use_msgint = 1;
570 571
  /* whether use of shared-secret msgint is required */
  stun->sh_req_msgint = req_msg_integrity;
Pekka Pessi's avatar
Pekka Pessi committed
572

573
  stun->sh_max_retries = STUN_MAX_RETRX;
Martti Mela's avatar
Martti Mela committed
574

575
  /* initialize username and password */
576 577
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
578
  
579
  stun->sh_nattype = stun_nat_unknown;
580 581 582 583
  
  /* initialize random number generator */
  srand(time(NULL));
  
584 585
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
586 587 588
  return stun;
}

589 590 591 592

/* Deprecated. Use stun_request_shared_secret() */
int stun_handle_request_shared_secret(stun_handle_t *sh)
{
593
  return stun_obtain_shared_secret(sh, NULL, NULL, TAG_NULL());
594 595
}

596 597 598 599 600
/** 
 * Performs shared secret request/response processing.
 * Result will be trigged in STUN handle callback (state
 * one of stun_tls_*).
 **/
601 602 603 604
int stun_obtain_shared_secret(stun_handle_t *sh,
			      stun_discovery_f sdf,
			      stun_discovery_magic_t *magic,
			      tag_type_t tag, tag_value_t value, ...)
Martti Mela's avatar
Martti Mela committed
605
{
606
#if defined(HAVE_OPENSSL)
Martti Mela's avatar
Martti Mela committed
607 608 609 610 611 612
  int events = -1;
  int one, err = -1;
  su_wait_t wait[1] = { SU_WAIT_INIT };
  su_socket_t s = SOCKET_ERROR;
  int family;
  su_addrinfo_t *ai = NULL;
613
  stun_discovery_t *sd;
Martti Mela's avatar
Martti Mela committed
614
  /* stun_request_t *req; */
Martti Mela's avatar
Martti Mela committed
615 616

  assert(sh);
Martti Mela's avatar
Martti Mela committed
617

Martti Mela's avatar
Martti Mela committed
618
  enter;
Martti Mela's avatar
Martti Mela committed
619

620 621 622
  if (!sh->sh_pri_addr[0].su_port) {
    /* no STUN server address, perform a DNS-SRV lookup */
    SU_DEBUG_5(("Delaying STUN shared-secret req. for DNS-SRV query.\n"));
623
    return priv_dns_queue_shared_secret(sh);
624 625
  }

Martti Mela's avatar
Martti Mela committed
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
  ai = &sh->sh_pri_info;

  if (sh->sh_use_msgint == 1) {
    SU_DEBUG_3(("Contacting Server to obtain shared secret. " \
		"Please wait.\n"));
  }
  else {
    SU_DEBUG_3(("No message integrity enabled.\n"));
    return errno = EFAULT, -1;
  }

  /* open tcp connection to server */
  s = su_socket(family = AF_INET, SOCK_STREAM, 0);

  if (s == -1) {
    STUN_ERROR(errno, socket);
    return -1;
  }

  /* asynchronous connect() */
  if (su_setblocking(s, 0) < 0) {
    STUN_ERROR(errno, su_setblocking);
    return -1;
  }
650

Martti Mela's avatar
Martti Mela committed
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
  if (setsockopt(s, SOL_TCP, TCP_NODELAY,
		 (void *)&one, sizeof one) == -1) {
    STUN_ERROR(errno, setsockopt);
    return -1;
  }

  /* Do an asynchronous connect(). Three error codes are ok,
   * others cause return -1. */
  if (connect(s, (struct sockaddr *) &sh->sh_pri_addr, 
	      ai->ai_addrlen) == SOCKET_ERROR) {
    err = su_errno();
    if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) {
      STUN_ERROR(err, connect);
      return -1;
    }
  }

  SU_DEBUG_9(("%s: %s: %s\n", __func__, "connect",
	      su_strerror(err)));
  
671
  sd = stun_discovery_create(sh, stun_action_tls_query, sdf, magic);
Martti Mela's avatar
Martti Mela committed
672
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
673
  /* req = stun_request_create(sd); */
Martti Mela's avatar
Martti Mela committed
674

Martti Mela's avatar
Martti Mela committed
675
  events = SU_WAIT_CONNECT | SU_WAIT_ERR;
Martti Mela's avatar
Martti Mela committed
676 677 678 679
  if (su_wait_create(wait, s, events) == -1)
    STUN_ERROR(errno, su_wait_create);

  su_root_eventmask(sh->sh_root, sh->sh_root_index, s, events);
680 681

  if ((sd->sd_index =
Martti Mela's avatar
Martti Mela committed
682
       su_root_register(sh->sh_root, wait, stun_tls_callback, (su_wakeup_arg_t *) sd, 0)) == -1) {
Martti Mela's avatar
Martti Mela committed
683 684 685 686
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

687
  sd->sd_state = stun_tls_connecting;
Martti Mela's avatar
Martti Mela committed
688 689 690 691

  /* Create and start timer for connect() timeout */
  SU_DEBUG_3(("%s: creating timeout timer for connect()\n", __func__));

692 693
  sd->sd_timer = su_timer_create(su_root_task(sh->sh_root),
				 STUN_TLS_CONNECT_TIMEOUT);
Martti Mela's avatar
Martti Mela committed
694

695
  su_timer_set(sd->sd_timer, stun_tls_connect_timer_cb, (su_wakeup_arg_t *) sd);
Martti Mela's avatar
Martti Mela committed
696 697

  return 0;
698 699 700
#else /* !HAVE_OPENSSL */
  return -1;
#endif
Martti Mela's avatar
Martti Mela committed
701 702
}

703
static stun_request_t *stun_request_create(stun_discovery_t *sd)
704
{
705
  stun_handle_t *sh = sd->sd_handle;
706 707
  stun_request_t *req = NULL;

Martti Mela's avatar
Martti Mela committed
708
  enter;
Martti Mela's avatar
Martti Mela committed
709

710
  req = calloc(sizeof(stun_request_t), 1);
711 712
  if (!req)
    return NULL;
713

714
  req->sr_handle = sh;
715
  req->sr_discovery = sd;
716 717

  /* This is the default */
718
  req->sr_socket = sd->sd_socket;
719 720 721 722 723 724 725
  
  req->sr_localinfo.li_addrlen = sizeof(su_sockaddr_t);
  req->sr_localinfo.li_addr = req->sr_local_addr;
  
  /* default timeout for next sendto() */
  req->sr_timeout = STUN_SENDTO_TIMEOUT;
  req->sr_retry_count = 0;
726
  /* req->sr_action = action; */
727 728 729
  req->sr_request_mask = 0;
  
  req->sr_msg = calloc(sizeof(stun_msg_t), 1);
730

731
  req->sr_state = stun_discovery_init;
732
  memcpy(req->sr_local_addr, sd->sd_bind_addr, sizeof(su_sockaddr_t));
733

734 735 736 737 738 739
  /* Insert this request to the request queue */
  if (sh->sh_requests)
    x_insert(sh->sh_requests, req, sr);
  else
    sh->sh_requests = req;

740 741 742
  return req;
}

743
void stun_request_destroy(stun_request_t *req)
744
{
745
  stun_handle_t *sh;
746 747
  assert(req);

Martti Mela's avatar
Martti Mela committed
748
  enter;
Martti Mela's avatar
Martti Mela committed
749

750 751
  sh = req->sr_handle;

752 753 754
  if (x_is_inserted(req, sr))
    x_remove(req, sr);

755 756 757
  if (!x_is_inserted(req, sr) && !req->sr_next)
    sh->sh_requests = NULL;

758
  req->sr_handle = NULL;
759
  req->sr_discovery = NULL;
Martti Mela's avatar
Martti Mela committed
760
  /* memset(req->sr_destination, 0, sizeof(su_sockaddr_t)); */
761

762 763
  if (req->sr_msg)
    stun_free_message(req->sr_msg);
764

765
  free(req);
766 767 768

  SU_DEBUG_9(("%s: request destroyed.\n", __func__));

769 770 771
  return;
}

772

Pekka Pessi's avatar
Pekka Pessi committed
773
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
774
void stun_handle_destroy(stun_handle_t *sh)
Pekka Pessi's avatar
Pekka Pessi committed
775
{ 
Martti Mela's avatar
Martti Mela committed
776
  stun_discovery_t *sd = NULL, *kill = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
777

Martti Mela's avatar
Martti Mela committed
778
  enter;
Martti Mela's avatar
Martti Mela committed
779

780 781
  if (sh->sh_dns_lookup)
    stun_dns_lookup_destroy(sh->sh_dns_lookup);
782

Martti Mela's avatar
Martti Mela committed
783 784
  /* There can be several discoveries using the same socket. It is
     still enough to deregister the socket in first of them */
Martti Mela's avatar
Martti Mela committed
785 786 787
  for (sd = sh->sh_discoveries; sd; ) {
    kill = sd;
    sd = sd->sd_next;
Martti Mela's avatar
Martti Mela committed
788

789 790 791
    /* Index has same value as sockfd, right? ... or not? */
    if (kill->sd_index != -1)
      su_root_deregister(sh->sh_root, kill->sd_index);
Martti Mela's avatar
Martti Mela committed
792 793
    if (kill->sd_action == stun_action_tls_query)
      su_close(kill->sd_socket);
794 795

    stun_discovery_destroy(kill);
Martti Mela's avatar
Martti Mela committed
796
  }
Martti Mela's avatar
Martti Mela committed
797

Martti Mela's avatar
Martti Mela committed
798
  su_home_zap(sh->sh_home);
Martti Mela's avatar
Martti Mela committed
799 800
}

Pekka Pessi's avatar
Pekka Pessi committed
801

Pekka Pessi's avatar
Pekka Pessi committed
802
/** Create wait object and register it to the handle callback */
803
int assign_socket(stun_discovery_t *sd, su_socket_t s, int reg_socket) 
804
{
805
  stun_handle_t *sh = sd->sd_handle;
806
  int events;
Martti Mela's avatar
Martti Mela committed
807
  stun_discovery_t *tmp;
808 809 810 811 812 813
  /* su_localinfo_t clientinfo[1]; */
  su_sockaddr_t bind_addr;
  socklen_t bind_len;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };
  su_sockaddr_t *sa;
  int err;
814
  
815 816
  su_wait_t wait[1] = { SU_WAIT_INIT };

Martti Mela's avatar
Martti Mela committed
817 818
  enter;

819 820 821 822 823
  if (s == -1) {
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
    return errno = EINVAL, -1;
  }

Martti Mela's avatar
Martti Mela committed
824
  for (tmp = sh->sh_discoveries; tmp; tmp = tmp->sd_next) {
825 826 827 828 829 830
    if (tmp->sd_socket == s) {
      sd->sd_socket = s;
      sd->sd_index = tmp->sd_index;
      memcpy(sd->sd_bind_addr, tmp->sd_bind_addr, sizeof(su_sockaddr_t));
      return 0;
    }
Martti Mela's avatar
Martti Mela committed
831
  }
832
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
833

834 835 836
  if (reg_socket != 1)
    return 0;

837 838 839 840 841 842 843 844
  /* set socket asynchronous */
  if (su_setblocking(s, 0) < 0) {
    STUN_ERROR(errno, su_setblocking);

    su_close(s);
    return -1;
  }

845
  /* xxx -- check if socket is already assigned to this root */
846 847 848 849 850 851 852 853
  events = SU_WAIT_IN | SU_WAIT_ERR;

  if (su_wait_create(wait, s, events) == -1) {
    STUN_ERROR(su_errno(), su_wait_create);
    return -1;
  }

  /* Register receiving function with events specified above */
854 855
  if ((sd->sd_index = su_root_register(sh->sh_root,
				       wait, stun_bind_callback,
Martti Mela's avatar
Martti Mela committed
856
				       (su_wakeup_arg_t *) sd, 0)) < 0) {
857 858 859 860
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

Martti Mela's avatar
Martti Mela committed
861 862
  SU_DEBUG_7(("%s: socket registered.\n", __func__));

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
  bind_len = sizeof bind_addr;

  sa = (void *) &bind_addr;
  bind_len = sizeof bind_addr;
  memset(sa, 0, sizeof(bind_addr));
  /* if bound check the error */
  err = getsockname(s, (struct sockaddr *) sa, &bind_len);
  if (err < 0 && errno == SOCKET_ERROR) {
    STUN_ERROR(errno, getsockname);
    return -1;
  }

  /* Not bound - bind it */
  if (sa->su_port == 0) {
#if defined (__CYGWIN__)
    get_localinfo(clientinfo);
#endif

    if ((err = bind(s, (struct sockaddr *) sa, bind_len)) < 0) {
      STUN_ERROR(errno, bind);
      SU_DEBUG_3(("%s: Error binding to %s:%u\n", __func__, 
		  inet_ntop(sa->su_family, SU_ADDR(sa), 
			    ipaddr, sizeof(ipaddr)),
		  (unsigned) ntohs(sa->su_port)));
      return -1;
    }

    /* bind_len = clientinfo->li_addrlen; */
    /* clientinfo->li_addrlen = bind_len; */
    sa->su_len = bind_len; /* ? */
  }

  memcpy(&sd->sd_bind_addr, &bind_addr, sizeof bind_addr);

  if (getsockname(s, (struct sockaddr *) &bind_addr, &bind_len) != 0) {
    STUN_ERROR(errno, getsockname);
    return -1;
  }

  SU_DEBUG_3(("%s: local socket is bound to %s:%u\n", __func__,
	      inet_ntop(bind_addr.su_family, SU_ADDR(&bind_addr), 
			ipaddr, sizeof(ipaddr)),
	      (unsigned) ntohs(bind_addr.su_port)));


  return 0;
909 910
}

911

912 913 914 915
/**
 * Helper function needed by Cygwin builds.
 */
#if defined (__CYGWIN__)
916 917 918
static int get_localinfo(su_localinfo_t *clientinfo)
{
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
919
  su_sockaddr_t *sa;
920 921 922
  int i, error, found = 0;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };

923

Martti Mela's avatar
Martti Mela committed
924
  hints->li_family = AF_INET;
925
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
926 927 928 929 930 931 932
    
    /* try to bind to the first available address */
    for (i = 0, li = res; li; li = li->li_next) {
      if (li->li_family != AF_INET)
	continue;
      
      clientinfo->li_family = li->li_family;
Martti Mela's avatar
Martti Mela committed
933
      clientinfo->li_addrlen = li->li_addrlen;
934
      
935
      sa = clientinfo->li_addr;
Martti Mela's avatar
Martti Mela committed
936
      memcpy(sa, li->li_addr, sizeof(su_sockaddr_t));
937 938 939 940
      SU_DEBUG_3(("%s: local address found to be %s.\n",
		  __func__, 
		  inet_ntop(clientinfo->li_family, SU_ADDR(sa),
			    ipaddr, sizeof(ipaddr))));
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
      found = 1;
      break;
    }
    
    if (!found) {
      STUN_ERROR(error, su_getlocalinfo);
      return -1;
    }
  }
  else {
    STUN_ERROR(error, su_getlocalinfo);
    return -1;
  }
  if (res)
    su_freelocalinfo(res);

  return 0;
}
Pekka Pessi's avatar
Pekka Pessi committed
959
#endif
Martti Mela's avatar
Martti Mela committed
960

961 962 963 964 965 966 967 968 969 970 971 972 973 974
/* Deprecated. Use stun_bind() */
int stun_handle_bind(stun_handle_t *sh,
		     tag_type_t tag, tag_value_t value,
		     ...)
{
  int err;
  ta_list ta;
  ta_start(ta, tag, value);
  err = stun_bind(sh, NULL, NULL, ta_tags(ta));
  ta_end(ta);
  
  return err; 
}

975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004

static void priv_lookup_cb(stun_dns_lookup_t *self,
			   stun_magic_t *magic)
{
  const char *udp_target = NULL;
  uint16_t udp_port = 0;
  int res;
  stun_handle_t *sh = (stun_handle_t *)magic;

  res = stun_dns_lookup_get_results(self, 
				    NULL, NULL, 
				    &udp_target, &udp_port);
  if (res == 0) {
    /* XXX: assumption that same host and port used for UDP/TLS */
    if (udp_target) {

      stun_atoaddr(AF_INET, &sh->sh_pri_info, udp_target);
      
      if (udp_port) 
	sh->sh_pri_addr[0].su_port = htons(udp_port);
      else
	sh->sh_pri_addr[0].su_port = htons(STUN_DEFAULT_PORT);

      /* step: now that server address is known, continue 
       *       the pending action */


      SU_DEBUG_5(("STUN server address found, running queue actions (%d).\n",
		  sh->sh_dns_pend_action));

1005 1006
      switch(sh->sh_dns_pend_action) {
      case stun_action_tls_query:
1007
	stun_request_shared_secret(sh);
1008
	break;
1009

1010
      case stun_action_binding_request:
1011
	priv_stun_bind_send(sh, sh->sh_dns_pend_req, sh->sh_dns_pend_sd);
1012 1013 1014 1015 1016
	break;

      default:
	SU_DEBUG_5(("Warning: unknown pending DNS-SRV action.\n"));
      }
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026

      sh->sh_dns_pend_action = 0;
    }
  }
}

/**
 * Queus an stun bind action for later execution once
 * DNS-SRV lookup is succesfully completed.
 */
1027
static int priv_dns_queue_bind(stun_handle_t *sh, stun_request_t *req, stun_discovery_t *sd)
1028
{
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
  if (!sh->sh_dns_pend_action) {

    if (!sh->sh_dns_lookup) {
      sh->sh_dns_lookup = stun_dns_lookup((stun_magic_t*)sh, sh->sh_root, priv_lookup_cb, sh->sh_domain);
    }
    sh->sh_dns_pend_req = req;
    sh->sh_dns_pend_sd = sd;
    sh->sh_dns_pend_action |= stun_action_binding_request;

    return 0;
1039
  }
1040 1041
  
  return -1;
1042 1043 1044 1045 1046 1047
}

/**
 * Queus an stun shared-secret action for later execution 
 * once DNS-SRV lookup is succesfully completed.
 */
1048
static int priv_dns_queue_shared_secret(stun_handle_t *sh)
1049
{
Kai Vehmanen's avatar