stun.c 57.2 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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
typedef enum stun_action_s {
  stun_action_no_action,
  stun_action_binding_request,
  stun_action_keepalive,
  stun_action_get_nattype,
  stun_action_get_lifetime,
} stun_action_t;

#define CHG_IP		0x001
#define CHG_PORT	0x004

#if 0

char const *stun_nattype(stun_handle_t *sh)
{
  switch(sh->sh_nattype) {
  case STUN_NAT_UNKNOWN: return stun_nat_unknown;
  case STUN_OPEN_INTERNET: return stun_open_internet;
  case STUN_UDP_BLOCKED: return stun_udp_blocked;
  case STUN_SYM_UDP_FW: return stun_sym_udp_fw;
  case STUN_NAT_FULL_CONE: return stun_nat_full_cone;
  case STUN_NAT_SYM: return stun_nat_sym;
  case STUN_NAT_RES_CONE: return stun_nat_res_cone;
  case STUN_NAT_PORT_RES_CONE: return stun_nat_port_res_cone;
  default: return "INVALID NAT TYPE";
  }
}

#endif
Pekka Pessi's avatar
Pekka Pessi committed
96

97 98 99 100 101 102 103 104 105 106 107
#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)

struct stun_request_s {
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
  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 */

  su_localinfo_t  sr_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sr_local_addr[1]; /**< local address */

#if 0
  int             sr_root_index;      /**< Index from su_root_register */
#endif
  int             sr_state;           /**< Progress states */
  int             sr_retry_count;     /**< current retry number */
  long            sr_timeout;         /**< timeout for next sendto() */
  stun_action_t   sr_action;          /**< Request type for protocol engine */
  int             sr_request_mask;    /**< Mask consisting of chg_ip and chg_port */
123 124
};

125

Martti Mela's avatar
Martti Mela committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#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)

struct stun_request_s {
  stun_request_t *sr_next, **sr_prev;
  stun_msg_t     *sr_msg; 
  stun_handle_t  *sr_handle;
  int             sr_root_index;
  int             sr_state;      /**< Progress states */
  int             sr_retry_count;   /**< current retry number */
  long            sr_timeout;       /**< timeout for next sendto() */
};

Martti Mela's avatar
Martti Mela committed
146
struct stun_handle_s
Pekka Pessi's avatar
Pekka Pessi committed
147
{
148 149 150 151
  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 */
152

153
  stun_request_t *sh_requests; /**< outgoing requests list */
154

155
  int             sh_max_retries;   /**< max resend for sendto() */
156

157 158
  su_addrinfo_t   sh_pri_info;      /**< server primary info */
  su_sockaddr_t   sh_pri_addr[1];   /**< server primary address */
159

160 161
  su_addrinfo_t   sh_sec_info;      /**< server secondary info */
  su_sockaddr_t   sh_sec_addr[1];   /**< server secondary address */
162

163 164
  su_localinfo_t  sh_localinfo;     /**< local addrinfo */
  su_sockaddr_t   sh_local_addr[1]; /**< local address */
165

166
  su_socket_t     sh_tls_socket;       /**< outbound socket */
167

168 169 170 171 172
  SSL_CTX        *sh_ctx;          /**< SSL context for TLS */
  SSL            *sh_ssl;          /**< SSL handle for TLS */
  stun_msg_t      sh_tls_request;
  stun_msg_t      sh_tls_response;
  int             sh_nattype;     /**< NAT-type, see stun_common.h */
Martti Mela's avatar
Martti Mela committed
173

174 175
  stun_event_f    sh_callback;     /**< callback for calling application */ 
  stun_magic_t   *sh_context;      /**< application context */
176

177 178
  stun_buffer_t   sh_username;
  stun_buffer_t   sh_passwd;
179

180
  int             sh_use_msgint;  /**< use message integrity? */
181

182
  int             sh_state;      /**< Progress states */
Martti Mela's avatar
Martti Mela committed
183

Pekka Pessi's avatar
Pekka Pessi committed
184

185
  int            sh_bind_socket;
186
  int            ss_root_index;   /**< object index of su_root_register() */
Pekka Pessi's avatar
Pekka Pessi committed
187 188
};

Martti Mela's avatar
Martti Mela committed
189

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
#define STUN_STATE_STR(x) case x: return #x

