stun.c 56.1 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 30
 * 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.
 *
 * @author Tat Chan <Tat.Chan@nokia.com>
31
 * @author Martti Mela <Martti.Mela@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
32 33 34 35 36 37 38 39
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Kai Vehmanen <Kai.Vehmanen@nokia.com>
 * 
 * @date Created: Thu Jul 24 17:21:00 2003 ppessi
 */

#include "config.h" 

40 41
#include <assert.h>

42
#define SU_ROOT_MAGIC_T struct stun_magic_t
43
#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

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

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

61
enum {
62
  STUN_SENDTO_TIMEOUT = 1000,
Martti Mela's avatar
Martti Mela committed
63
  STUN_TLS_CONNECT_TIMEOUT = 8000,
64 65
};

66

67 68 69 70 71 72 73 74 75 76 77 78
/* 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;

79 80 81
#define CHG_IP		0x001
#define CHG_PORT	0x004

82 83 84 85 86 87 88 89 90 91
#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)

92
struct stun_discovery_s {
93 94 95 96 97 98 99 100
#if 0 /* take into use if needed / looks cool. */
  stun_action_t   *sd_next, **sd_prev; /**< Linked list */
#endif

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

101 102
  su_socket_t      sd_socket;          /**< Alternative socket */

103 104
  /* NAT type related */
  stun_nattype_t   sd_nattype;
105 106 107 108
  int              sd_first;
  int              sd_second;
  int              sd_third;
  int              sd_fourth;
109 110 111 112 113 114

  /* Life time related */

  int              sd_lt_cur;
  int              sd_lt;
  int              sd_lt_max;
115 116
};

117 118 119 120 121
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 */

122 123 124 125
  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;
126

127
  stun_state_t      sr_state;           /**< Progress states */
128 129
  int               sr_retry_count;     /**< current retry number */
  long              sr_timeout;         /**< timeout for next sendto() */
130
#if 0
131
  stun_action_t     sr_action;          /**< Request type for protocol engine */
132
#endif
133 134 135


  int               sr_from_y;
136 137 138
  int               sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
  stun_discovery_t *sr_discovery;
};
139

Martti Mela's avatar
Martti Mela committed
140
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
141
{
142 143 144 145
  su_home_t       sh_home[1];
  su_root_t      *sh_root;          /**< event loop */
  int             sh_root_index;    /**< object index of su_root_register() */
  su_timer_t     *sh_connect_timer; /**< timer for TLS connection */
146

147
  stun_request_t *sh_requests; /**< outgoing requests list */
148

149
  int             sh_max_retries;   /**< max resend for sendto() */
150

151 152
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
153

154 155
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
156

157 158
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
159

160
  su_socket_t     sh_tls_socket;    /**< outbound socket */
161

162 163
  SSL_CTX        *sh_ctx;           /**< SSL context for TLS */
  SSL            *sh_ssl;           /**< SSL handle for TLS */
164 165
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
166
  int             sh_nattype;       /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
167

168 169
  stun_event_f    sh_callback;      /**< callback for calling application */ 
  stun_magic_t   *sh_context;       /**< application context */
170

171 172
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
173

174 175
  int             sh_use_msgint;    /**< use message integrity? */
  int             sh_state;         /**< Progress states */
176

177 178
  int             sh_bind_socket;
  int             ss_root_index;    /**< object index of su_root_register() */
Pekka Pessi's avatar
Pekka Pessi committed
179 180
};

Martti Mela's avatar
Martti Mela committed
181

182 183
#define STUN_STATE_STR(x) case x: return #x

184
char const *stun_str_state(stun_state_t state)
185 186 187
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
188
  STUN_STATE_STR(stun_dispose_me);
189 190 191 192 193
  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);
194 195 196
  STUN_STATE_STR(stun_discovery_init);
  STUN_STATE_STR(stun_discovery_processing);
  STUN_STATE_STR(stun_discovery_done);
197 198 199 200 201 202 203 204 205
  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);
206
  STUN_STATE_STR(stun_discovery_timeout);
207 208 209 210 211 212
  
  case stun_error:
  default: return "stun_error";
  }
}

213
/* char const *stun_nattype(stun_handle_t *sh) */
214
char const *stun_nattype(stun_discovery_t *sd)
215
{
Martti Mela's avatar
Martti Mela committed
216 217 218 219 220 221 222 223 224 225 226
  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
227 228 229 230
  if (sd)
    return stun_nattype_str[sd->sd_nattype];
  else
    return stun_nattype_str[stun_nat_unknown];
231 232 233
}


