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

25
/**@internal
Pekka Pessi's avatar
Pekka Pessi committed
26
 * @file stun_common.c
27 28
 * @brief
 *
Pekka Pessi's avatar
Pekka Pessi committed
29
 * @author Tat Chan <Tat.Chan@nokia.com>
30
 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
31
 *
Pekka Pessi's avatar
Pekka Pessi committed
32
 * @date Created: Fri Oct  3 13:40:41 2003 ppessi
33
 *
Pekka Pessi's avatar
Pekka Pessi committed
34 35 36 37 38 39
 */

#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>
Michael Jerris's avatar
Michael Jerris committed
47
#include <stdlib.h>
48 49
#include <assert.h>

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

Pekka Pessi's avatar
Pekka Pessi committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
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";

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

78 79 80 81 82 83
#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
84 85 86 87 88 89
  unsigned len;
  int i;
  unsigned char *p;

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

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

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

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

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

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

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

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

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

137
  switch (attr->attr_type) {
Pekka Pessi's avatar
Pekka Pessi committed
138 139 140 141 142 143 144 145 146 147
  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
148 149 150 151
    if (stun_parse_attr_address(attr, p, len) < 0) {
      free(attr);
      return -1;
    }
Pekka Pessi's avatar
Pekka Pessi committed
152 153
    break;
  case ERROR_CODE:
154
    if (stun_parse_attr_error_code(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
155 156
    break;
  case UNKNOWN_ATTRIBUTES:
157
    if(stun_parse_attr_unknown_attributes(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
158 159 160 161 162 163 164
    break;
  case CHANGE_REQUEST:
#ifdef USE_TURN
  case TURN_LIFETIME:
  case TURN_MAGIC_COOKIE:
  case TURN_BANDWIDTH:
#endif
165
    if (stun_parse_attr_uint32(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
166 167 168
    break;
  case USERNAME:
  case PASSWORD:
169 170
  case STUN_A_REALM:
  case STUN_A_NONCE:
Pekka Pessi's avatar
Pekka Pessi committed
171 172 173 174
#ifdef USE_TURN
  case TURN_DATA:
  case TURN_NONCE:
#endif
175
    if (stun_parse_attr_buffer(attr, p, len) <0) { free(attr); return -1; }
Pekka Pessi's avatar
Pekka Pessi committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
    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;
}

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

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

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

215
  if (*(p+1) == 1) { /* expected value for IPv4 */
216
    addr->su_sin.sin_family = AF_INET;
Pekka Pessi's avatar
Pekka Pessi committed
217 218 219 220 221
  }
  else {
    free(addr);
    return -1;
  }
222 223
  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
224

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

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

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

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

  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;
273
  buf = (stun_buffer_t *) malloc(sizeof(stun_buffer_t));
Pekka Pessi's avatar
Pekka Pessi committed
274
  buf->size = len;
275
  buf->data = (unsigned char *) malloc(len);
Pekka Pessi's avatar
Pekka Pessi committed
276 277 278 279 280 281 282
  memcpy(buf->data, p, len);
  attr->pattr = buf;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

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

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

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

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

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

int stun_free_buffer(stun_buffer_t *p) {
308
  if (p->data)
309
    free(p->data), p->data = NULL;
Pekka Pessi's avatar
Pekka Pessi committed
310 311 312 313 314 315 316 317 318 319 320 321 322
  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) {
323
  if (status <100 || status >600)
Pekka Pessi's avatar
Pekka Pessi committed
324
    return NULL;
325 326

  switch (status) {
Pekka Pessi's avatar
Pekka Pessi committed
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 360
  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;

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

  tmp = htons(0x01); /* FAMILY = 0x01 */
  memcpy(attr->enc_buf.data+4, &tmp, sizeof(tmp));
367 368 369
  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
370 371 372 373 374 375
  return attr->enc_buf.size;
}

int stun_encode_uint32(stun_attr_t *attr) {
  uint32_t tmp;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    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
460

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

463
  memcpy(attr->enc_buf.data + 4, sha1_hmac, 20);
Pekka Pessi's avatar
Pekka Pessi committed
464 465 466
  free(padded_text);
  return attr->enc_buf.size;
}
Martti Mela's avatar
Martti Mela committed
467 468 469 470 471 472 473 474 475
#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
476 477 478 479

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

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

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

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

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

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

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

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

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

#if defined(HAVE_OPENSSL)

Pekka Pessi's avatar
Pekka Pessi committed
518
  /* message integrity not received */
Martti Mela's avatar
Martti Mela committed
519 520
  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
521 522 523 524
    return -1;
  }

  /* zero padding */
525
  len = msg->enc_buf.size - 24;
Martti Mela's avatar
Martti Mela committed
526 527
  padded_len = len + (len % 64 == 0 ? 0 : 64 - (len % 64));
  padded_text = (unsigned char *) malloc(padded_len);
Pekka Pessi's avatar
Pekka Pessi committed
528 529 530 531 532
  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);

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

  free(padded_text);

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

void debug_print(stun_buffer_t *buf) {
553
  unsigned i;
Martti Mela's avatar
Martti Mela committed
554
  for(i = 0; i < buf->size/4; i++) {
Pekka Pessi's avatar
Pekka Pessi committed
555
    SU_DEBUG_9(("%02x %02x %02x %02x\n",
Martti Mela's avatar
Martti Mela committed
556 557 558 559 560 561
		*(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
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
  }
  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 */
579
  memset(&msg->stun_hdr, 0, sizeof msg->stun_hdr);
Pekka Pessi's avatar
Pekka Pessi committed
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 610

  /* 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
611

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

  stun_encode_message(msg, pwd);

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

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

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

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

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

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

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

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

Martti Mela's avatar
Martti Mela committed
652
  return err;
653
}
654

Pekka Pessi's avatar
Pekka Pessi committed
655

Pekka Pessi's avatar
Pekka Pessi committed
656
/** Send a STUN message.
Pekka Pessi's avatar
Pekka Pessi committed
657 658 659 660 661
 *  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) {

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

666
  if (msg->enc_buf.data == NULL) {
Pekka Pessi's avatar
Pekka Pessi committed
667 668 669
    /* convert msg to binary format */
    /* convert attributes to binary format for transmission */
    len = 0;
670
    for (attr = msg->stun_attr; attr ; attr = attr->next) {
Pekka Pessi's avatar
Pekka Pessi committed
671 672 673 674 675
      switch(attr->attr_type) {
      case RESPONSE_ADDRESS:
      case MAPPED_ADDRESS:
      case SOURCE_ADDRESS:
      case CHANGED_ADDRESS:
676
      case REFLECTED_FROM:
Pekka Pessi's avatar
Pekka Pessi committed
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
#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;
692

Pekka Pessi's avatar
Pekka Pessi committed
693 694 695 696 697 698 699 700 701 702 703
      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;
704
	z = 24;
Pekka Pessi's avatar
Pekka Pessi committed
705 706 707 708 709 710
	break;
      case ERROR_CODE:
	z = stun_encode_error_code(attr);
      default:
	break;
      }
711 712 713 714

      if(z < 0) return z;

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

    msg->stun_hdr.msg_len = len;
718 719
    buf_len = 20 + msg->stun_hdr.msg_len;
    buf = (unsigned char *) malloc(buf_len);
720

Pekka Pessi's avatar
Pekka Pessi committed
721
    /* convert to binary format for transmission */
722 723
    set16(buf, 0, msg->stun_hdr.msg_type);
    set16(buf, 2, msg->stun_hdr.msg_len);
724
    memcpy(buf + 4, msg->stun_hdr.tran_id, STUN_TID_BYTES);
725 726

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

    /* 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) {
733
	memcpy(buf+len, (void *)attr->enc_buf.data, attr->enc_buf.size);
Pekka Pessi's avatar
Pekka Pessi committed
734 735 736 737
	len += attr->enc_buf.size;
      }
      attr = attr->next;
    }
738 739

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

Pekka Pessi's avatar
Pekka Pessi committed
749
    /* save binary buffer for future reference */
750 751
    if (msg->enc_buf.data)
      free(msg->enc_buf.data);
752

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

  return 0;
}

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

char *stun_determine_ip_address(int family)
{

  char *local_ip_address;
  su_localinfo_t *li = NULL, hints[1] = {{ LI_CANONNAME|LI_NUMERIC }};
768 769
  int error;
  size_t address_size;
Pekka Pessi's avatar
Pekka Pessi committed
770 771 772 773 774 775
  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) {
776 777
    SU_DEBUG_5(("%s: stun_determine_ip_address, su_getlocalinfo: %s\n",
		__func__, su_gli_strerror(error)));
Pekka Pessi's avatar
Pekka Pessi committed
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

786
  local_ip_address = malloc(address_size + 1);
Pekka Pessi's avatar
Pekka Pessi committed
787
  strcpy(local_ip_address, (char *) inet_ntoa(sa->sin_addr)); /* otherwise? */
788

Pekka Pessi's avatar
Pekka Pessi committed
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
  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";
809
  case STUN_A_ALTERNATE_SERVER:
810
  case STUN_A_ALTERNATE_SERVER_DEP:
811 812 813 814
    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
815 816 817 818 819 820 821 822 823 824 825 826 827 828
#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";
  }
}