491 lines
11 KiB
C
491 lines
11 KiB
C
/**
|
|
* @file stun/attr.c STUN Attributes
|
|
*
|
|
* Copyright (C) 2010 Creytiv.com
|
|
*/
|
|
#include <re_types.h>
|
|
#include <re_mem.h>
|
|
#include <re_mbuf.h>
|
|
#include <re_sa.h>
|
|
#include <re_list.h>
|
|
#include <re_fmt.h>
|
|
#include <re_sys.h>
|
|
#include <re_stun.h>
|
|
#include "stun.h"
|
|
|
|
|
|
static int str_decode(struct mbuf *mb, char **str, size_t len)
|
|
{
|
|
if (mbuf_get_left(mb) < len)
|
|
return EBADMSG;
|
|
|
|
return mbuf_strdup(mb, str, len);
|
|
}
|
|
|
|
|
|
static void destructor(void *arg)
|
|
{
|
|
struct stun_attr *attr = arg;
|
|
|
|
switch (attr->type) {
|
|
|
|
case STUN_ATTR_USERNAME:
|
|
case STUN_ATTR_REALM:
|
|
case STUN_ATTR_NONCE:
|
|
case STUN_ATTR_SOFTWARE:
|
|
mem_deref(attr->v.str);
|
|
break;
|
|
|
|
case STUN_ATTR_ERR_CODE:
|
|
mem_deref(attr->v.err_code.reason);
|
|
break;
|
|
|
|
case STUN_ATTR_DATA:
|
|
case STUN_ATTR_PADDING:
|
|
mem_deref(attr->v.mb.buf);
|
|
break;
|
|
}
|
|
|
|
list_unlink(&attr->le);
|
|
}
|
|
|
|
|
|
int stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v,
|
|
const uint8_t *tid, uint8_t padding)
|
|
{
|
|
const struct stun_change_req *ch_req = v;
|
|
const struct stun_errcode *err_code = v;
|
|
const struct stun_unknown_attr *ua = v;
|
|
const unsigned int *num = v;
|
|
const struct mbuf *mbd = v;
|
|
size_t start, len;
|
|
uint32_t i, n;
|
|
int err = 0;
|
|
|
|
if (!mb || !v)
|
|
return EINVAL;
|
|
|
|
mb->pos += 4;
|
|
start = mb->pos;
|
|
|
|
switch (type) {
|
|
|
|
case STUN_ATTR_MAPPED_ADDR:
|
|
case STUN_ATTR_ALT_SERVER:
|
|
case STUN_ATTR_RESP_ORIGIN:
|
|
case STUN_ATTR_OTHER_ADDR:
|
|
tid = NULL;
|
|
/*@fallthrough@*/
|
|
case STUN_ATTR_XOR_PEER_ADDR:
|
|
case STUN_ATTR_XOR_RELAY_ADDR:
|
|
case STUN_ATTR_XOR_MAPPED_ADDR:
|
|
err |= stun_addr_encode(mb, v, tid);
|
|
break;
|
|
|
|
case STUN_ATTR_CHANGE_REQ:
|
|
n = (uint32_t)ch_req->ip << 2 | (uint32_t)ch_req->port << 1;
|
|
err |= mbuf_write_u32(mb, htonl(n));
|
|
break;
|
|
|
|
case STUN_ATTR_USERNAME:
|
|
case STUN_ATTR_REALM:
|
|
case STUN_ATTR_NONCE:
|
|
case STUN_ATTR_SOFTWARE:
|
|
err |= mbuf_write_str(mb, v);
|
|
break;
|
|
|
|
case STUN_ATTR_MSG_INTEGRITY:
|
|
err |= mbuf_write_mem(mb, v, 20);
|
|
break;
|
|
|
|
case STUN_ATTR_ERR_CODE:
|
|
err |= mbuf_write_u16(mb, 0x00);
|
|
err |= mbuf_write_u8(mb, err_code->code/100);
|
|
err |= mbuf_write_u8(mb, err_code->code%100);
|
|
err |= mbuf_write_str(mb, err_code->reason);
|
|
break;
|
|
|
|
case STUN_ATTR_UNKNOWN_ATTR:
|
|
for (i=0; i<ua->typec; i++)
|
|
err |= mbuf_write_u16(mb, htons(ua->typev[i]));
|
|
break;
|
|
|
|
case STUN_ATTR_CHANNEL_NUMBER:
|
|
case STUN_ATTR_RESP_PORT:
|
|
err |= mbuf_write_u16(mb, htons(*num));
|
|
err |= mbuf_write_u16(mb, 0x0000);
|
|
break;
|
|
|
|
case STUN_ATTR_LIFETIME:
|
|
case STUN_ATTR_PRIORITY:
|
|
case STUN_ATTR_FINGERPRINT:
|
|
err |= mbuf_write_u32(mb, htonl(*num));
|
|
break;
|
|
|
|
case STUN_ATTR_DATA:
|
|
case STUN_ATTR_PADDING:
|
|
if (mb == mbd) {
|
|
mb->pos = mb->end;
|
|
break;
|
|
}
|
|
err |= mbuf_write_mem(mb, mbuf_buf(mbd), mbuf_get_left(mbd));
|
|
break;
|
|
|
|
case STUN_ATTR_REQ_ADDR_FAMILY:
|
|
case STUN_ATTR_REQ_TRANSPORT:
|
|
err |= mbuf_write_u8(mb, *num);
|
|
err |= mbuf_write_u8(mb, 0x00);
|
|
err |= mbuf_write_u16(mb, 0x0000);
|
|
break;
|
|
|
|
case STUN_ATTR_EVEN_PORT:
|
|
err |= mbuf_write_u8(mb, ((struct stun_even_port *)v)->r << 7);
|
|
break;
|
|
|
|
case STUN_ATTR_DONT_FRAGMENT:
|
|
case STUN_ATTR_USE_CAND:
|
|
/* no value */
|
|
break;
|
|
|
|
case STUN_ATTR_RSV_TOKEN:
|
|
case STUN_ATTR_CONTROLLED:
|
|
case STUN_ATTR_CONTROLLING:
|
|
err |= mbuf_write_u64(mb, sys_htonll(*(uint64_t *)v));
|
|
break;
|
|
|
|
default:
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* header */
|
|
len = mb->pos - start;
|
|
|
|
mb->pos = start - 4;
|
|
err |= mbuf_write_u16(mb, htons(type));
|
|
err |= mbuf_write_u16(mb, htons(len));
|
|
mb->pos += len;
|
|
|
|
/* padding */
|
|
while ((mb->pos - start) & 0x03)
|
|
err |= mbuf_write_u8(mb, padding);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb,
|
|
const uint8_t *tid, struct stun_unknown_attr *ua)
|
|
{
|
|
struct stun_attr *attr;
|
|
size_t start, len;
|
|
uint32_t i, n;
|
|
int err = 0;
|
|
|
|
if (!mb || !attrp)
|
|
return EINVAL;
|
|
|
|
if (mbuf_get_left(mb) < 4)
|
|
return EBADMSG;
|
|
|
|
attr = mem_zalloc(sizeof(*attr), destructor);
|
|
if (!attr)
|
|
return ENOMEM;
|
|
|
|
attr->type = htons(mbuf_read_u16(mb));
|
|
len = htons(mbuf_read_u16(mb));
|
|
|
|
if (mbuf_get_left(mb) < len)
|
|
goto badmsg;
|
|
|
|
start = mb->pos;
|
|
|
|
switch (attr->type) {
|
|
|
|
case STUN_ATTR_MAPPED_ADDR:
|
|
case STUN_ATTR_ALT_SERVER:
|
|
case STUN_ATTR_RESP_ORIGIN:
|
|
case STUN_ATTR_OTHER_ADDR:
|
|
tid = NULL;
|
|
/*@fallthrough@*/
|
|
case STUN_ATTR_XOR_PEER_ADDR:
|
|
case STUN_ATTR_XOR_RELAY_ADDR:
|
|
case STUN_ATTR_XOR_MAPPED_ADDR:
|
|
err = stun_addr_decode(mb, &attr->v.sa, tid);
|
|
break;
|
|
|
|
case STUN_ATTR_CHANGE_REQ:
|
|
if (len != 4)
|
|
goto badmsg;
|
|
|
|
n = ntohl(mbuf_read_u32(mb));
|
|
attr->v.change_req.ip = (n >> 2) & 0x1;
|
|
attr->v.change_req.port = (n >> 1) & 0x1;
|
|
break;
|
|
|
|
case STUN_ATTR_USERNAME:
|
|
case STUN_ATTR_REALM:
|
|
case STUN_ATTR_NONCE:
|
|
case STUN_ATTR_SOFTWARE:
|
|
err = str_decode(mb, &attr->v.str, len);
|
|
break;
|
|
|
|
case STUN_ATTR_MSG_INTEGRITY:
|
|
if (len != 20)
|
|
goto badmsg;
|
|
|
|
err = mbuf_read_mem(mb, attr->v.msg_integrity, 20);
|
|
break;
|
|
|
|
case STUN_ATTR_ERR_CODE:
|
|
if (len < 4)
|
|
goto badmsg;
|
|
|
|
mb->pos += 2;
|
|
attr->v.err_code.code = (mbuf_read_u8(mb) & 0x7) * 100;
|
|
attr->v.err_code.code += mbuf_read_u8(mb);
|
|
err = str_decode(mb, &attr->v.err_code.reason, len - 4);
|
|
break;
|
|
|
|
case STUN_ATTR_UNKNOWN_ATTR:
|
|
for (i=0; i<len/2; i++) {
|
|
uint16_t type = ntohs(mbuf_read_u16(mb));
|
|
|
|
if (i >= ARRAY_SIZE(attr->v.unknown_attr.typev))
|
|
continue;
|
|
|
|
attr->v.unknown_attr.typev[i] = type;
|
|
attr->v.unknown_attr.typec++;
|
|
}
|
|
break;
|
|
|
|
case STUN_ATTR_CHANNEL_NUMBER:
|
|
case STUN_ATTR_RESP_PORT:
|
|
if (len < 2)
|
|
goto badmsg;
|
|
|
|
attr->v.uint16 = ntohs(mbuf_read_u16(mb));
|
|
break;
|
|
|
|
case STUN_ATTR_LIFETIME:
|
|
case STUN_ATTR_PRIORITY:
|
|
case STUN_ATTR_FINGERPRINT:
|
|
if (len != 4)
|
|
goto badmsg;
|
|
|
|
attr->v.uint32 = ntohl(mbuf_read_u32(mb));
|
|
break;
|
|
|
|
case STUN_ATTR_DATA:
|
|
case STUN_ATTR_PADDING:
|
|
attr->v.mb.buf = mem_ref(mb->buf);
|
|
attr->v.mb.size = mb->size;
|
|
attr->v.mb.pos = mb->pos;
|
|
attr->v.mb.end = mb->pos + len;
|
|
mb->pos += len;
|
|
break;
|
|
|
|
case STUN_ATTR_REQ_ADDR_FAMILY:
|
|
case STUN_ATTR_REQ_TRANSPORT:
|
|
if (len < 1)
|
|
goto badmsg;
|
|
|
|
attr->v.uint8 = mbuf_read_u8(mb);
|
|
break;
|
|
|
|
case STUN_ATTR_EVEN_PORT:
|
|
if (len < 1)
|
|
goto badmsg;
|
|
|
|
attr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1;
|
|
break;
|
|
|
|
case STUN_ATTR_DONT_FRAGMENT:
|
|
case STUN_ATTR_USE_CAND:
|
|
if (len > 0)
|
|
goto badmsg;
|
|
|
|
/* no value */
|
|
break;
|
|
|
|
case STUN_ATTR_RSV_TOKEN:
|
|
case STUN_ATTR_CONTROLLING:
|
|
case STUN_ATTR_CONTROLLED:
|
|
if (len != 8)
|
|
goto badmsg;
|
|
|
|
attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb));
|
|
break;
|
|
|
|
default:
|
|
mb->pos += len;
|
|
|
|
if (attr->type >= 0x8000)
|
|
break;
|
|
|
|
if (ua && ua->typec < ARRAY_SIZE(ua->typev))
|
|
ua->typev[ua->typec++] = attr->type;
|
|
break;
|
|
}
|
|
|
|
if (err)
|
|
goto error;
|
|
|
|
/* padding */
|
|
while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
|
|
++mb->pos;
|
|
|
|
*attrp = attr;
|
|
|
|
return 0;
|
|
|
|
badmsg:
|
|
err = EBADMSG;
|
|
error:
|
|
mem_deref(attr);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
const char *stun_attr_name(uint16_t type)
|
|
{
|
|
switch (type) {
|
|
|
|
case STUN_ATTR_MAPPED_ADDR: return "MAPPED-ADDRESS";
|
|
case STUN_ATTR_CHANGE_REQ: return "CHANGE-REQUEST";
|
|
case STUN_ATTR_USERNAME: return "USERNAME";
|
|
case STUN_ATTR_MSG_INTEGRITY: return "MESSAGE-INTEGRITY";
|
|
case STUN_ATTR_ERR_CODE: return "ERROR-CODE";
|
|
case STUN_ATTR_UNKNOWN_ATTR: return "UNKNOWN-ATTRIBUTE";
|
|
case STUN_ATTR_CHANNEL_NUMBER: return "CHANNEL-NUMBER";
|
|
case STUN_ATTR_LIFETIME: return "LIFETIME";
|
|
case STUN_ATTR_XOR_PEER_ADDR: return "XOR-PEER-ADDRESS";
|
|
case STUN_ATTR_DATA: return "DATA";
|
|
case STUN_ATTR_REALM: return "REALM";
|
|
case STUN_ATTR_NONCE: return "NONCE";
|
|
case STUN_ATTR_XOR_RELAY_ADDR: return "XOR-RELAYED-ADDRESS";
|
|
case STUN_ATTR_REQ_ADDR_FAMILY: return "REQUESTED-ADDRESS-FAMILY";
|
|
case STUN_ATTR_EVEN_PORT: return "EVEN_PORT";
|
|
case STUN_ATTR_REQ_TRANSPORT: return "REQUESTED-TRANSPORT";
|
|
case STUN_ATTR_DONT_FRAGMENT: return "DONT-FRAGMENT";
|
|
case STUN_ATTR_XOR_MAPPED_ADDR: return "XOR-MAPPED-ADDRESS";
|
|
case STUN_ATTR_RSV_TOKEN: return "RESERVATION-TOKEN";
|
|
case STUN_ATTR_PRIORITY: return "PRIORITY";
|
|
case STUN_ATTR_USE_CAND: return "USE-CANDIDATE";
|
|
case STUN_ATTR_PADDING: return "PADDING";
|
|
case STUN_ATTR_RESP_PORT: return "RESPONSE-PORT";
|
|
case STUN_ATTR_SOFTWARE: return "SOFTWARE";
|
|
case STUN_ATTR_ALT_SERVER: return "ALTERNATE-SERVER";
|
|
case STUN_ATTR_FINGERPRINT: return "FINGERPRINT";
|
|
case STUN_ATTR_CONTROLLING: return "ICE-CONTROLLING";
|
|
case STUN_ATTR_CONTROLLED: return "ICE-CONTROLLED";
|
|
case STUN_ATTR_RESP_ORIGIN: return "RESPONSE-ORIGIN";
|
|
case STUN_ATTR_OTHER_ADDR: return "OTHER-ADDR";
|
|
default: return "???";
|
|
}
|
|
}
|
|
|
|
|
|
void stun_attr_dump(const struct stun_attr *a)
|
|
{
|
|
uint32_t i;
|
|
size_t len;
|
|
|
|
if (!a)
|
|
return;
|
|
|
|
(void)re_printf(" %-25s", stun_attr_name(a->type));
|
|
|
|
switch (a->type) {
|
|
|
|
case STUN_ATTR_MAPPED_ADDR:
|
|
case STUN_ATTR_XOR_PEER_ADDR:
|
|
case STUN_ATTR_XOR_RELAY_ADDR:
|
|
case STUN_ATTR_XOR_MAPPED_ADDR:
|
|
case STUN_ATTR_ALT_SERVER:
|
|
case STUN_ATTR_RESP_ORIGIN:
|
|
case STUN_ATTR_OTHER_ADDR:
|
|
(void)re_printf("%J", &a->v.sa);
|
|
break;
|
|
|
|
case STUN_ATTR_CHANGE_REQ:
|
|
(void)re_printf("ip=%u port=%u", a->v.change_req.ip,
|
|
a->v.change_req.port);
|
|
break;
|
|
|
|
case STUN_ATTR_USERNAME:
|
|
case STUN_ATTR_REALM:
|
|
case STUN_ATTR_NONCE:
|
|
case STUN_ATTR_SOFTWARE:
|
|
(void)re_printf("%s", a->v.str);
|
|
break;
|
|
|
|
case STUN_ATTR_MSG_INTEGRITY:
|
|
(void)re_printf("%w", &a->v.msg_integrity, 20);
|
|
break;
|
|
|
|
case STUN_ATTR_ERR_CODE:
|
|
(void)re_printf("%u %s", a->v.err_code.code,
|
|
a->v.err_code.reason);
|
|
break;
|
|
|
|
case STUN_ATTR_UNKNOWN_ATTR:
|
|
for (i=0; i<a->v.unknown_attr.typec; i++)
|
|
(void)re_printf("0x%04x ", a->v.unknown_attr.typev[i]);
|
|
break;
|
|
|
|
case STUN_ATTR_CHANNEL_NUMBER:
|
|
(void)re_printf("0x%04x", a->v.uint16);
|
|
break;
|
|
|
|
case STUN_ATTR_LIFETIME:
|
|
case STUN_ATTR_PRIORITY:
|
|
(void)re_printf("%u", a->v.uint32);
|
|
break;
|
|
|
|
case STUN_ATTR_DATA:
|
|
case STUN_ATTR_PADDING:
|
|
len = mbuf_get_left(&a->v.mb);
|
|
(void)re_printf("%w%s (%u bytes)", mbuf_buf(&a->v.mb),
|
|
MIN(len, 16), len > 16 ? "..." : "", len);
|
|
break;
|
|
|
|
case STUN_ATTR_REQ_ADDR_FAMILY:
|
|
case STUN_ATTR_REQ_TRANSPORT:
|
|
(void)re_printf("%u", a->v.uint8);
|
|
break;
|
|
|
|
case STUN_ATTR_EVEN_PORT:
|
|
(void)re_printf("r=%u", a->v.even_port.r);
|
|
break;
|
|
|
|
case STUN_ATTR_DONT_FRAGMENT:
|
|
case STUN_ATTR_USE_CAND:
|
|
/* no value */
|
|
break;
|
|
|
|
case STUN_ATTR_RSV_TOKEN:
|
|
(void)re_printf("0x%016llx", a->v.rsv_token);
|
|
break;
|
|
|
|
case STUN_ATTR_RESP_PORT:
|
|
(void)re_printf("%u", a->v.uint16);
|
|
break;
|
|
|
|
case STUN_ATTR_FINGERPRINT:
|
|
(void)re_printf("0x%08x", a->v.fingerprint);
|
|
break;
|
|
|
|
case STUN_ATTR_CONTROLLING:
|
|
case STUN_ATTR_CONTROLLED:
|
|
(void)re_printf("%llu", a->v.uint64);
|
|
break;
|
|
|
|
default:
|
|
(void)re_printf("???");
|
|
break;
|
|
}
|
|
|
|
(void)re_printf("\n");
|
|
}
|