234 235
int stun_lifetime(stun_discovery_t *sd)
{
Pekka Pessi's avatar
Pekka Pessi committed
236
  return sd ? sd->sd_lt_cur : -1;
237 238 239
}


Pekka Pessi's avatar
Pekka Pessi committed
240
char const stun_version[] = 
Pekka Pessi's avatar
Pekka Pessi committed
241
 "sofia-sip-stun using " OPENSSL_VERSION_TEXT;
Pekka Pessi's avatar
Pekka Pessi committed
242

243
int do_action(stun_handle_t *sh, stun_msg_t *binding_response);
244
int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
245 246 247
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action);
int stun_discovery_destroy(stun_discovery_t *sd);
248 249
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);
250 251
int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);

252
stun_request_t *stun_request_create(stun_discovery_t *sd);
253 254 255 256 257
int stun_send_binding_request(stun_request_t *req,
			      su_sockaddr_t *srvr_addr);
int stun_bind_callback(stun_magic_t *m, su_wait_t *w, stun_handle_t *self);
void stun_sendto_timer_cb(su_root_magic_t *magic, 
			  su_timer_t *t,
258
			  su_timer_arg_t *arg);
259

260
void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
261 262
			       su_timer_t *t,
			       su_timer_arg_t *arg);
263

264
/**
Martti Mela's avatar
Martti Mela committed
265 266
 *  Return the socket associated with the stun_socket_t structure
 */
267
int stun_handle_get_bind_socket(stun_handle_t *sh)
Martti Mela's avatar
Martti Mela committed
268
{
269 270
  assert(sh);
  return sh->sh_bind_socket;
Martti Mela's avatar
Martti Mela committed
271 272 273 274 275
}


/**
 * Return su_root_t assigned to stun_handle_t.
276
 *
Martti Mela's avatar
Martti Mela committed
277
 * @param self stun_handle_t object
278 279
 * @return su_root_t object, NULL if self not given.
 */
Martti Mela's avatar
Martti Mela committed
280
su_root_t *stun_handle_root(stun_handle_t *self)
281
{
282
  return self ? self->sh_root : NULL;
283 284 285
}


Pekka Pessi's avatar
Pekka Pessi committed
286
/**
Martti Mela's avatar
Martti Mela committed
287
 * Check if a STUN handle should be created.
Pekka Pessi's avatar
Pekka Pessi committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
 *
 * 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;

  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;
}

308
/** 
Martti Mela's avatar
Martti Mela committed
309
 * Create a STUN handle 
310 311 312
 *
 * @param tag,value,... tag-value list 
 *
Pekka Pessi's avatar
Pekka Pessi committed
313
 * @TAGS
314 315 316 317
 * @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
318 319 320 321
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
322
{
Martti Mela's avatar
Martti Mela committed
323
  stun_handle_t *stun = NULL;
324 325
  char const *server = NULL;
  int msg_integrity = 1;
326
  int err;
327
  ta_list ta;
328
  
329 330 331 332 333 334 335
  ta_start(ta, tag, value);

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

336 337 338 339 340 341 342
  stun = su_home_clone(NULL, sizeof(*stun));

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

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

349
  SU_DEBUG_5(("%s(\"%s\"): called\n", 
Martti Mela's avatar
Martti Mela committed
350
	      "stun_handle_tcreate", server));
351

352 353
  if (!server)
    return NULL;
354
  
355
  err = stun_atoaddr(AF_INET, &stun->sh_pri_info, stun->sh_pri_addr, server);
356

357 358
  if (err < 0)
    return NULL;
359

360 361
  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
362

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

366 367
  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;
368

369
  stun->sh_nattype = stun_nat_unknown;
370

371 372 373 374
  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
375

376
  stun->sh_max_retries = STUN_MAX_RETRX;
Martti Mela's avatar
Martti Mela committed
377

378
  /* initialize username and password */
379 380
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
381
  
382
  stun->sh_nattype = stun_nat_unknown;
383 384 385 386
  
  /* initialize random number generator */
  srand(time(NULL));
  
387 388
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
389 390 391
  return stun;
}

