stun_common.c 19.3 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 40 41 42 43 44 45
 *  
 * @date Created: Fri Oct  3 13:40:41 2003 ppessi
 * 
 */

#ifndef WIN32
#include "config.h"
#endif

#ifdef USE_TURN
#include "../turn/turn_common.h"
#undef LARGEST_ATTRIBUTE
#define LARGEST_ATTRIBUTE TURN_LARGEST_ATTRIBUTE
#endif

Martti Mela's avatar
Martti Mela committed
46 47
#include "stun_internal.h"

Pekka Pessi's avatar
Pekka Pessi committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
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";

int stun_parse_message(stun_msg_t *msg) {

  unsigned len;
  int i;
  unsigned char *p;
  uint16_t tmp16;

  /* parse header first */
  p = msg->enc_buf.data;
  memcpy(&tmp16, p, 2); p+=2;
  msg->stun_hdr.msg_type = ntohs(tmp16);
  memcpy(&tmp16, p, 2); p+=2;
  msg->stun_hdr.msg_len = ntohs(tmp16);
78

79
  for (i = 0; i < 8; i++) {
Pekka Pessi's avatar
Pekka Pessi committed
80 81 82
    memcpy(&tmp16, p, 2); p+=2;
    msg->stun_hdr.tran_id[i] = ntohs(tmp16);
  }
83

84 85
  SU_DEBUG_5(("%s: Parse STUN message: Length = %d\n", __func__,
	      msg->stun_hdr.msg_len));
Pekka Pessi's avatar
Pekka Pessi committed
86 87 88 89 90

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

int stun_parse_attribute(stun_msg_t *msg, unsigned char *p) {

  int len;
  uint16_t tmp16;
  stun_attr_t *attr, *next;

  attr = (stun_attr_t *) malloc(sizeof(stun_attr_t));
  attr->next = NULL;

  memcpy(&tmp16, p, 2);
  p+=2;
  attr->attr_type = ntohs(tmp16);
115
  if (attr->attr_type > LARGEST_ATTRIBUTE && attr->attr_type < OPTIONAL_ATTRIBUTE) {
116
    free(attr);
Pekka Pessi's avatar
Pekka Pessi committed
117 118 119 120 121 122 123
    return -1;
  }

  memcpy(&tmp16, p, 2);
  p+=2;
  len = ntohs(tmp16);

124
  SU_DEBUG_5(("%s: received attribute: Type %02X, Length %d - %s\n", __func__,
125
	      attr->attr_type, len, stun_attr_phrase(attr->attr_type)));
126

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

188 189
int stun_parse_attr_address(stun_attr_t *attr, const unsigned char *p, unsigned len)
{
Pekka Pessi's avatar
Pekka Pessi committed
190 191 192
  stun_attr_sockaddr_t *addr;
  int addrlen;

193
  if (len != 8) {
Pekka Pessi's avatar
Pekka Pessi committed
194 195 196 197 198 199
    return -1;
  }

  addrlen = sizeof(stun_attr_sockaddr_t);
  addr = (stun_attr_sockaddr_t *)malloc(addrlen);

200
  if (*(p+1) == 1) { /* expected value for IPv4 */
Pekka Pessi's avatar
Pekka Pessi committed
201 202 203 204 205 206
    addr->sin_family = AF_INET;
  }
  else {
    free(addr);
    return -1;
  }
207 208
  memcpy(&addr->sin_port, p + 2, 2);
  memcpy(&addr->sin_addr.s_addr, p + 4, 4);
Pekka Pessi's avatar
Pekka Pessi committed
209

210 211
  SU_DEBUG_5(("%s: address attribute: %s:%d\n", __func__,
	      inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)));
Pekka Pessi's avatar
Pekka Pessi committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231

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

  error->phrase = (char *) malloc(len-4);

232
  strncpy(error->phrase, (char*)p+4, len-4);
Pekka Pessi's avatar
Pekka Pessi committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

  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;
256
  buf = (stun_buffer_t *) malloc(sizeof(stun_buffer_t));
Pekka Pessi's avatar
Pekka Pessi committed
257
  buf->size = len;
258
  buf->data = (unsigned char *) malloc(len);
Pekka Pessi's avatar
Pekka Pessi committed
259 260 261 262 263 264 265
  memcpy(buf->data, p, len);
  attr->pattr = buf;
  stun_init_buffer(&attr->enc_buf);

  return 0;
}

266 267 268
int stun_parse_attr_unknown_attributes(stun_attr_t *attr,
				       const unsigned char *p,
				       unsigned len)
Pekka Pessi's avatar
Pekka Pessi committed
269 270 271 272 273 274 275
{
  return 0;
}

/** scan thru attribute list and return the requested attr */
stun_attr_t *stun_get_attr(stun_attr_t *attr, uint16_t attr_type) {
  stun_attr_t *p;
276 277

  for (p = attr; p != NULL; p = p->next) {
278
    if (p->attr_type == attr_type)
279
      break;
Pekka Pessi's avatar
Pekka Pessi committed
280
  }
281 282

  return p;
Pekka Pessi's avatar
Pekka Pessi committed
283 284 285 286 287 288 289 290
}

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

int stun_free_buffer(stun_buffer_t *p) {
291
  if (p->data)
Pekka Pessi's avatar
Pekka Pessi committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305
    free(p->data);
  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) {
306
  if (status <100 || status >600)
Pekka Pessi's avatar
Pekka Pessi committed
307
    return NULL;
308 309

  switch (status) {
Pekka Pessi's avatar
Pekka Pessi committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  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;

344
  if (stun_encode_type_len(attr, 2*sizeof(uint32_t)) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    return -1;
  }

  tmp = htons(0x01); /* FAMILY = 0x01 */
  memcpy(attr->enc_buf.data+4, &tmp, sizeof(tmp));
  memcpy(attr->enc_buf.data+6, &a->sin_port, sizeof(uint32_t));
  memcpy(attr->enc_buf.data+8, &a->sin_addr.s_addr, sizeof(uint32_t));
  return attr->enc_buf.size;
}

int stun_encode_uint32(stun_attr_t *attr) {
  uint32_t tmp;

  if(stun_encode_type_len(attr, sizeof(tmp))<0) {
    return -1;
  }

362
  tmp = htonl(((stun_attr_changerequest_t *) attr->pattr)->value);
Pekka Pessi's avatar
Pekka Pessi committed
363 364 365 366 367 368 369 370 371 372
  memcpy(attr->enc_buf.data+4, &tmp, sizeof(tmp));
  return attr->enc_buf.size;
}

int stun_encode_error_code(stun_attr_t *attr) {
  short int class, num;
  char *reason;
  int len;
  stun_attr_errorcode_t *error;

373 374 375
  error = (stun_attr_errorcode_t *) attr->pattr;
  class = error->code / 100;
  num = error->code % 100;
Pekka Pessi's avatar
Pekka Pessi committed
376
  len = strlen(error->phrase);
377
  attr->enc_buf.size = len + (len % 4 == 0? 0 : 4 - (len % 4));
Pekka Pessi's avatar
Pekka Pessi committed
378 379 380 381 382
  reason = malloc(attr->enc_buf.size);
  memset(reason, 0, attr->enc_buf.size);
  memcpy(reason, error->phrase, len);

  attr->enc_buf.size +=4;
383
  if (stun_encode_type_len(attr, attr->enc_buf.size) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
384 385 386 387 388
    return -1;
  }
  memset(attr->enc_buf.data+4, 0, 2);
  memcpy(attr->enc_buf.data+6, &class, 1);
  memcpy(attr->enc_buf.data+7, &num, 1);
389
  memcpy(attr->enc_buf.data+8, reason, attr->enc_buf.size - 4);
Pekka Pessi's avatar
Pekka Pessi committed
390 391 392 393 394 395 396 397 398

  return attr->enc_buf.size;
}

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

  a = (stun_buffer_t *)attr->pattr;

399
  if (stun_encode_type_len(attr, a->size) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
400 401 402 403 404 405 406
    return -1;
  }

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

Martti Mela's avatar
Martti Mela committed
407
#if defined(HAVE_OPENSSL)
408 409 410 411
int stun_encode_message_integrity(stun_attr_t *attr,
				  unsigned char *buf,
				  int len,
				  stun_buffer_t *pwd) {
412 413
  int padded_len;
  size_t dig_len;
Pekka Pessi's avatar
Pekka Pessi committed
414 415
  unsigned char *padded_text;

416
  if (stun_encode_type_len(attr, 20) < 0) {
Pekka Pessi's avatar
Pekka Pessi committed
417 418 419 420
    return -1;
  }

  /* zero padding */
421 422
  padded_len = len + (len % 64 == 0 ? 0 : 64 - (len % 64));
  padded_text = (unsigned char *) malloc(padded_len);
Pekka Pessi's avatar
Pekka Pessi committed
423 424 425 426 427 428 429 430
  memset(padded_text, 0, padded_len);
  memcpy(padded_text, buf, len);

  memcpy(attr->enc_buf.data+4, HMAC(EVP_sha1(), pwd->data, pwd->size, padded_text, padded_len, NULL, &dig_len), 20);

  free(padded_text);
  return attr->enc_buf.size;
}
Martti Mela's avatar
Martti Mela committed
431 432 433 434 435 436 437 438 439
#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
440 441 442 443

/** 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;
444 445
  attr->enc_buf.data = (unsigned char *) malloc(len + 4);
  memset(attr->enc_buf.data, 0, len + 4);
Pekka Pessi's avatar
Pekka Pessi committed
446 447 448
  tmp = htons(attr->attr_type);
  memcpy(attr->enc_buf.data, &tmp, 2);
  tmp = htons(len);
449 450
  memcpy(attr->enc_buf.data + 2, &tmp, 2);
  attr->enc_buf.size = len + 4;
Pekka Pessi's avatar
Pekka Pessi committed
451 452 453 454 455 456
  return 0;
}

/** validate message integrity based on pwd
 *  received content is in msg->enc_buf
 */
457 458 459
#if defined(HAVE_OPENSSL)
int stun_validate_message_integrity(stun_msg_t *msg, stun_buffer_t *pwd)
{
Pekka Pessi's avatar
Pekka Pessi committed
460

461 462
  int padded_len, len;
  size_t dig_len;
Pekka Pessi's avatar
Pekka Pessi committed
463 464 465
  unsigned char dig[20]; /* received sha1 digest */
  unsigned char *padded_text;

Martti Mela's avatar
Martti Mela committed
466 467 468
  if (pwd->data == NULL)
    return 0;

Pekka Pessi's avatar
Pekka Pessi committed
469
  /* message integrity not received */
Martti Mela's avatar
Martti Mela committed
470 471
  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
472 473 474 475
    return -1;
  }

  /* zero padding */
476
  len = msg->enc_buf.size - 24;
Martti Mela's avatar
Martti Mela committed
477 478
  padded_len = len + (len % 64 == 0 ? 0 : 64 - (len % 64));
  padded_text = (unsigned char *) malloc(padded_len);
Pekka Pessi's avatar
Pekka Pessi committed
479 480 481 482 483
  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);

484
  if (memcmp(dig, msg->enc_buf.data + msg->enc_buf.size - 20, 20) != 0) {
Pekka Pessi's avatar
Pekka Pessi committed
485
    /* does not match, but try the test server's password */
Martti Mela's avatar
Martti Mela committed
486 487
    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
488 489 490 491
      return -1;
    }
  }
  else {
Martti Mela's avatar
Martti Mela committed
492
    SU_DEBUG_5(("%s: message integrity validated.\n", __func__));
Pekka Pessi's avatar
Pekka Pessi committed
493 494 495 496 497 498
  }

  free(padded_text);

  return 0;
}
499 500 501 502 503 504
#else
int stun_validate_message_integrity(stun_msg_t *msg, stun_buffer_t *pwd)
{
  return -1;
}
#endif /* HAVE_OPENSSL */
Pekka Pessi's avatar
Pekka Pessi committed
505 506 507

