stun.c 66.3 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

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

Pekka Pessi's avatar
Pekka Pessi committed
55 56
#include <openssl/opensslv.h>

Pekka Pessi's avatar
Pekka Pessi committed
57 58 59 60 61
/* Missing socket symbols */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif

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

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

70

Martti Mela's avatar
Martti Mela committed
71 72 73 74 75 76 77 78
int stun_change_map[4][4] = {
  {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 */
};


79 80 81 82 83 84 85 86 87 88 89 90
/* 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;

91 92 93
#define CHG_IP		0x001
#define CHG_PORT	0x004

94 95 96 97 98 99 100 101 102 103
#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)

104
struct stun_discovery_s {
105
  stun_discovery_t   *sd_next, **sd_prev; /**< Linked list */
106 107 108

  stun_handle_t   *sd_handle;

109 110 111 112 113 114 115 116 117 118
  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 */
119 120
  su_sockaddr_t    sd_bind_addr[1]; /**< local address */

121
  su_socket_t      sd_socket2;       /**< Alternative socket */
122

123
  int              sd_index;         /**< root_register index */
Martti Mela's avatar
Martti Mela committed
124 125
  
  /* Binding discovery */
126
  su_sockaddr_t    sd_addr_seen_outside[1];   /**< local address */
Martti Mela's avatar
Martti Mela committed
127

128

129
  /* NAT type related */
130 131
  stun_nattype_t   sd_nattype;       /**< Determined NAT type */
  int              sd_first;         /**< These are the requests  */
132 133 134
  int              sd_second;
  int              sd_third;
  int              sd_fourth;
135 136 137 138 139

  /* Life time related */
  int              sd_lt_cur;
  int              sd_lt;
  int              sd_lt_max;
Martti Mela's avatar
Martti Mela committed
140 141 142 143

  /* Keepalive timeout */
  unsigned int     sd_timeout;
  su_timer_t      *sd_timer;
144 145
};

146 147 148 149 150
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 */

151 152 153
  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
154
  su_sockaddr_t     sr_destination[1];
155

156
  stun_state_t      sr_state;           /**< Progress states */
157 158
  int               sr_retry_count;     /**< current retry number */
  long              sr_timeout;         /**< timeout for next sendto() */
159 160

  int               sr_from_y;
161 162 163
  int               sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
  stun_discovery_t *sr_discovery;
};
164

Martti Mela's avatar
Martti Mela committed
165
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
166
{
167 168 169
  su_home_t       sh_home[1];
  su_root_t      *sh_root;          /**< event loop */
  int             sh_root_index;    /**< object index of su_root_register() */
170

171
  stun_request_t *sh_requests; /**< outgoing requests list */
172
  stun_discovery_t *sh_discoveries; /**< Actions list */
173

174
  int             sh_max_retries;   /**< max resend for sendto() */
175

176 177
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
178

179 180
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
181

182 183
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
184

185 186
  SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
  SSL            *sh_ssl;           /**< SSL handle for TLS */
187 188
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
189
  int             sh_nattype;       /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
190

191 192
  stun_event_f    sh_callback;      /**< callback for calling application */ 
  stun_magic_t   *sh_context;       /**< application context */
193

194 195
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
196

197 198
  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
199 200
};

Martti Mela's avatar
Martti Mela committed
201

202 203
#define STUN_STATE_STR(x) case x: return #x

204
char const *stun_str_state(stun_state_t state)
205 206 207
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
208
  STUN_STATE_STR(stun_dispose_me);
209 210 211 212 213
  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);
214 215 216
  STUN_STATE_STR(stun_discovery_init);
  STUN_STATE_STR(stun_discovery_processing);
  STUN_STATE_STR(stun_discovery_done);
217 218 219 220 221 222 223 224 225
  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);
226
  STUN_STATE_STR(stun_discovery_timeout);
227
  STUN_STATE_STR(stun_request_timeout);
228 229 230 231 232 233
  
  case stun_error:
  default: return "stun_error";
  }
}

234
/* char const *stun_nattype(stun_handle_t *sh) */
235
char const *stun_nattype(stun_discovery_t *sd)
236
{
Martti Mela's avatar
Martti Mela committed
237 238 239 240 241 242 243 244 245 246 247
  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
248 249 250 251
  if (sd)
    return stun_nattype_str[sd->sd_nattype];
  else
    return stun_nattype_str[stun_nat_unknown];
252 253 254
}


255 256
int stun_lifetime(stun_discovery_t *sd)
{
Pekka Pessi's avatar
Pekka Pessi committed
257
  return sd ? sd->sd_lt_cur : -1;
258 259 260
}


Pekka Pessi's avatar
Pekka Pessi committed
261
char const stun_version[] = 
Pekka Pessi's avatar
Pekka Pessi committed
262
 "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
Pekka Pessi's avatar
Pekka Pessi committed
263