392
stun_request_t *stun_request_create(stun_discovery_t *sd)
393
{
394
  stun_handle_t *sh = sd->sd_handle;
395 396 397
  stun_request_t *req = NULL;

  req = calloc(sizeof(stun_request_t), 1);
398 399
  if (!req)
    return NULL;
400

401
  req->sr_handle = sh;
402
  req->sr_discovery = sd;
403 404 405

  /* This is the default */
  req->sr_socket = sh->sh_bind_socket;
406 407 408 409 410 411 412
  
  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;
413
  /* req->sr_action = action; */
414 415 416
  req->sr_request_mask = 0;
  
  req->sr_msg = calloc(sizeof(stun_msg_t), 1);
417

418
#if 0
419
  if (action == stun_action_get_nattype)
420
#endif
421
    req->sr_state = stun_discovery_init;
422 423
#if 0
  else if (action == stun_action_binding_request)
424
    req->sr_state = stun_bind_init;
425
#endif
426

427 428 429 430 431 432
  /* Insert this request to the request queue */
  if (sh->sh_requests)
    x_insert(sh->sh_requests, req, sr);
  else
    sh->sh_requests = req;

433 434 435
  return req;
}

436
void stun_request_destroy(stun_request_t *req)
437 438 439
{
  assert(req);

440 441 442
  if (x_is_inserted(req, sr))
    x_remove(req, sr);

443
  req->sr_handle = NULL;
444
  req->sr_discovery = NULL;
445

446 447
  if (req->sr_msg)
    stun_free_message(req->sr_msg);
448

449
  free(req);
450 451 452

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

453 454 455
  return;
}

456

Pekka Pessi's avatar
Pekka Pessi committed
457
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
458
void stun_handle_destroy(stun_handle_t *self)
Pekka Pessi's avatar
Pekka Pessi committed
459
{ 
460 461
  if (self->sh_bind_socket > 0)
    su_close(self->sh_bind_socket);
Pekka Pessi's avatar
Pekka Pessi committed
462

463 464
  if (self->sh_tls_socket > 0)
    su_close(self->sh_tls_socket);
Martti Mela's avatar
Martti Mela committed
465

466
  su_home_zap(self->sh_home);
Martti Mela's avatar
Martti Mela committed
467 468
}

Pekka Pessi's avatar
Pekka Pessi committed
469

Pekka Pessi's avatar
Pekka Pessi committed
470
/** Create wait object and register it to the handle callback */
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
int assign_socket(stun_handle_t *sh, su_socket_t s) 
{
  int events;
  int index;
  su_wait_t wait[1] = { SU_WAIT_INIT };

  /* 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,
				sh, 0)) < 0) {
    STUN_ERROR(errno, su_root_register);
    return -1;
  }

  return 0;
}

503 504 505 506 507 508 509 510
static int get_localinfo(su_localinfo_t *clientinfo)
{
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
  int i, error, found = 0;
  unsigned int port;
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };

  hints->li_family = AF_INET;
511
  if ((error = su_getlocalinfo(hints, &res)) == 0) {
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
    
    /* 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;
      clientinfo->li_addrlen = li->li_addrlen;
      
      memcpy(clientinfo->li_addr, li->li_addr, sizeof(su_addrinfo_t));
      inet_ntop(clientinfo->li_family, SU_ADDR(clientinfo->li_addr),
		ipaddr, sizeof(ipaddr));
      port = ntohs(clientinfo->li_addr->su_port);
      SU_DEBUG_3(("%s: local address found to be %s:%u\n",
		  __func__, ipaddr, port));
      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
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
/** 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.
 * 
 */
574
int stun_handle_bind(stun_handle_t *sh,
575 576
		     tag_type_t tag, tag_value_t value,
		     ...)
Pekka Pessi's avatar
Pekka Pessi committed
577
{
578
  su_socket_t s = -1;
579
  su_localinfo_t clientinfo[1];
580
  su_sockaddr_t bind_addr;
Pekka Pessi's avatar
Pekka Pessi committed
581
  socklen_t bind_len;
582
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };
583
  stun_request_t *req = NULL;
584
  stun_discovery_t *sd = NULL;
585
  ta_list ta;
586
  stun_action_t action = stun_action_binding_request;
Pekka Pessi's avatar
Pekka Pessi committed
587 588 589

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

591
  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
592

593 594 595 596
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
	  TAG_END());

597 598
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
599
  if (s == -1) {
600
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
601
    return errno = EINVAL, -1;
602 603
  }

604 605
  if (assign_socket(sh, s) < 0)
    return -1;
606

607 608
  sh->sh_bind_socket = s;

609
  bind_len = sizeof bind_addr;
610