char const *stun_str_state(stun_states_t state)
{
  switch (state) {
  STUN_STATE_STR(stun_no_assigned_event);
  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);
  STUN_STATE_STR(stun_bind_init);
  STUN_STATE_STR(stun_bind_started);
  STUN_STATE_STR(stun_bind_sending);
  STUN_STATE_STR(stun_bind_sent);
  STUN_STATE_STR(stun_bind_receiving);
  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);
  
  case stun_error:
  default: return "stun_error";
  }
}

/* NAT TYPES */
typedef enum stun_nattype_t {
  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_e;


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


char const *stun_nattype(stun_handle_t *sh)
{
  return stun_nattype_str[sh->sh_nattype];
}


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

254

255 256 257 258 259 260 261 262 263
static
int process_binding_request(stun_request_t *req, stun_msg_t *binding_response);
static
int process_get_nattype(stun_request_t *req, stun_msg_t *binding_response);
static
int process_get_lifetime(stun_request_t *req, stun_msg_t *binding_response);

static
stun_request_t *stun_create_request(stun_handle_t *sh, stun_action_t action);
264 265 266 267 268 269 270 271
static
int stun_send_binding_request(stun_request_t *req,
			      su_sockaddr_t *srvr_addr);
static
int stun_bind_callback(stun_magic_t *m, su_wait_t *w, stun_handle_t *self);
static
void stun_sendto_timer_cb(su_root_magic_t *magic, 
			  su_timer_t *t,
272
			  su_timer_arg_t *arg);
273

274 275
static
void stun_tls_connect_timer_cb(su_root_magic_t *magic, 
276 277
			       su_timer_t *t,
			       su_timer_arg_t *arg);
Martti Mela's avatar
Martti Mela committed
278
				      stun_handle_t *se);
279

280
/**
Martti Mela's avatar
Martti Mela committed
281 282
 *  Return the socket associated with the stun_socket_t structure
 */
283
int stun_handle_get_bind_socket(stun_handle_t *sh)
Martti Mela's avatar
Martti Mela committed
284
{
285 286
  assert(sh);
  return sh->sh_bind_socket;
Martti Mela's avatar
Martti Mela committed
287 288 289 290 291
}


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


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

324
/** 
Martti Mela's avatar
Martti Mela committed
325
 * Creates a STUN handle 
326
 *
Pekka Pessi's avatar
Pekka Pessi committed
327
 * @param server hostname or IPv4 address 
328
 * @param msg_integrity true if msg integr. should be used
329 330
 *
 */
Martti Mela's avatar
Martti Mela committed
331
stun_handle_t *stun_handle_create(stun_magic_t *context,
332
				  su_root_t *root,
333
				  stun_event_f cb,
334
				  char const *server, 
335
				  int msg_integrity)
336
{
Martti Mela's avatar
Martti Mela committed
337
  return stun_handle_tcreate(context,
338 339 340
			     root,
			     cb,
			     STUNTAG_SERVER(server), 
341 342 343 344 345
			     STUNTAG_INTEGRITY(msg_integrity), 
			     TAG_END());
}

/** 
Martti Mela's avatar
Martti Mela committed
346
 * Create a STUN handle 
347 348 349
 *
 * @param tag,value,... tag-value list 
 *
Pekka Pessi's avatar
Pekka Pessi committed
350
 * @TAGS
351 352 353 354
 * @TAG STUNTAG_SERVER() stun server hostname or dotted IPv4 address
 * @TAG STUNTAG_INTEGRITY() true if msg integrity should be used
 *
 */
Martti Mela's avatar
Martti Mela committed
355
stun_handle_t *stun_handle_tcreate(stun_magic_t *context,
356
				   su_root_t *root,
357
				   stun_event_f cb,
358
				   tag_type_t tag, tag_value_t value, ...)
