stun_common.c 20.5 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_common.c
 * @brief 
 * 
 * @author Tat Chan <Tat.Chan@nokia.com>
30
 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
31 32 33 34 35 36 37 38 39
 *  
 * @date Created: Fri Oct  3 13:40:41 2003 ppessi
 * 
 */

#include "config.h"

#ifdef USE_TURN
#include "../turn/turn_common.h"
40 41
#undef STUN_A_LAST_MANDATORY
#define STUN_A_LAST_MANDATORY TURN_LARGEST_ATTRIBUTE
Pekka Pessi's avatar
Pekka Pessi committed
42 43
#endif

44 45
#include "stun_internal.h"

46
#include <string.h>
47 48
#include <assert.h>

49 50 51 52 53 54 55
#if HAVE_FUNC
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
#define __func__ "stun_common"
#endif

Pekka Pessi's avatar
Pekka Pessi committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
const char stun_400_Bad_request[] = "Bad Request",
  stun_401_Unauthorized[] = "Unauthorized",
  stun_420_Unknown_attribute[] = "Unknown Attribute",
  stun_430_Stale_credentials[] = "Stale Credentials",
  stun_431_Integrity_check_failure[] = "Integrity Check Failure",
  stun_432_Missing_username[] = "Missing Username",
  stun_433_Use_tls[] = "Use TLS",
#ifdef USE_TURN
  turn_434_Missing_realm[] = "Missing Realm",
  turn_435_Missing_nonce[] = "Missing Nonce",
  turn_436_Unknown_username[] = "Unknown Username",
  turn_437_No_binding[] = "No Binding",
  turn_439_Illegal_port[] = "Illegal Port",
#endif
  stun_500_Server_error[] = "Server Error",
  stun_600_Global_failure[] = "Global Failure";

73 74 75
#define set16(b, offset, value)			\
  (((b)[(offset) + 0] = ((value) >> 8) & 255),	\
   ((b)[(offset) + 1] = (value) & 255))
Pekka Pessi's avatar
Pekka Pessi committed
76

77 78 79 80 81 82
#define get16(b, offset)	\
  (((b)[(offset) + 0] << 8) |	\
   ((b)[(offset) + 1] << 0))