611
  if (getsockname(s, (struct sockaddr *) &bind_addr, &bind_len) != 0) {
Martti Mela's avatar
Martti Mela committed
612 613 614
    STUN_ERROR(errno, getsockname);
    return -1;
  }
Pekka Pessi's avatar
Pekka Pessi committed
615

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
  memset(clientinfo, 0, sizeof clientinfo);
  clientinfo->li_addr = &bind_addr;
  clientinfo->li_addrlen = bind_len;

  if (!SU_HAS_INADDR_ANY(&bind_addr)) {
    /* already bound */
    clientinfo->li_family = bind_addr.su_family;
    /* clientinfo->li_socktype = su_getsocktype(s); */
  }
  else {
    /* Not bound - bind it */
    get_localinfo(clientinfo);

    if (bind(s, (struct sockaddr *) &clientinfo->li_addr, clientinfo->li_addrlen) < 0) {
      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;
    }
636

637 638 639 640 641 642 643 644 645 646 647 648
    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)));
649

650 651 652 653 654 655 656 657 658 659 660
  sd = stun_discovery_create(sh, action);
  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);
661
    stun_free_message(req->sr_msg);
662
    return -1;
663
  }
Pekka Pessi's avatar
Pekka Pessi committed
664 665

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

Pekka Pessi's avatar
Pekka Pessi committed
667 668 669 670
  return 0;

}

Martti Mela's avatar
Martti Mela committed
671 672 673 674 675

/** 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.
 */
676
su_localinfo_t *stun_request_get_localinfo(stun_request_t *req)
Martti Mela's avatar
Martti Mela committed
677
{
678
  return &req->sr_localinfo;
Martti Mela's avatar
Martti Mela committed
679 680
}

681 682
stun_discovery_t *stun_discovery_create(stun_handle_t *sh,
					stun_action_t action)
Martti Mela's avatar
Martti Mela committed
683
{
684
  stun_discovery_t *sd = NULL;
685
  sd = calloc(1, sizeof(stun_discovery_t));
Martti Mela's avatar
Martti Mela committed
686

687 688 689
  sd->sd_action = action;
  sd->sd_handle = sh;

690 691 692 693
  sd->sd_lt_cur = 0;
  sd->sd_lt = STUN_LIFETIME_EST;
  sd->sd_lt_max = STUN_LIFETIME_MAX;

694
  return sd;
695
}
Martti Mela's avatar
Martti Mela committed
696

697
int stun_discovery_destroy(stun_discovery_t *sd)
698 699 700 701
{
  free(sd);
  return 0;
}
Martti Mela's avatar
Martti Mela committed
702 703


704 705 706
int stun_handle_get_nattype(stun_handle_t *sh,
			    tag_type_t tag, tag_value_t value,
			    ...)
Pekka Pessi's avatar
Pekka Pessi committed
707
{
708
  int err, s = -1;
709
  ta_list ta;
710 711
  stun_request_t *req = NULL;
  stun_discovery_t *sd = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
712
  
713
  ta_start(ta, tag, value);
Martti Mela's avatar
Martti Mela committed
714

715 716 717 718 719
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
	  TAG_END());

  /* Check if a UDP socket has already been assigned to stun_handle */
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
  if (sh->sh_bind_socket > 0 && s > 0) {
    SU_DEBUG_3(("%s: socket already assigned for STUN binding request.\n",
		__func__));
    /* return -1; */
  }

  if (sh->sh_bind_socket > 0 && s < 0)
    ;
  else if (s == sh->sh_bind_socket) {
    SU_DEBUG_3(("%s: socket is same as previously assigned.\n", __func__));
  }
  else if (assign_socket(sh, s)) {
    sh->sh_bind_socket = s;
  }
  else
735
    return -1;
736

737
  sd = stun_discovery_create(sh, stun_action_get_nattype);
738 739 740 741 742
  req = stun_request_create(sd);
  if (stun_make_binding_req(sh, req, req->sr_msg, 
			    STUNTAG_CHANGE_IP(0), 
			    STUNTAG_CHANGE_PORT(0),
			    TAG_END()) < 0) 
743
    return -1;
744

745 746 747 748 749
  err = stun_send_binding_request(req, sh->sh_pri_addr);
  if (err < 0) {
    stun_free_message(req->sr_msg);
    return -1;
  }
Martti Mela's avatar
Martti Mela committed
750

751 752
  /* Same Public IP and port, Test III, server ip 0 or 1 should be
     the same */
