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

Martti Mela's avatar
Martti Mela committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
  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
476
  /* req = stun_request_create(sd); */
Martti Mela's avatar
Martti Mela committed
477

Martti Mela's avatar
Martti Mela committed
478
  events = SU_WAIT_CONNECT | SU_WAIT_ERR;
Martti Mela's avatar
Martti Mela committed
479 480 481 482
  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);
483 484

  if ((sd->sd_index =
Martti Mela's avatar
Martti Mela committed
485
       su_root_register(sh->sh_root, wait, stun_tls_callback, (su_wakeup_arg_t *) sd, 0)) == -1) {
Martti Mela's avatar
Martti Mela committed
486 487 488 489
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

490
  sd->sd_state = stun_tls_connecting;
Martti Mela's avatar
Martti Mela committed
491 492 493 494 495 496 497

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

498
  /* sd->sd_connect_timer = connect_timer; */
Martti Mela's avatar
Martti Mela committed
499
  su_timer_set(connect_timer, stun_tls_connect_timer_cb, (su_wakeup_arg_t *) sd);
Martti Mela's avatar
Martti Mela committed
500 501 502 503 504

  return 0;
}


505
stun_request_t *stun_request_create(stun_discovery_t *sd)
506
{
507
  stun_handle_t *sh = sd->sd_handle;
508 509
  stun_request_t *req = NULL;

Martti Mela's avatar
Martti Mela committed
510
  enter;
Martti Mela's avatar
Martti Mela committed
511

512
  req = calloc(sizeof(stun_request_t), 1);
513 514
  if (!req)
    return NULL;
515

516
  req->sr_handle = sh;
517
  req->sr_discovery = sd;
518 519

  /* This is the default */
520
  req->sr_socket = sd->sd_socket;
521 522 523 524 525 526 527
  
  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;
528
  /* req->sr_action = action; */
529 530 531
  req->sr_request_mask = 0;
  
  req->sr_msg = calloc(sizeof(stun_msg_t), 1);
532

533
  req->sr_state = stun_discovery_init;
534

535 536 537 538 539 540
  /* Insert this request to the request queue */
  if (sh->sh_requests)
    x_insert(sh->sh_requests, req, sr);
  else
    sh->sh_requests = req;

541 542 543
  return req;
}

544
void stun_request_destroy(stun_request_t *req)
545
{
546
  stun_handle_t *sh;
547 548
  assert(req);

Martti Mela's avatar
Martti Mela committed
549
  enter;
Martti Mela's avatar
Martti Mela committed
550

551 552
  sh = req->sr_handle;

553 554 555
  if (x_is_inserted(req, sr))
    x_remove(req, sr);

556 557 558
  if (!x_is_inserted(req, sr) && !req->sr_next)
    sh->sh_requests = NULL;

559
  req->sr_handle = NULL;
560
  req->sr_discovery = NULL;
561
  req->sr_destination = NULL;
562

563 564
  if (req->sr_msg)
    stun_free_message(req->sr_msg);
565

566
  free(req);
567 568 569

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

570 571 572
  return;
}

573

