stun.c 60.8 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * 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 28 29
 * 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
 *
 * See RFC 3489 for further information.
 *
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
Martti Mela's avatar
Martti Mela committed
43
/* #define SU_WAKEUP_ARG_T struct stun_handle_s */
44

Pekka Pessi's avatar
Pekka Pessi committed
45
#include "stun.h"
46
#include "stun_internal.h"
47
#include "stun_tag.h"
Pekka Pessi's avatar
Pekka Pessi committed
48 49

#include <su_alloc.h>
50
#include <su_tagarg.h>
Pekka Pessi's avatar
Pekka Pessi committed
51 52 53 54
#include <su_log.h>
#include <su.h>
#include <su_localinfo.h>

55 56 57 58 59
#if defined(__APPLE_CC__)
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif
#endif
60

Pekka Pessi's avatar
Pekka Pessi committed
61 62
#include <openssl/opensslv.h>

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

66
enum {
67
  STUN_SENDTO_TIMEOUT = 1000,
Martti Mela's avatar
Martti Mela committed
68
  STUN_TLS_CONNECT_TIMEOUT = 8000,
69 70
};

71

72 73 74 75 76 77 78 79 80 81 82 83
/* 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;

84 85 86
#define CHG_IP		0x001
#define CHG_PORT	0x004

87 88 89 90 91 92 93 94 95 96
#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)

97
struct stun_discovery_s {
98
  stun_discovery_t   *sd_next, **sd_prev; /**< Linked list */
99 100 101

  stun_handle_t   *sd_handle;

102 103 104 105 106 107 108 109 110 111 112
  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 */
  su_socket_t      sd_socket2;       /**< Alternative socket */
113

114
  int              sd_index;         /**< root_register index */
Martti Mela's avatar
Martti Mela committed
115 116 117 118
  
  /* Binding discovery */
  su_sockaddr_t    sd_local_addr[1];   /**< local address */

119

120
  /* NAT type related */
121 122
  stun_nattype_t   sd_nattype;       /**< Determined NAT type */
  int              sd_first;         /**< These are the requests  */
123 124 125
  int              sd_second;
  int              sd_third;
  int              sd_fourth;
126 127 128 129 130

  /* Life time related */
  int              sd_lt_cur;
  int              sd_lt;
  int              sd_lt_max;
131 132
};

133 134 135 136 137
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 */

138 139 140 141
  su_socket_t       sr_socket;          /**< Alternative socket */
  su_localinfo_t    sr_localinfo;       /**< local addrinfo */
  su_sockaddr_t     sr_local_addr[1];   /**< local address */
  su_sockaddr_t    *sr_destination;
142

143
  stun_state_t      sr_state;           /**< Progress states */
144 145
  int               sr_retry_count;     /**< current retry number */
  long              sr_timeout;         /**< timeout for next sendto() */
146 147

  int               sr_from_y;
148 149 150
  int               sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
  stun_discovery_t *sr_discovery;
};
151

Martti Mela's avatar
Martti Mela committed
152
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
153
{
154 155 156
  su_home_t       sh_home[1];
  su_root_t      *sh_root;          /**< event loop */
  int             sh_root_index;    /**< object index of su_root_register() */
157 158

#if 0
159
  su_timer_t     *sh_connect_timer; /**< timer for TLS connection */
160
#endif
161

162
  stun_request_t *sh_requests; /**< outgoing requests list */
163
  stun_discovery_t *sh_discoveries; /**< Actions list */
164

165
  int             sh_max_retries;   /**< max resend for sendto() */
166

167 168
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
169

170 171
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
172

173 174
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
175

176 177
  SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
  SSL            *sh_ssl;           /**< SSL handle for TLS */
178 179
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
180
  int             sh_nattype;       /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
181

182 183
  stun_event_f    sh_callback;      /**< callback for calling application */ 
  stun_magic_t   *sh_context;       /**< application context */
184

185 186
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
187

188
  int             sh_use_msgint;    /**< use message integrity? */
Martti Mela's avatar
Martti Mela committed
189
  /* int             sh_state; */         /**< Progress states */
Pekka Pessi's avatar
Pekka Pessi committed
190 191
};