753 754 755 756 757
  req = stun_request_create(sd);
  if (stun_make_binding_req(sh, req, req->sr_msg, 
			    STUNTAG_CHANGE_IP(0), 
			    STUNTAG_CHANGE_PORT(1),
			    TAG_END()) < 0) 
758 759 760 761 762 763
    return -1;

  err = stun_send_binding_request(req, sh->sh_pri_addr);
  if (err < 0) {
    stun_free_message(req->sr_msg);
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
764
  }
765

766 767 768 769 770
  req = stun_request_create(sd);
  if (stun_make_binding_req(sh, req, req->sr_msg, 
			    STUNTAG_CHANGE_IP(1), 
			    STUNTAG_CHANGE_PORT(1),
			    TAG_END()) < 0) 
771
    return -1;
772
  
773 774 775 776 777
  err = stun_send_binding_request(req, sh->sh_pri_addr);
  if (err < 0) {
    stun_free_message(req->sr_msg);
  }

778 779
  ta_end(ta);

780 781 782 783
  if (err == -1)
    return -1;

  return 0;
Pekka Pessi's avatar
Pekka Pessi committed
784
}
785

Pekka Pessi's avatar
Pekka Pessi committed
786 787 788 789
/********************************************************************
 * Internal functions
 *******************************************************************/

790
static 
791

Martti Mela's avatar
Martti Mela committed
792
int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, stun_handle_t *self)
Pekka Pessi's avatar
Pekka Pessi committed
793
{
Martti Mela's avatar
Martti Mela committed
794
  stun_msg_t *req, *resp;
Martti Mela's avatar
Martti Mela committed
795
  int z, err;
796
  int events = su_wait_events(w, self->sh_tls_socket);
Pekka Pessi's avatar
Pekka Pessi committed
797 798 799 800 801
  SSL_CTX* ctx;
  SSL *ssl;
  X509* server_cert;
  unsigned char buf[512];
  stun_attr_t *password, *username;
Martti Mela's avatar
Martti Mela committed
802
  int state;
Martti Mela's avatar
Martti Mela committed
803

Martti Mela's avatar
Martti Mela committed
804
  SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, self,
Martti Mela's avatar
Martti Mela committed
805
	      events & SU_WAIT_CONNECT ? " CONNECTED" : "",
Martti Mela's avatar
Martti Mela committed
806 807 808
	      events & SU_WAIT_ERR     ? " ERR"       : "",
	      events & SU_WAIT_IN      ? " IN"        : "",
	      events & SU_WAIT_OUT     ? " OUT"       : ""));
Martti Mela's avatar
Martti Mela committed
809

Martti Mela's avatar
Martti Mela committed
810
  if (events & SU_WAIT_ERR) {
811
    su_wait_destroy(w);
812
    su_root_deregister(self->sh_root, self->sh_root_index);
813

Martti Mela's avatar
Martti Mela committed
814
    /* Destroy the timeout timer */
815
    su_timer_destroy(self->sh_connect_timer);
Martti Mela's avatar
Martti Mela committed
816 817 818

    SU_DEBUG_3(("%s: shared secret not obtained from server. "	\
		"Proceed without username/password.\n", __func__));
819
    self->sh_state = stun_tls_connection_failed;
820 821
    self->sh_callback(self->sh_context, self, NULL, NULL,
		      stun_action_no_action, self->sh_state);
Martti Mela's avatar
Martti Mela committed
822 823
    return 0;
  }
Pekka Pessi's avatar
Pekka Pessi committed
824

Martti Mela's avatar
Martti Mela committed
825
  /* Can be NULL, too */
826 827 828
  ssl  = self->sh_ssl;
  req  = &self->sh_tls_request;
  resp = &self->sh_tls_response;
829

830
  state = self->sh_state;
