828 lines
20 KiB
C
828 lines
20 KiB
C
/*
|
|
* This file is part of the Sofia-SIP package
|
|
*
|
|
* Copyright (C) 2005 Nokia Corporation.
|
|
*
|
|
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* 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>
|
|
* @author Kai Vehmanen <kai.vehmanen@nokia.com>
|
|
*
|
|
* @date Created: Fri Oct 3 13:40:41 2003 ppessi
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef USE_TURN
|
|
#include "../turn/turn_common.h"
|
|
#undef STUN_A_LAST_MANDATORY
|
|
#define STUN_A_LAST_MANDATORY TURN_LARGEST_ATTRIBUTE
|
|
#endif
|
|
|
|
#include "stun_internal.h"
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#if HAVE_FUNC
|
|
#elif HAVE_FUNCTION
|
|
#define __func__ __FUNCTION__
|
|
#else
|
|
#define __func__ "stun_common"
|
|
#endif
|
|
|
|
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";
|
|
|
|
#define set16(b, offset, value) \
|
|
(((b)[(offset) + 0] = ((value) >> 8) & 255), \
|
|
((b)[(offset) + 1] = (value) & 255))
|
|
|
|
#define get16(b, offset) \
|
|
(((b)[(offset) + 0] << 8) | \
|
|
((b)[(offset) + 1] << 0))
|
|
|
|
int stun_parse_message(stun_msg_t *msg)
|
|
{
|
|
unsigned len;
|
|
int i;
|
|
unsigned char *p;
|
|
|
|
/* parse header first */
|
|
p = msg->enc_buf.data;
|
|
msg->stun_hdr.msg_type = get16(p, 0);
|
|
msg->stun_hdr.msg_len = get16(p, 2);
|
|
memcpy(msg->stun_hdr.tran_id, p + 4, STUN_TID_BYTES);
|
|
|
|
SU_DEBUG_5(("%s: Parse STUN message: Length = %d\n", __func__,
|
|
msg->stun_hdr.msg_len));
|
|
|
|
/* parse attributes */
|
|
len = msg->stun_hdr.msg_len;
|
|
p = msg->enc_buf.data + 20;
|
|
msg->stun_attr = NULL;
|
|
while (len > 0) {
|
|
i = stun_parse_attribute(msg, p);
|
|
if (i <= 0) {
|
|
SU_DEBUG_3(("%s: Error parsing attribute.\n", __func__));
|
|
return -1;
|
|
}
|
|
p += i;
|
|
len -= i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stun_parse_attribute(stun_msg_t *msg, unsigned char *p)
|
|
{
|
|
int len;
|
|
uint16_t attr_type;
|
|
stun_attr_t *attr, *next;
|
|
|
|
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)));
|
|
|
|
if (attr_type > STUN_A_LAST_MANDATORY && attr_type < STUN_A_OPTIONAL) {
|
|
return -1;
|
|
}
|
|
|
|
attr = (stun_attr_t *)calloc(1, sizeof(stun_attr_t));
|
|
if (!attr)
|
|
return -1;
|
|
attr->next = NULL;
|
|
attr->attr_type = attr_type;
|
|
p += 4;
|
|
|
|
switch (attr->attr_type) {
|
|
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
|
|
if (stun_parse_attr_address(attr, p, len) < 0) {
|
|
free(attr);
|
|
return -1;
|
|
}
|
|
break;
|
|
case ERROR_CODE:
|
|
if (stun_parse_attr_error_code(attr, p, len) <0) { free(attr); return -1; }
|
|
break;
|
|
case UNKNOWN_ATTRIBUTES:
|
|
if(stun_parse_attr_unknown_attributes(attr, p, len) <0) { free(attr); return -1; }
|
|
break;
|
|
case CHANGE_REQUEST:
|
|
#ifdef USE_TURN
|
|
case TURN_LIFETIME:
|
|
case TURN_MAGIC_COOKIE:
|
|
case TURN_BANDWIDTH:
|
|
#endif
|
|
if (stun_parse_attr_uint32(attr, p, len) <0) { free(attr); return -1; }
|
|
break;
|
|
case USERNAME:
|
|
case PASSWORD:
|
|
case STUN_A_REALM:
|
|
case STUN_A_NONCE:
|
|
#ifdef USE_TURN
|
|
case TURN_DATA:
|
|
case TURN_NONCE:
|
|
#endif
|
|
if (stun_parse_attr_buffer(attr, p, len) <0) { free(attr); return -1; }
|
|
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;
|
|
}
|
|
|
|
int stun_parse_attr_address(stun_attr_t *attr,
|
|
const unsigned char *p,
|
|
unsigned len)
|
|
{
|
|
su_sockaddr_t *addr;
|
|
int addrlen;
|
|
char ipaddr[SU_ADDRSIZE + 2];
|
|
|
|
if (len != 8) {
|
|
return -1;
|
|
}
|
|
|
|
addrlen = sizeof(su_sockaddr_t);
|
|
addr = (su_sockaddr_t *) malloc(addrlen);
|
|
|
|
if (*(p+1) == 1) { /* expected value for IPv4 */
|
|
addr->su_sin.sin_family = AF_INET;
|
|
}
|
|
else {
|
|
free(addr);
|
|
return -1;
|
|
}
|
|
memcpy(&addr->su_sin.sin_port, p + 2, 2);
|
|
memcpy(&addr->su_sin.sin_addr.s_addr, p + 4, 4);
|
|
|
|
SU_DEBUG_5(("%s: address attribute: %s:%d\n", __func__,
|
|
inet_ntop(addr->su_family, SU_ADDR(addr), ipaddr, sizeof(ipaddr)),
|
|
(unsigned) ntohs(addr->su_sin.sin_port)));
|
|
|
|
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-3);
|
|
|
|
strncpy(error->phrase, (char*)p+4, len-4);
|
|
error->phrase[len - 4] = '\0';
|
|
|
|
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;
|
|
buf = (stun_buffer_t *) malloc(sizeof(stun_buffer_t));
|
|
buf->size = len;
|
|
buf->data = (unsigned char *) malloc(len);
|
|
memcpy(buf->data, p, len);
|
|
attr->pattr = buf;
|
|
stun_init_buffer(&attr->enc_buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stun_parse_attr_unknown_attributes(stun_attr_t *attr,
|
|
const unsigned char *p,
|
|
unsigned len)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/** scan thru attribute list and return the next requested attr */
|
|
stun_attr_t *stun_get_attr(stun_attr_t *attr, uint16_t attr_type) {
|
|
stun_attr_t *p;
|
|
|
|
for (p = attr; p != NULL; p = p->next) {
|
|
if (p->attr_type == attr_type)
|
|
break;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
void stun_init_buffer(stun_buffer_t *p) {
|
|
p->data = NULL;
|
|
p->size = 0;
|
|
}
|
|
|
|
int stun_free_buffer(stun_buffer_t *p) {
|
|
if (p->data)
|
|
free(p->data), p->data = NULL;
|
|
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) {
|
|
if (status <100 || status >600)
|
|
return NULL;
|
|
|
|
switch (status) {
|
|
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;
|
|
|
|
if (stun_encode_type_len(attr, 8) < 0) {
|
|
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, 2);
|
|
memcpy(attr->enc_buf.data+8, &a->sin_addr.s_addr, 4);
|
|
|
|
return attr->enc_buf.size;
|
|
}
|
|
|
|
int stun_encode_uint32(stun_attr_t *attr) {
|
|
uint32_t tmp;
|
|
|
|
if (stun_encode_type_len(attr, 4) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
tmp = htonl(((stun_attr_changerequest_t *) attr->pattr)->value);
|
|
memcpy(attr->enc_buf.data+4, &tmp, 4);
|
|
return attr->enc_buf.size;
|
|
}
|
|
|
|
int stun_encode_error_code(stun_attr_t *attr) {
|
|
short int class, num;
|
|
size_t phrase_len, padded;
|
|
stun_attr_errorcode_t *error;
|
|
|
|
error = (stun_attr_errorcode_t *) attr->pattr;
|
|
class = error->code / 100;
|
|
num = error->code % 100;
|
|
|
|
phrase_len = strlen(error->phrase);
|
|
if (phrase_len + 8 > 65536)
|
|
phrase_len = 65536 - 8;
|
|
|
|
/* note: align the phrase len (see RFC3489:11.2.9) */
|
|
padded = phrase_len + (phrase_len % 4 == 0 ? 0 : 4 - (phrase_len % 4));
|
|
|
|
/* note: error-code has four octets of headers plus the
|
|
* reason field -> len+4 octets */
|
|
if (stun_encode_type_len(attr, (uint16_t)(padded + 4)) < 0) {
|
|
return -1;
|
|
}
|
|
else {
|
|
assert(attr->enc_buf.size == padded + 8);
|
|
memset(attr->enc_buf.data+4, 0, 2);
|
|
attr->enc_buf.data[6] = class;
|
|
attr->enc_buf.data[7] = num;
|
|
/* note: 4 octets of TLV header and 4 octets of error-code header */
|
|
memcpy(attr->enc_buf.data+8, error->phrase,
|
|
phrase_len);
|
|
memset(attr->enc_buf.data + 8 + phrase_len, 0, padded - phrase_len);
|
|
}
|
|
|
|
return attr->enc_buf.size;
|
|
}
|
|
|
|
int stun_encode_buffer(stun_attr_t *attr) {
|
|
stun_buffer_t *a;
|
|
|
|
a = (stun_buffer_t *)attr->pattr;
|
|
assert(a->size < 65536);
|
|
if (stun_encode_type_len(attr, (uint16_t)a->size) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy(attr->enc_buf.data+4, a->data, a->size);
|
|
return attr->enc_buf.size;
|
|
}
|
|
|
|
#if defined(HAVE_OPENSSL)
|
|
int stun_encode_message_integrity(stun_attr_t *attr,
|
|
unsigned char *buf,
|
|
int len,
|
|
stun_buffer_t *pwd) {
|
|
int padded_len;
|
|
unsigned int dig_len;
|
|
unsigned char *padded_text = NULL;
|
|
void *sha1_hmac;
|
|
|
|
if (stun_encode_type_len(attr, 20) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* zero padding */
|
|
if (len % 64 != 0) {
|
|
|
|
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);
|
|
}
|
|
|
|
assert(dig_len == 20);
|
|
|
|
memcpy(attr->enc_buf.data + 4, sha1_hmac, 20);
|
|
free(padded_text);
|
|
return attr->enc_buf.size;
|
|
}
|
|
#else
|
|
int stun_encode_message_integrity(stun_attr_t *attr,
|
|
unsigned char *buf,
|
|
int len,
|
|
stun_buffer_t *pwd) {
|
|
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_OPENSSL */
|
|
|
|
/** 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;
|
|
|
|
attr->enc_buf.data = (unsigned char *) malloc(len + 4);
|
|
memset(attr->enc_buf.data, 0, len + 4);
|
|
|
|
tmp = htons(attr->attr_type);
|
|
memcpy(attr->enc_buf.data, &tmp, 2);
|
|
|
|
tmp = htons(len);
|
|
memcpy(attr->enc_buf.data + 2, &tmp, 2);
|
|
attr->enc_buf.size = len + 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Validate the message integrity based on given
|
|
* STUN password 'pwd'. The received content should be
|
|
* in msg->enc_buf.
|
|
*/
|
|
int stun_validate_message_integrity(stun_msg_t *msg, stun_buffer_t *pwd)
|
|
{
|
|
|
|
#if defined(HAVE_OPENSSL)
|
|
int padded_len, len;
|
|
unsigned int dig_len;
|
|
unsigned char dig[20]; /* received sha1 digest */
|
|
unsigned char *padded_text;
|
|
#endif
|
|
|
|
/* password NULL so shared-secret not established and
|
|
messege integrity checks can be skipped */
|
|
if (pwd->data == NULL)
|
|
return 0;
|
|
|
|
/* otherwise the check must match */
|
|
|
|
#if defined(HAVE_OPENSSL)
|
|
|
|
/* message integrity not received */
|
|
if (stun_get_attr(msg->stun_attr, MESSAGE_INTEGRITY) == NULL) {
|
|
SU_DEBUG_5(("%s: error: message integrity missing.\n", __func__));
|
|
return -1;
|
|
}
|
|
|
|
/* zero padding */
|
|
len = msg->enc_buf.size - 24;
|
|
padded_len = len + (len % 64 == 0 ? 0 : 64 - (len % 64));
|
|
padded_text = (unsigned char *) malloc(padded_len);
|
|
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);
|
|
|
|
if (memcmp(dig, msg->enc_buf.data + msg->enc_buf.size - 20, 20) != 0) {
|
|
/* does not match, but try the test server's password */
|
|
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__));
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
SU_DEBUG_5(("%s: message integrity validated.\n", __func__));
|
|
}
|
|
|
|
free(padded_text);
|
|
|
|
return 0;
|
|
#else /* HAVE_OPENSSL */
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void debug_print(stun_buffer_t *buf) {
|
|
unsigned i;
|
|
for(i = 0; i < buf->size/4; i++) {
|
|
SU_DEBUG_9(("%02x %02x %02x %02x\n",
|
|
*(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"));
|
|
}
|
|
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 */
|
|
memset(&msg->stun_hdr, 0, sizeof msg->stun_hdr);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
int stun_send_message(su_socket_t s, su_sockaddr_t *to_addr,
|
|
stun_msg_t *msg, stun_buffer_t *pwd)
|
|
{
|
|
int err;
|
|
char ipaddr[SU_ADDRSIZE + 2];
|
|
stun_attr_t **a, *b;
|
|
|
|
stun_encode_message(msg, pwd);
|
|
|
|
err = su_sendto(s, msg->enc_buf.data, msg->enc_buf.size, 0,
|
|
to_addr, SU_SOCKADDR_SIZE(to_addr));
|
|
|
|
free(msg->enc_buf.data), msg->enc_buf.data = NULL;
|
|
msg->enc_buf.size = 0;
|
|
|
|
for (a = &msg->stun_attr; *a;) {
|
|
|
|
if ((*a)->pattr)
|
|
free((*a)->pattr), (*a)->pattr = NULL;
|
|
|
|
if ((*a)->enc_buf.data)
|
|
free((*a)->enc_buf.data), (*a)->enc_buf.data = NULL;
|
|
|
|
b = *a;
|
|
b = b->next;
|
|
free(*a);
|
|
*a = NULL;
|
|
*a = b;
|
|
}
|
|
|
|
if (err > 0) {
|
|
inet_ntop(to_addr->su_family, SU_ADDR(to_addr), ipaddr, sizeof(ipaddr));
|
|
SU_DEBUG_5(("%s: message sent to %s:%u\n", __func__,
|
|
ipaddr, ntohs(to_addr->su_port)));
|
|
|
|
debug_print(&msg->enc_buf);
|
|
}
|
|
else
|
|
STUN_ERROR(errno, sendto);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/** 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 z = -1, len, buf_len = 0;
|
|
unsigned char *buf;
|
|
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 */
|
|
len = 0;
|
|
for (attr = msg->stun_attr; attr ; attr = attr->next) {
|
|
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;
|
|
|
|
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;
|
|
z = 24;
|
|
break;
|
|
case ERROR_CODE:
|
|
z = stun_encode_error_code(attr);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(z < 0) return z;
|
|
|
|
len += z;
|
|
}
|
|
|
|
msg->stun_hdr.msg_len = len;
|
|
buf_len = 20 + msg->stun_hdr.msg_len;
|
|
buf = (unsigned char *) malloc(buf_len);
|
|
|
|
/* convert to binary format for transmission */
|
|
set16(buf, 0, msg->stun_hdr.msg_type);
|
|
set16(buf, 2, msg->stun_hdr.msg_len);
|
|
memcpy(buf + 4, msg->stun_hdr.tran_id, STUN_TID_BYTES);
|
|
|
|
len = 20;
|
|
|
|
/* 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, (void *)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) {
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
memcpy(buf+len, (void *)msg_int->enc_buf.data,
|
|
msg_int->enc_buf.size);
|
|
}
|
|
|
|
/* save binary buffer for future reference */
|
|
if (msg->enc_buf.data)
|
|
free(msg->enc_buf.data);
|
|
|
|
msg->enc_buf.data = buf; msg->enc_buf.size = buf_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include <sofia-sip/su.h>
|
|
#include <sofia-sip/su_debug.h>
|
|
#include <sofia-sip/su_localinfo.h>
|
|
|
|
char *stun_determine_ip_address(int family)
|
|
{
|
|
|
|
char *local_ip_address;
|
|
su_localinfo_t *li = NULL, hints[1] = {{ LI_CANONNAME|LI_NUMERIC }};
|
|
int error;
|
|
size_t 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) {
|
|
SU_DEBUG_5(("%s: stun_determine_ip_address, su_getlocalinfo: %s\n",
|
|
__func__, su_gli_strerror(error)));
|
|
return NULL;
|
|
}
|
|
|
|
temp = li->li_addr;
|
|
sa = &temp->su_sin;
|
|
|
|
address_size = strlen(inet_ntoa(sa->sin_addr));
|
|
|
|
local_ip_address = malloc(address_size + 1);
|
|
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";
|
|
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";
|
|
#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";
|
|
}
|
|
}
|