Martti Mela's avatar
Martti Mela committed
192

193 194
#define STUN_STATE_STR(x) case x: return #x

195
char const *stun_str_state(stun_state_t state)
196 197 198
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
199
  STUN_STATE_STR(stun_dispose_me);
200 201 202 203 204
  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);
205 206 207
  STUN_STATE_STR(stun_discovery_init);
  STUN_STATE_STR(stun_discovery_processing);
  STUN_STATE_STR(stun_discovery_done);
208 209 210 211 212 213 214 215 216
  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);
217
  STUN_STATE_STR(stun_discovery_timeout);
218
  STUN_STATE_STR(stun_request_timeout);
219 220 221 222 223 224
  
  case stun_error:
  default: return "stun_error";
  }
}

225
/* char const *stun_nattype(stun_handle_t *sh) */
226
char const *stun_nattype(stun_discovery_t *sd)
227
{
Martti Mela's avatar
Martti Mela committed
228 229 230 231 232 233 234 235 236 237 238
  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
239 240 241 242
  if (sd)
    return stun_nattype_str[sd->sd_nattype];
  else
    return stun_nattype_str[stun_nat_unknown];
243 244 245
}


246 247
int stun_lifetime(stun_discovery_t *sd)
{
Pekka Pessi's avatar
Pekka Pessi committed
248
  return sd ? sd->sd_lt_cur : -1;
249 250 251
}


Pekka Pessi's avatar
Pekka Pessi committed
252
char const stun_version[] = 
Pekka Pessi's avatar
Pekka Pessi committed
253
 "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
Pekka Pessi's avatar
Pekka Pessi committed
254

255
int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
Martti Mela's avatar
Martti Mela committed
256
int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
257
int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
258 259 260
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action);
int stun_discovery_destroy(stun_discovery_t *sd);
261 262
int action_bind(stun_request_t *req, stun_msg_t *binding_response);
int action_determine_nattype(stun_request_t *req, stun_msg_t *binding_response);
263 264
int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);

265
stun_request_t *stun_request_create(stun_discovery_t *sd);
266 267
int stun_send_binding_request(stun_request_t *req,
			      su_sockaddr_t *srvr_addr);
Martti Mela's avatar
Martti Mela committed
268
int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
269 270
void stun_sendto_timer_cb(su_root_magic_t *magic, 
			  su_timer_t *t,
271
			  su_timer_arg_t *arg);
272

273
void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
274 275
			       su_timer_t *t,
			       su_timer_arg_t *arg);
276

Martti Mela's avatar
Martti Mela committed
277 278
#if 0
/* not needed.(?) User is responsible for the sockets */
279
/**
Martti Mela's avatar
Martti Mela committed
280 281
 *  Return the socket associated with the stun_socket_t structure
 */
282
su_socket_t stun_handle_get_bind_socket(stun_handle_t *sh)
Martti Mela's avatar
Martti Mela committed
283
{
284 285
  assert(sh);
  return sh->sh_bind_socket;
Martti Mela's avatar
Martti Mela committed
286
}
Martti Mela's avatar
Martti Mela committed
287
#endif
Martti Mela's avatar
Martti Mela committed
288 289 290

/**
 * Return su_root_t assigned to stun_handle_t.
291
 *
Martti Mela's avatar
Martti Mela committed
292
 * @param self stun_handle_t object
293 294
 * @return su_root_t object, NULL if self not given.
 */
Martti Mela's avatar
Martti Mela committed
295
su_root_t *stun_handle_root(stun_handle_t *self)
296
{
297
  return self ? self->sh_root : NULL;
298 299 300
}


Pekka Pessi's avatar
Pekka Pessi committed
301
/**
Martti Mela's avatar
Martti Mela committed
302
 * Check if a STUN handle should be created.
Pekka Pessi's avatar
Pekka Pessi committed
303 304 305 306 307 308 309 310 311 312 313 314
 *
 * Return true either there is a tag STUNTAG_SERVER() in list or if
 * STUN_SERVER environment variable is set.
 *
 * @param tag,value,... tag-value list
 */