Pekka Pessi's avatar
Pekka Pessi committed
574
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
575
void stun_handle_destroy(stun_handle_t *sh)
Pekka Pessi's avatar
Pekka Pessi committed
576
{ 
Martti Mela's avatar
Martti Mela committed
577
  stun_discovery_t *sd = NULL, *kill = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
578

Martti Mela's avatar
Martti Mela committed
579
  enter;
Martti Mela's avatar
Martti Mela committed
580

Martti Mela's avatar
Martti Mela committed
581 582
  /* 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
583 584 585
  for (sd = sh->sh_discoveries; sd; ) {
    kill = sd;
    sd = sd->sd_next;
Martti Mela's avatar
Martti Mela committed
586

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

Martti Mela's avatar
Martti Mela committed
590 591
    if (kill->sd_action == stun_action_tls_query)
      su_close(kill->sd_socket);
592 593

    stun_discovery_destroy(kill);
Martti Mela's avatar
Martti Mela committed
594
  }
Martti Mela's avatar
Martti Mela committed
595

Martti Mela's avatar
Martti Mela committed
596
  su_home_zap(sh->sh_home);
Martti Mela's avatar
Martti Mela committed
597 598
}

Pekka Pessi's avatar
Pekka Pessi committed
599

Pekka Pessi's avatar
Pekka Pessi committed
600
/** Create wait object and register it to the handle callback */
601 602 603 604
int assign_socket(stun_handle_t *sh, su_socket_t s) 
{
  int events;
  int index;
Martti Mela's avatar
Martti Mela committed
605
  stun_discovery_t *tmp;
606
  
607 608
  su_wait_t wait[1] = { SU_WAIT_INIT };

Martti Mela's avatar
Martti Mela committed
609 610 611 612 613 614
  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
615

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
  /* 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
634
				(su_wakeup_arg_t *) sh, 0)) < 0) {
635 636 637 638
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

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

641
  return index;
642 643
}

644 645 646
static int get_localinfo(su_localinfo_t *clientinfo)
{
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
647
  su_sockaddr_t *sa;
648 649 650
  int i, error, found = 0;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };

651

Martti Mela's avatar
Martti Mela committed
652
  hints->li_family = AF_INET;
653
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
654 655 656 657 658 659 660
    
    /* 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
661
      clientinfo->li_addrlen = li->li_addrlen;
662
      
663
      sa = clientinfo->li_addr;
Martti Mela's avatar
Martti Mela committed
664
      memcpy(sa, li->li_addr, sizeof(su_sockaddr_t));
665 666 667 668
      SU_DEBUG_3(("%s: local address found to be %s.\n",
		  __func__, 
		  inet_ntop(clientinfo->li_family, SU_ADDR(sa),
			    ipaddr, sizeof(ipaddr))));
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
      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
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 715
/** 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.
 * 
 */
716
int stun_handle_bind(stun_handle_t *sh,
717 718
		     tag_type_t tag, tag_value_t value,
		     ...)
Pekka Pessi's avatar
Pekka Pessi committed
719
{
720
  su_socket_t s = -1;
721
  su_localinfo_t clientinfo[1];
722
  su_sockaddr_t bind_addr;
Pekka Pessi's avatar
Pekka Pessi committed
723
  socklen_t bind_len;
724
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };
725
  stun_request_t *req = NULL;
726
  stun_discovery_t *sd = NULL;
727
  ta_list ta;
728
  stun_action_t action = stun_action_binding_request;
Martti Mela's avatar
Martti Mela committed
729 730
  su_sockaddr_t *sa;
  int err, index;
Martti Mela's avatar
Martti Mela committed
731

Martti Mela's avatar
Martti Mela committed
732
  enter;
Pekka Pessi's avatar
Pekka Pessi committed
733 734 735

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

737
  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
738

739 740 741 742
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
	  TAG_END());

743 744
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
745
  if (s == -1) {
746
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
747
    return errno = EINVAL, -1;
748 749
  }

Martti Mela's avatar
Martti Mela committed
750
  if ((index = assign_socket(sh, s)) < 0)
751
    return -1;
752

753
  bind_len = sizeof bind_addr;
754

Martti Mela's avatar
Martti Mela committed
755 756 757 758
  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
759
  err = getsockname(s, (struct sockaddr *) sa, &bind_len);
Martti Mela's avatar
Martti Mela committed
760
  if (err < 0 && errno == SOCKET_ERROR) {
Martti Mela's avatar
Martti Mela committed
761 762 763
    STUN_ERROR(errno, getsockname);
    return -1;
  }
Pekka Pessi's avatar
Pekka Pessi committed
764

765 766 767 768
  memset(clientinfo, 0, sizeof clientinfo);
  clientinfo->li_addr = &bind_addr;
  clientinfo->li_addrlen = bind_len;

Martti Mela's avatar
Martti Mela committed
769
  if (bind_addr.su_port != 0) {
770 771 772 773 774 775
    /* already bound */
    clientinfo->li_family = bind_addr.su_family;
    /* clientinfo->li_socktype = su_getsocktype(s); */
  }
  else {
    /* Not bound - bind it */
776 777 778
#if defined (__CYGWIN__)
    get_localinfo(clientinfo);
#endif
779

780
    /* clientinfo->li_family = AF_INET6; */
Martti Mela's avatar
Martti Mela committed
781
    if ((err = bind(s, (struct sockaddr *) sa, bind_len)) < 0) {
Martti Mela's avatar
Martti Mela committed
782
      STUN_ERROR(errno, bind);
783 784 785 786 787 788
      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;
    }
789

790 791 792 793 794 795 796 797 798 799 800 801
    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)));