Pekka Pessi's avatar
Pekka Pessi committed
359
{
Martti Mela's avatar
Martti Mela committed
360
  stun_handle_t *stun = NULL;
361 362
  char const *server = NULL;
  int msg_integrity = 1;
363
  int err;
364
  ta_list ta;
365
  
366 367 368 369 370 371 372
  ta_start(ta, tag, value);

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

373 374 375 376 377 378 379
  stun = su_home_clone(NULL, sizeof(*stun));

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

380 381 382
  /* Enviroment overrides */
  if (getenv("STUN_SERVER")) {
    server = getenv("STUN_SERVER");
383
    SU_DEBUG_5(("%s: using STUN_SERVER=%s\n", __func__, server));
384
  }
385

386
  SU_DEBUG_5(("%s(\"%s\"): called\n", 
Martti Mela's avatar
Martti Mela committed
387
	      "stun_handle_tcreate", server));
388

389 390
  if (!server)
    return NULL;
391
  
392
  err = stun_atoaddr(AF_INET, &stun->sh_pri_info, stun->sh_pri_addr, server);
393

394 395
  if (err < 0)
    return NULL;
396

397 398
  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
399

400 401
  stun->sh_sec_info.ai_addrlen = 16;
  stun->sh_sec_info.ai_addr = &stun->sh_sec_addr->su_sa;
402

403 404
  stun->sh_localinfo.li_addrlen = 16;
  stun->sh_localinfo.li_addr = stun->sh_local_addr;
405

406
  stun->sh_nattype = stun_nat_unknown;
407

408 409 410 411
  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
412

413
  stun->sh_max_retries = STUN_MAX_RETRX;
Martti Mela's avatar
Martti Mela committed
414

415
  /* initialize username and password */
416 417
  stun_init_buffer(&stun->sh_username);
  stun_init_buffer(&stun->sh_passwd);
418
  
419
  stun->sh_nattype = stun_nat_unknown;
420 421 422 423
  
  /* initialize random number generator */
  srand(time(NULL));
  
424 425
  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
426 427 428
  return stun;
}

429

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
stun_request_t *stun_action_create_binding_req(stun_handle_t *sh)
{
  return stun_create_request(sh, stun_action_binding_request);
}

stun_request_t *stun_action_create_nattype_discovery(stun_handle_t *sh)
{
  return stun_create_request(sh, stun_action_get_nattype);
}

stun_request_t *stun_action_create_timeout_discovery(stun_handle_t *sh)
{
  return stun_create_request(sh, stun_action_get_lifetime);
}

stun_request_t *stun_action_create_keepalive(stun_handle_t *sh)
{
  return stun_create_request(sh, stun_action_keepalive);
}


stun_request_t *stun_create_request(stun_handle_t *sh, stun_action_t action)
452 453 454 455 456 457 458
{
  stun_request_t *req = NULL;

  req = calloc(sizeof(stun_request_t), 1);
  if (req) {
    req->sr_handle = sh;

459 460 461 462
    /* STUN bind related */
    req->sr_localinfo.li_addrlen = sizeof(su_sockaddr_t);
    req->sr_localinfo.li_addr = req->sr_local_addr;

463 464 465
    /* default timeout for next sendto() */
    req->sr_timeout = STUN_SENDTO_TIMEOUT;
    req->sr_retry_count = 0;
466 467
    req->sr_action = action;
    req->sr_request_mask = 0;
468 469 470 471 472 473 474

    req->sr_msg = calloc(sizeof(stun_msg_t), 1);
  }

  return req;
}

475 476 477 478 479 480 481 482 483 484 485 486
void stun_destroy_request(stun_request_t *req)
{
  assert(req);

  req->sr_handle = NULL;

  free(req->sr_msg);
  free(req);

  return;
}

487

Pekka Pessi's avatar
Pekka Pessi committed
488
/** Destroy a STUN client */ 
Martti Mela's avatar
Martti Mela committed
489
void stun_handle_destroy(stun_handle_t *self)
Pekka Pessi's avatar
Pekka Pessi committed
490
{ 
491 492
  if (self->sh_bind_socket > 0)
    su_close(self->sh_bind_socket);
Pekka Pessi's avatar
Pekka Pessi committed
493

494 495
  if (self->sh_tls_socket > 0)
    su_close(self->sh_tls_socket);
Martti Mela's avatar
Martti Mela committed
496

497
  su_home_zap(self->sh_home);
Martti Mela's avatar
Martti Mela committed
498 499
}

Pekka Pessi's avatar
Pekka Pessi committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

/** 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.
 * 
 */
528
int stun_handle_bind(stun_handle_t *sh,
529 530 531
		     int *lifetime,
		     tag_type_t tag, tag_value_t value,
		     ...)