int stun_is_requested(tag_type_t tag, tag_value_t value, ...)
{
  ta_list ta;
  tagi_t const *t;
  char const *stun_server;

Martti Mela's avatar
Martti Mela committed
315
  enter;
Martti Mela's avatar
Martti Mela committed
316

Pekka Pessi's avatar
Pekka Pessi committed
317 318 319 320 321 322 323 324
  ta_start(ta, tag, value);
  t = tl_find(ta_args(ta), stuntag_server);
  stun_server = t && t->t_value ? (char *)t->t_value : getenv("STUN_SERVER");
  ta_end(ta);

  return stun_server != NULL;
}

325
/** 
Martti Mela's avatar
Martti Mela committed
326
 * Create a STUN handle 
327 328 329
 *
 * @param tag,value,... tag-value list 
 *
Pekka Pessi's avatar
Pekka Pessi committed
330
 * @TAGS
331 332 333 334
 * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address
 * @TAG STUNTAG_INTEGRITY() true if msg integrity should be used
 *
 */
Pekka Pessi's avatar
Pekka Pessi committed
335 336 337 338
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
339
{
Martti Mela's avatar
Martti Mela committed
340
  stun_handle_t *stun = NULL;
341 342
  char const *server = NULL;
  int msg_integrity = 1;
343
  int err;
344
  ta_list ta;
345
  
Martti Mela's avatar
Martti Mela committed
346
  enter;
Martti Mela's avatar
Martti Mela committed
347

348 349 350 351 352 353 354
  ta_start(ta, tag, value);

  tl_gets(ta_args(ta),
	  STUNTAG_SERVER_REF(server),
	  STUNTAG_INTEGRITY_REF(msg_integrity),
	  TAG_END());

355 356 357 358 359 360 361
  stun = su_home_clone(NULL, sizeof(*stun));

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

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

368
  SU_DEBUG_5(("%s(\"%s\"): called\n", 
Martti Mela's avatar
Martti Mela committed
369
	      "stun_handle_tcreate", server));
370

371 372
  if (!server)
    return NULL;
373
  
374 375
  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
376

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

380 381
  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;
382

383 384 385 386 387
  err = stun_atoaddr(AF_INET, &stun->sh_pri_info, server);

  if (err < 0)
    return NULL;

388
  stun->sh_nattype = stun_nat_unknown;
389

390 391 392 393
  stun->sh_root     = root;
  stun->sh_context  = context;
  stun->sh_callback = cb;
  stun->sh_use_msgint = msg_integrity;
Pekka Pessi's avatar
Pekka Pessi committed
394

395
  stun->sh_max_retries = STUN_MAX_RETRX;
Martti Mela's avatar
Martti Mela committed
396

397
  /* initialize username and password */
398 399
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
400
  
401
  stun->sh_nattype = stun_nat_unknown;
402 403 404 405
  
  /* initialize random number generator */
  srand(time(NULL));
  
406 407
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
408 409 410
  return stun;
}

Martti Mela's avatar
Martti Mela committed
411 412 413 414 415 416 417 418 419 420 421

/** Shared secret request/response processing */
int stun_handle_request_shared_secret(stun_handle_t *sh)
{
  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;
  su_timer_t *connect_timer = NULL;
422
  stun_discovery_t *sd;
Martti Mela's avatar
Martti Mela committed
423
  /* stun_request_t *req; */
Martti Mela's avatar
Martti Mela committed
424 425

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

Martti Mela's avatar
Martti Mela committed
427
  enter;
Martti Mela's avatar
Martti Mela committed
428

Martti Mela's avatar
Martti Mela committed
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
  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;
  }
453 454 455 456 457 458 459 460 461 462 463