802

803
  sd = stun_discovery_create(sh, action);
804
  sd->sd_socket = s;
Martti Mela's avatar
Martti Mela committed
805
  sd->sd_index = index;
806

807 808 809 810 811 812 813 814 815 816
  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);
817
    stun_free_message(req->sr_msg);
818
    return -1;
819
  }
Pekka Pessi's avatar
Pekka Pessi committed
820 821

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

Pekka Pessi's avatar
Pekka Pessi committed
823 824 825 826
  return 0;

}

Martti Mela's avatar
Martti Mela committed
827 828 829 830 831

/** 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
832
su_sockaddr_t *stun_discovery_get_address(stun_discovery_t *sd)
Martti Mela's avatar
Martti Mela committed
833
{
Martti Mela's avatar
Martti Mela committed
834

Martti Mela's avatar
Martti Mela committed
835
  enter;
Martti Mela's avatar
Martti Mela committed
836 837

  return sd->sd_local_addr;
Martti Mela's avatar
Martti Mela committed
838 839
}

840 841
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action)
Martti Mela's avatar
Martti Mela committed
842
{
843
  stun_discovery_t *sd = NULL;
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
  sd = calloc(1, sizeof(stun_discovery_t));
Martti Mela's avatar
Martti Mela committed
848

849 850 851
  sd->sd_action = action;
  sd->sd_handle = sh;

852 853 854 855
  sd->sd_lt_cur = 0;
  sd->sd_lt = STUN_LIFETIME_EST;
  sd->sd_lt_max = STUN_LIFETIME_MAX;

856 857 858 859 860 861 862 863 864
  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;

865
  return sd;
866
}
Martti Mela's avatar
Martti Mela committed
867

868
int stun_discovery_destroy(stun_discovery_t *sd)
869
{
Martti Mela's avatar
Martti Mela committed
870 871
  stun_handle_t *sh;

Martti Mela's avatar
Martti Mela committed
872
  enter;
Martti Mela's avatar
Martti Mela committed
873

Martti Mela's avatar
Martti Mela committed
874 875 876 877 878 879 880 881 882 883 884 885
  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;

886 887 888
  sd->sd_prev = NULL;
  sd->sd_next = NULL;

889 890 891
  free(sd);
  return 0;
}
Martti Mela's avatar
Martti Mela committed
892 893


894 895 896
int stun_handle_get_nattype(stun_handle_t *sh,
			    tag_type_t tag, tag_value_t value,
			    ...)
Pekka Pessi's avatar
Pekka Pessi committed
897
{
Martti Mela's avatar
Martti Mela committed
898
  int err, index = 0;
899
  ta_list ta;
900
  char const *server = NULL;
901 902
  stun_request_t *req = NULL;
  stun_discovery_t *sd = NULL;
903 904 905 906 907
  su_sockaddr_t bind_addr;
  su_socket_t s = -1;
  socklen_t bind_len;
  su_sockaddr_t *destination = NULL;

908
  ta_start(ta, tag, value);
Martti Mela's avatar
Martti Mela committed
909

Martti Mela's avatar
Martti Mela committed
910
  enter;
Martti Mela's avatar
Martti Mela committed
911