264
int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
Martti Mela's avatar
Martti Mela committed
265
int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
266
int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
267 268 269
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action);
int stun_discovery_destroy(stun_discovery_t *sd);
270 271
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);
272 273
int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);

274
stun_request_t *stun_request_create(stun_discovery_t *sd);
275 276
int stun_send_binding_request(stun_request_t *req,
			      su_sockaddr_t *srvr_addr);
Martti Mela's avatar
Martti Mela committed
277
int stun_bind_callback(stun_magic_t *m, su_wait_t *w, su_wakeup_arg_t *arg);
278 279

/* timers */
280 281
void stun_sendto_timer_cb(su_root_magic_t *magic, 
			  su_timer_t *t,
282
			  su_timer_arg_t *arg);
283
void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
284 285
			       su_timer_t *t,
			       su_timer_arg_t *arg);
Martti Mela's avatar
Martti Mela committed
286 287 288
void stun_get_lifetime_timer_cb(su_root_magic_t *magic, 
				su_timer_t *t,
				su_timer_arg_t *arg);
289 290 291
void stun_keepalive_timer_cb(su_root_magic_t *magic, 
			     su_timer_t *t,
			     su_timer_arg_t *arg);
292

Martti Mela's avatar
Martti Mela committed
293 294 295

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


Pekka Pessi's avatar
Pekka Pessi committed
306
/**
Martti Mela's avatar
Martti Mela committed
307
 * Check if a STUN handle should be created.
Pekka Pessi's avatar
Pekka Pessi committed
308 309 310 311 312 313 314 315 316 317 318 319
 *
 * 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
320
  enter;
Martti Mela's avatar
Martti Mela committed
321

Pekka Pessi's avatar
Pekka Pessi committed
322 323 324 325 326 327 328 329
  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;
}

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

354 355 356 357
  ta_start(ta, tag, value);

  tl_gets(ta_args(ta),
	  STUNTAG_SERVER_REF(server),
358
	  STUNTAG_REQUIRE_INTEGRITY_REF(req_msg_integrity),
359 360
	  TAG_END());

361 362 363 364 365 366 367
  stun = su_home_clone(NULL, sizeof(*stun));

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

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

374
  SU_DEBUG_5(("%s(\"%s\"): called\n", 
Martti Mela's avatar
Martti Mela committed
375
	      "stun_handle_tcreate", server));
376

377 378
  if (!server)
    return NULL;
379
  
380 381
  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
382

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

386 387
  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;
388

389 390 391 392 393
  err = stun_atoaddr(AF_INET, &stun->sh_pri_info, server);

  if (err < 0)
    return NULL;

394
  stun->sh_nattype = stun_nat_unknown;
395

396 397 398
  stun->sh_root     = root;
  stun->sh_context  = context;
  stun->sh_callback = cb;
399 400 401 402
  /* always try TLS: */
  stun->sh_use_msgint = 1; 
  /* whether use of shared-secret msgint is required */
  stun->sh_req_msgint = req_msg_integrity;
Pekka Pessi's avatar
Pekka Pessi committed
403

404
  stun->sh_max_retries = STUN_MAX_RETRX;
Martti Mela's avatar
Martti Mela committed
405

406
  /* initialize username and password */
407 408
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
409
  
410
  stun->sh_nattype = stun_nat_unknown;
411 412 413 414
  
  /* initialize random number generator */
  srand(time(NULL));
  
415 416
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
417 418 419
  return stun;
}

Martti Mela's avatar
Martti Mela committed
420 421 422 423 424 425 426 427 428 429 430

/** 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;
431
  stun_discovery_t *sd;
Martti Mela's avatar
Martti Mela committed
432
  /* stun_request_t *req; */
Martti Mela's avatar
Martti Mela committed
433 434

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

Martti Mela's avatar
Martti Mela committed
436
  enter;
Martti Mela's avatar
Martti Mela committed
437

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

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

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

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

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

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

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

  return 0;
}


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

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

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

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

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

542
  req->sr_state = stun_discovery_init;
543
  memcpy(req->sr_local_addr, sd->sd_bind_addr, sizeof(su_sockaddr_t));
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;
Martti Mela's avatar
Martti Mela committed
571
  /* memset(req->sr_destination, 0, sizeof(su_sockaddr_t)); */
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

597 598 599
    /* Index has same value as sockfd, right? ... or not? */
    if (kill->sd_index != -1)
      su_root_deregister(sh->sh_root, kill->sd_index);
600

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

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

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

Pekka Pessi's avatar
Pekka Pessi committed
610