#if defined(__APPLE_CC__)
  /* OS X 10.3 does not recognize failure in connect() and would kick
   * ass with SIGPIPE. Not nice. */
  if (setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE,
		 (void *)&one, sizeof one) == -1) {
    STUN_ERROR(errno, setsockopt);
    return -1;
  }
#endif

Martti Mela's avatar
Martti Mela committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
  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)));
  
  sd = stun_discovery_create(sh, stun_action_tls_query);
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
486
  /* req = stun_request_create(sd); */
Martti Mela's avatar
Martti Mela committed
487

Martti Mela's avatar
Martti Mela committed
488
  events = SU_WAIT_CONNECT | SU_WAIT_ERR;
Martti Mela's avatar
Martti Mela committed
489 490 491 492
  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);
493 494

  if ((sd->sd_index =
Martti Mela's avatar
Martti Mela committed
495
       su_root_register(sh->sh_root, wait, stun_tls_callback, (su_wakeup_arg_t *) sd, 0)) == -1) {
Martti Mela's avatar
Martti Mela committed
496 497 498 499
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

500
  sd->sd_state = stun_tls_connecting;
Martti Mela's avatar
Martti Mela committed
501 502 503 504 505 506 507

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

  connect_timer = su_timer_create(su_root_task(sh->sh_root),
				  STUN_TLS_CONNECT_TIMEOUT);

508
  /* sd->sd_connect_timer = connect_timer; */
Martti Mela's avatar
Martti Mela committed
509
  su_timer_set(connect_timer, stun_tls_connect_timer_cb, (su_wakeup_arg_t *) sd);
Martti Mela's avatar
Martti Mela committed
510 511 512 513 514

  return 0;
}


515
stun_request_t *stun_request_create(stun_discovery_t *sd)
516
{
517
  stun_handle_t *sh = sd->sd_handle;
518 519
  stun_request_t *req = NULL;

Martti Mela's avatar
Martti Mela committed
520
  enter;
Martti Mela's avatar
Martti Mela committed
521

522
  req = calloc(sizeof(stun_request_t), 1);
523 524
  if (!req)
    return NULL;
525

526
  req->sr_handle = sh;
527
  req->sr_discovery = sd;
528 529

  /* This is the default */
530
  req->sr_socket = sd->sd_socket;
531 532 533 534 535 536 537
  
  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;
538
  /* req->sr_action = action; */
539 540 541
  req->sr_request_mask = 0;
  
  req->sr_msg = calloc(sizeof(stun_msg_t), 1);
542

543
  req->sr_state = stun_discovery_init;
544

545 546 547 548 549 550
  /* Insert this request to the request queue */
  if (sh->sh_requests)
    x_insert(sh->sh_requests, req, sr);
  else
    sh->sh_requests = req;

551 552 553
  return req;
}

554
void stun_request_destroy(stun_request_t *req)
555
{
556
  stun_handle_t *sh;
557 558
  assert(req);

Martti Mela's avatar
Martti Mela committed
559
  enter;
Martti Mela's avatar
Martti Mela committed
560

561 562
  sh = req->sr_handle;

563 564 565
  if (x_is_inserted(req, sr))
    x_remove(req, sr);

566 567 568
  if (!x_is_inserted(req, sr) && !req->sr_next)
    sh->sh_requests = NULL;

569
  req->sr_handle = NULL;
570
  req->sr_discovery = NULL;
571
  req->sr_destination = NULL;
572

573 574
  if (req->sr_msg)
    stun_free_message(req->sr_msg);
575

576
  free(req);
577 578 579

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

580 581 582
  return;
}

583