Pekka Pessi's avatar
Pekka Pessi committed
532
{
533 534
  int retval = -1;
  su_socket_t s = -1;
535
  su_localinfo_t *clientinfo = NULL;
536
  su_sockaddr_t bind_addr;
Pekka Pessi's avatar
Pekka Pessi committed
537
  socklen_t bind_len;
538
  char ipaddr[SU_ADDRSIZE + 2] = { 0 };
539
  stun_request_t *req = NULL;
540
  int index;
541
  int events = -1;
Pekka Pessi's avatar
Pekka Pessi committed
542
  su_localinfo_t  hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, *li, *res = NULL;
543
  int i, error, found = 0;
544
  unsigned int port;
545
  ta_list ta;
546
  su_wait_t wait[1] = { SU_WAIT_INIT };
547
  int action = stun_action_no_action;
Pekka Pessi's avatar
Pekka Pessi committed
548
  
549
  assert(sh);
Martti Mela's avatar
Martti Mela committed
550

551
  ta_start(ta, tag, value);
Pekka Pessi's avatar
Pekka Pessi committed
552

553 554
  tl_gets(ta_args(ta),
	  STUNTAG_SOCKET_REF(s),
555
	  STUNTAG_ACTION_REF(action),
556 557 558 559 560 561 562
	  TAG_END());

  if (s < 0) {
    SU_DEBUG_3(("%s: invalid socket.\n", __func__));
    return -1;
  }

563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
  if (action != stun_action_no_action)
    req = stun_create_request(sh, action);
  else
    req = stun_action_create_binding_req(sh);

  /* Insert this request to the request queue */
  if (sh->sh_requests)
    x_insert(sh->sh_requests, req, sr);
  else
    sh->sh_requests = req;

  clientinfo = &req->sr_localinfo;

  if (action != stun_action_no_action)
    goto skip_init;

#if 0  
580
  /* Close the previous associated socket */
581 582 583
  if (sh->sh_bind_socket > 0)
    su_close(sh->sh_bind_socket);
#endif
584 585 586 587 588 589 590 591

  /* set socket asynchronous */
  if (su_setblocking(s, 0) < 0) {
    STUN_ERROR(errno, su_setblocking);

    su_close(s);
    return -1;
  }
592
  sh->sh_bind_socket = s;
Martti Mela's avatar
Martti Mela committed
593 594

  hints->li_family = AF_INET;
Martti Mela's avatar
Martti Mela committed
595
  if((error = su_getlocalinfo(hints, &res)) == 0) {
Martti Mela's avatar
Martti Mela committed
596
    
Martti Mela's avatar
Martti Mela committed
597 598 599 600 601
    /* 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;
      
602 603
      clientinfo->li_family = li->li_family;
      clientinfo->li_addrlen = li->li_addrlen;
Martti Mela's avatar
Martti Mela committed
604
      
605 606
      memcpy(clientinfo->li_addr, li->li_addr, sizeof(su_addrinfo_t));
      inet_ntop(clientinfo->li_family, SU_ADDR(clientinfo->li_addr),
607
		ipaddr, sizeof(ipaddr));
608
      port = ntohs(clientinfo->li_addr->su_port);
609 610
      SU_DEBUG_3(("%s: local address found to be %s:%u\n",
		  __func__, ipaddr, port));
Martti Mela's avatar
Martti Mela committed
611 612
      found = 1;
      break;
Pekka Pessi's avatar
Pekka Pessi committed
613
    }
Martti Mela's avatar
Martti Mela committed
614 615 616 617
    
    if (!found) {
      STUN_ERROR(error, su_getlocalinfo);
      return -1;
Pekka Pessi's avatar
Pekka Pessi committed
618 619
    }
  }
Martti Mela's avatar
Martti Mela committed
620 621 622 623 624 625
  else {
    STUN_ERROR(error, su_getlocalinfo);
    return -1;
  }
  if (res)
    su_freelocalinfo(res);
Pekka Pessi's avatar
Pekka Pessi committed
626

627
  s = sh->sh_bind_socket;
628 629 630 631 632 633 634 635 636

  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 */
637
  if ((index = su_root_register(sh->sh_root,
638
				wait, stun_bind_callback,
639
				sh, 0)) < 0) {
640 641 642
    STUN_ERROR(errno, su_root_register);
    return -1;
  }
Pekka Pessi's avatar
Pekka Pessi committed
643

644 645
  inet_ntop(clientinfo->li_family, SU_ADDR(clientinfo->li_addr), ipaddr, sizeof(ipaddr));
  if (bind(s, (struct sockaddr *) &clientinfo->li_addr, clientinfo->li_addrlen) < 0) {
646
    SU_DEBUG_3(("%s: Error binding to %s:%u\n", __func__, ipaddr,
647
		(unsigned) ntohs(clientinfo->li_addr->su_port)));
Pekka Pessi's avatar
Pekka Pessi committed
648 649 650
    return -1;
  }

651
  SU_DEBUG_3(("%s: socket bound to %s:%u\n", __func__, ipaddr,
652
	      (unsigned) ntohs(clientinfo->li_addr->su_port)));
653

654
  bind_len = clientinfo->li_addrlen;
655
  if (getsockname(s, (struct sockaddr *) &bind_addr, &bind_len) != 0) {
Martti Mela's avatar
Martti Mela committed
656 657 658
    STUN_ERROR(errno, getsockname);
    return -1;
  }
Pekka Pessi's avatar
Pekka Pessi committed
659
  
660
  inet_ntop(clientinfo->li_family, SU_ADDR(&bind_addr), ipaddr, sizeof(ipaddr));
661
  SU_DEBUG_3(("%s: Local socket bound to: %s:%u\n", __func__, ipaddr, 
662
	      (unsigned) ntohs(bind_addr.su_port)));
Pekka Pessi's avatar
Pekka Pessi committed
663

664
 skip_init:
665

666
  /* Create default message (last two params zeros) */
667
  if (stun_make_binding_req(sh, req, req->sr_msg, 0, 0) < 0) 
668 669
    return -1;

670
  retval = stun_send_binding_request(req, sh->sh_pri_addr);
671 672

  if (retval < 0) {
673
    stun_free_message(req->sr_msg);
674
  }
Pekka Pessi's avatar
Pekka Pessi committed
675 676 677 678 679 680 681 682
  if (lifetime) {
    if (retval == 0)
      *lifetime = 3600;
    else
      *lifetime = -1;
  }

  /* note: we always report success if bind() succeeds */
683 684 685

  ta_end(ta);

Pekka Pessi's avatar
Pekka Pessi committed
686 687 688 689
  return 0;

}

Martti Mela's avatar
Martti Mela committed
690 691 692 693 694

/** 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.
 */
695
su_localinfo_t *stun_request_get_localinfo(stun_request_t *req)
Martti Mela's avatar
Martti Mela committed
696
{
697
  return &req->sr_localinfo;
Martti Mela's avatar
Martti Mela committed
698 699 700 701
}



702
#if 0
Pekka Pessi's avatar
Pekka Pessi committed
703 704
/** Return type of NAT
 *  This function may take a long time to finish.
Martti Mela's avatar
Martti Mela committed
705
 *  XXX - mela: not for long!!!
706
 *  nat type is set in ss->se_handle.sh_nattype
Pekka Pessi's avatar
Pekka Pessi committed
707
 */
708
int stun_handle_get_nattype(stun_handle_t *sh,
Martti Mela's avatar
Martti Mela committed
709 710 711 712 713 714 715
			    int *addrlen)
{
  int retval, lifetime, sockfd;
  socklen_t locallen, len;
  su_sockaddr_t local, /* mapped_addr1, */ mapped_addr2;
  su_localinfo_t *mapped_addr1;

716
  sockfd = sh->sh_bind_socket;
Martti Mela's avatar
Martti Mela committed
717

718
  assert(sh);
Martti Mela's avatar
Martti Mela committed
719

720 721 722
  if ((sh->sh_state != stun_bind_done) &&
      (sh->sh_state != stun_bind_timeout) &&
      (sh->sh_state != stun_bind_error))
Martti Mela's avatar
Martti Mela committed
723 724 725
    return -1;

  
726
  mapped_addr1 = stun_handle_get_local_addr(sh);
Martti Mela's avatar
Martti Mela committed
727 728 729 730 731 732 733 734

#if 0  
  len = sizeof(mapped_addr1);
  memcpy(&mapped_addr1, my_addr, len); 
  /* mapped_addr1.li_addr.su_port = 0; */ /* wild card for get_nattype */
  /* retval = stun_bind(ss, &mapped_addr1, &lifetime); */
#endif

735 736
  if (sh->sh_state == stun_bind_timeout) {
    sh->sh_nattype = stun_udp_blocked;
Martti Mela's avatar
Martti Mela committed
737 738 739
    /* otherwise unknown nat type */
    return 0;
  }
740 741
  else if (sh->sh_state == stun_bind_error) {
    sh->sh_nattype = stun_nat_unknown;
Martti Mela's avatar
Martti Mela committed
742 743 744 745 746 747 748 749 750 751 752
    return 0;
  }
  else {
    memset(&local, 0, sizeof(local));
    locallen = sizeof(local);
    getsockname(sockfd, (struct sockaddr *) &local, &locallen);

    /* Same IP and port*/
    if (memcmp(&local, &mapped_addr1->li_addr, 8) == 0) {
      /* conduct TEST II */      
      memset(&mapped_addr2, 0, sizeof(mapped_addr2));
753
      retval = stun_send_binding_request(sh, sh->sh_pri_addr, 1, 1);
Martti Mela's avatar
Martti Mela committed
754 755 756 757
      if (retval == -1) {
	if (errno == ETIMEDOUT) {
	  /* No Response: Type 3 - Sym UDP FW */
	  retval = 0;
758
	  sh->sh_nattype = stun_sym_udp_fw;	  
Martti Mela's avatar
Martti Mela committed
759 760 761 762
	} /* otherwise unknown nat type */
      } 
      else {
	/* Response: Type 1 - Open Internet */
763
	sh->sh_nattype = stun_open_internet;
Martti Mela's avatar
Martti Mela committed
764 765 766 767 768
      }
    }
    /* Different IP */
    else {
      memset(&mapped_addr2, 0, sizeof(mapped_addr2));
769
      retval = stun_send_binding_request(sh, sh->sh_pri_addr, 1, 1);
Martti Mela's avatar
Martti Mela committed
770 771 772
      if (retval == -1) {
	if (errno == ETIMEDOUT) {
	  /* No Response */
773
	  retval = stun_send_binding_request(sh, sh->sh_sec_addr, 0, 0);
Martti Mela's avatar
Martti Mela committed
774 775 776 777 778
	  /* response comes back, has to be the case */
	  if (retval == 0) {
	    if (memcmp(&mapped_addr1, &mapped_addr2, 8) == 0) {
	      /* Same Public IP and port, Test III, server ip 0 or 1 should be
		 same */
779
	      retval = stun_send_binding_request(sh, sh->sh_pri_addr, 0, 1);
Martti Mela's avatar
Martti Mela committed
780 781
	      if(retval==0) {
		/* Response: Type 6 - Restricted */
782
		sh->sh_nattype = stun_nat_res_cone;
Martti Mela's avatar
Martti Mela committed
783 784 785 786
	      }
	      else if(errno==ETIMEDOUT) {
		/* No response: Type 7 - Port Restricted */
		retval = 0;
787
		sh->sh_nattype = stun_nat_port_res_cone;
Martti Mela's avatar
Martti Mela committed
788 789 790 791
	      }
	    }
	    else {
	      /* Different Public IP: Type 5 - Sym NAT */
792
	      sh->sh_nattype = stun_nat_sym;
Martti Mela's avatar
Martti Mela committed
793 794 795 796 797 798
	    }
	  } /* otherwise there is a sudden network problem */	  
	} /* otherwise unknown nat type */
      }
      else {
	/* Response: Type 4 - Full Cone */
799
	sh->sh_nattype = stun_nat_full_cone;
Martti Mela's avatar
Martti Mela committed
800 801 802 803 804 805
      }
    }
  }
  
  return retval;
}
806
#endif /* if 0 */
Martti Mela's avatar
Martti Mela committed
807

808 809 810
int stun_handle_get_nattype(stun_handle_t *sh,
			    tag_type_t tag, tag_value_t value,
			    ...)
Pekka Pessi's avatar
Pekka Pessi committed
811
{
812
  int nattype = stun_nat_unknown;
813
  int retval, lifetime, sockfd;
814 815
  /* socklen_t locallen, len; */
#if 0
Martti Mela's avatar
Martti Mela committed
816 817
  struct sockaddr_in local, /* mapped_addr1, */ mapped_addr2;
  su_localinfo_t mapped_addr1;
818 819
#endif
  ta_list ta;
Pekka Pessi's avatar
Pekka Pessi committed
820
  
821
  ta_start(ta, tag, value);
Martti Mela's avatar
Martti Mela committed
822

823 824 825 826

  sockfd = sh->sh_bind_socket;

#if 0
Martti Mela's avatar
Martti Mela committed
827
  assert(my_addr && my_addr->li_addrlen != 0);
Pekka Pessi's avatar
Pekka Pessi committed
828 829
  len = sizeof(mapped_addr1);
  memcpy(&mapped_addr1, my_addr, len); 
Martti Mela's avatar
Martti Mela committed
830
  /* mapped_addr1.li_addr.su_port = 0; */ /* wild card for get_nattype */
831
#endif
Martti Mela's avatar
Martti Mela committed
832 833

  /* retval = stun_bind(ss, &mapped_addr1, &lifetime); */
834 835 836 837 838 839 840

  /* This launches the binding process, but with a different state
   * machine than the default one: get_nattype is the specified action
   * here */
  retval = stun_handle_bind(sh, &lifetime,
			    STUNTAG_ACTION(stun_action_get_nattype),
			    TAG_NEXT(ta_args(ta)));
Martti Mela's avatar
Martti Mela committed
841 842
  if (retval == -1) {
    if (errno == ETIMEDOUT) {
Pekka Pessi's avatar
Pekka Pessi committed
843 844
      /* No Response: Type 2 - UDP Blocked */
      retval = 0; /* time out is a legitimate response */
845
      nattype = stun_udp_blocked;
Pekka Pessi's avatar
Pekka Pessi committed
846 847
    } /* otherwise unknown nat type */
  }
848 849 850 851 852 853
  
  ta_end(ta);
  return 0;
}

#if 0
Pekka Pessi's avatar
Pekka Pessi committed
854 855 856
  else { /* Response comes back */
    memset(&local, 0, sizeof(local)); locallen = sizeof(local);
    getsockname(sockfd, (struct sockaddr *)&local, &locallen);
Martti Mela's avatar
Martti Mela committed
857
    if (memcmp(&local, &mapped_addr1, 8) == 0) { /* Same IP and port*/
Pekka Pessi's avatar
Pekka Pessi committed
858 859
      /* conduct TEST II */      
      memset(&mapped_addr2, 0, sizeof(mapped_addr2));
860
      retval = stun_send_binding_request(sh, sh->sh_pri_addr, &mapped_addr2, 1, 1);
Martti Mela's avatar
Martti Mela committed
861 862
      if (retval == -1) {
	if (errno == ETIMEDOUT) {
Pekka Pessi's avatar
Pekka Pessi committed
863 864 865 866 867 868 869 870 871 872 873 874
	  /* No Response: Type 3 - Sym UDP FW */
	  retval = 0;
	  nattype = STUN_SYM_UDP_FW;	  
	} /* otherwise unknown nat type */
      } 
      else {
	/* Response: Type 1 - Open Internet */
	nattype = STUN_OPEN_INTERNET;
      }
    }
    else { /* Different IP */
      memset(&mapped_addr2, 0, sizeof(mapped_addr2));
875
      retval = stun_send_binding_request(sh, sh->sh_pri_addr, &mapped_addr2, 1, 1);
Martti Mela's avatar
Martti Mela committed
876 877
      if (retval == -1) {
	if (errno == ETIMEDOUT) {
Pekka Pessi's avatar
Pekka Pessi committed
878
	  /* No Response */
879
	  retval = stun_send_binding_request(sh, sh->sh_sec_addr, &mapped_addr2, 0, 0);
Martti Mela's avatar
Martti Mela committed
880 881 882
	  /* response comes back, has to be the case */
	  if (retval == 0) {
	    if (memcmp(&mapped_addr1, &mapped_addr2, 8) == 0) {
Pekka Pessi's avatar
Pekka Pessi committed
883 884
	      /* Same Public IP and port, Test III, server ip 0 or 1 should be
		 same */
885
	      retval = stun_send_binding_request(sh, sh->sh_pri_addr, &mapped_addr2, 0, 1);
Pekka Pessi's avatar
Pekka Pessi committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
	      if(retval==0) {
		/* Response: Type 6 - Restricted */
		nattype = STUN_NAT_RES_CONE;
	      }
	      else if(errno==ETIMEDOUT) {
		/* No response: Type 7 - Port Restricted */
		retval = 0;
		nattype = STUN_NAT_PORT_RES_CONE;
	      }
	    }
	    else {
	      /* Different Public IP: Type 5 - Sym NAT */
	      nattype = STUN_NAT_SYM;
	    }
	  } /* otherwise there is a sudden network problem */	  
	} /* otherwise unknown nat type */
      }
      else {
	/* Response: Type 4 - Full Cone */
	nattype = STUN_NAT_FULL_CONE;
      }
    }
  }
  
910
  sh->sh_nattype = nattype;
Pekka Pessi's avatar
Pekka Pessi committed
911 912
  return retval;
}
Martti Mela's avatar
Martti Mela committed
913
#endif /* if 0 */
Pekka Pessi's avatar
Pekka Pessi committed
914 915 916 917 918 919 920 921 922 923 924 925 926

