stun.c 81 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
#include <assert.h>
41
#include <string.h>
42

43
#define SU_ROOT_MAGIC_T struct stun_magic_t
44

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

49 50 51 52 53
#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
54

55 56 57 58 59
/* avoid Open C complaints */
#if !HAVE_OPEN_C
#include <netinet/tcp.h>
#endif

Michael Jerris's avatar
Michael Jerris committed
60 61 62 63 64
#if HAVE_WINSOCK2_H
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

65
#if HAVE_OPENSSL
Pekka Pessi's avatar
Pekka Pessi committed
66
#include <openssl/opensslv.h>
Martti Mela's avatar
Martti Mela committed
67
#endif
Pekka Pessi's avatar
Pekka Pessi committed
68

69 70 71 72
#if !defined(ETIMEDOUT) && defined(_WIN32)
#define ETIMEDOUT WSAETIMEDOUT
#endif

Pekka Pessi's avatar
Pekka Pessi committed
73 74 75 76 77
/* Missing socket symbols */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif

78 79 80 81 82 83 84
#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
static char const __func__[] = "stun";
#endif

Pekka Pessi's avatar
Pekka Pessi committed
85
/** STUN log. */
Pekka Pessi's avatar
Pekka Pessi committed
86 87 88 89 90 91 92 93 94 95 96 97
su_log_t stun_log[] = { SU_LOG_INIT("stun", "STUN_DEBUG", 3) }; 

/**@var STUN_DEBUG
 *
 * Environment variable determining the debug log level for @b stun module.
 *
 * The STUN_DEBUG environment variable is used to determine the debug logging
 * level for @b stun module. The default level is 3.
 * 
 * @sa <sofia-sip/su_debug.h>, stun_log, SOFIA_DEBUG
 */
extern char const STUN__DEBUG[];
Pekka Pessi's avatar
Pekka Pessi committed
98

99
enum {
100
  STUN_SENDTO_TIMEOUT = 1000,
Martti Mela's avatar
Martti Mela committed
101
  STUN_TLS_CONNECT_TIMEOUT = 8000,
102 103
};

104 105 106 107 108 109 110 111 112 113 114 115 116
/**
 * States of STUN requests. See stun_state_e for states
 * discovery processes.
 */ 
typedef enum stun_req_state_e {
  stun_req_no_assigned_event,
  stun_req_dispose_me,            /**< request can be disposed */
  stun_req_discovery_init,
  stun_req_discovery_processing,

  stun_req_error,
  stun_req_timeout
} stun_req_state_t;
117

118 119 120
#define CHG_IP		0x001
#define CHG_PORT	0x004

121 122 123 124 125 126 127 128 129 130
#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)

131
struct stun_discovery_s {
132
  stun_discovery_t   *sd_next, **sd_prev; /**< Linked list */
133

134 135 136
  stun_handle_t          *sd_handle;
  stun_discovery_f        sd_callback;
  stun_discovery_magic_t *sd_magic;
137

138 139 140
  tagi_t          *sd_tags;          /** stored tags for the discovery */


141 142 143 144 145 146 147 148 149 150
  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 */
151 152
  su_sockaddr_t    sd_bind_addr[1]; /**< local address */

153
  su_socket_t      sd_socket2;       /**< Alternative socket */
154

155
  int              sd_index;         /**< root_register index */
156

Martti Mela's avatar
Martti Mela committed
157
  /* Binding discovery */
158
  su_sockaddr_t    sd_addr_seen_outside[1];   /**< local address */
Martti Mela's avatar
Martti Mela committed
159

160
  /* NAT type related */
161
  stun_nattype_t   sd_nattype;       /**< Determined NAT type */
162 163
  int              sd_mapped_addr_match; /** Mapped addresses match? */
  int              sd_first;         /**< These are the requests  */
164 165 166
  int              sd_second;
  int              sd_third;
  int              sd_fourth;
167 168 169 170 171