Pekka Pessi's avatar
Pekka Pessi committed
584
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
585
void stun_handle_destroy(stun_handle_t *sh)
Pekka Pessi's avatar
Pekka Pessi committed
586
{ 
Martti Mela's avatar
Martti Mela committed
587
  stun_discovery_t *sd = NULL, *kill = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
588

Martti Mela's avatar
Martti Mela committed
589
  enter;
Martti Mela's avatar
Martti Mela committed
590

Martti Mela's avatar
Martti Mela committed
591 592
  /* 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
593 594 595
  for (sd = sh->sh_discoveries; sd; ) {
    kill = sd;
    sd = sd->sd_next;
Martti Mela's avatar
Martti Mela committed
596

Martti Mela's avatar
Martti Mela committed
597
    /* Index has same value as sockfd, right? */
Martti Mela's avatar
Martti Mela committed
598
    su_root_deregister(sh->sh_root, kill->sd_socket);
599

Martti Mela's avatar
Martti Mela committed
600 601
    if (kill->sd_action == stun_action_tls_query)
      su_close(kill->sd_socket);
602 603

    stun_discovery_destroy(kill);
Martti Mela's avatar
Martti Mela committed
604
  }
Martti Mela's avatar
Martti Mela committed
605

Martti Mela's avatar
Martti Mela committed
606
  su_home_zap(sh->sh_home);
Martti Mela's avatar
Martti Mela committed
607 608
}

Pekka Pessi's avatar
Pekka Pessi committed
609

Pekka Pessi's avatar
Pekka Pessi committed
610
/** Create wait object and register it to the handle callback */
611 612 613 614
int assign_socket(stun_handle_t *sh, su_socket_t s) 
{
  int events;
  int index;
Martti Mela's avatar
Martti Mela committed
615
  stun_discovery_t *tmp;
616
  
617 618
  su_wait_t wait[1] = { SU_WAIT_INIT };

Martti Mela's avatar
Martti Mela committed
619 620 621 622 623 624
  enter;

  for (tmp = sh->sh_discoveries; tmp; tmp = tmp->sd_next) {
    if (tmp->sd_socket == s)
      return tmp->sd_index;
  }
Martti Mela's avatar
Martti Mela committed
625

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
  /* set socket asynchronous */
  if (su_setblocking(s, 0) < 0) {
    STUN_ERROR(errno, su_setblocking);

    su_close(s);
    return -1;
  }

  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 */
  if ((index = su_root_register(sh->sh_root,
				wait, stun_bind_callback,
Martti Mela's avatar
Martti Mela committed
644
				(su_wakeup_arg_t *) sh, 0)) < 0) {
645 646 647 648
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

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

651
  return index;
652 653
}

654 655 656
static int get_localinfo(su_localinfo_t *clientinfo)
{
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
657
  su_sockaddr_t *sa;
658 659 660
  int i, error, found = 0;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };

661

Martti Mela's avatar
Martti Mela committed
662
  hints->li_family = AF_INET;
663
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
664 665 666 667 668 669 670
    
    /* 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
671
      clientinfo->li_addrlen = li->li_addrlen;
672
      
673
      sa = clientinfo->li_addr;
Martti Mela's avatar
Martti Mela committed
674
      memcpy(sa, li->li_addr, sizeof(su_sockaddr_t));
675 676 677 678
      SU_DEBUG_3(("%s: local address found to be %s.\n",
		  __func__, 
		  inet_ntop(clientinfo->li_family, SU_ADDR(sa),
			    ipaddr, sizeof(ipaddr))));
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
      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
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
/** Bind a socket using STUN client. 
 *
 * The function stun_bind() obtains a global address for a UDP socket using
 * a STUN server. 
 * 
 * @param ss       dpointer to a STUN client object (IN)
 * @param my_addr  public address for socket (IN/OUT)
 * @param addrlen  length of pub_addr (IN/OUT)
 * @param lifetime return value pointer to lifetime of 
 *                 binding, -1 if no STUN not used (OUT)
 *
 * @return
 * On success, zero is returned.  Upon error, -1 is returned, and @e errno is
 * set appropriately.
 * 
 * @ERRORS
 * @ERROR EFAULT          An invalid address is given as argument
 * @ERROR EPROTONOSUPPORT Not a UDP socket.
 * @ERROR EINVAL          The socket is already bound to an address.
 * @ERROR EACCESS   	  The address is protected, and the user is not 
 *                  	  the super-user.
 * @ERROR ENOTSOCK  	  Argument is a descriptor for a file, not a socket.
 * @ERROR EAGAIN          Operation in progress. Application should call 
 *                        stun_bind() again when there is data available on 
 *                        the socket.
 * 
 */
726
int stun_handle_bind(stun_handle_t *sh,
727 728
		     tag_type_t tag, tag_value_t value,
		     ...)
Pekka Pessi's avatar
Pekka Pessi committed
729
{
730
  su_socket_t s = -1;
731
  su_localinfo_t clientinfo[1];
732
  su_sockaddr_t bind_addr;
Pekka Pessi's avatar
Pekka Pessi committed
733
  socklen_t bind_len;
734
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };
735
  stun_request_t *req = NULL;
736
  stun_discovery_t *sd = NULL;
737
  ta_list ta;
738
  stun_action_t action = stun_action_binding_request;
Martti Mela's avatar
Martti Mela committed
739 740
  su_sockaddr_t *sa;
  int err, index;
Martti Mela's avatar
Martti Mela committed
741

Martti Mela's avatar
Martti Mela committed
742
  enter;
Pekka Pessi's avatar
Pekka Pessi committed
743 744 745

  if (sh == NULL)
    return errno = EFAULT, -1;
Martti Mela's avatar
Martti Mela committed
746

747
  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
748

749 750 751 752
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
	  TAG_END());