void debug_print(stun_buffer_t *buf) {
  int i;
Martti Mela's avatar
Martti Mela committed
508
  for(i = 0; i < buf->size/4; i++) {
Pekka Pessi's avatar
Pekka Pessi committed
509
    SU_DEBUG_9(("%02x %02x %02x %02x\n",
Martti Mela's avatar
Martti Mela committed
510 511 512 513 514 515
		*(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
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
  }
  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) {

  int i;
  stun_attr_t *p, *p2;

  /* clearing header */
  msg->stun_hdr.msg_type = 0;
  msg->stun_hdr.msg_len = 0;
  for(i=0; i<8; i++) msg->stun_hdr.tran_id[i] = 0;

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

569 570
int stun_send_message(su_socket_t s, su_sockaddr_t *to_addr,
		      stun_msg_t *msg, stun_buffer_t *pwd) 
571
{
Martti Mela's avatar
Martti Mela committed
572 573
  int err;
  char ipaddr[SU_ADDRSIZE + 2];
574 575 576

  stun_encode_message(msg, pwd);

577
  err = sendto(s, msg->enc_buf.data, msg->enc_buf.size, 
Pekka Pessi's avatar
Pekka Pessi committed
578
	       0, (struct sockaddr *)to_addr, sizeof(struct sockaddr_in));
Martti Mela's avatar
Martti Mela committed
579 580 581

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

585
    debug_print(&msg->enc_buf);
Martti Mela's avatar
Martti Mela committed
586 587 588
  }
  else
    STUN_ERROR(errno, sendto);
589
  
Martti Mela's avatar
Martti Mela committed
590
  return err;
591 592
}  

Pekka Pessi's avatar
Pekka Pessi committed
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

/** send a STUN message.
 *  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) {

  int i, z, len, buf_len = 0;
  unsigned char *buf;
  uint16_t tmp16;
  stun_attr_t *attr, *msg_int=NULL;

  if(msg->enc_buf.data == NULL) {
    /* convert msg to binary format */
    /* convert attributes to binary format for transmission */
    attr = msg->stun_attr;
    len = 0;
    while(attr) {
      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);
	if(z < 0) return z;
	len += z;
	break;
      case CHANGE_REQUEST:
#ifdef USE_TURN
      case TURN_LIFETIME:
      case TURN_MAGIC_COOKIE:
      case TURN_BANDWIDTH:
#endif
	z = stun_encode_uint32(attr);
	if(z < 0) return z;
	len += z;
	break;
      case USERNAME:
      case PASSWORD:
#ifdef USE_TURN
      case TURN_REALM:
      case TURN_NONCE:
      case TURN_DATA:
#endif
	z = stun_encode_buffer(attr);
	if(z < 0) return z;
	len += z;
	break;
      case MESSAGE_INTEGRITY:
	msg_int = attr;
	len += 24; /* postpone encoding to 2nd round */
	break;
      case ERROR_CODE:
	z = stun_encode_error_code(attr);
	if(z < 0) return z;
	len += z;
      default:
	break;
      }
      attr = attr->next;
    }

    msg->stun_hdr.msg_len = len;
    buf_len = 20+msg->stun_hdr.msg_len;
663
    buf = (unsigned char *)malloc(buf_len);
Pekka Pessi's avatar
Pekka Pessi committed
664 665 666 667
    
    /* convert to binary format for transmission */
    len = 0;
    tmp16 = htons(msg->stun_hdr.msg_type);
668
    memcpy(buf, (unsigned char *)&tmp16, 2); len+=2;
Pekka Pessi's avatar
Pekka Pessi committed
669
    tmp16 = htons(msg->stun_hdr.msg_len);
670
    memcpy(buf+len, (unsigned char *)&tmp16, 2); len+=2;
Pekka Pessi's avatar
Pekka Pessi committed
671 672
    for(i=0; i<8; i++) {
      tmp16 = htons(msg->stun_hdr.tran_id[i]);
673
      memcpy(buf+len, (unsigned char *)&tmp16, 2); len+=2;
Pekka Pessi's avatar
Pekka Pessi committed
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
    }

    /* 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) {
	memcpy(buf+len, (unsigned char *)attr->enc_buf.data, attr->enc_buf.size); 
	len += attr->enc_buf.size;
      }
      attr = attr->next;
    }
    if(msg_int) {
      /* compute message integrity */
      if(stun_encode_message_integrity(msg_int, buf, len, pwd)!=24) {
689
	free(buf);
Pekka Pessi's avatar
Pekka Pessi committed
690 691 692 693 694 695
	return -1;
      }
      memcpy(buf+len, (unsigned char *)msg_int->enc_buf.data, msg_int->enc_buf.size);
    }
    
    /* save binary buffer for future reference */
696 697
    if (msg->enc_buf.data)
      free(msg->enc_buf.data);
Pekka Pessi's avatar
Pekka Pessi committed
698 699 700 701 702 703
    msg->enc_buf.data = buf; msg->enc_buf.size = buf_len;
  }

  return 0;
}

704 705 706
#include <sofia-sip/su.h>
#include <sofia-sip/su_debug.h>
#include <sofia-sip/su_localinfo.h>
Pekka Pessi's avatar
Pekka Pessi committed
707 708 709 710 711 712 713 714 715 716 717 718 719

char *stun_determine_ip_address(int family)
{

  char *local_ip_address;
  su_localinfo_t *li = NULL, hints[1] = {{ LI_CANONNAME|LI_NUMERIC }};
  int error, address_size;
  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) {
720 721
    SU_DEBUG_5(("%s: stun_determine_ip_address, su_getlocalinfo: %s\n",
		__func__, su_gli_strerror(error)));
Pekka Pessi's avatar
Pekka Pessi committed
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
    return NULL;
  }

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

  address_size = strlen(inet_ntoa(sa->sin_addr));
  
  local_ip_address = malloc(address_size);
  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";
#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";
  }
}