  /* Life time related */
  int              sd_lt_cur;
  int              sd_lt;
  int              sd_lt_max;
Martti Mela's avatar
Martti Mela committed
172 173 174 175

  /* Keepalive timeout */
  unsigned int     sd_timeout;
  su_timer_t      *sd_timer;
176 177
};

178
struct stun_request_s {
179
  su_timer_t       *sr_timer;
180 181 182 183
  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 */

184
  su_socket_t       sr_socket;          /**< Alternative socket */
185 186
  su_localinfo_t    sr_localinfo;       /**< local addrinfo */
  su_sockaddr_t     sr_local_addr[1];   /**< local address */
Martti Mela's avatar
Martti Mela committed
187
  su_sockaddr_t     sr_destination[1];
188

189
  stun_req_state_t  sr_state;           /**< Progress states */
190 191
  int               sr_retry_count;     /**< current retry number */
  long              sr_timeout;         /**< timeout for next sendto() */
192 193

  int               sr_from_y;
194 195 196
  int               sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
  stun_discovery_t *sr_discovery;
};
197

Martti Mela's avatar
Martti Mela committed
198
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
199
{
200 201 202
  su_home_t       sh_home[1];
  su_root_t      *sh_root;          /**< event loop */
  int             sh_root_index;    /**< object index of su_root_register() */
203

204
  stun_request_t *sh_requests; /**< outgoing requests list */
205
  stun_discovery_t *sh_discoveries; /**< Actions list */
206

207
  int             sh_max_retries;   /**< max resend for sendto() */
208

209 210
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
211

212 213
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
214

215 216
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
217

218 219
  char           *sh_domain;        /**< domain address for DNS-SRV lookups */

220 221
  stun_dns_lookup_t  *sh_dns_lookup;
  stun_action_t       sh_dns_pend_action; 
222 223 224
  stun_discovery_f    sh_dns_pend_cb;
  stun_discovery_magic_t *sh_dns_pend_ctx;
  tagi_t             *sh_dns_pend_tags;
225

226
#if HAVE_OPENSSL
227 228
  SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
  SSL            *sh_ssl;           /**< SSL handle for TLS */
Martti Mela's avatar
Martti Mela committed
229 230 231 232 233
#else
  void           *sh_ctx;           /**< SSL context for TLS */
  void           *sh_ssl;           /**< SSL handle for TLS */
#endif

234 235
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
236
  int             sh_nattype;       /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
237

238 239
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
240

241 242
  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
243 244
};

Martti Mela's avatar
Martti Mela committed
245

246 247
#define STUN_STATE_STR(x) case x: return #x

248
char const *stun_str_state(stun_state_t state)
249 250 251
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
252
  /* STUN_STATE_STR(stun_req_dispose_me); */
253 254 255 256 257
  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);
258 259
  /* STUN_STATE_STR(stun_req_discovery_init); */
  /* STUN_STATE_STR(stun_req_discovery_processing); */
260
  STUN_STATE_STR(stun_discovery_done);
261 262 263
  STUN_STATE_STR(stun_tls_connection_timeout);
  STUN_STATE_STR(stun_tls_connection_failed);
  STUN_STATE_STR(stun_tls_ssl_connect_failed);
264
  STUN_STATE_STR(stun_discovery_timeout);
265
  /* STUN_STATE_STR(stun_req_timeout); */
266 267 268 269 270 271
  
  case stun_error:
  default: return "stun_error";
  }
}

272 273
/** 
 * Returns the NAT type attached to STUN discovery handle.
274 275 276
 *
 * @see stun_nattype_str().
 * @see stun_test_nattype().
277
 */
