|
|
|
@ -3,6 +3,7 @@ |
|
|
|
#include <sys/types.h> |
|
|
|
#include <string.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <zlib.h> |
|
|
|
|
|
|
|
#include "str.h" |
|
|
|
#include "aux.h" |
|
|
|
@ -22,7 +23,9 @@ struct tlv { |
|
|
|
struct stun_attrs { |
|
|
|
str username; |
|
|
|
str msg_integrity; |
|
|
|
char *fingerprint_attr; |
|
|
|
u_int32_t priority; |
|
|
|
u_int32_t fingerprint; |
|
|
|
int use:1, |
|
|
|
controlled:1, |
|
|
|
controlling:1; |
|
|
|
@ -32,7 +35,12 @@ struct stun_error { |
|
|
|
struct stun stun; |
|
|
|
struct tlv error_code; |
|
|
|
u_int32_t codes; |
|
|
|
}; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct stun_fingerprint { |
|
|
|
struct tlv tlv; |
|
|
|
u_int32_t crc; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -59,12 +67,21 @@ 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) != 0x8028) |
|
|
|
return -1; |
|
|
|
|
|
|
|
switch (ntohs(tlv->type)) { |
|
|
|
case 0x0006: /* username */ |
|
|
|
out->username = attr; |
|
|
|
break; |
|
|
|
case 0x0008: /* message-integrity */ |
|
|
|
out->msg_integrity = attr; |
|
|
|
break; |
|
|
|
case 0x8028: /* fingerprint */ |
|
|
|
if (attr.len != 4) |
|
|
|
return -1; |
|
|
|
out->fingerprint_attr = (void *) tlv; |
|
|
|
out->fingerprint = ntohl(*(u_int32_t *) attr.s); |
|
|
|
goto out; |
|
|
|
|
|
|
|
case 0x0025: /* use-candidate */ |
|
|
|
@ -93,8 +110,9 @@ static inline void stun_error_len(int fd, struct sockaddr_in6 *sin, struct stun |
|
|
|
int code, char *reason, int len) |
|
|
|
{ |
|
|
|
struct stun_error err; |
|
|
|
struct stun_fingerprint fp; |
|
|
|
struct msghdr mh; |
|
|
|
struct iovec iov[2]; |
|
|
|
struct iovec iov[3]; |
|
|
|
|
|
|
|
err.stun.msg_type = htons(0x0111); /* binding error response */ |
|
|
|
err.stun.cookie = htonl(STUN_COOKIE); |
|
|
|
@ -110,13 +128,22 @@ static inline void stun_error_len(int fd, struct sockaddr_in6 *sin, struct stun |
|
|
|
iov[0].iov_len = sizeof(err); |
|
|
|
iov[1].iov_base = reason; |
|
|
|
iov[1].iov_len = (len + 3) & 0xfffc; |
|
|
|
iov[2].iov_base = &fp; |
|
|
|
iov[2].iov_len = sizeof(fp); |
|
|
|
|
|
|
|
err.stun.msg_len = htons(iov[1].iov_len + sizeof(err.codes) + sizeof(err.error_code)); |
|
|
|
err.stun.msg_len = htons(iov[1].iov_len + sizeof(err.codes) + sizeof(err.error_code) |
|
|
|
+ iov[2].iov_len); |
|
|
|
|
|
|
|
fp.crc = crc32(0, iov[0].iov_base, iov[0].iov_len); |
|
|
|
fp.crc = crc32(fp.crc, iov[1].iov_base, iov[1].iov_len); |
|
|
|
fp.crc = htonl(fp.crc ^ 0x5354554eUL); |
|
|
|
fp.tlv.type = htons(0x8028); |
|
|
|
fp.tlv.len = htons(4); |
|
|
|
|
|
|
|
mh.msg_name = sin; |
|
|
|
mh.msg_namelen = sizeof(*sin); |
|
|
|
mh.msg_iov = iov; |
|
|
|
mh.msg_iovlen = 2; |
|
|
|
mh.msg_iovlen = 3; |
|
|
|
|
|
|
|
sendmsg(fd, &mh, 0); |
|
|
|
} |
|
|
|
@ -124,6 +151,19 @@ static inline void stun_error_len(int fd, struct sockaddr_in6 *sin, struct stun |
|
|
|
#define stun_error(fd, sin, str, code, reason) \ |
|
|
|
stun_error_len(fd, sin, str, code, reason "\0\0\0", strlen(reason)) |
|
|
|
|
|
|
|
static int check_fingerprint(str *msg, struct stun_attrs *attrs) { |
|
|
|
int len; |
|
|
|
u_int32_t crc; |
|
|
|
|
|
|
|
len = attrs->fingerprint_attr - msg->s; |
|
|
|
crc = crc32(0, (void *) msg->s, len); |
|
|
|
crc ^= 0x5354554eUL; |
|
|
|
if (crc != attrs->fingerprint) |
|
|
|
return -1; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* XXX add error reporting */ |
|
|
|
int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { |
|
|
|
struct stun *s = (void *) b->s; |
|
|
|
@ -146,10 +186,15 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { |
|
|
|
if (stun_attributes(&attrs, &attr_str)) |
|
|
|
return -1; |
|
|
|
|
|
|
|
if (class == 0x0) { /* request */ |
|
|
|
if (!attrs.username.s || !attrs.msg_integrity.s) |
|
|
|
goto bad_req; |
|
|
|
} |
|
|
|
if (class != 0x0) |
|
|
|
return -1; /* XXX ? */ |
|
|
|
|
|
|
|
/* request */ |
|
|
|
if (!attrs.username.s || !attrs.msg_integrity.s || !attrs.fingerprint_attr) |
|
|
|
goto bad_req; |
|
|
|
|
|
|
|
if (check_fingerprint(b, &attrs)) |
|
|
|
return -1; |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|