753 754
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
755
  if (s == -1) {
756
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
757
    return errno = EINVAL, -1;
758 759
  }

Martti Mela's avatar
Martti Mela committed
760
  if ((index = assign_socket(sh, s)) < 0)
761
    return -1;
762

763
  bind_len = sizeof bind_addr;
764

Martti Mela's avatar
Martti Mela committed
765 766 767 768
  sa = (void *) &bind_addr;
  bind_len = sizeof bind_addr;
  memset(sa, 0, sizeof(bind_addr));
  /* if bound check the error */
Martti Mela's avatar
Martti Mela committed
769
  err = getsockname(s, (struct sockaddr *) sa, &bind_len);
Martti Mela's avatar
Martti Mela committed
770
  if (err < 0 && errno == SOCKET_ERROR) {
Martti Mela's avatar
Martti Mela committed
771 772 773
    STUN_ERROR(errno, getsockname);
    return -1;
  }
Pekka Pessi's avatar
Pekka Pessi committed
774

775 776 777 778
  memset(clientinfo, 0, sizeof clientinfo);
  clientinfo->li_addr = &bind_addr;
  clientinfo->li_addrlen = bind_len;

Martti Mela's avatar
Martti Mela committed
779
  if (bind_addr.su_port != 0) {
780 781 782 783 784 785
    /* already bound */
    clientinfo->li_family = bind_addr.su_family;
    /* clientinfo->li_socktype = su_getsocktype(s); */
  }
  else {
    /* Not bound - bind it */
786 787 788
#if defined (__CYGWIN__)
    get_localinfo(clientinfo);
#endif
789

790
    /* clientinfo->li_family = AF_INET6; */
Martti Mela's avatar
Martti Mela committed
791
    if ((err = bind(s, (struct sockaddr *) sa, bind_len)) < 0) {
Martti Mela's avatar
Martti Mela committed
792
      STUN_ERROR(errno, bind);
793 794 795 796 797 798
      SU_DEBUG_3(("%s: Error binding to %s:%u\n", __func__, 
		  inet_ntop(clientinfo->li_family, SU_ADDR(clientinfo->li_addr), 
			    ipaddr, sizeof(ipaddr)),
		  (unsigned) ntohs(clientinfo->li_addr->su_port)));
      return -1;
    }
799

800 801 802 803 804 805 806 807 808 809 810 811
    bind_len = clientinfo->li_addrlen;
    if (getsockname(s, (struct sockaddr *) &bind_addr, &bind_len) != 0) {
      STUN_ERROR(errno, getsockname);
      return -1;
    }
    clientinfo->li_addrlen = bind_len;
  }
   
  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(clientinfo->li_addr->su_port)));