278
char const *stun_nattype_str(stun_discovery_t *sd)
279
{
Martti Mela's avatar
Martti Mela committed
280 281 282 283 284
  char const *stun_nattype_str[] = {
    "NAT type undetermined",
    "Open Internet",
    "UDP traffic is blocked or server unreachable",
    "Symmetric UDP Firewall",
285 286 287 288 289 290
    "Full-Cone NAT (endpoint independent filtering and mapping)",
    "Restricted Cone NAT (endpoint independent mapping)",
    "Port Restricted Cone NAT (endpoint independent mapping)",
    "Endpoint independent filtering, endpoint dependent mapping",
    "Address dependent filtering, endpoint dependent mapping",
    "Symmetric NAT (address and port dependent filtering, endpoint dependent mapping)",
Martti Mela's avatar
Martti Mela committed
291 292
  };

Pekka Pessi's avatar
Pekka Pessi committed
293 294 295 296
  if (sd)
    return stun_nattype_str[sd->sd_nattype];
  else
    return stun_nattype_str[stun_nat_unknown];
297 298
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312
/**
 * Returns the detected NAT type.
 * 
 * @see stun_nattype_str().
 * @see stun_test_nattype().
 */
stun_nattype_t stun_nattype(stun_discovery_t *sd)
{
  if (!sd)
    return stun_nat_unknown;

  return sd->sd_nattype;
}

313 314 315 316
su_addrinfo_t const *stun_server_address(stun_handle_t *sh)
{
  return &sh->sh_pri_info;
}
317

318 319
int stun_lifetime(stun_discovery_t *sd)
{
Pekka Pessi's avatar
Pekka Pessi committed
320
  return sd ? sd->sd_lt_cur : -1;
321 322 323
}


324
#if HAVE_OPENSSL
Pekka Pessi's avatar
Pekka Pessi committed
325
char const stun_version[] = 
Pekka Pessi's avatar
Pekka Pessi committed
326
 "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
Martti Mela's avatar
Martti Mela committed
327 328 329 330
#else
char const stun_version[] = 
 "sofia-sip-stun";
#endif
Pekka Pessi's avatar
Pekka Pessi committed
331

332
static int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
333
#if HAVE_OPENSSL
334
static int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
335
#endif
336 337 338 339 340 341 342 343
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);
344
static int process_test_lifetime(stun_request_t *req, stun_msg_t *binding_response);
345 346 347

static stun_request_t *stun_request_create(stun_discovery_t *sd);
static int stun_send_binding_request(stun_request_t *req,
348
			      su_sockaddr_t *srvr_addr);
349
static int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
350

351
#if defined (__CYGWIN__)
352
static int get_localinfo(int family, su_sockaddr_t *su, socklen_t *return_len);
353
#endif
354

355
/* timers */
356 357 358
static void stun_sendto_timer_cb(su_root_magic_t *magic, 
				 su_timer_t *t,
				 su_timer_arg_t *arg);
359
#if HAVE_OPENSSL
360 361 362
static void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
				      su_timer_t *t,
				      su_timer_arg_t *arg);
363
#endif
364 365 366
static void stun_test_lifetime_timer_cb(su_root_magic_t *magic, 
					su_timer_t *t,
					su_timer_arg_t *arg);
367 368 369 370 371 372
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);
373 374 375 376 377
static int priv_dns_queue_action(stun_handle_t *sh,
				 stun_action_t action,
				 stun_discovery_f sdf,
				 stun_discovery_magic_t *magic,
				 tag_type_t tag, tag_value_t value, ...);
Martti Mela's avatar
Martti Mela committed
378 379 380

/**
 * Return su_root_t assigned to stun_handle_t.
381
 *
Martti Mela's avatar
Martti Mela committed
382
 * @param self stun_handle_t object
383 384
 * @return su_root_t object, NULL if self not given.
 */
385
su_root_t *stun_root(stun_handle_t *self)
386
{
387
  return self ? self->sh_root : NULL;
388 389 390
}


Pekka Pessi's avatar
Pekka Pessi committed
391
/**
Martti Mela's avatar
Martti Mela committed
392
 * Check if a STUN handle should be created.
Pekka Pessi's avatar
Pekka Pessi committed
393
 *
394 395 396 397 398 399 400
 * 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
401 402 403 404 405 406
 *
 * @param tag,value,... tag-value list
 */