Martti Mela's avatar
Martti Mela committed
831 832
  switch (state) {
  case stun_tls_connecting:
Pekka Pessi's avatar
Pekka Pessi committed
833

Martti Mela's avatar
Martti Mela committed
834 835 836 837 838 839 840 841 842 843 844
    /* compose shared secret request */
    if (stun_make_sharedsecret_req(req) != 0) {
      STUN_ERROR(errno, stun_make_sharedsecret_req);
      stun_free_buffer(&req->enc_buf);
      return -1;
    }
    
    /* openssl initiation */
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(TLSv1_client_method());
845
    self->sh_ctx = ctx;
Martti Mela's avatar
Martti Mela committed
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

    if (ctx == NULL) {
      STUN_ERROR(errno, SSL_CTX_new);
      stun_free_buffer(&req->enc_buf);
      return -1;
    }
    
    if (SSL_CTX_set_cipher_list(ctx, "AES128-SHA") == 0) {
      STUN_ERROR(errno, SSL_CTX_set_cipher_list);
      stun_free_buffer(&req->enc_buf);
      return -1;
    }
    
    /* Start TLS negotiation */
    ssl = SSL_new(ctx);
861
    self->sh_ssl = ssl;
Pekka Pessi's avatar
Pekka Pessi committed
862

863
    if (SSL_set_fd(ssl, self->sh_tls_socket) == 0) {
Martti Mela's avatar
Martti Mela committed
864 865 866 867
      STUN_ERROR(err, connect);
      stun_free_buffer(&req->enc_buf);
      return -1;
    }
Pekka Pessi's avatar
Pekka Pessi committed
868

Martti Mela's avatar
Martti Mela committed
869 870 871
    /* No break here! Continue to SSL_connect. If SSL_continue returns
     * less than 1 because of nonblocking, have a different state
     * (ssl_connecting) for it */
Pekka Pessi's avatar
Pekka Pessi committed
872

Martti Mela's avatar
Martti Mela committed
873
  case stun_tls_ssl_connecting:
874
    events = SU_WAIT_ERR | SU_WAIT_IN;
875 876
    su_root_eventmask(self->sh_root, self->sh_root_index,
		      self->sh_tls_socket, events);
877

Martti Mela's avatar
Martti Mela committed
878 879 880
    z = SSL_connect(ssl);
    err = SSL_get_error(ssl, z);
    if (z < 1 && (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)) {
881
      self->sh_state = stun_tls_ssl_connecting;
Martti Mela's avatar
Martti Mela committed
882 883 884 885
      return 0;
    }
    else if (z < 1) {
      stun_free_buffer(&req->enc_buf);
886
      self->sh_state = stun_tls_ssl_connect_failed;
887
      self->sh_callback(self->sh_context, self, NULL, NULL, stun_action_no_action, self->sh_state);
Martti Mela's avatar
Martti Mela committed
888 889 890
      return -1;
    }
    
891
    /* Inform application about the progress  */
892 893
    self->sh_state = stun_tls_writing;
    /* self->sh_callback(self->sh_context, self, self->sh_state); */
Martti Mela's avatar
Martti Mela committed
894

895
    events = SU_WAIT_ERR | SU_WAIT_OUT;
896 897
    su_root_eventmask(self->sh_root, self->sh_root_index,
		      self->sh_tls_socket, events);
Martti Mela's avatar
Martti Mela committed
898

Martti Mela's avatar
Martti Mela committed
899
    break;
Pekka Pessi's avatar
Pekka Pessi committed
900

Martti Mela's avatar
Martti Mela committed
901
  case stun_tls_writing:
902 903

    events = SU_WAIT_ERR | SU_WAIT_IN;
904 905
    su_root_eventmask(self->sh_root, self->sh_root_index,
		      self->sh_tls_socket, events);
906

Martti Mela's avatar
Martti Mela committed
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
    SU_DEBUG_3(("TLS connection using %s\n", SSL_get_cipher(ssl)));
    
    server_cert = SSL_get_peer_certificate(ssl); 
    if(server_cert) {
      SU_DEBUG_3(("\t subject: %s\n", X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0)));
      SU_DEBUG_3(("\t issuer: %s\n", X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0)));
    }
    X509_free(server_cert);
    
    z = SSL_write(ssl, req->enc_buf.data, req->enc_buf.size);
    
    if (z < 0) {
      err = SSL_get_error(ssl, z);
      if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
	return 0;
      else {
	STUN_ERROR(errno, SSL_write);
	stun_free_buffer(&req->enc_buf);
	return -1;
      }
    }
928
    self->sh_state = stun_tls_reading;
Martti Mela's avatar
Martti Mela committed
929

Martti Mela's avatar
Martti Mela committed
930
    break;
Pekka Pessi's avatar
Pekka Pessi committed
931

Martti Mela's avatar
Martti Mela committed
932
  case stun_tls_reading:
933
    events = SU_WAIT_ERR | SU_WAIT_OUT;
934 935
    su_root_eventmask(self->sh_root, self->sh_root_index,
		      self->sh_tls_socket, events);
936

Martti Mela's avatar
Martti Mela committed
937 938 939 940 941 942 943 944 945 94