re/src/stun/attr.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");
}