int stun_is_requested(tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
407
  tagi_t const *t, *t2;
Pekka Pessi's avatar
Pekka Pessi committed
408 409
  char const *stun_server;

Martti Mela's avatar
Martti Mela committed
410
  enter;
Martti Mela's avatar
Martti Mela committed
411

Pekka Pessi's avatar
Pekka Pessi committed
412 413
  ta_start(ta, tag, value);
  t = tl_find(ta_args(ta), stuntag_server);
414 415 416 417
  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)
418
    stun_server = (char *)t2->t_value;
419 420
  else
    stun_server = getenv("STUN_SERVER");
Pekka Pessi's avatar
Pekka Pessi committed
421 422 423 424 425
  ta_end(ta);

  return stun_server != NULL;
}

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

/** 
 * 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());

462 463
  ta_end(ta);

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
  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 */
481 482
  if (!server && !domain) {
    errno = ENOENT;
483
    return NULL;
484
  }
485 486 487 488 489 490 491 492 493 494 495 496 497 498
  
  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) {
499
    err = stun_atoaddr(stun->sh_home, AF_INET, &stun->sh_pri_info, server);
500 501
    if (err < 0) {
      errno = ENOENT;
502
      return NULL;
503
    }
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
  }

  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));
524
 
525 526 527
  return stun;
}

528 529 530 531 532
/** 
 * Performs shared secret request/response processing.
 * Result will be trigged in STUN handle callback (state
 * one of stun_tls_*).
 **/
533 534 535 536
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
537
{
538
#if HAVE_OPENSSL
Martti Mela's avatar
Martti Mela committed
539 540 541
  int events = -1;
  int one, err = -1;
  su_wait_t wait[1] = { SU_WAIT_INIT };
542
  su_socket_t s = INVALID_SOCKET;
Martti Mela's avatar
Martti Mela committed
543 544
  int family;
  su_addrinfo_t *ai = NULL;
545
  stun_discovery_t *sd;
Martti Mela's avatar
Martti Mela committed
546
  /* stun_request_t *req; */
Martti Mela's avatar
Martti Mela committed
547

548 549
  ta_list ta;

Martti Mela's avatar
Martti Mela committed
550
  assert(sh);
Martti Mela's avatar
Martti Mela committed
551

Martti Mela's avatar
Martti Mela committed
552
  enter;
Martti Mela's avatar
Martti Mela committed
553

554 555
  if (!sh->sh_pri_addr[0].su_port) {
    /* no STUN server address, perform a DNS-SRV lookup */
556 557 558
   
    ta_list ta;
    ta_start(ta, tag, value);
559
    SU_DEBUG_5(("Delaying STUN shared-secret req. for DNS-SRV query.\n"));
560 561 562 563
    err = priv_dns_queue_action(sh, stun_action_tls_query, sdf, magic, ta_tags(ta));
    ta_end(ta);
       
    return err;
564 565
  }

Martti Mela's avatar
Martti Mela committed
566 567 568
  ai = &sh->sh_pri_info;

  if (sh->sh_use_msgint == 1) {
Martti Mela's avatar
Martti Mela committed
569
    SU_DEBUG_3(("%s: Obtaining shared secret.\n", __func__));
Martti Mela's avatar
Martti Mela committed
570 571 572 573 574 575 576 577 578
  }
  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);

579
  if (s == INVALID_SOCKET) {
580
    return STUN_ERROR(errno, socket);
Martti Mela's avatar
Martti Mela committed
581 582 583 584
  }

  /* asynchronous connect() */
  if (su_setblocking(s, 0) < 0) {
585
    return STUN_ERROR(errno, su_setblocking);
Martti Mela's avatar
Martti Mela committed
586
  }
587

