|
|
|
|
@ -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;
|
|
|
|
|
}
|
|
|
|
|
|