/** Application should call this at regular intervals 
 *  while binding is active.
 */
int stun_poll(stun_socket_t *ss)
{
  return 0;
}

/********************************************************************
 * Internal functions
 *******************************************************************/

927
static 
928

Martti Mela's avatar
Martti Mela committed
929
int stun_tls_callback(su_root_magic_t *m, su_wait_t *w, stun_handle_t *self)
Pekka Pessi's avatar
Pekka Pessi committed
930
{
Martti Mela's avatar
Martti Mela committed
931
  stun_msg_t *req, *resp;
Martti Mela's avatar
Martti Mela committed
932
  int z, err;
933
  int events = su_wait_events(w, self->sh_tls_socket);
Pekka Pessi's avatar
Pekka Pessi committed
934 935 936 937 938
  SSL_CTX* ctx;
  SSL *ssl;
  X509* server_cert;
  unsigned char buf[512];
  stun_attr_t *password, *username;
Martti Mela's avatar
Martti Mela committed
939
  int state;
Martti Mela's avatar
Martti Mela committed
940

Martti Mela's avatar
Martti Mela committed
941
  SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, self,
Martti Mela's avatar
Martti Mela committed
942
	      events & SU_WAIT_CONNECT ? " CONNECTED" : "",
Martti Mela's avatar
Martti Mela committed
943 944 945
	      events & SU_WAIT_ERR     ? " ERR"       : "",
	      events & SU_WAIT_IN      ? " IN"        : "",
	      events & SU_WAIT_OUT     ? " OUT"       : ""));