Martti Mela's avatar
Martti Mela committed
588 589
  if (setsockopt(s, SOL_TCP, TCP_NODELAY,
		 (void *)&one, sizeof one) == -1) {
590
    return STUN_ERROR(errno, setsockopt);
Martti Mela's avatar
Martti Mela committed
591 592 593 594 595 596 597 598
  }

  /* 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) {
599
      return STUN_ERROR(err, connect);
Martti Mela's avatar
Martti Mela committed
600 601 602 603 604 605
    }
  }

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

Martti Mela's avatar
Martti Mela committed
610
  events = SU_WAIT_CONNECT | SU_WAIT_ERR;
Martti Mela's avatar
Martti Mela committed
611
  if (su_wait_create(wait, s, events) == -1)
612
    return STUN_ERROR(errno, su_wait_create);
Martti Mela's avatar
Martti Mela committed
613

Martti Mela's avatar
Martti Mela committed
614
  /* su_root_eventmask(sh->sh_root, sh->sh_root_index, s, events); */
615 616

  if ((sd->sd_index =
Martti Mela's avatar
Martti Mela committed
617
       su_root_register(sh->sh_root, wait, stun_tls_callback, (su_wakeup_arg_t *) sd, 0)) == -1) {
618
    return STUN_ERROR(errno, su_root_register);
Martti Mela's avatar
Martti Mela committed
619 620
  }

621 622 623 624
  ta_start(ta, tag, value);
  sd->sd_tags = tl_adup(sh->sh_home, ta_args(ta));
  ta_end(ta);

625
  sd->sd_state = stun_tls_connecting;
Martti Mela's avatar
Martti Mela committed
626 627 628 629

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

630 631
  sd->sd_timer = su_timer_create(su_root_task(sh->sh_root),
				 STUN_TLS_CONNECT_TIMEOUT);
Martti Mela's avatar
Martti Mela committed
632

633
  su_timer_set(sd->sd_timer, stun_tls_connect_timer_cb, (su_wakeup_arg_t *) sd);
Martti Mela's avatar
Martti Mela committed
634 635

  return 0;
636 637 638
#else /* !HAVE_OPENSSL */
  return -1;
#endif
Martti Mela's avatar
Martti Mela committed
639 640
}

641
static stun_request_t *stun_request_create(stun_discovery_t *sd)
642
{
643
  stun_handle_t *sh = sd->sd_handle;
644 645
  stun_request_t *req = NULL;

Martti Mela's avatar
Martti Mela committed
646
  enter;
Martti Mela's avatar
Martti Mela committed
647

648
  req = calloc(sizeof(stun_request_t), 1);
649 650
  if (!req)
    return NULL;
651

652
  req->sr_handle = sh;
653
  req->sr_discovery = sd;
654 655

  /* This is the default */
656
  req->sr_socket = sd->sd_socket;
657 658 659 660 661 662 663
  
  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;
664
  /* req->sr_action = action; */
665 666 667
  req->sr_request_mask = 0;
  
  req->sr_msg = calloc(sizeof(stun_msg_t), 1);
668

669
  req->sr_state = stun_req_discovery_init;
670
  memcpy(req->sr_local_addr, sd->sd_bind_addr, sizeof(su_sockaddr_t));
671

672
  /* Insert this request to the request queue */
Pekka Pessi's avatar
Pekka Pessi committed
673
  x_insert(sh->sh_requests, req, sr);
674

675 676 677
  return req;
}

678
void stun_request_destroy(stun_request_t *req)
679
{
680
  stun_handle_t *sh;
681 682
  assert(req);

Martti Mela's avatar
Martti Mela committed
683
  enter;
Martti Mela's avatar
Martti Mela committed
684

685 686
  sh = req->sr_handle;

687 688 689
  if (x_is_inserted(req, sr))
    x_remove(req, sr);

690
  req->sr_handle = NULL;
691
  req->sr_discovery = NULL;
Martti Mela's avatar
Martti Mela committed
692
  /* memset(req->sr_destination, 0, sizeof(su_sockaddr_t)); */
693

694 695 696 697 698 699 700 701 702 703
  if (req->sr_timer) {
    su_timer_destroy(req->sr_timer);
    req->sr_timer = NULL;
    SU_DEBUG_7(("%s: timer destroyed.\n", __func__));
  }

  if (req->sr_msg) {
    free(req->sr_msg);
    req->sr_msg = NULL;
  }
704

705
  free(req);
706 707 708

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

709 710 711
  return;
}