812

813
  sd = stun_discovery_create(sh, action);
814
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
815
  sd->sd_index = index;
816

817 818 819 820 821 822 823 824 825 826
  req = stun_request_create(sd);
  
  clientinfo->li_addr = 
    memcpy(req->sr_localinfo.li_addr, clientinfo->li_addr,
	   clientinfo->li_addrlen);
  memcpy(&req->sr_localinfo, clientinfo, sizeof clientinfo);

  if (stun_make_binding_req(sh, req, req->sr_msg, 0, 0) < 0 ||
      stun_send_binding_request(req, sh->sh_pri_addr) < 0) {
    stun_discovery_destroy(sd);
827
    stun_free_message(req->sr_msg);
828
    return -1;
829
  }
Pekka Pessi's avatar
Pekka Pessi committed
830 831

  /* note: we always report success if bind() succeeds */
832

Pekka Pessi's avatar
Pekka Pessi committed
833 834 835 836
  return 0;

}

Martti Mela's avatar
Martti Mela committed
837 838 839 840 841

/** Return local NATed address 
 * This function returns the local address seen from outside.
 * Note that the address is not valid until the event stun_clien_done is launched.
 */
Martti Mela's avatar
Martti Mela committed
842
su_sockaddr_t *stun_discovery_get_address(stun_discovery_t *sd)
Martti Mela's avatar
Martti Mela committed
843
{
Martti Mela's avatar
Martti Mela committed
844

Martti Mela's avatar
Martti Mela committed
845
  enter;
Martti Mela's avatar
Martti Mela committed
846 847

  return sd->sd_local_addr;
Martti Mela's avatar
Martti Mela committed
848 849
}

850 851
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action)
Martti Mela's avatar
Martti Mela committed
852
{
853
  stun_discovery_t *sd = NULL;
Martti Mela's avatar
Martti Mela committed
854

Martti Mela's avatar
Martti Mela committed
855
  enter;
Martti Mela's avatar
Martti Mela committed
856

857
  sd = calloc(1, sizeof(stun_discovery_t));
Martti Mela's avatar
Martti Mela committed
858

859 860 861
  sd->sd_action = action;
  sd->sd_handle = sh;

862 863 864 865
  sd->sd_lt_cur = 0;
  sd->sd_lt = STUN_LIFETIME_EST;
  sd->sd_lt_max = STUN_LIFETIME_MAX;

866 867 868 869 870 871 872 873 874
  sd->sd_pri_info.ai_addrlen = 16;
  sd->sd_pri_info.ai_addr = &sd->sd_pri_addr->su_sa;

  /* Insert this action to the discovery queue */
  if (sh->sh_discoveries)
    x_insert(sh->sh_discoveries, sd, sd);
  else
    sh->sh_discoveries = sd;

875
  return sd;
876
}
Martti Mela's avatar
Martti Mela committed
877

878
int stun_discovery_destroy(stun_discovery_t *sd)
879
{
Martti Mela's avatar
Martti Mela committed
880 881
  stun_handle_t *sh;

Martti Mela's avatar
Martti Mela committed
882
  enter;
Martti Mela's avatar
Martti Mela committed
883

Martti Mela's avatar
Martti Mela committed
884 885 886 887 888 889 890 891 892 893 894 895
  if (!sd)
    return errno = EFAULT, -1;

  sh = sd->sd_handle;

  /* if we are in the queue*/
  if (x_is_inserted(sd, sd))
    x_remove(sd, sd);
  /* if we were the only one */
  else if (!sd->sd_next)
    sh->sh_discoveries = NULL;

896 897 898
  sd->sd_prev = NULL;
  sd->sd_next = NULL;

899 900 901
  free(sd);
  return 0;
}
Martti Mela's avatar
Martti Mela committed
902 903


904 905 906
int stun_handle_get_nattype(stun_handle_t *sh,
			    tag_type_t tag, tag_value_t value,
			    ...)
Pekka Pessi's avatar
Pekka Pessi committed
907
{