Martti Mela's avatar
Martti Mela committed
946

Martti Mela's avatar
Martti Mela committed
947
  if (events & SU_WAIT_ERR) {
948
    su_wait_destroy(w);
949
    su_root_deregister(self->sh_root, self->sh_root_index);
950

Martti Mela's avatar
Martti Mela committed
951
    /* Destroy the timeout timer */
952
    su_timer_destroy(self->sh_connect_timer);
Martti Mela's avatar
Martti Mela committed
953 954 955

    SU_DEBUG_3(("%s: shared secret not obtained from server. "	\
		"Proceed without username/password.\n", __func__));
956 957
    self->sh_state = stun_tls_connection_failed;
    self->sh_callback(self->sh_context, self, NULL, self->sh_state);
Martti Mela's avatar
Martti Mela committed
958 959
    return 0;
  }
Pekka Pessi's avatar
Pekka Pessi committed
960

Martti Mela's avatar
Martti Mela committed
961
  /* Can be NULL, too */
962 963 964
  ssl  = self->sh_ssl;
  req  = &self->sh_tls_request;
  resp = &self->sh_tls_response;
965

966
  state = self->sh_state;
Martti Mela's avatar
Martti Mela committed
967 968
  switch (state) {
  case stun_tls_connecting:
Pekka Pessi's avatar
Pekka Pessi committed
969

Martti Mela's avatar
Martti Mela committed
970 971 972 973 974 975 976 977 978 979 980
    /* 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());
981
    self->sh_ctx = ctx;
Martti Mela's avatar
Martti Mela committed
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996

    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);
997
    self->sh_ssl = ssl;
Pekka Pessi's avatar
Pekka Pessi committed
998

999
    if (SSL_set_fd(ssl, self->sh_tls_socket) == 0) {
Martti Mela's avatar
Martti Mela committed
1000 1001 1002 1003
      STUN_ERROR(err, connect);
      stun_free_buffer(&req->enc_buf);
      return -1;
    }
Pekka Pessi's avatar
Pekka Pessi committed
1004

Martti Mela's avatar
Martti Mela committed
1005 1006 1007
    /* 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
1008

Martti Mela's avatar
Martti Mela committed
1009
  case stun_tls_ssl_connecting:
1010
    events = SU_WAIT_ERR | SU_WAIT_IN;
1011 1012
    su_root_eventmask(self->sh_root, self->sh_root_index,
		      self->sh_tls_socket, events);
1013