Pekka Pessi's avatar
Pekka Pessi committed
611
/** Create wait object and register it to the handle callback */
612
int assign_socket(stun_discovery_t *sd, su_socket_t s) 
613
{
614
  stun_handle_t *sh = sd->sd_handle;
615
  int events;
Martti Mela's avatar
Martti Mela committed
616
  stun_discovery_t *tmp;
617 618 619 620 621 622
  /* 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;
623
  
624 625
  su_wait_t wait[1] = { SU_WAIT_INIT };

Martti Mela's avatar
Martti Mela committed
626 627
  enter;

628 629 630 631 632
  if (s == -1) {
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
    return errno = EINVAL, -1;
  }

Martti Mela's avatar
Martti Mela committed
633
  for (tmp = sh->sh_discoveries; tmp; tmp = tmp->sd_next) {
634 635 636 637 638 639
    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
640
  }
641
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
642

643 644 645 646 647 648 649 650
  /* set socket asynchronous */
  if (su_setblocking(s, 0) < 0) {
    STUN_ERROR(errno, su_setblocking);

    su_close(s);
    return -1;
  }

651
  /* xxx -- check if socket is already assigned to this root */
652 653 654 655 656 657 658 659
  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 */
660 661 662
  if ((sd->sd_index = su_root_register(sh->sh_root,
				       wait, stun_bind_callback,
				       (su_wakeup_arg_t *) sh, 0)) < 0) {
663 664 665 666
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

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

669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
  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;
715 716
}

717

718 719 720 721
/**
 * Helper function needed by Cygwin builds.
 */
#if defined (__CYGWIN__)
722 723 724
static int get_localinfo(su_localinfo_t *clientinfo)
{
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
725
  su_sockaddr_t *sa;
726 727 728
  int i, error, found = 0;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };

729

Martti Mela's avatar
Martti Mela committed
730
  hints->li_family = AF_INET;
731
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
732 733 734 735 736 737 738
    
    /* 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
739
      clientinfo->li_addrlen = li->li_addrlen;
740
      
741
      sa = clientinfo->li_addr;
Martti Mela's avatar
Martti Mela committed
742
      memcpy(sa, li->li_addr, sizeof(su_sockaddr_t));
743 744 745 746
      SU_DEBUG_3(("%s: local address found to be %s.\n",
		  __func__, 
		  inet_ntop(clientinfo->li_family, SU_ADDR(sa),
			    ipaddr, sizeof(ipaddr))));
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
      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;
}
765
#endif
766

Martti Mela's avatar
Martti Mela committed
767

Pekka Pessi's avatar
Pekka Pessi committed
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
/** 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.
 * 
 */
795
int stun_handle_bind(stun_handle_t *sh,
796 797
		     tag_type_t tag, tag_value_t value,
		     ...)
Pekka Pessi's avatar
Pekka Pessi committed
798
{
799
  su_socket_t s = -1;
800
  stun_request_t *req = NULL;
801
  stun_discovery_t *sd = NULL;
802
  ta_list ta;
803
  stun_action_t action = stun_action_binding_request;
804
  int index;
Martti Mela's avatar
Martti Mela committed
805

Martti Mela's avatar
Martti Mela committed
806
  enter;
Pekka Pessi's avatar
Pekka Pessi committed
807 808 809

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

811
  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
812

813 814 815 816
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
	  TAG_END());

817 818 819
  ta_end(ta);

  sd = stun_discovery_create(sh, action);
820 821
  if ((index = assign_socket(sd, s)) < 0)
    return -1;
822

823 824 825 826 827
  req = stun_request_create(sd);

  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);
828
    stun_free_message(req->sr_msg);
829
    return -1;
830
  }
Pekka Pessi's avatar
Pekka Pessi committed
831 832

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

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

}

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

/** 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
843
su_sockaddr_t *stun_discovery_get_address(stun_discovery_t *sd)
Martti Mela's avatar
Martti Mela committed
844
{
Martti Mela's avatar
Martti Mela committed
845

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

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

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

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

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

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

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

867 868 869 870 871 872 873 874 875
  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;

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

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

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

Martti Mela's avatar
Martti Mela committed
885 886 887 888 889 890 891 892 893 894 895 896
  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;

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

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


905 906 907
int stun_handle_get_nattype(stun_handle_t *sh,
			    tag_type_t tag, tag_value_t value,
			    ...)
Pekka Pessi's avatar
Pekka Pessi committed
908
{
909
  int err = 0, index = 0;
910
  ta_list ta;
911
  char const *server = NULL;
912 913
  stun_request_t *req = NULL;
  stun_discovery_t *sd = NULL;
914 915 916 917 918
  su_sockaddr_t bind_addr;
  su_socket_t s = -1;
  socklen_t bind_len;
  su_sockaddr_t *destination = NULL;

919
  ta_start(ta, tag, value);
Martti Mela's avatar
Martti Mela committed
920

Martti Mela's avatar
Martti Mela committed
921
  enter;
Martti Mela's avatar
Martti Mela committed
922

923 924
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
925
	  STUNTAG_SERVER_REF(server),
926 927
	  TAG_END());

928 929
  ta_end(ta);

930 931 932