712

Pekka Pessi's avatar
Pekka Pessi committed
713
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
714
void stun_handle_destroy(stun_handle_t *sh)
Pekka Pessi's avatar
Pekka Pessi committed
715
{ 
Martti Mela's avatar
Martti Mela committed
716
  stun_discovery_t *sd = NULL, *kill = NULL;
717
  stun_request_t *a, *b;
Pekka Pessi's avatar
Pekka Pessi committed
718

Martti Mela's avatar
Martti Mela committed
719
  enter;
Martti Mela's avatar
Martti Mela committed
720

721 722
  if (sh->sh_dns_lookup)
    stun_dns_lookup_destroy(sh->sh_dns_lookup);
723

724 725 726
  if (sh->sh_dns_pend_tags)
    su_free(sh->sh_home, sh->sh_dns_pend_tags);

727 728 729 730 731 732 733
  for (a = sh->sh_requests; a; ) {
    b = a->sr_next;
    stun_request_destroy(a);
    a = b;
  }


Martti Mela's avatar
Martti Mela committed
734 735
  /* 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
736 737 738
  for (sd = sh->sh_discoveries; sd; ) {
    kill = sd;
    sd = sd->sd_next;
Martti Mela's avatar
Martti Mela committed
739

740 741 742
    /* 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
743 744
    if (kill->sd_action == stun_action_tls_query)
      su_close(kill->sd_socket);
745 746

    stun_discovery_destroy(kill);
Martti Mela's avatar
Martti Mela committed
747
  }
Martti Mela's avatar
Martti Mela committed
748

749 750 751 752 753
  stun_free_message(&sh->sh_tls_request);
  stun_free_message(&sh->sh_tls_response);
  stun_free_buffer(&sh->sh_username);
  stun_free_buffer(&sh->sh_passwd);

Martti Mela's avatar
Martti Mela committed
754
  su_home_zap(sh->sh_home);
Martti Mela's avatar
Martti Mela committed
755 756
}

Pekka Pessi's avatar
Pekka Pessi committed
757

Pekka Pessi's avatar
Pekka Pessi committed
758
/** Create wait object and register it to the handle callback */
759 760
static
int assign_socket(stun_discovery_t *sd, su_socket_t s, int register_socket) 
761
{
762
  stun_handle_t *sh = sd->sd_handle;
763
  int events;
Martti Mela's avatar
Martti Mela committed
764
  stun_discovery_t *tmp;
765
  /* su_localinfo_t clientinfo[1]; */
Pekka Pessi's avatar
Pekka Pessi committed
766 767
  su_sockaddr_t su[1];
  socklen_t sulen;
768
  char addr[SU_ADDRSIZE];
769
  int err;
770
  
771 772
  su_wait_t wait[1] = { SU_WAIT_INIT };

Martti Mela's avatar
Martti Mela committed
773 774
  enter;

775
  if (s == INVALID_SOCKET) {
776
    SU_DEBUG_3(("%s: invalid socket\n", __func__));
777 778 779
    return errno = EINVAL, -1;
  }

Martti Mela's avatar
Martti Mela committed
780
  for (tmp = sh->sh_discoveries; tmp; tmp = tmp->sd_next) {
781 782 783 784 785 786
    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
787
  }
788
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
789

790
  if (!register_socket)
791 792
    return 0;

793 794
  /* set socket asynchronous */
  if (su_setblocking(s, 0) < 0) {
795
    return STUN_ERROR(errno, su_setblocking);
796 797
  }

798
  /* xxx -- check if socket is already assigned to this root */
799

Pekka Pessi's avatar
Pekka Pessi committed
800
  memset(su, 0, sulen = sizeof su);
801

Pekka Pessi's avatar
Pekka Pessi committed
802 803
  /* Try to bind it if not already bound */
  if (getsockname(s, &su->su_sa, &sulen) == -1 || su->su_port == 0) {
Martti Mela's avatar
Martti Mela committed
804

Pekka Pessi's avatar
Pekka Pessi committed
805 806
    sulen = su->su_len = sizeof su->su_sin;
    su->su_family = AF_INET;
807 808

#if defined (__CYGWIN__)
809
    get_localinfo(AF_INET, su, &sulen);
810 811
#endif

Pekka Pessi's avatar
Pekka Pessi committed
812 813
    if ((err = bind(s, &su->su_sa, sulen)) < 0) {
      SU_DEBUG_3(("%s: bind(%s:%u): %s\n",  __func__, 
814
		  inet_ntop(su->su_family, SU_ADDR(su), addr, sizeof(addr)),
Pekka Pessi's avatar
Pekka Pessi committed
815 816
		  (unsigned) ntohs(su->su_port),
		  su_strerror(su_errno())));
817 818 819
      return -1;
    }

Pekka Pessi's avatar
Pekka Pessi committed
820 821 822
    if (getsockname(s, &su->su_sa, &sulen) == -1) {
      return STUN_ERROR(errno, getsockname);
    }
823 824
  }

Pekka Pessi's avatar
Pekka Pessi committed
825
  memcpy(&sd->sd_bind_addr, su, sulen);
826 827

  SU_DEBUG_3(("%s: local socket is bound to %s:%u\n", __func__,
828
	      inet_ntop(su->su_family, SU_ADDR(su), addr, sizeof(addr)),
Pekka Pessi's avatar
Pekka Pessi committed
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
	      (unsigned) ntohs(su->su_port)));

  events = SU_WAIT_IN | SU_WAIT_ERR;

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

  /* Register receiving function with events specified above */
  if ((sd->sd_index = su_root_register(sh->sh_root,
				       wait, stun_bind_callback,
				       (su_wakeup_arg_t *) sd, 0)) < 0) {
    return STUN_ERROR(errno, su_root_register);
  }

  SU_DEBUG_7(("%s: socket registered.\n", __func__));
845 846

  return 0;
847 848
}

849

850 851 852 853
/**
 * Helper function needed by Cygwin builds.
 */
#if defined (__CYGWIN__)
854
static int get_localinfo(int family, su_sockaddr_t *su, socklen_t *return_len)
855
{
Pekka Pessi's avatar
Pekka Pessi committed
856 857
  su_localinfo_t hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
  int i, error;
858
  char addr[SU_ADDRSIZE];
859

860
  hints->li_family = family;
Pekka Pessi's avatar
Pekka Pessi committed
861

862
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
863 864
    /* try to bind to the first available address */
    for (i = 0, li = res; li; li = li->li_next) {
865
      if (li->li_family != family)
866
	continue;
867 868 869 870 871 872 873 874 875
      if (li->li_scope == LI_SCOPE_HOST)
	continue;

      assert(*return_len >= li->li_addrlen);

      memcpy(su, li->li_addr, *return_len = li->li_addrlen);

      SU_DEBUG_3(("%s: using local address %s\n", __func__, 
		  inet_ntop(family, SU_ADDR(su), addr, sizeof(addr))));
876 877
      break;
    }
Pekka Pessi's avatar
Pekka Pessi committed
878 879

    su_freelocalinfo(res);
880
    
Pekka Pessi's avatar
Pekka Pessi committed
881 882
    if (!li) {			/* Not found */
      return STUN_ERROR(error, su_getlocalinfo);
883
    }
Pekka Pessi's avatar