/** * @file stun/attr.c STUN Attributes * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #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; itypec; 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= 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; iv.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"); }