int stun_parse_message(stun_msg_t *msg)
{
Pekka Pessi's avatar
Pekka Pessi committed
83 84 85 86 87 88
  unsigned len;
  int i;
  unsigned char *p;

  /* parse header first */
  p = msg->enc_buf.data;
89 90
  msg->stun_hdr.msg_type = get16(p, 0);
  msg->stun_hdr.msg_len = get16(p, 2); 
91
  memcpy(msg->stun_hdr.tran_id, p + 4, STUN_TID_BYTES);
92

93 94
  SU_DEBUG_5(("%s: Parse STUN message: Length = %d\n", __func__,
	      msg->stun_hdr.msg_len));
Pekka Pessi's avatar
Pekka Pessi committed
95 96 97 98 99

  /* parse attributes */
  len = msg->stun_hdr.msg_len;
  p = msg->enc_buf.data + 20;
  msg->stun_attr = NULL;
100
  while (len > 0) {
Pekka Pessi's avatar
Pekka Pessi committed
101
    i = stun_parse_attribute(msg, p);
102
    if (i <= 0) {
Martti Mela's avatar
Martti Mela committed
103
      SU_DEBUG_3(("%s: Error parsing attribute.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
104 105 106 107 108
      return -1;
    }
    p += i;
    len -= i;
  }
109

Pekka Pessi's avatar
Pekka Pessi committed
110 111 112
  return 0;
}

113 114
int stun_parse_attribute(stun_msg_t *msg, unsigned char *p)
{
Pekka Pessi's avatar
Pekka Pessi committed
115
  int len;
116
  uint16_t attr_type;
Pekka Pessi's avatar
Pekka Pessi committed
117 118
  stun_attr_t *attr, *next;

119 120 121 122 123
  attr_type = get16(p, 0);
  len = get16(p, 2);

  SU_DEBUG_5(("%s: received attribute: Type %02X, Length %d - %s\n", 
	      __func__, attr_type, len, stun_attr_phrase(attr_type)));
Pekka Pessi's avatar
Pekka Pessi committed
124

125
  if (attr_type > STUN_A_LAST_MANDATORY && attr_type < STUN_A_OPTIONAL) {
Pekka Pessi's avatar
Pekka Pessi committed
126 127 128
    return -1;
  }

129 130 131 132 133 134
  attr = (stun_attr_t *)calloc(1, sizeof(stun_attr_t));
  if (!attr)
    return -1;
  attr->next = NULL;
  attr->attr_type = attr_type;
  p += 4;
135

136
  switch (attr->attr_type) {
Pekka Pessi's avatar
Pekka Pessi committed
137 138 139 140 141 142 143 144 145 146
  case MAPPED_ADDRESS:
  case RESPONSE_ADDRESS:
  case SOURCE_ADDRESS:
  case CHANGED_ADDRESS:
  case REFLECTED_FROM:
#ifdef USE_TURN
  case TURN_ALTERNATE_SERVER:
  case TURN_DESTINATION_ADDRESS:
  case TURN_SOURCE_ADDRESS:
#endif
Martti Mela's avatar
Martti Mela committed
147 148 149 150
    if (stun_parse_attr_address(attr, p, len) < 0) {
      free(attr);
      return -1;
    }
Pekka Pessi's avatar
Pekka Pessi committed
151 152
    break;
  case ERROR_CODE:
153
    if (stun_parse_attr_error_code(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
154 155
    break;
  case UNKNOWN_ATTRIBUTES:
156
    if(stun_parse_attr_unknown_attributes(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
157 158 159 160 161 162 163
    break;
  case CHANGE_REQUEST:
#ifdef USE_TURN
  case TURN_LIFETIME:
  case TURN_MAGIC_COOKIE:
  case TURN_BANDWIDTH:
#endif
164
    if (stun_parse_attr_uint32(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
165 166 167
    break;
  case USERNAME:
  case PASSWORD:
168 169
  case STUN_A_REALM:
  case STUN_A_NONCE:
Pekka Pessi's avatar
Pekka Pessi committed
170 171 172 173
#ifdef USE_TURN
  case TURN_DATA:
  case TURN_NONCE:
#endif
174
    if (stun_parse_attr_buffer(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
    break;
  default:
    /* just copy as is */
    attr->pattr = NULL;
    attr->enc_buf.size = len;
    attr->enc_buf.data = (unsigned char *) malloc(len);
    memcpy(attr->enc_buf.data, p, len);
    break;
  }

  /* skip to end of list */
  if(msg->stun_attr==NULL) {
    msg->stun_attr = attr;
  }
  else {
    next = msg->stun_attr;
    while(next->next!=NULL) {
      next = next->next;
    }
    next->next = attr;
  }
  return len+4;
}

199 200 201
int stun_parse_attr_address(stun_attr_t *attr, 
			    const unsigned char *p, 
			    unsigned len)
202
{
203
  su_sockaddr_t *addr;
Pekka Pessi's avatar
Pekka Pessi committed
204
  int addrlen;
205
  char ipaddr[SU_ADDRSIZE + 2];
Pekka Pessi's avatar
Pekka Pessi committed
206

207
  if (len != 8) {
Pekka Pessi's avatar
Pekka Pessi committed
208 209 210
    return -1;
  }

211 212
  addrlen = sizeof(su_sockaddr_t);
  addr = (su_sockaddr_t *) malloc(addrlen);
Pekka Pessi's avatar
Pekka Pessi committed
213

214
  if (*(p+1) == 1) { /* expected value for IPv4 */
215
    addr->su_sin.sin_family = AF_INET;
Pekka Pessi's avatar
Pekka Pessi committed
216 217 218 219 220
  }
  else {
    free(addr);
    return -1;
  }
221 222
  memcpy(&addr->su_sin.sin_port, p + 2, 2);
  memcpy(&addr->su_sin.sin_addr.s_addr, p + 4, 4);
Pekka Pessi's avatar
Pekka Pessi committed
223

224
  SU_DEBUG_5(("%s: address attribute: %s:%d\n", __func__,
225 226
	      inet_ntop(addr->su_family, SU_ADDR(addr), ipaddr, sizeof(ipaddr)),
	      (unsigned) ntohs(addr->su_sin.sin_port)));
Pekka Pessi's avatar
Pekka Pessi committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

  attr->pattr = addr;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

int stun_parse_attr_error_code(stun_attr_t *attr, const unsigned char *p, unsigned len) {

  uint32_t tmp;
  stun_attr_errorcode_t *error;

  memcpy(&tmp, p, sizeof(uint32_t));
  tmp = ntohl(tmp);
  error = (stun_attr_errorcode_t *) malloc(sizeof(*error));

  error->code = (tmp & STUN_EC_CLASS)*100 + (tmp & STUN_EC_NUM);

245
  error->phrase = (char *) malloc(len-3);
Pekka Pessi's avatar
Pekka Pessi committed
246

247
  strncpy(error->phrase, (char*)p+4, len-4);
248
  error->phrase[len - 4] = '\0';
Pekka Pessi's avatar
Pekka Pessi committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

  attr->pattr = error;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

int stun_parse_attr_uint32(stun_attr_t *attr, const unsigned char *p, unsigned len)
{
  uint32_t tmp;
  stun_attr_changerequest_t *cr;
  cr = (stun_attr_changerequest_t *) malloc(sizeof(*cr));
  memcpy(&tmp, p, sizeof(uint32_t));
  cr->value = ntohl(tmp);
  attr->pattr = cr;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

int stun_parse_attr_buffer(stun_attr_t *attr, const unsigned char *p, unsigned len)
{
  stun_buffer_t *buf;
272
  buf = (stun_buffer_t *) malloc(sizeof(stun_buffer_t));
Pekka Pessi's avatar
Pekka Pessi committed
273
  buf->size = len;
274
  buf->data = (unsigned char *) malloc(len);
Pekka Pessi's avatar
Pekka Pessi committed
275 276 277 278 279 280 281
  memcpy(buf->data, p, len);
  attr->pattr = buf;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

282 283 284
int stun_parse_attr_unknown_attributes(stun_attr_t *attr,
				       const unsigned char *p,
				       unsigned len)
Pekka Pessi's avatar
Pekka Pessi committed
285 286 287 288
{
  return 0;
}

289
/** scan thru attribute list and return the next requested attr */
Pekka Pessi's avatar
Pekka Pessi committed
290 291
stun_attr_t *stun_get_attr(stun_attr_t *attr, uint16_t attr_type) {
  stun_attr_t *p;
292 293

  for (p = attr; p != NULL; p = p->next) {
294
    if (p->attr_type == attr_type)
295
      break;
Pekka Pessi's avatar
Pekka Pessi committed
296
  }
297 298

  return p;
Pekka Pessi's avatar
Pekka Pessi committed
299 300 301 302 303 304 305 306
}

void stun_init_buffer(stun_buffer_t *p) {
  p->data = NULL;
  p->size = 0;
}

int stun_free_buffer(stun_buffer_t *p) {
307
  if (p->data)
308
    free(p->data), p->data = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
309 310 311 312 313 314 315 316 317 318 319 320 321
  p->size = 0;
  return 0;
}

int stun_copy_buffer(stun_buffer_t *p, stun_buffer_t *p2) {
  stun_free_buffer(p); /* clean up existing data */
  p->size = p2->size;
  p->data = (unsigned char *) malloc(p->size);
  memcpy(p->data, p2->data, p->size);
  return p->size;
}

const char *stun_response_phrase(int status) {
322
  if (status <100 || status >600)
Pekka Pessi's avatar
Pekka Pessi committed
323
    return NULL;
324 325

  switch (status) {
Pekka Pessi's avatar
Pekka Pessi committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
  case STUN_400_BAD_REQUEST: return stun_400_Bad_request;
  case STUN_401_UNAUTHORIZED: return stun_401_Unauthorized;
  case STUN_420_UNKNOWN_ATTRIBUTE: return stun_420_Unknown_attribute;
  case STUN_430_STALE_CREDENTIALS: return stun_430_Stale_credentials;
  case STUN_431_INTEGRITY_CHECK_FAILURE: return stun_431_Integrity_check_failure;
  case STUN_432_MISSING_USERNAME: return stun_432_Missing_username;
  case STUN_433_USE_TLS: return stun_433_Use_tls;
#ifdef USE_TURN
  case TURN_MISSING_REALM: return turn_434_Missing_realm;
  case TURN_MISSING_NONCE: return turn_435_Missing_nonce;
  case TURN_UNKNOWN_USERNAME: return turn_436_Unknown_username;
  case TURN_NO_BINDING: return turn_437_No_binding;
  case TURN_ILLEGAL_PORT: return turn_439_Illegal_port;
#endif
  case STUN_500_SERVER_ERROR: return stun_500_Server_error;
  case STUN_600_GLOBAL_FAILURE: return stun_600_Global_failure;
  }
  return "Response";
}

/** The set of functions encodes the corresponding attribute to
 *    network format, and save the result to the enc_buf. Return the
 *    size of the buffer.
 */


/* This function is used to encode any attribute of the form ADDRESS
   */
int stun_encode_address(stun_attr_t *attr) {
  stun_attr_sockaddr_t *a;
  uint16_t tmp;

  a = (stun_attr_sockaddr_t *)attr->pattr;

360
  if (stun_encode_type_len(attr, 8) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
361 362 363 364 365
    return -1;
  }

  tmp = htons(0x01); /* FAMILY = 0x01 */
  memcpy(attr->enc_buf.data+4, &tmp, sizeof(tmp));
366 367 368
  memcpy(attr->enc_buf.data+6, &a->sin_port, 2);
  memcpy(attr->enc_buf.data+8, &a->sin_addr.s_addr, 4);

Pekka Pessi's avatar
Pekka Pessi committed
369 370 371 372 373 374
  return attr->enc_buf.size;
}

int stun_encode_uint32(stun_attr_t *attr) {
  uint32_t tmp;

375
  if (stun_encode_type_len(attr, 4) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
376 377 378
    return -1;
  }

379
  tmp = htonl(((stun_attr_changerequest_t *) attr->pattr)->value);
380
  memcpy(attr->enc_buf.data+4, &tmp, 4);
Pekka Pessi's avatar
Pekka Pessi committed
381 382 383 384 385
  return attr->enc_buf.size;
}

int stun_encode_error_code(stun_attr_t *attr) {
  short int class, num;
386
  size_t phrase_len, padded;
Pekka Pessi's avatar
Pekka Pessi committed
387 388
  stun_attr_errorcode_t *error;

389 390 391
  error = (stun_attr_errorcode_t *) attr->pattr;
  class = error->code / 100;
  num = error->code % 100;
392

393
  phrase_len = strlen(error->phrase);
394 395 396
  if (phrase_len + 8 > 65536)
    phrase_len = 65536 - 8;

397
  /* note: align the phrase len (see RFC3489:11.2.9) */
398
  padded = phrase_len + (phrase_len % 4 == 0 ? 0 : 4 - (phrase_len % 4));
399 400 401

  /* note: error-code has four octets of headers plus the 
   *       reason field -> len+4 octets */
402 403
  if (stun_encode_type_len(attr, (uint16_t)(padded + 4)) < 0) {
    return -1;
Pekka Pessi's avatar
Pekka Pessi committed
404
  }
405
  else {
406
    assert(attr->enc_buf.size == padded + 8);
407
    memset(attr->enc_buf.data+4, 0, 2);
408 409
    attr->enc_buf.data[6] = class;
    attr->enc_buf.data[7] = num;
410
    /* note: 4 octets of TLV header and 4 octets of error-code header */
411 412 413
    memcpy(attr->enc_buf.data+8, error->phrase, 
	   phrase_len);
    memset(attr->enc_buf.data + 8 + phrase_len, 0, padded - phrase_len);
414 415
  }

Pekka Pessi's avatar
Pekka Pessi committed
416 417 418 419 420 421 422
  return attr->enc_buf.size;
}

int stun_encode_buffer(stun_attr_t *attr) {
  stun_buffer_t *a;

  a = (stun_buffer_t *)attr->pattr;
423 424
  assert(a->size < 65536);
  if (stun_encode_type_len(attr, (uint16_t)a->size) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
425 426 427 428 429 430 431
    return -1;
  }

  memcpy(attr->enc_buf.data+4, a->data, a->size);
  return attr->enc_buf.size;
}

Martti Mela's avatar
Martti Mela committed
432
#if defined(HAVE_OPENSSL)
433 434 435 436
int stun_encode_message_integrity(stun_attr_t *attr,
				  unsigned char *buf,
				  int len,
				  stun_buffer_t *pwd) {
437
  int padded_len;
438 439 440
  unsigned int dig_len;
  unsigned char *padded_text = NULL;
  void *sha1_hmac;
Pekka Pessi's avatar
Pekka Pessi committed
441

442
  if (stun_encode_type_len(attr, 20) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
443 444 445 446
    return -1;
  }

  /* zero padding */
447
  if (len % 64 != 0) {
448 449 450 451 452 453 454 455 456 457 458

    padded_len = len + (64 - (len % 64));
    padded_text = (unsigned char *) malloc(padded_len);
    memcpy(padded_text, buf, len);
    memset(padded_text + len, 0, padded_len - len);

    sha1_hmac = HMAC(EVP_sha1(), pwd->data, pwd->size, padded_text, padded_len, NULL, &dig_len);
  }
  else {
    sha1_hmac = HMAC(EVP_sha1(), pwd->data, pwd->size, buf, len, NULL, &dig_len);
  }
Pekka Pessi's avatar
Pekka Pessi committed
459

460
  assert(dig_len == 20);
Pekka Pessi's avatar
Pekka Pessi committed
461

462
  memcpy(attr->enc_buf.data + 4, sha1_hmac, 20);
Pekka Pessi's avatar
Pekka Pessi committed
463 464 465
  free(padded_text);
  return attr->enc_buf.size;
}
Martti Mela's avatar
Martti Mela committed
466 467 468 469 470 471 472 473 474
#else
int stun_encode_message_integrity(stun_attr_t *attr,
				  unsigned char *buf,
				  int len,
				  stun_buffer_t *pwd) {

  return 0;
}
#endif /* HAVE_OPENSSL */
Pekka Pessi's avatar
Pekka Pessi committed
475 476 477 478

/** this function allocates the enc_buf, fills in type, length */
int stun_encode_type_len(stun_attr_t *attr, uint16_t len) {
  uint16_t tmp;
479

480 481
  attr->enc_buf.data = (unsigned char *) malloc(len + 4);
  memset(attr->enc_buf.data, 0, len + 4);
482

Pekka Pessi's avatar
Pekka Pessi committed
483 484
  tmp = htons(attr->attr_type);
  memcpy(attr->enc_buf.data, &tmp, 2);
485

Pekka Pessi's avatar
Pekka Pessi committed
486
  tmp = htons(len);
487 488
  memcpy(attr->enc_buf.data + 2, &tmp, 2);
  attr->enc_buf.size = len + 4;
489

Pekka Pessi's avatar
Pekka Pessi committed
490 491 492
  return 0;
}

493 494 495 496
/** 
 * Validate the message integrity based on given 
 * STUN password 'pwd'. The received content should be
 * in msg->enc_buf.
Pekka Pessi's avatar
Pekka Pessi committed
497
 */
498 499
int stun_validate_message_integrity(stun_msg_t *msg, stun_buffer_t *pwd)
{
Pekka Pessi's avatar
Pekka Pessi committed
500

501
#if defined(HAVE_OPENSSL)
502
  int padded_len, len;
503
  unsigned int dig_len;
Pekka Pessi's avatar
Pekka Pessi committed
504 505
  unsigned char dig[20]; /* received sha1 digest */
  unsigned char *padded_text;
506
#endif
Pekka Pessi's avatar
Pekka Pessi committed
507

508 509
  /* password NULL so shared-secret not established and 
     messege integrity checks can be skipped */
Martti Mela's avatar
Martti Mela committed
510 511 512
  if (pwd->data == NULL)
    return 0;

513 514 515 516
  /* otherwise the check must match */

#if defined(HAVE_OPENSSL)

Pekka Pessi's avatar
Pekka Pessi committed
517
  /* message integrity not received */
Martti Mela's avatar
Martti Mela committed
518 519
  if (stun_get_attr(msg->stun_attr, MESSAGE_INTEGRITY) == NULL) {
    SU_DEBUG_5(("%s: error: message integrity missing.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
520 521 522 523
    return -1;
  }

  /* zero padding */
524
  len = msg->enc_buf.size - 24;
Martti Mela's avatar
Martti Mela committed
525 526
  padded_len = len + (len % 64 == 0 ? 0 : 64 - (len % 64));
  padded_text = (unsigned char *) malloc(padded_len);
Pekka Pessi's avatar
Pekka Pessi committed
527 528 529 530 531
  memset(padded_text, 0, padded_len);
  memcpy(padded_text, msg->enc_buf.data, len);

  memcpy(dig, HMAC(EVP_sha1(), pwd->data, pwd->size, padded_text, padded_len, NULL, &dig_len), 20);

532
  if (memcmp(dig, msg->enc_buf.data + msg->enc_buf.size - 20, 20) != 0) {
Pekka Pessi's avatar
Pekka Pessi committed
533
    /* does not match, but try the test server's password */
Martti Mela's avatar
Martti Mela committed
534 535
    if (memcmp(msg->enc_buf.data+msg->enc_buf.size-20, "hmac-not-implemented", 20) != 0) {
      SU_DEBUG_5(("%s: error: message digest problem.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
536 537 538 539
      return -1;
    }
  }
  else {
Martti Mela's avatar
Martti Mela committed
540
    SU_DEBUG_5(("%s: message integrity validated.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
541 542 543 544 545
  }

  free(padded_text);

  return 0;
546
#else /* HAVE_OPENSSL */
547
  return -1;
548
#endif
549
}
Pekka Pessi's avatar
Pekka Pessi committed
550 551

void debug_print(stun_buffer_t *buf) {
552
  unsigned i;
Martti Mela's avatar
Martti Mela committed
553
  for(i = 0; i < buf->size/4; i++) {
Pekka Pessi's avatar
Pekka Pessi committed
554
    SU_DEBUG_9(("%02x %02x %02x %02x\n",
Martti Mela's avatar
Martti Mela committed
555 556 557 558 559 560
		*(buf->data + i*4),
		*(buf->data + i*4 +1),
		*(buf->data + i*4 +2),
		*(buf->data + i*4 +3)));
    if (i == 4)
      SU_DEBUG_9(("---------------------\n"));
Pekka Pessi's avatar
Pekka Pessi committed
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  }
  SU_DEBUG_9(("\n"));
}

int stun_init_message(stun_msg_t *msg) {
  msg->stun_hdr.msg_type = 0;
  msg->stun_hdr.msg_len = 0;
  msg->stun_attr = NULL;
  stun_init_buffer(&msg->enc_buf);
  return 0;
}

int stun_free_message(stun_msg_t *msg) {

  stun_attr_t *p, *p2;

  /* clearing header */
578
  memset(&msg->stun_hdr, 0, sizeof msg->stun_hdr);
Pekka Pessi's avatar
Pekka Pessi committed
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609

  /* clearing attr */
  p = msg->stun_attr;
  while(p) {
    if(p->pattr) {
      switch(p->attr_type) {
      case USERNAME:
      case PASSWORD:
#ifdef USE_TURN
      case TURN_DATA:
      case TURN_NONCE:
#endif
	stun_free_buffer(p->pattr);
	break;
      default:
	free(p->pattr);
      }
    }
    stun_free_buffer(&p->enc_buf);
    p2 = p->next;
    free(p);
    p = p2;
  }
  msg->stun_attr = NULL;

  /* clearing buffer */
  stun_free_buffer(&msg->enc_buf);

  return 0;
}

Martti Mela's avatar
Martti Mela committed
610

611 612
int stun_send_message(su_socket_t s, su_sockaddr_t *to_addr,
		      stun_msg_t *msg, stun_buffer_t *pwd) 
613
{
Martti Mela's avatar
Martti Mela committed
614 615
  int err;
  char ipaddr[SU_ADDRSIZE + 2];
616
  stun_attr_t **a, *b;
617 618 619

  stun_encode_message(msg, pwd);

620 621
  err = su_sendto(s, msg->enc_buf.data, msg->enc_buf.size, 0, 
		  to_addr, SU_SOCKADDR_SIZE(to_addr));
Martti Mela's avatar
Martti Mela committed
622

623 624 625 626 627 628
  free(msg->enc_buf.data), msg->enc_buf.data = NULL;
  msg->enc_buf.size = 0;

  for (a = &msg->stun_attr; *a;) {

    if ((*a)->pattr)
629
      free((*a)->pattr), (*a)->pattr = NULL;
630 631

    if ((*a)->enc_buf.data)
632
      free((*a)->enc_buf.data), (*a)->enc_buf.data = NULL;
633 634 635 636 637 638 639 640

    b = *a;
    b = b->next;
    free(*a);
    *a = NULL;
    *a = b;
  }

Martti Mela's avatar
Martti Mela committed
641 642
  if (err > 0) {
    inet_ntop(to_addr->su_family, SU_ADDR(to_addr), ipaddr, sizeof(ipaddr));
643
    SU_DEBUG_5(("%s: message sent to %s:%u\n", __func__,
Martti Mela's avatar
Martti Mela committed
644 645
		ipaddr, ntohs(to_addr->su_port)));

646
    debug_print(&msg->enc_buf);
Martti Mela's avatar
Martti Mela committed
647 648 649
  }
  else
    STUN_ERROR(errno, sendto);
650
  
Martti Mela's avatar
Martti Mela committed
651
  return err;
652 653
}  

Pekka Pessi's avatar
Pekka Pessi committed
654

Pekka Pessi's avatar
Pekka Pessi committed
655
/** Send a STUN message.
Pekka Pessi's avatar
Pekka Pessi committed
656 657 658 659 660
 *  This will convert the stun_msg_t to the binary format based on the
 *  spec
 */
int stun_encode_message(stun_msg_t *msg, stun_buffer_t *pwd) {

661
  int z = -1, len, buf_len = 0;
Pekka Pessi's avatar
Pekka Pessi committed
662 663 664
  unsigned char *buf;
  stun_attr_t *attr, *msg_int=NULL;

665
  if (msg->enc_buf.data == NULL) {
Pekka Pessi's avatar
Pekka Pessi committed
666 667 668
    /* convert msg to binary format */
    /* convert attributes to binary format for transmission */
    len = 0;
669
    for (attr = msg->stun_attr; attr ; attr = attr->next) {
Pekka Pessi's avatar
Pekka Pessi committed
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
      switch(attr->attr_type) {
      case RESPONSE_ADDRESS:
      case MAPPED_ADDRESS:
      case SOURCE_ADDRESS:
      case CHANGED_ADDRESS:
      case REFLECTED_FROM:	
#ifdef USE_TURN
      case TURN_ALTERNATE_SERVER:
      case TURN_DESTINATION_ADDRESS:
      case TURN_SOURCE_ADDRESS:
#endif
	z = stun_encode_address(attr);
	break;
      case CHANGE_REQUEST:
#ifdef USE_TURN
      case TURN_LIFETIME:
      case TURN_MAGIC_COOKIE:
      case TURN_BANDWIDTH:
#endif
	z = stun_encode_uint32(attr);
	break;
691

Pekka Pessi's avatar
Pekka Pessi committed
692 693 694 695 696 697 698 699 700 701 702
      case USERNAME:
      case PASSWORD:
#ifdef USE_TURN
      case TURN_REALM:
      case TURN_NONCE:
      case TURN_DATA:
#endif
	z = stun_encode_buffer(attr);
	break;
      case MESSAGE_INTEGRITY:
	msg_int = attr;
703
	z = 24;
Pekka Pessi's avatar
Pekka Pessi committed
704 705 706 707 708 709
	break;
      case ERROR_CODE:
	z = stun_encode_error_code(attr);
      default:
	break;
      }
710 711 712 713

      if(z < 0) return z;

      len += z;
Pekka Pessi's avatar
Pekka Pessi committed
714 715 716
    }

    msg->stun_hdr.msg_len = len;
717 718
    buf_len = 20 + msg->stun_hdr.msg_len;
    buf = (unsigned char *) malloc(buf_len);
Pekka Pessi's avatar
Pekka Pessi committed
719 720
    
    /* convert to binary format for transmission */
721 722
    set16(buf, 0, msg->stun_hdr.msg_type);
    set16(buf, 2, msg->stun_hdr.msg_len);
723
    memcpy(buf + 4, msg->stun_hdr.tran_id, STUN_TID_BYTES);
724 725

    len = 20;
Pekka Pessi's avatar
Pekka Pessi committed
726 727 728 729 730 731

    /* attaching encoded attributes */
    attr = msg->stun_attr;
    while(attr) {
      /* attach only if enc_buf is not null */
      if(attr->enc_buf.data && attr->attr_type != MESSAGE_INTEGRITY) {
732
	memcpy(buf+len, (void *)attr->enc_buf.data, attr->enc_buf.size); 
Pekka Pessi's avatar
Pekka Pessi committed
733 734 735 736
	len += attr->enc_buf.size;
      }
      attr = attr->next;
    }
737 738

    if (msg_int) {
Pekka Pessi's avatar
Pekka Pessi committed
739 740
      /* compute message integrity */
      if(stun_encode_message_integrity(msg_int, buf, len, pwd)!=24) {
741
	free(buf);
Pekka Pessi's avatar
Pekka Pessi committed
742 743
	return -1;
      }
744 745
      memcpy(buf+len, (void *)msg_int->enc_buf.data, 
	     msg_int->enc_buf.size);
Pekka Pessi's avatar
Pekka Pessi committed
746 747 748
    }
    
    /* save binary buffer for future reference */
749 750
    if (msg->enc_buf.data)
      free(msg->enc_buf.data);
751

Pekka Pessi's avatar
Pekka Pessi committed
752 753 754 755 756 757
    msg->enc_buf.data = buf; msg->enc_buf.size = buf_len;
  }

  return 0;
}

758 759 760
#include <sofia-sip/su.h>
#include <sofia-sip/su_debug.h>
#include <sofia-sip/su_localinfo.h>
Pekka Pessi's avatar
Pekka Pessi committed
761 762 763 764 765 766

char *stun_determine_ip_address(int family)
{

  char *local_ip_address;
  su_localinfo_t *li = NULL, hints[1] = {{ LI_CANONNAME|LI_NUMERIC }};
767 768
  int error;
  size_t address_size;
Pekka Pessi's avatar
Pekka Pessi committed
769 770 771 772 773 774
  struct sockaddr_in *sa = NULL;
  su_sockaddr_t *temp;

  hints->li_family = family;
  hints->li_canonname = getenv("HOSTADDRESS");
  if ((error = su_getlocalinfo(hints, &li)) < 0) {
775 776
    SU_DEBUG_5(("%s: stun_determine_ip_address, su_getlocalinfo: %s\n",
		__func__, su_gli_strerror(error)));
Pekka Pessi's avatar
Pekka Pessi committed
777 778 779 780 781 782 783 784
    return NULL;
  }

  temp = li->li_addr;
  sa = &temp->su_sin;

  address_size = strlen(inet_ntoa(sa->sin_addr));
  
785
  local_ip_address = malloc(address_size + 1);
Pekka Pessi's avatar
Pekka Pessi committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
  strcpy(local_ip_address, (char *) inet_ntoa(sa->sin_addr)); /* otherwise? */
    
  su_freelocalinfo(li);

  return local_ip_address;

}

const char *stun_attr_phrase(uint16_t type)
{
  switch(type) {
  case MAPPED_ADDRESS: return "MAPPED-ADDRESS";
  case RESPONSE_ADDRESS: return "RESPONSE-ADDRESS";
  case CHANGE_REQUEST: return "CHANGE-REQUEST";
  case SOURCE_ADDRESS: return "SOURCE-ADDRESS";
  case CHANGED_ADDRESS: return "CHANGED-ADDRESS";
  case USERNAME: return "USERNAME";
  case PASSWORD: return "PASSWORD";
  case MESSAGE_INTEGRITY: return "MESSAGE-INTEGRITY";
  case ERROR_CODE: return "ERROR-CODE";
  case UNKNOWN_ATTRIBUTES: return "UNKNOWN-ATTRIBUTES";
  case REFLECTED_FROM: return "REFLECTED-FROM";
808 809 810 811 812 813
  case STUN_A_ALTERNATE_SERVER:
  case STUN_A_ALTERNATE_SERVER_DEP: 
    return "ALTERNATE-SERVER";
  case STUN_A_REALM: return "REALM";
  case STUN_A_NONCE: return "NONCE";
  case STUN_A_XOR_MAPPED_ADDRESS: return "XOR-MAPPED-ADDRESS";
Pekka Pessi's avatar
Pekka Pessi committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827
#ifdef USE_TURN
  case TURN_REALM: return "REALM";
  case TURN_LIFETIME: return "LIFETIME";
  case TURN_ALTERNATE_SERVER: return "ALTERNATE_SERVER";
  case TURN_MAGIC_COOKIE: return "MAGIC_COOKIE";
  case TURN_BANDWIDTH: return "BANDWIDTH";
  case TURN_DESTINATION_ADDRESS: return "DESTINATION_ADDRESS";
  case TURN_SOURCE_ADDRESS: return "SOURCE_ADDRESS";
  case TURN_DATA: return "DATA";
  case TURN_NONCE: return "NONCE";
#endif
  default: return "Attribute undefined";
  }
}