diff --git a/daemon/stun.c b/daemon/stun.c index 6ce13cf58..9b93fcdbb 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -16,12 +16,15 @@ #define STUN_USERNAME 0x0006 #define STUN_MESSAGE_INTEGRITY 0x0008 #define STUN_ERROR_CODE 0x0009 +#define STUN_UNKNOWN_ATTRIBUTES 0x000a #define STUN_XOR_MAPPED_ADDRESS 0x0020 #define STUN_FINGERPRINT 0x8028 #define STUN_BINDING_SUCCESS_RESPONSE 0x0101 #define STUN_BINDING_ERROR_RESPONSE 0x0111 +#define UNKNOWNS_COUNT 16 + struct stun_attrs { @@ -48,6 +51,10 @@ struct tlv { u_int16_t len; } __attribute__ ((packed)); +struct generic { + struct tlv tlv; +} __attribute__ ((packed)); + struct error_code { struct tlv tlv; u_int32_t codes; @@ -72,16 +79,18 @@ struct xor_mapped_address { -static int stun_attributes(struct stun_attrs *out, str *s) { +static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns) { struct tlv *tlv; - int len; + int len, type, uc; str attr; ZERO(*out); + uc = 0; + unknowns[0] = 0xffff; while (1) { if (!s->len) - return 0; + break; tlv = (void *) s->s; if (str_shift(s, sizeof(*tlv))) @@ -95,10 +104,11 @@ static int stun_attributes(struct stun_attrs *out, str *s) { if (str_shift(s, len)) return -1; - if (out->msg_integrity.s && ntohs(tlv->type) != STUN_FINGERPRINT) + type = ntohs(tlv->type); + if (out->msg_integrity.s && type != STUN_FINGERPRINT) return -1; - switch (ntohs(tlv->type)) { + switch (type) { case STUN_USERNAME: out->username = attr; break; @@ -130,11 +140,20 @@ static int stun_attributes(struct stun_attrs *out, str *s) { return -1; out->priority = ntohl(*((u_int32_t *) attr.s)); break; + + default: + if ((type & 0x8000)) + break; + unknowns[uc] = tlv->type; + unknowns[++uc] = 0xffff; + if (uc >= UNKNOWNS_COUNT - 1) + return -1; + break; } } out: - return 0; + return uc ? -1 : 0; } static void output_init(struct msghdr *mh, struct iovec *iov, struct sockaddr_in6 *sin, @@ -241,18 +260,22 @@ static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) { } static void stun_error_len(int fd, struct sockaddr_in6 *sin, struct header *req, - int code, char *reason, int len) + int code, char *reason, int len, u_int16_t add_attr, void *attr_cont, + int attr_len) { struct header hdr; struct error_code ec; struct fingerprint fp; + struct generic aa; struct msghdr mh; - struct iovec iov[4]; /* hdr, ec, reason, fp */ + struct iovec iov[6]; /* hdr, ec, reason, aa, attr_cont, fp */ output_init(&mh, iov, sin, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction); ec.codes = htonl(((code / 100) << 8) | (code % 100)); output_add_data(&mh, &ec, STUN_ERROR_CODE, reason, len); + if (attr_cont) + output_add_data(&mh, &aa, add_attr, attr_cont, attr_len); fingerprint(&mh, &fp); @@ -261,7 +284,11 @@ static void stun_error_len(int fd, struct sockaddr_in6 *sin, struct header *req, } #define stun_error(fd, sin, str, code, reason) \ - stun_error_len(fd, sin, str, code, reason "\0\0\0", strlen(reason)) + stun_error_len(fd, sin, str, code, reason "\0\0\0", strlen(reason), \ + 0, NULL, 0) +#define stun_error_attrs(fd, sin, str, code, reason, type, content, len) \ + stun_error_len(fd, sin, str, code, reason "\0\0\0", strlen(reason), \ + type, content, len) @@ -350,18 +377,26 @@ static int stun_binding_success(int fd, struct header *req, struct stun_attrs *a return 0; } +static inline int u_int16_t_arr_len(u_int16_t *arr) { + int i; + for (i = 0; arr[i] != 0xffff; i++) + ; + return i; +} + /* XXX add error reporting */ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { - struct header *s = (void *) b->s; + struct header *req = (void *) b->s; int msglen, method, class; str attr_str; struct stun_attrs attrs; + u_int16_t unknowns[UNKNOWNS_COUNT]; - msglen = ntohs(s->msg_len); + msglen = ntohs(req->msg_len); if (msglen + 20 > b->len || msglen < 0) return -1; - class = method = ntohs(s->msg_type); + class = method = ntohs(req->msg_type); class = ((class & 0x10) >> 4) | ((class & 0x100) >> 7); method = (method & 0xf) | ((method & 0xe0) >> 1) | ((method & 0x3e00) >> 2); if (method != 0x1) /* binding */ @@ -369,8 +404,14 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { attr_str.s = &b->s[20]; attr_str.len = b->len - 20; - if (stun_attributes(&attrs, &attr_str)) - return -1; + if (stun_attributes(&attrs, &attr_str, unknowns)) { + if (unknowns[0] == 0xffff) + return -1; + stun_error_attrs(sr->fd.fd, sin, req, 420, "Unknown attribute", + STUN_UNKNOWN_ATTRIBUTES, unknowns, + u_int16_t_arr_len(unknowns) * 2); + return 0; + } if (class != 0x0) return -1; /* XXX ? */ @@ -384,14 +425,14 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { if (check_auth(b, &attrs, sr->up)) goto unauth; - stun_binding_success(sr->fd.fd, s, &attrs, sin, sr->up); + stun_binding_success(sr->fd.fd, req, &attrs, sin, sr->up); return 0; bad_req: - stun_error(sr->fd.fd, sin, s, 400, "Bad request"); + stun_error(sr->fd.fd, sin, req, 400, "Bad request"); return 0; unauth: - stun_error(sr->fd.fd, sin, s, 401, "Unauthorized"); + stun_error(sr->fd.fd, sin, req, 401, "Unauthorized"); return 0; }