Commit f73f9b5c authored by Kai Vehmanen's avatar Kai Vehmanen

Convert indentation from tabs to spaces.

darcs-hash:20070918150713-77cd4-7bdd3f21524c846c1bf0c2c8cb96eb6104c96ad8.gz
parent d3e16af2
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
struct stun_bind_s struct stun_bind_s
{ {
stun_trans_t trans; stun_trans_t trans;
}; };
...@@ -81,37 +81,37 @@ static int ...@@ -81,37 +81,37 @@ static int
stun_bind_alloc (stun_bind_t **restrict context, int fd, stun_bind_alloc (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
{ {
int val; int val;
stun_bind_t *ctx = malloc (sizeof (*ctx)); stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL) if (ctx == NULL)
return ENOMEM; return ENOMEM;
memset (ctx, 0, sizeof (*ctx)); memset (ctx, 0, sizeof (*ctx));
*context = ctx; *context = ctx;
val = (fd != -1) val = (fd != -1)
? stun_trans_init (&ctx->trans, fd, srv, srvlen) ? stun_trans_init (&ctx->trans, fd, srv, srvlen)
: stun_trans_create (&ctx->trans, SOCK_DGRAM, 0, srv, srvlen); : stun_trans_create (&ctx->trans, SOCK_DGRAM, 0, srv, srvlen);
if (val) if (val)
{ {
free (ctx); free (ctx);
return val; return val;
} }
stun_init_request (ctx->trans.msg.buf, STUN_BINDING); stun_init_request (ctx->trans.msg.buf, STUN_BINDING);
return 0; return 0;
} }
static int static int
stun_bind_launch (stun_bind_t *ctx) stun_bind_launch (stun_bind_t *ctx)
{ {
int val = stun_trans_start (&ctx->trans); int val = stun_trans_start (&ctx->trans);
if (val) if (val)
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
return val; return val;
} }
...@@ -119,30 +119,30 @@ int stun_bind_start (stun_bind_t **restrict context, int fd, ...@@ -119,30 +119,30 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, const struct sockaddr *restrict srv,
socklen_t srvlen) socklen_t srvlen)
{ {
stun_bind_t *ctx; stun_bind_t *ctx;
int val = stun_bind_alloc (context, fd, srv, srvlen); int val = stun_bind_alloc (context, fd, srv, srvlen);
if (val) if (val)
return val; return val;
ctx = *context; ctx = *context;
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf); ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish (ctx->trans.msg.buf, &ctx->trans.msg.length); val = stun_finish (ctx->trans.msg.buf, &ctx->trans.msg.length);
if (val) if (val)
{ {
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
return val; return val;
} }
return stun_bind_launch (ctx); return stun_bind_launch (ctx);
} }
void stun_bind_cancel (stun_bind_t *context) void stun_bind_cancel (stun_bind_t *context)
{ {
stun_trans_deinit (&context->trans); stun_trans_deinit (&context->trans);
free (context); free (context);
} }
...@@ -150,17 +150,17 @@ void stun_bind_cancel (stun_bind_t *context) ...@@ -150,17 +150,17 @@ void stun_bind_cancel (stun_bind_t *context)
unsigned stun_bind_timeout (const stun_bind_t *context) unsigned stun_bind_timeout (const stun_bind_t *context)
{ {
assert (context != NULL); assert (context != NULL);
return stun_trans_timeout (&context->trans); return stun_trans_timeout (&context->trans);
} }
int stun_bind_elapse (stun_bind_t *context) int stun_bind_elapse (stun_bind_t *context)
{ {
int val = stun_trans_tick (&context->trans); int val = stun_trans_tick (&context->trans);
if (val != EAGAIN) if (val != EAGAIN)
stun_bind_cancel (context); stun_bind_cancel (context);
return val; return val;
} }
...@@ -170,40 +170,40 @@ int stun_bind_process (stun_bind_t *restrict ctx, ...@@ -170,40 +170,40 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len, const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen) struct sockaddr *restrict addr, socklen_t *addrlen)
{ {
int val, code; int val, code;
assert (ctx != NULL); assert (ctx != NULL);
val = stun_trans_preprocess (&ctx->trans, &code, buf, len); val = stun_trans_preprocess (&ctx->trans, &code, buf, len);
switch (val) switch (val)
{ {
case EAGAIN: case EAGAIN:
return EAGAIN; return EAGAIN;
case 0: case 0:
break; break;
default: default:
if (code == STUN_ROLE_CONFLICT) if (code == STUN_ROLE_CONFLICT)
val = ECONNRESET; val = ECONNRESET;
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
return val; return val;
} }
val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen); val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen);
if (val) if (val)
{ {
DBG (" No XOR-MAPPED-ADDRESS: %s\n", strerror (val)); DBG (" No XOR-MAPPED-ADDRESS: %s\n", strerror (val));
val = stun_find_addr (buf, STUN_MAPPED_ADDRESS, addr, addrlen); val = stun_find_addr (buf, STUN_MAPPED_ADDRESS, addr, addrlen);
if (val) if (val)
{ {
DBG (" No MAPPED-ADDRESS: %s\n", strerror (val)); DBG (" No MAPPED-ADDRESS: %s\n", strerror (val));
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
return val; return val;
} }
} }
DBG (" Mapped address found!\n"); DBG (" Mapped address found!\n");
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
return 0; return 0;
} }
...@@ -213,28 +213,28 @@ int stun_bind_run (int fd, ...@@ -213,28 +213,28 @@ int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen, const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen) struct sockaddr *restrict addr, socklen_t *addrlen)
{ {
stun_bind_t *ctx; stun_bind_t *ctx;
uint8_t buf[STUN_MAXMSG]; uint8_t buf[STUN_MAXMSG];
ssize_t val; ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen); val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val) if (val)
return val; return val;
do do
{ {
val = stun_trans_recv (&ctx->trans, buf, sizeof (buf)); val = stun_trans_recv (&ctx->trans, buf, sizeof (buf));
if (val == -1) if (val == -1)
{ {
val = errno; val = errno;
continue; continue;
} }
val = stun_bind_process (ctx, buf, val, addr, addrlen); val = stun_bind_process (ctx, buf, val, addr, addrlen);
} }
while (val == EAGAIN); while (val == EAGAIN);
return val; return val;
} }
...@@ -244,19 +244,19 @@ int ...@@ -244,19 +244,19 @@ int
stun_bind_keepalive (int fd, const struct sockaddr *restrict srv, stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen) socklen_t srvlen)
{ {
uint8_t buf[28]; uint8_t buf[28];
size_t len = sizeof (buf); size_t len = sizeof (buf);
int val; int val;
stun_init_indication (buf, STUN_BINDING); stun_init_indication (buf, STUN_BINDING);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
(void)val; (void)val;
/* NOTE: hopefully, this is only needed for non-stream sockets */ /* NOTE: hopefully, this is only needed for non-stream sockets */
if (stun_sendto (fd, buf, len, srv, srvlen) == -1) if (stun_sendto (fd, buf, len, srv, srvlen) == -1)
return errno; return errno;
return 0; return 0;
} }
...@@ -270,58 +270,58 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd, ...@@ -270,58 +270,58 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd,
bool cand_use, bool controlling, uint32_t priority, bool cand_use, bool controlling, uint32_t priority,
uint64_t tie) uint64_t tie)
{ {
int val; int val;
stun_bind_t *ctx; stun_bind_t *ctx;
assert (username != NULL); assert (username != NULL);
assert (password != NULL); assert (password != NULL);
val = stun_bind_alloc (&ctx, fd, srv, srvlen); val = stun_bind_alloc (&ctx, fd, srv, srvlen);
if (val) if (val)
return val; return val;
ctx->trans.key.length = strlen (password); ctx->trans.key.length = strlen (password);
ctx->trans.key.value = malloc (ctx->trans.key.length); ctx->trans.key.value = malloc (ctx->trans.key.length);
if (ctx->trans.key.value == NULL) if (ctx->trans.key.value == NULL)
{ {
val = ENOMEM; val = ENOMEM;
goto error; goto error;
} }
*context = ctx; *context = ctx;
memcpy (ctx->trans.key.value, password, ctx->trans.key.length); memcpy (ctx->trans.key.value, password, ctx->trans.key.length);
if (cand_use) if (cand_use)
{ {
val = stun_append_flag (ctx->trans.msg.buf, val = stun_append_flag (ctx->trans.msg.buf,
sizeof (ctx->trans.msg.buf), sizeof (ctx->trans.msg.buf),
STUN_USE_CANDIDATE); STUN_USE_CANDIDATE);
if (val) if (val)
goto error; goto error;
} }
val = stun_append32 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf), val = stun_append32 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf),
STUN_PRIORITY, priority); STUN_PRIORITY, priority);
if (val) if (val)
goto error; goto error;
val = stun_append64 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf), val = stun_append64 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf),
controlling ? STUN_ICE_CONTROLLING controlling ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, tie); : STUN_ICE_CONTROLLED, tie);
if (val) if (val)
goto error; goto error;
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf); ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length, val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length,
username, password, NULL); username, password, NULL);
if (val) if (val)
goto error; goto error;
return stun_bind_launch (ctx); return stun_bind_launch (ctx);
error: error:
stun_bind_cancel (*context); stun_bind_cancel (*context);
return val; return val;
} }
...@@ -52,20 +52,20 @@ static inline uint32_t crc32 (const struct iovec *iov, size_t size); ...@@ -52,20 +52,20 @@ static inline uint32_t crc32 (const struct iovec *iov, size_t size);
uint32_t stun_fingerprint (const uint8_t *msg, size_t len) uint32_t stun_fingerprint (const uint8_t *msg, size_t len)
{ {
struct iovec iov[3]; struct iovec iov[3];
uint16_t fakelen = htons (len - 20u); uint16_t fakelen = htons (len - 20u);
assert (len >= 28u); assert (len >= 28u);
iov[0].iov_base = (void *)msg; iov[0].iov_base = (void *)msg;
iov[0].iov_len = 2; iov[0].iov_len = 2;
iov[1].iov_base = &fakelen; iov[1].iov_base = &fakelen;
iov[1].iov_len = 2; iov[1].iov_len = 2;
iov[2].iov_base = (void *)(msg + 4); iov[2].iov_base = (void *)(msg + 4);
/* first 4 bytes done, last 8 bytes not summed */ /* first 4 bytes done, last 8 bytes not summed */
iov[2].iov_len = len - 12u; iov[2].iov_len = len - 12u;
return crc32 (iov, sizeof (iov) / sizeof (iov[0])) ^ 0x5354554e; return crc32 (iov, sizeof (iov) / sizeof (iov[0])) ^ 0x5354554e;
} }
/*- /*-
...@@ -165,17 +165,17 @@ static const uint32_t crc32_tab[] = { ...@@ -165,17 +165,17 @@ static const uint32_t crc32_tab[] = {
static inline static inline
uint32_t crc32 (const struct iovec *iov, size_t n) uint32_t crc32 (const struct iovec *iov, size_t n)
{ {
size_t i; size_t i;
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
const uint8_t *p = iov[i].iov_base; const uint8_t *p = iov[i].iov_base;
size_t size = iov[i].iov_len; size_t size = iov[i].iov_len;
while (size--) while (size--)
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
} }
return crc ^ 0xffffffff; return crc ^ 0xffffffff;
} }
...@@ -53,71 +53,71 @@ ...@@ -53,71 +53,71 @@
void stun_sha1 (const uint8_t *msg, size_t len, uint8_t *sha, void stun_sha1 (const uint8_t *msg, size_t len, uint8_t *sha,
const void *restrict key, size_t keylen) const void *restrict key, size_t keylen)
{ {
HMAC_CTX ctx; HMAC_CTX ctx;
uint16_t fakelen = htons (len - 20u); uint16_t fakelen = htons (len - 20u);
assert (len >= 44u); assert (len >= 44u);
HMAC_CTX_init (&ctx); HMAC_CTX_init (&ctx);
HMAC_Init_ex (&ctx, key, keylen, EVP_sha1 (), NULL); HMAC_Init_ex (&ctx, key, keylen, EVP_sha1 (), NULL);
HMAC_Update (&ctx, msg, 2); HMAC_Update (&ctx, msg, 2);
HMAC_Update (&ctx, (const uint8_t *)&fakelen, 2); HMAC_Update (&ctx, (const uint8_t *)&fakelen, 2);
/* first 4 bytes done, last 24 bytes not summed */ /* first 4 bytes done, last 24 bytes not summed */
HMAC_Update (&ctx, msg + 4, len - 28u); HMAC_Update (&ctx, msg + 4, len - 28u);
HMAC_Final (&ctx, sha, NULL); HMAC_Final (&ctx, sha, NULL);
HMAC_CTX_cleanup (&ctx); HMAC_CTX_cleanup (&ctx);
} }
void stun_hash_creds (const char *realm, const char *login, const char *pw, void stun_hash_creds (const char *realm, const char *login, const char *pw,
unsigned char md5[16]) unsigned char md5[16])
{ {
EVP_MD_CTX ctx; EVP_MD_CTX ctx;
assert (realm && login && pw && md5); assert (realm && login && pw && md5);
EVP_MD_CTX_init (&ctx); EVP_MD_CTX_init (&ctx);
EVP_DigestInit_ex (&ctx, EVP_md5 (), NULL); EVP_DigestInit_ex (&ctx, EVP_md5 (), NULL);
EVP_DigestUpdate (&ctx, realm, strlen (realm)); EVP_DigestUpdate (&ctx, realm, strlen (realm));
EVP_DigestUpdate (&ctx, ":", 1); EVP_DigestUpdate (&ctx, ":", 1);
EVP_DigestUpdate (&ctx, login, strlen (login)); EVP_DigestUpdate (&ctx, login, strlen (login));
EVP_DigestUpdate (&ctx, ":", 1); EVP_DigestUpdate (&ctx, ":", 1);
EVP_DigestUpdate (&ctx, pw, strlen (pw)); EVP_DigestUpdate (&ctx, pw, strlen (pw));
EVP_DigestFinal (&ctx, md5, NULL); EVP_DigestFinal (&ctx, md5, NULL);
} }
void stun_make_transid (stun_transid_t id) void stun_make_transid (stun_transid_t id)
{ {
/* /*
* transid = (HMAC_SHA1 (secret, counter) >> 64) * transid = (HMAC_SHA1 (secret, counter) >> 64)
* This consumes sizeof (secret) bytes of entropy every 2^64 messages. * This consumes sizeof (secret) bytes of entropy every 2^64 messages.
*/ */
static struct static struct
{ {
pthread_mutex_t lock; pthread_mutex_t lock;
uint64_t counter; uint64_t counter;
uint8_t secret[16]; uint8_t secret[16];
} store = { PTHREAD_MUTEX_INITIALIZER, 0, "" }; } store = { PTHREAD_MUTEX_INITIALIZER, 0, "" };
union union
{ {
uint64_t value; uint64_t value;
uint8_t bytes[1]; uint8_t bytes[1];
} counter; } counter;
uint8_t key[16], sha[20]; uint8_t key[16], sha[20];
pthread_mutex_lock (&store.lock); pthread_mutex_lock (&store.lock);
counter.value = store.counter++; counter.value = store.counter++;
if (counter.value == 0) if (counter.value == 0)
RAND_pseudo_bytes (store.secret, sizeof (store.secret)); RAND_pseudo_bytes (store.secret, sizeof (store.secret));
memcpy (key, store.secret, sizeof (key)); memcpy (key, store.secret, sizeof (key));
pthread_mutex_unlock (&store.lock); pthread_mutex_unlock (&store.lock);
/* Computes hash out of contentious area */ /* Computes hash out of contentious area */
HMAC (EVP_sha1 (), key, sizeof (key), counter.bytes, sizeof (counter), HMAC (EVP_sha1 (), key, sizeof (key), counter.bytes, sizeof (counter),
sha, NULL); sha, NULL);
memcpy (id, sha, 12); memcpy (id, sha, 12);
} }
...@@ -48,7 +48,7 @@ static uint8_t unique_id[UNIQUE_SIZE]; ...@@ -48,7 +48,7 @@ static uint8_t unique_id[UNIQUE_SIZE];
static void generate_unique_id (void) static void generate_unique_id (void)
{ {
RAND_pseudo_bytes (unique_id, sizeof (unique_id)); RAND_pseudo_bytes (unique_id, sizeof (unique_id));
} }
...@@ -56,45 +56,45 @@ static void ...@@ -56,45 +56,45 @@ static void
stun_generate_nonce (uint8_t *nonce, time_t now, stun_generate_nonce (uint8_t *nonce, time_t now,
const struct sockaddr_storage *restrict addr) const struct sockaddr_storage *restrict addr)
{ {
static pthread_once_t once = PTHREAD_ONCE_INIT; static pthread_once_t once = PTHREAD_ONCE_INIT;
HMAC_CTX ctx; HMAC_CTX ctx;
uint32_t stamp = now; uint32_t stamp = now;
pthread_once (&once, generate_unique_id); pthread_once (&once, generate_unique_id);
/* /*
* Nonce are generated from the current time and the client address and * Nonce are generated from the current time and the client address and
* port number, keyed with a pseudo-random secret. * port number, keyed with a pseudo-random secret.
*/ */
HMAC_CTX_init (&ctx); HMAC_CTX_init (&ctx);
HMAC_Init_ex (&ctx, unique_id, sizeof (unique_id), EVP_sha1 (), NULL); HMAC_Init_ex (&ctx, unique_id, sizeof (unique_id), EVP_sha1 (), NULL);
HMAC_Update (&ctx, &stamp, 4); HMAC_Update (&ctx, &stamp, 4);
HMAC_Update (&ctx, &ss->family, sizeof (ss->family)); HMAC_Update (&ctx, &ss->family, sizeof (ss->family));
switch (addr->ss_family) switch (addr->ss_family)
{ {
case AF_INET: case AF_INET:
{ {
const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr; const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
HMAC_Update (&ctx, &ip4->sin_addr, 4); HMAC_Update (&ctx, &ip4->sin_addr, 4);
HMAC_Update (&ctx, &ip4->sin_port, 2); HMAC_Update (&ctx, &ip4->sin_port, 2);
break; break;
} }
case AF_INET6: case AF_INET6:
{ {
const struct sockaddr_in6*ip6 = (const struct sockaddr_in6*)addr; const struct sockaddr_in6*ip6 = (const struct sockaddr_in6*)addr;
HMAC_Update (&ctx, &ip6->sin6_addr, 16); HMAC_Update (&ctx, &ip6->sin6_addr, 16);
HMAC_Update (&ctx, &ip6->sin6_port, 2); HMAC_Update (&ctx, &ip6->sin6_port, 2);
if (IN6_IS_ADDR_LINK_LOCAL (&ip6->sin6_addr)) if (IN6_IS_ADDR_LINK_LOCAL (&ip6->sin6_addr))
HMAC_Update (&ctx, &ip6->sin6_scope_id HMAC_Update (&ctx, &ip6->sin6_scope_id
sizeof (ip6->sin6_scope_id)); sizeof (ip6->sin6_scope_id));
break; break;
} }
} }
HMAC_Final (&ctx, nonce, NULL); HMAC_Final (&ctx, nonce, NULL);
HMAC_CTX_cleanup (&ctx); HMAC_CTX_cleanup (&ctx);
memcpy (nonce + 20, &stamp, 4); memcpy (nonce + 20, &stamp, 4);
} }
...@@ -102,9 +102,9 @@ static int ...@@ -102,9 +102,9 @@ static int
stun_append_nonce (uint8_t *buf, size_t buflen, stun_append_nonce (uint8_t *buf, size_t buflen,
const struct sockaddr_storage *restrict addr) const struct sockaddr_storage *restrict addr)
{ {
uint8_t nonce[NONCE_SIZE]; uint8_t nonce[NONCE_SIZE];
stun_generate_nonce (nonce, time (NULL), addr); stun_generate_nonce (nonce, time (NULL), addr);
return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce)); return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce));
} }
...@@ -112,7 +112,7 @@ static int ...@@ -112,7 +112,7 @@ static int
stun_verify_nonce (const uint8_t *buf, unsigned valid_time, stun_verify_nonce (const uint8_t *buf, unsigned valid_time,
const struct sockaddr_storage *restrict addr) const struct sockaddr_storage *restrict addr)
{ {
const uint8_t * const uint8_t *
stun_generate_nonce (nonce, time (NULL), addr); stun_generate_nonce (nonce, time (NULL), addr);
return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce)); return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce));
} }
...@@ -54,24 +54,24 @@ static int ...@@ -54,24 +54,24 @@ static int
stun_bind_error (uint8_t *buf, size_t *plen, const uint8_t *req, stun_bind_error (uint8_t *buf, size_t *plen, const uint8_t *req,
stun_error_t code, const char *pass) stun_error_t code, const char *pass)
{ {
size_t len = *plen; size_t len = *plen;
int val; int val;
*plen = 0; *plen = 0;
DBG ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len); DBG ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len);
val = stun_init_error (buf, len, req, code); val = stun_init_error (buf, len, req, code);
if (val) if (val)
return val; return val;
val = stun_finish_short (buf, &len, NULL, pass, NULL); val = stun_finish_short (buf, &len, NULL, pass, NULL);
if (val) if (val)
return val; return val;
*plen = len; *plen = len;
DBG (" Error response (%u) of %u bytes\n", (unsigned)code, DBG (" Error response (%u) of %u bytes\n", (unsigned)code,
(unsigned)*plen); (unsigned)*plen);
return 0; return 0;
} }
...@@ -80,125 +80,125 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, ...@@ -80,125 +80,125 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen, const struct sockaddr *restrict src, socklen_t srclen,
const char *pass, bool *restrict control, uint64_t tie) const char *pass, bool *restrict control, uint64_t tie)
{ {
size_t len = *plen; size_t len = *plen;
uint64_t q; uint64_t q;
int val, ret = 0; int val, ret = 0;
#define err( code ) \ #define err( code ) \
stun_bind_error (buf, &len, msg, code, pass); \ stun_bind_error (buf, &len, msg, code, pass); \
*plen = len *plen = len
*plen = 0; *plen = 0;
DBG ("STUN Reply (buffer size = %u)...\n", (unsigned)len); DBG ("STUN Reply (buffer size = %u)...\n", (unsigned)len);
if (stun_get_class (msg) != STUN_REQUEST) if (stun_get_class (msg) != STUN_REQUEST)
{ {
DBG (" Unhandled non-request (class %u) message.\n", DBG (" Unhandled non-request (class %u) message.\n",
stun_get_class (msg)); stun_get_class (msg));
return EINVAL; return EINVAL;
} }
if (!stun_demux (msg)) if (!stun_demux (msg))
{ {
DBG (" Incorrectly multiplexed STUN message ignored.\n"); DBG (" Incorrectly multiplexed STUN message ignored.\n");
return EINVAL; return EINVAL;
} }
if (stun_has_unknown (msg)) if (stun_has_unknown (msg))
{ {
DBG (" Unknown mandatory attributes in message.\n"); DBG (" Unknown mandatory attributes in message.\n");
val = stun_init_error_unknown (buf, len, msg); val = stun_init_error_unknown (buf, len, msg);
if (!val) if (!val)
val = stun_finish_short (buf, &len, NULL, pass, NULL); val = stun_finish_short (buf, &len, NULL, pass, NULL);
if (val) if (val)
goto failure; goto failure;
*plen = len; *plen = len;
return EPROTO; return EPROTO;
} }
/* Short term credentials checking */ /* Short term credentials checking */
val = 0; val = 0;
if (!stun_present (msg, STUN_MESSAGE_INTEGRITY) // FIXME: wrong! if (!stun_present (msg, STUN_MESSAGE_INTEGRITY) // FIXME: wrong!
|| !stun_present (msg, STUN_USERNAME)) || !stun_present (msg, STUN_USERNAME))
{ {
DBG (" Missing USERNAME or MESSAGE-INTEGRITY.\n"); DBG (" Missing USERNAME or MESSAGE-INTEGRITY.\n");
val = STUN_BAD_REQUEST; val = STUN_BAD_REQUEST;
} }
else else
/* FIXME: verify USERNAME, return STUN_UNAUTHORIZED if wrong */ /* FIXME: verify USERNAME, return STUN_UNAUTHORIZED if wrong */
if (stun_verify_password (msg, pass)) if (stun_verify_password (msg, pass))
{ {
DBG (" Integrity check failed.\n"); DBG (" Integrity check failed.\n");
val = STUN_UNAUTHORIZED; val = STUN_UNAUTHORIZED;
} }
if (val) if (val)
{ {
stun_bind_error (buf, &len, msg, val, NULL); stun_bind_error (buf, &len, msg, val, NULL);
*plen = len; *plen = len;
return EPERM; return EPERM;
} }
if (stun_get_method (msg) != STUN_BINDING) if (stun_get_method (msg) != STUN_BINDING)
{ {
DBG (" Bad request (method %u) message.\n", DBG (" Bad request (method %u) message.\n",
stun_get_method (msg)); stun_get_method (msg));
err (STUN_BAD_REQUEST); err (STUN_BAD_REQUEST);
return EPROTO; return EPROTO;
} }
/* Role conflict handling */ /* Role conflict handling */
assert (control != NULL); assert (control != NULL);
if (!stun_find64 (msg, *control ? STUN_ICE_CONTROLLING if (!stun_find64 (msg, *control ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, &q)) : STUN_ICE_CONTROLLED, &q))
{ {
DBG ("STUN Role Conflict detected:\n"); DBG ("STUN Role Conflict detected:\n");
if (tie < q) if (tie < q)
{ {
DBG (" switching role from \"controll%s\" to \"controll%s\"\n", DBG (" switching role from \"controll%s\" to \"controll%s\"\n",
*control ? "ing" : "ed", *control ? "ed" : "ing"); *control ? "ing" : "ed", *control ? "ed" : "ing");
*control = !*control; *control = !*control;
ret = EACCES; ret = EACCES;
} }
else else
{ {
DBG (" staying \"controll%s\" (sending error)\n", DBG (" staying \"controll%s\" (sending error)\n",
*control ? "ing" : "ed"); *control ? "ing" : "ed");
*plen = len; *plen = len;
err (STUN_ROLE_CONFLICT); err (STUN_ROLE_CONFLICT);
return 0; return 0;
} }
} }
#ifndef NDEBUG #ifndef NDEBUG
else else
if (stun_find64 (msg, *control ? STUN_ICE_CONTROLLED if (stun_find64 (msg, *control ? STUN_ICE_CONTROLLED
: STUN_ICE_CONTROLLING, &q)) : STUN_ICE_CONTROLLING, &q))
DBG ("STUN Role not specified by peer!\n"); DBG ("STUN Role not specified by peer!\n");
#endif #endif
stun_init_response (buf, len, msg); stun_init_response (buf, len, msg);
val = stun_append_xor_addr (buf, len, STUN_XOR_MAPPED_ADDRESS, val = stun_append_xor_addr (buf, len, STUN_XOR_MAPPED_ADDRESS,
src, srclen); src, srclen);
if (val) if (val)
{ {
DBG (" Mapped address problem: %s\n", strerror (val)); DBG (" Mapped address problem: %s\n", strerror (val));
goto failure; goto failure;
} }
val = stun_finish_short (buf, &len, NULL, pass, NULL); val = stun_finish_short (buf, &len, NULL, pass, NULL);
if (val) if (val)
goto failure; goto failure;
*plen = len; *plen = len;
DBG (" All done (response size: %u)\n", (unsigned)len); DBG (" All done (response size: %u)\n", (unsigned)len);
return ret; return ret;
failure: failure:
assert (*plen == 0); assert (*plen == 0);
DBG (" Fatal error formatting Response: %s\n", strerror (val)); DBG (" Fatal error formatting Response: %s\n", strerror (val));
return val; return val;
} }
#undef err #undef err
...@@ -207,38 +207,38 @@ failure: ...@@ -207,38 +207,38 @@ failure:
char *stun_conncheck_username (const uint8_t *restrict msg, char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen) char *restrict buf, size_t buflen)
{ {
size_t i; size_t i;
if ((buflen == 0) if ((buflen == 0)
|| stun_find_string (msg, STUN_USERNAME, buf, (buflen - 1) / 6)) || stun_find_string (msg, STUN_USERNAME, buf, (buflen - 1) / 6))
return NULL; return NULL;
for (i = 0; buf[i]; i++) for (i = 0; buf[i]; i++)
{ {
char c = buf[i]; char c = buf[i];
/* ref ICE sect 7.1.1.4. (ID-16) */ /* ref ICE sect 7.1.1.4. (ID-16) */
if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z'))
|| ((c >= 'a') && (c <= 'z')) || (c == '+') || (c == ':')) || ((c >= 'a') && (c <= 'z')) || (c == '+') || (c == ':'))
continue; continue;
return NULL; return NULL;
} }
return buf; return buf;
} }
uint32_t stun_conncheck_priority (const uint8_t *msg) uint32_t stun_conncheck_priority (const uint8_t *msg)
{ {
uint32_t value; uint32_t value;
if (stun_find32 (msg, STUN_PRIORITY, &value)) if (stun_find32 (msg, STUN_PRIORITY, &value))
return 0; return 0;
return value; return value;
} }
bool stun_conncheck_use_candidate (const uint8_t *msg) bool stun_conncheck_use_candidate (const uint8_t *msg)
{ {
return !stun_find_flag (msg, STUN_USE_CANDIDATE); return !stun_find_flag (msg, STUN_USE_CANDIDATE);
} }
...@@ -49,19 +49,19 @@ ...@@ -49,19 +49,19 @@
# include <stdarg.h> # include <stdarg.h>
static inline void DBG (const char *fmt, ...) static inline void DBG (const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start (ap, fmt); va_start (ap, fmt);
vfprintf (stderr, fmt, ap); vfprintf (stderr, fmt, ap);
va_end (ap); va_end (ap);
} }
static inline void DBG_bytes (const void *data, size_t len) static inline void DBG_bytes (const void *data, size_t len)
{ {
size_t i; size_t i;
DBG ("0x"); DBG ("0x");
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
DBG ("%02x", ((const unsigned char *)data)[i]); DBG ("%02x", ((const unsigned char *)data)[i]);
} }
# else # else
# define DBG( ... ) (void)0 # define DBG( ... ) (void)0
...@@ -80,10 +80,10 @@ static inline void DBG_bytes (const void *data, size_t len) ...@@ -80,10 +80,10 @@ static inline void DBG_bytes (const void *data, size_t len)
typedef struct stun_hdr_s typedef struct stun_hdr_s
{ {
uint16_t msg_type; uint16_t msg_type;
uint16_t msg_len; uint16_t msg_len;
uint32_t msg_cookie; uint32_t msg_cookie;
uint32_t msg_id[3]; uint32_t msg_id[3];
} stun_hdr_t; } stun_hdr_t;
...@@ -92,23 +92,23 @@ typedef uint8_t stun_msg_t[STUN_MAXMSG]; ...@@ -92,23 +92,23 @@ typedef uint8_t stun_msg_t[STUN_MAXMSG];
/* Message classes */ /* Message classes */
typedef enum typedef enum
{ {
STUN_REQUEST=0, STUN_REQUEST=0,
STUN_INDICATION=1, STUN_INDICATION=1,
STUN_RESPONSE=2, STUN_RESPONSE=2,
STUN_ERROR=3 STUN_ERROR=3
} stun_class_t; } stun_class_t;
/* Message methods */ /* Message methods */
typedef enum typedef enum
{ {
STUN_BINDING=0x001, /* RFC3489bis-08 */ STUN_BINDING=0x001, /* RFC3489bis-08 */
STUN_OLD_SHARED_SECRET=0x002, /* old RFC3489 */ STUN_OLD_SHARED_SECRET=0x002, /* old RFC3489 */
STUN_ALLOCATE=0x003, /* TURN-04 */ STUN_ALLOCATE=0x003, /* TURN-04 */
STUN_SET_ACTIVE_DST=0x004, /* TURN-04 */ STUN_SET_ACTIVE_DST=0x004, /* TURN-04 */
STUN_CONNECT=0x005, /* TURN-04 */ STUN_CONNECT=0x005, /* TURN-04 */
STUN_IND_SEND=0x006, /* TURN-04 */ STUN_IND_SEND=0x006, /* TURN-04 */
STUN_IND_DATA=0x007, /* TURN-04 */ STUN_IND_DATA=0x007, /* TURN-04 */
STUN_IND_CONNECT_STATUS=0x008 /* TURN-04 */ STUN_IND_CONNECT_STATUS=0x008 /* TURN-04 */
} stun_method_t; } stun_method_t;
/** /**
...@@ -117,65 +117,65 @@ typedef enum ...@@ -117,65 +117,65 @@ typedef enum
*/ */
typedef enum typedef enum
{ {
/* Mandatory attributes */ /* Mandatory attributes */
/* 0x0000 */ /* reserved */ /* 0x0000 */ /* reserved */
STUN_MAPPED_ADDRESS=0x0001, /* RFC3489bis-08 */ STUN_MAPPED_ADDRESS=0x0001, /* RFC3489bis-08 */
STUN_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */ STUN_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */
STUN_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */ STUN_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */
STUN_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */ STUN_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */
STUN_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */ STUN_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */
STUN_USERNAME=0x0006, /* RFC3489bis-08 */ STUN_USERNAME=0x0006, /* RFC3489bis-08 */
STUN_OLD_PASSWORD=0x0007, /* old RFC3489 */ STUN_OLD_PASSWORD=0x0007, /* old RFC3489 */
STUN_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-08 */ STUN_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-08 */
STUN_ERROR_CODE=0x0009, /* RFC3489bis-08 */ STUN_ERROR_CODE=0x0009, /* RFC3489bis-08 */
STUN_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-08 */ STUN_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-08 */
STUN_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */ STUN_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */
/* 0x000C */ /* reserved */ /* 0x000C */ /* reserved */
STUN_LIFETIME=0x000D, /* TURN-04 */ STUN_LIFETIME=0x000D, /* TURN-04 */
/* 0x000E */ /* reserved */ /* 0x000E */ /* reserved */
/* 0x000F */ /* reserved */ /* 0x000F */ /* reserved */
STUN_BANDWIDTH=0x0010, /* TURN-04 */ STUN_BANDWIDTH=0x0010, /* TURN-04 */
/* 0x0011 */ /* reserved */ /* 0x0011 */ /* reserved */
STUN_REMOTE_ADDRESS=0x0012, /* TURN-04 */ STUN_REMOTE_ADDRESS=0x0012, /* TURN-04 */
STUN_DATA=0x0013, /* TURN-04 */ STUN_DATA=0x0013, /* TURN-04 */
STUN_REALM=0x0014, /* RFC3489bis-08 */ STUN_REALM=0x0014, /* RFC3489bis-08 */
STUN_NONCE=0x0015, /* RFC3489bis-08 */ STUN_NONCE=0x0015, /* RFC3489bis-08 */
STUN_RELAY_ADDRESS=0x0016, /* TURN-04 */ STUN_RELAY_ADDRESS=0x0016, /* TURN-04 */
STUN_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */ STUN_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */
STUN_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */ STUN_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */
STUN_REQUESTED_TRANSPORT=0x0019, /* TURN-04 */ STUN_REQUESTED_TRANSPORT=0x0019, /* TURN-04 */
/* 0x001A */ /* reserved */ /* 0x001A */ /* reserved */
/* 0x001B */ /* reserved */ /* 0x001B */ /* reserved */
/* 0x001C */ /* reserved */ /* 0x001C */ /* reserved */
/* 0x001D */ /* reserved */ /* 0x001D */ /* reserved */
/* 0x001E */ /* reserved */ /* 0x001E */ /* reserved */
/* 0x001F */ /* reserved */ /* 0x001F */ /* reserved */
STUN_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-08 */ STUN_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-08 */
STUN_TIMER_VAL=0x0021, /* TURN-04 */ STUN_TIMER_VAL=0x0021, /* TURN-04 */
STUN_REQUESTED_IP=0x0022, /* TURN-04 */ STUN_REQUESTED_IP=0x0022, /* TURN-04 */
STUN_CONNECT_STAT=0x0023, /* TURN-04 */ STUN_CONNECT_STAT=0x0023, /* TURN-04 */
STUN_PRIORITY=0x0024, /* ICE-17 */ STUN_PRIORITY=0x0024, /* ICE-17 */
STUN_USE_CANDIDATE=0x0025, /* ICE-17 */ STUN_USE_CANDIDATE=0x0025, /* ICE-17 */
/* 0x0026-0x7fff */ /* reserved */ /* 0x0026-0x7fff */ /* reserved */
/* Optional attributes */ /* Optional attributes */
/* 0x8000-0x8021 */ /* reserved */ /* 0x8000-0x8021 */ /* reserved */
STUN_SERVER=0x8022, /* RFC3489bis-08 */ STUN_SERVER=0x8022, /* RFC3489bis-08 */
STUN_ALTERNATE_SERVER=0x8023, /* RFC3489bis-08 */ STUN_ALTERNATE_SERVER=0x8023, /* RFC3489bis-08 */
STUN_REFRESH_INTERVAL=0x8024, /* RFC3489bis-08 */ STUN_REFRESH_INTERVAL=0x8024, /* RFC3489bis-08 */
/* 0x8025 */ /* reserved */ /* 0x8025 */ /* reserved */
/* 0x8026 */ /* reserved */ /* 0x8026 */ /* reserved */
/* 0x8027 */ /* reserved */ /* 0x8027 */ /* reserved */
STUN_FINGERPRINT=0x8028, /* RFC3489bis-08 */ STUN_FINGERPRINT=0x8028, /* RFC3489bis-08 */
STUN_ICE_CONTROLLED=0x8029, /* ICE-17 */ STUN_ICE_CONTROLLED=0x8029, /* ICE-17 */
STUN_ICE_CONTROLLING=0x802A, /* ICE-17 */ STUN_ICE_CONTROLLING=0x802A, /* ICE-17 */
/* 0x802B-0xFFFF */ /* reserved */ /* 0x802B-0xFFFF */ /* reserved */
} stun_attr_type_t; } stun_attr_type_t;
static inline bool stun_optional (uint16_t t) static inline bool stun_optional (uint16_t t)
{ {
return (t >> 15) == 1; return (t >> 15) == 1;
} }
typedef uint8_t stun_transid_t[12]; typedef uint8_t stun_transid_t[12];
...@@ -186,24 +186,24 @@ typedef uint8_t stun_transid_t[12]; ...@@ -186,24 +186,24 @@ typedef uint8_t stun_transid_t[12];
*/ */
typedef enum typedef enum
{ {
STUN_TRY_ALTERNATE=300, /* RFC3489bis-08 */ STUN_TRY_ALTERNATE=300, /* RFC3489bis-08 */
STUN_BAD_REQUEST=400, /* RFC3489bis-08 */ STUN_BAD_REQUEST=400, /* RFC3489bis-08 */
STUN_UNAUTHORIZED=401, /* RFC3489bis-08 */ STUN_UNAUTHORIZED=401, /* RFC3489bis-08 */
STUN_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-08 */ STUN_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-08 */
STUN_NO_BINDING=437, /* TURN-04 */ STUN_NO_BINDING=437, /* TURN-04 */
STUN_STALE_NONCE=438, /* RFC3489bis-08 */ STUN_STALE_NONCE=438, /* RFC3489bis-08 */
STUN_ACT_DST_ALREADY=439, /* TURN-04 */ STUN_ACT_DST_ALREADY=439, /* TURN-04 */
STUN_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */ STUN_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */
STUN_UNSUPP_TRANSPORT=442, /* TURN-04 */ STUN_UNSUPP_TRANSPORT=442, /* TURN-04 */
STUN_INVALID_IP=443, /* TURN-04 */ STUN_INVALID_IP=443, /* TURN-04 */
STUN_INVALID_PORT=444, /* TURN-04 */ STUN_INVALID_PORT=444, /* TURN-04 */
STUN_OP_TCP_ONLY=445, /* TURN-04 */ STUN_OP_TCP_ONLY=445, /* TURN-04 */
STUN_CONN_ALREADY=446, /* TURN-04 */ STUN_CONN_ALREADY=446, /* TURN-04 */
STUN_ALLOC_OVER_QUOTA=486, /* TURN-04 */ STUN_ALLOC_OVER_QUOTA=486, /* TURN-04 */
STUN_ROLE_CONFLICT=487, /* ICE-17 */ STUN_ROLE_CONFLICT=487, /* ICE-17 */
STUN_SERVER_ERROR=500, /* RFC3489bis-08 */ STUN_SERVER_ERROR=500, /* RFC3489bis-08 */
STUN_SERVER_CAPACITY=507, /* TURN-04 */ STUN_SERVER_CAPACITY=507, /* TURN-04 */
STUN_ERROR_MAX=699 STUN_ERROR_MAX=699
} stun_error_t; } stun_error_t;
...@@ -212,7 +212,7 @@ typedef enum ...@@ -212,7 +212,7 @@ typedef enum
*/ */
static inline size_t stun_padding (size_t l) static inline size_t stun_padding (size_t l)
{ {
return (4 - (l & 3)) & 3; return (4 - (l & 3)) & 3;
} }
...@@ -221,7 +221,7 @@ static inline size_t stun_padding (size_t l) ...@@ -221,7 +221,7 @@ static inline size_t stun_padding (size_t l)
*/ */
static inline size_t stun_align (size_t l) static inline size_t stun_align (size_t l)
{ {
return (l + 3) & ~3; return (l + 3) & ~3;
} }
...@@ -231,12 +231,12 @@ static inline size_t stun_align (size_t l) ...@@ -231,12 +231,12 @@ static inline size_t stun_align (size_t l)
*/ */
static inline uint16_t stun_getw (const uint8_t *ptr) static inline uint16_t stun_getw (const uint8_t *ptr)
{ {
return ((ptr)[0] << 8) | ptr[1]; return ((ptr)[0] << 8) | ptr[1];
} }
static inline uint16_t stun_length (const uint8_t *ptr) static inline uint16_t stun_length (const uint8_t *ptr)
{ {
return stun_getw (ptr + 2); return stun_getw (ptr + 2);
} }
...@@ -245,8 +245,8 @@ static inline uint16_t stun_length (const uint8_t *ptr) ...@@ -245,8 +245,8 @@ static inline uint16_t stun_length (const uint8_t *ptr)
*/ */
static inline stun_class_t stun_get_class (const uint8_t *msg) static inline stun_class_t stun_get_class (const uint8_t *msg)
{ {
uint16_t t = stun_getw (msg); uint16_t t = stun_getw (msg);
return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4)); return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
} }
/** /**
...@@ -254,9 +254,9 @@ static inline stun_class_t stun_get_class (const uint8_t *msg) ...@@ -254,9 +254,9 @@ static inline stun_class_t stun_get_class (const uint8_t *msg)
*/ */
static inline stun_method_t stun_get_method (const uint8_t *msg) static inline stun_method_t stun_get_method (const uint8_t *msg)
{ {
uint16_t t = stun_getw (msg); uint16_t t = stun_getw (msg);
return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) | return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
(t & 0x000f)); (t & 0x000f));
} }
bool stun_has_cookie (const uint8_t *msg); bool stun_has_cookie (const uint8_t *msg);
...@@ -267,8 +267,8 @@ bool stun_has_cookie (const uint8_t *msg); ...@@ -267,8 +267,8 @@ bool stun_has_cookie (const uint8_t *msg);
*/ */
static inline const uint8_t *stun_id (const uint8_t *msg) static inline const uint8_t *stun_id (const uint8_t *msg)
{ {
//assert (stun_valid (req)); //assert (stun_valid (req));
return msg + 8; return msg + 8;
} }
...@@ -382,8 +382,8 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -382,8 +382,8 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
*/ */
static inline bool stun_present (const uint8_t *msg, stun_attr_type_t type) static inline bool stun_present (const uint8_t *msg, stun_attr_type_t type)
{ {
uint16_t dummy; uint16_t dummy;
return stun_find (msg, type, &dummy) != NULL; return stun_find (msg, type, &dummy) != NULL;
} }
...@@ -678,8 +678,8 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize, ...@@ -678,8 +678,8 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
*/ */
static inline bool stun_has_unknown (const void *msg) static inline bool stun_has_unknown (const void *msg)
{ {
uint16_t dummy; uint16_t dummy;
return stun_find_unknown (msg, &dummy, 1); return stun_find_unknown (msg, &dummy, 1);
} }
...@@ -695,8 +695,8 @@ static inline bool stun_has_unknown (const void *msg) ...@@ -695,8 +695,8 @@ static inline bool stun_has_unknown (const void *msg)
*/ */
static inline bool stun_valid (const uint8_t *msg) static inline bool stun_valid (const uint8_t *msg)
{ {
size_t length = 20u + stun_length (msg); size_t length = 20u + stun_length (msg);
return stun_validate (msg, length) == (ssize_t)length; return stun_validate (msg, length) == (ssize_t)length;
} }
# endif # endif
......
...@@ -53,120 +53,120 @@ ...@@ -53,120 +53,120 @@
static void static void
printaddr (const char *str, const struct sockaddr *addr, socklen_t addrlen) printaddr (const char *str, const struct sockaddr *addr, socklen_t addrlen)
{ {
char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
int val = getnameinfo (addr, addrlen, hostbuf, sizeof (hostbuf), int val = getnameinfo (addr, addrlen, hostbuf, sizeof (hostbuf),
servbuf, sizeof (servbuf), servbuf, sizeof (servbuf),
NI_NUMERICHOST | NI_NUMERICSERV); NI_NUMERICHOST | NI_NUMERICSERV);
if (val) if (val)
printf ("%s: %s\n", str, gai_strerror (val)); printf ("%s: %s\n", str, gai_strerror (val));
else else
printf ("%s: %s port %s\n", str, hostbuf, servbuf); printf ("%s: %s port %s\n", str, hostbuf, servbuf);
} }
static int run (int family, const char *hostname, const char *service) static int run (int family, const char *hostname, const char *service)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
const struct addrinfo *ptr; const struct addrinfo *ptr;
int ret = -1; int ret = -1;
memset (&hints, 0, sizeof (hints)); memset (&hints, 0, sizeof (hints));
hints.ai_family = family; hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
if (service == NULL) if (service == NULL)
service = "3478"; service = "3478";
ret = getaddrinfo (hostname, service, &hints, &res); ret = getaddrinfo (hostname, service, &hints, &res);
if (ret) if (ret)
{ {
fprintf (stderr, "%s (port %s): %s\n", hostname, service, fprintf (stderr, "%s (port %s): %s\n", hostname, service,
gai_strerror (ret)); gai_strerror (ret));
return -1; return -1;
} }
for (ptr = res; ptr != NULL; ptr = ptr->ai_next) for (ptr = res; ptr != NULL; ptr = ptr->ai_next)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
int val; int val;
printaddr ("Server address", ptr->ai_addr, ptr->ai_addrlen); printaddr ("Server address", ptr->ai_addr, ptr->ai_addrlen);
val = stun_bind_run (-1, ptr->ai_addr, ptr->ai_addrlen, val = stun_bind_run (-1, ptr->ai_addr, ptr->ai_addrlen,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
if (val) if (val)
fprintf (stderr, "%s\n", strerror (val)); fprintf (stderr, "%s\n", strerror (val));
else else
{ {
printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen); printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen);
ret = 0; ret = 0;
} }
} }
freeaddrinfo (res); freeaddrinfo (res);
return ret; return ret;
} }
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
static const struct option opts[] = static const struct option opts[] =
{ {
{ "ipv4", no_argument, NULL, '4' }, { "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' }, { "ipv6", no_argument, NULL, '6' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
const char *server = NULL, *port = NULL; const char *server = NULL, *port = NULL;
int family = AF_UNSPEC; int family = AF_UNSPEC;
for (;;) for (;;)
{ {
int val = getopt_long (argc, argv, "46hV", opts, NULL); int val = getopt_long (argc, argv, "46hV", opts, NULL);
if (val == EOF) if (val == EOF)
break; break;
switch (val) switch (val)
{ {
case '4': case '4':
family = AF_INET; family = AF_INET;
break; break;
case '6': case '6':
family = AF_INET6; family = AF_INET6;
break; break;
case 'h': case 'h':
printf ("Usage: %s [-4|-6] <server> [port number]\n" printf ("Usage: %s [-4|-6] <server> [port number]\n"
"Performs STUN Binding Discovery\n" "Performs STUN Binding Discovery\n"
"\n" "\n"
" -4, --ipv4 Force IP version 4\n" " -4, --ipv4 Force IP version 4\n"
" -6, --ipv6 Force IP version 6\n" " -6, --ipv6 Force IP version 6\n"
"\n", argv[0]); "\n", argv[0]);
return 0; return 0;
case 'V': case 'V':
printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n", printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n",
PACKAGE, VERSION); PACKAGE, VERSION);
return 0; return 0;
default: default:
return 2; return 2;
} }
} }
if (optind < argc) if (optind < argc)
server = argv[optind++]; server = argv[optind++];
if (optind < argc) if (optind < argc)
port = argv[optind++]; port = argv[optind++];
if (optind < argc) if (optind < argc)
{ {
fprintf (stderr, "%s: extra parameter `%s'\n", argv[0], argv[optind]); fprintf (stderr, "%s: extra parameter `%s'\n", argv[0], argv[optind]);
return 2; return 2;
} }
return run (family, server, port) ? 1 : 0; return run (family, server, port) ? 1 : 0;
} }
...@@ -70,77 +70,77 @@ ...@@ -70,77 +70,77 @@
*/ */
int listen_socket (int fam, int type, int proto, uint16_t port) int listen_socket (int fam, int type, int proto, uint16_t port)
{ {
int yes = 1; int yes = 1;
int fd = socket (fam, type, proto); int fd = socket (fam, type, proto);
if (fd == -1) if (fd == -1)
{ {
perror ("Error opening IP port"); perror ("Error opening IP port");
return -1; return -1;
} }
if (fd < 3) if (fd < 3)
goto error; goto error;
struct sockaddr_storage addr; struct sockaddr_storage addr;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.ss_family = fam; addr.ss_family = fam;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
addr.ss_len = sizeof (addr); addr.ss_len = sizeof (addr);
#endif #endif
switch (fam) switch (fam)
{ {
case AF_INET: case AF_INET:
((struct sockaddr_in *)&addr)->sin_port = port; ((struct sockaddr_in *)&addr)->sin_port = port;
break; break;
case AF_INET6: case AF_INET6:
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)); setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes));
#endif #endif
((struct sockaddr_in6 *)&addr)->sin6_port = port; ((struct sockaddr_in6 *)&addr)->sin6_port = port;
break; break;
} }
if (bind (fd, (struct sockaddr *)&addr, sizeof (addr))) if (bind (fd, (struct sockaddr *)&addr, sizeof (addr)))
{ {
perror ("Error opening IP port"); perror ("Error opening IP port");
goto error; goto error;
} }
if ((type == SOCK_DGRAM) || (type == SOCK_RAW)) if ((type == SOCK_DGRAM) || (type == SOCK_RAW))
{ {
switch (fam) switch (fam)
{ {
case AF_INET: case AF_INET:
setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes)); setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes));
#ifdef IP_RECVERR #ifdef IP_RECVERR
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes)); setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
#endif #endif
break; break;
case AF_INET6: case AF_INET6:
setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes, setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes,
sizeof (yes)); sizeof (yes));
#ifdef IPV6_RECVERR #ifdef IPV6_RECVERR
setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes)); setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
#endif #endif
break; break;
} }
} }
else else
{ {
if (listen (fd, INT_MAX)) if (listen (fd, INT_MAX))
{ {
perror ("Error opening IP port"); perror ("Error opening IP port");
goto error; goto error;
} }
} }
return fd; return fd;
error: error:
close (fd); close (fd);
return -1; return -1;
} }
...@@ -148,9 +148,9 @@ error: ...@@ -148,9 +148,9 @@ error:
static int recv_err (int fd) static int recv_err (int fd)
{ {
#ifdef MSG_ERRQUEUE #ifdef MSG_ERRQUEUE
struct msghdr hdr; struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr)); memset (&hdr, 0, sizeof (hdr));
return recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0; return recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0;
#endif #endif
} }
...@@ -158,104 +158,99 @@ static int recv_err (int fd) ...@@ -158,104 +158,99 @@ static int recv_err (int fd)
/** Receives a message or dequeues an error from a socket */ /** Receives a message or dequeues an error from a socket */
ssize_t recv_safe (int fd, struct msghdr *msg) ssize_t recv_safe (int fd, struct msghdr *msg)
{ {
ssize_t len = recvmsg (fd, msg, 0); ssize_t len = recvmsg (fd, msg, 0);
if (len == -1) if (len == -1)
recv_err (fd); recv_err (fd);
else else
if (msg->msg_flags & MSG_TRUNC) if (msg->msg_flags & MSG_TRUNC)
{ {
errno = EMSGSIZE; errno = EMSGSIZE;
return -1; return -1;
} }
return len; return len;
} }
/** Sends a message through a socket */ /** Sends a message through a socket */
ssize_t send_safe (int fd, const struct msghdr *msg) ssize_t send_safe (int fd, const struct msghdr *msg)
{ {
ssize_t len; ssize_t len;
do do
len = sendmsg (fd, msg, 0); len = sendmsg (fd, msg, 0);
while ((len == -1) && (recv_err (fd) == 0)); while ((len == -1) && (recv_err (fd) == 0));
return len; return len;
} }
static int dgram_process (int sock) static int dgram_process (int sock)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
uint8_t buf[STUN_MAXMSG]; uint8_t buf[STUN_MAXMSG];
char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))]; char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))];
struct iovec iov = { buf, sizeof (buf) }; struct iovec iov = { buf, sizeof (buf) };
struct msghdr mh = struct msghdr mh =
{ {
.msg_name = (struct sockaddr *)&addr, .msg_name = (struct sockaddr *)&addr,
.msg_namelen = sizeof (addr), .msg_namelen = sizeof (addr),
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,
.msg_control = ctlbuf, .msg_control = ctlbuf,
.msg_controllen = sizeof (ctlbuf) .msg_controllen = sizeof (ctlbuf)
}; };
size_t len = recv_safe (sock, &mh); size_t len = recv_safe (sock, &mh);
if (len == (size_t)-1) if (len == (size_t)-1)
return -1; return -1;
/* Mal-formatted packets */ /* Mal-formatted packets */
if ((stun_validate (buf, len) <= 0) if ((stun_validate (buf, len) <= 0)
|| (stun_get_class (buf) != STUN_REQUEST)) || (stun_get_class (buf) != STUN_REQUEST))
return -1; return -1;
/* Unknown attributes */ /* Unknown attributes */
if (stun_has_unknown (buf)) if (stun_has_unknown (buf))
{ {
stun_init_error_unknown (buf, sizeof (buf), buf); stun_init_error_unknown (buf, sizeof (buf), buf);
goto finish; goto finish;
} }
switch (stun_get_method (buf)) switch (stun_get_method (buf))
{ {
case STUN_BINDING: case STUN_BINDING:
stun_init_response (buf, sizeof (buf), buf); stun_init_response (buf, sizeof (buf), buf);
if (stun_has_cookie (buf)) if (stun_has_cookie (buf))
stun_append_xor_addr (buf, sizeof (buf), stun_append_xor_addr (buf, sizeof (buf),
STUN_XOR_MAPPED_ADDRESS, STUN_XOR_MAPPED_ADDRESS,
mh.msg_name, mh.msg_namelen); mh.msg_name, mh.msg_namelen);
else else
stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS, stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS,
mh.msg_name, mh.msg_namelen); mh.msg_name, mh.msg_namelen);
break; break;
case STUN_ALLOCATE: default:
case STUN_CONNECT: stun_init_error (buf, sizeof (buf), buf, STUN_BAD_REQUEST);
case STUN_SET_ACTIVE_DST: }
goto send;
default:
stun_init_error (buf, sizeof (buf), buf, STUN_BAD_REQUEST);
}
finish: finish:
stun_finish (buf, &iov.iov_len); stun_finish (buf, &iov.iov_len);
send: send:
len = send_safe (sock, &mh); len = send_safe (sock, &mh);
return (len < iov.iov_len) ? -1 : 0; return (len < iov.iov_len) ? -1 : 0;
} }
static int run (int family, int protocol, unsigned port) static int run (int family, int protocol, unsigned port)
{ {
int sock = listen_socket (family, SOCK_DGRAM, protocol, htons (port)); int sock = listen_socket (family, SOCK_DGRAM, protocol, htons (port));
if (sock == -1) if (sock == -1)
return -1; return -1;
for (;;) for (;;)
dgram_process (sock); dgram_process (sock);
} }
...@@ -263,38 +258,38 @@ static int run (int family, int protocol, unsigned port) ...@@ -263,38 +258,38 @@ static int run (int family, int protocol, unsigned port)
* But calling exit() is needed for gcov to work properly. */ * But calling exit() is needed for gcov to work properly. */
static void exit_handler (int signum) static void exit_handler (int signum)
{ {
(void)signum; (void)signum;
exit (0); exit (0);
} }
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
int family = AF_INET; int family = AF_INET;
unsigned port = IPPORT_STUN; unsigned port = IPPORT_STUN;
for (;;) for (;;)
{ {
int c = getopt (argc, argv, "46"); int c = getopt (argc, argv, "46");
if (c == EOF) if (c == EOF)
break; break;
switch (c) switch (c)
{ {
case '4': case '4':
family = AF_INET; family = AF_INET;
break; break;
case '6': case '6':
family = AF_INET6; family = AF_INET6;
break; break;
} }
} }
if (optind < argc) if (optind < argc)
port = atoi (argv[optind++]); port = atoi (argv[optind++]);
signal (SIGINT, exit_handler); signal (SIGINT, exit_handler);
signal (SIGTERM, exit_handler); signal (SIGTERM, exit_handler);
return run (family, IPPROTO_UDP, port) ? EXIT_FAILURE : EXIT_SUCCESS; return run (family, IPPROTO_UDP, port) ? EXIT_FAILURE : EXIT_SUCCESS;
} }
...@@ -49,65 +49,65 @@ ...@@ -49,65 +49,65 @@
ssize_t stun_validate (const uint8_t *msg, size_t len) ssize_t stun_validate (const uint8_t *msg, size_t len)
{ {
size_t mlen; size_t mlen;
if (len < 1) if (len < 1)
{ {
DBG ("STUN error: No data!\n"); DBG ("STUN error: No data!\n");
return 0; return 0;
} }
if (msg[0] >> 6) if (msg[0] >> 6)
{ {
DBG ("STUN error: RTP or other non-protocol packet!\n"); DBG ("STUN error: RTP or other non-protocol packet!\n");
return -1; // RTP or other non-STUN packet return -1; // RTP or other non-STUN packet
} }
if (len < 4) if (len < 4)
{ {
DBG ("STUN error: Incomplete STUN message header!\n"); DBG ("STUN error: Incomplete STUN message header!\n");
return 0; return 0;
} }
mlen = 20u + stun_length (msg); mlen = 20u + stun_length (msg);
if (stun_padding (mlen)) if (stun_padding (mlen))
{ {
DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen); DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
return -1; // wrong padding return -1; // wrong padding
} }
if (len < mlen) if (len < mlen)
{ {
DBG ("STUN error: Incomplete message: %u of %u bytes!\n", DBG ("STUN error: Incomplete message: %u of %u bytes!\n",
(unsigned)len, (unsigned)mlen); (unsigned)len, (unsigned)mlen);
return 0; // partial message return 0; // partial message
} }
msg += 20; msg += 20;
len = mlen - 20; len = mlen - 20;
/* from then on, we know we have the entire packet in buffer */ /* from then on, we know we have the entire packet in buffer */
while (len > 0) while (len > 0)
{ {
size_t alen = stun_align (stun_length (msg)); size_t alen = stun_align (stun_length (msg));
/* thanks to padding check, if (end > msg) then there is not only one /* thanks to padding check, if (end > msg) then there is not only one
* but at least 4 bytes left */ * but at least 4 bytes left */
assert (len >= 4); assert (len >= 4);
len -= 4; len -= 4;
if (len < alen) if (len < alen)
{ {
DBG ("STUN error: %u instead of %u bytes for attribute!\n", DBG ("STUN error: %u instead of %u bytes for attribute!\n",
(unsigned)len, (unsigned)alen); (unsigned)len, (unsigned)alen);
return -1; // no room for attribute value + padding return -1; // no room for attribute value + padding
} }
len -= alen; len -= alen;
msg += 4 + alen; msg += 4 + alen;
} }
return mlen; return mlen;
} }
...@@ -115,62 +115,62 @@ const void * ...@@ -115,62 +115,62 @@ const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type, stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen) uint16_t *restrict palen)
{ {
size_t length = stun_length (msg); size_t length = stun_length (msg);
assert (stun_valid (msg)); assert (stun_valid (msg));
assert (palen != NULL); assert (palen != NULL);
msg += 20; msg += 20;
while (length > 0) while (length > 0)
{ {
size_t alen = stun_length (msg); size_t alen = stun_length (msg);
uint16_t atype = stun_getw (msg); uint16_t atype = stun_getw (msg);
assert (length >= 4); assert (length >= 4);
length -= 4; length -= 4;
msg += 4; msg += 4;
assert (length >= stun_align (alen)); assert (length >= stun_align (alen));
if (atype == type) if (atype == type)
{ {
assert (alen <= 0xffff); assert (alen <= 0xffff);
*palen = alen; *palen = alen;
return msg; return msg;
} }
/* Look for and ignore misordered attributes */ /* Look for and ignore misordered attributes */
switch (atype) switch (atype)
{ {
case STUN_MESSAGE_INTEGRITY: case STUN_MESSAGE_INTEGRITY:
/* Only fingerprint may come after M-I */ /* Only fingerprint may come after M-I */
if (type == STUN_FINGERPRINT) if (type == STUN_FINGERPRINT)
break; break;
case STUN_FINGERPRINT: case STUN_FINGERPRINT:
/* Nothing may come after FPR */ /* Nothing may come after FPR */
return NULL; return NULL;
} }
alen = stun_align (alen); alen = stun_align (alen);
length -= alen; length -= alen;
msg += alen; msg += alen;
} }
return NULL; return NULL;
} }
int stun_find_flag (const uint8_t *msg, stun_attr_type_t type) int stun_find_flag (const uint8_t *msg, stun_attr_type_t type)
{ {
const void *ptr; const void *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
return (len == 0) ? 0 : EINVAL; return (len == 0) ? 0 : EINVAL;
} }
...@@ -178,130 +178,130 @@ int ...@@ -178,130 +178,130 @@ int
stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type, stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type,
uint32_t *restrict pval) uint32_t *restrict pval)
{ {
const void *ptr; const void *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (len == 4) if (len == 4)
{ {
uint32_t val; uint32_t val;
memcpy (&val, ptr, sizeof (val)); memcpy (&val, ptr, sizeof (val));
*pval = ntohl (val); *pval = ntohl (val);
return 0; return 0;
} }
return EINVAL; return EINVAL;
} }
int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval) int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
{ {
const void *ptr; const void *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (len == 8) if (len == 8)
{ {
uint32_t tab[2]; uint32_t tab[2];
memcpy (tab, ptr, sizeof (tab)); memcpy (tab, ptr, sizeof (tab));
*pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]); *pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]);
return 0; return 0;
} }
return EINVAL; return EINVAL;
} }
int stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type, int stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t maxcp) char *buf, size_t maxcp)
{ {
const unsigned char *ptr; const unsigned char *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
/* UTF-8 validation */ /* UTF-8 validation */
while (len > 0) while (len > 0)
{ {
unsigned i; unsigned i;
unsigned char c = *ptr++; unsigned char c = *ptr++;
if (maxcp == 0) if (maxcp == 0)
return ENOBUFS; return ENOBUFS;
maxcp--; maxcp--;
if (c == 0) if (c == 0)
return EINVAL; /* unexpected nul byte */ return EINVAL; /* unexpected nul byte */
else else
if (c < 0x80) if (c < 0x80)
i = 0; /* 0x01-0x7F: ASCII code point */ i = 0; /* 0x01-0x7F: ASCII code point */
else else
if (c < 0xC2) if (c < 0xC2)
/* 0x80-0xBF: continuation byte */ /* 0x80-0xBF: continuation byte */
/* 0xC0-0xC1: overlongs */ /* 0xC0-0xC1: overlongs */
return EINVAL; return EINVAL;
else else
if (c < 0xE0) if (c < 0xE0)
i = 1; /* 0xC2-0xDF: two bytes code point */ i = 1; /* 0xC2-0xDF: two bytes code point */
else else
if (c < 0xF0) if (c < 0xF0)
{ {
if (*ptr < 0xA0) if (*ptr < 0xA0)
return EINVAL; /* overlong */ return EINVAL; /* overlong */
i = 2; /* 0xE0-0xEF: three bytes code point */ i = 2; /* 0xE0-0xEF: three bytes code point */
} }
else else
if (c < 0xF8) if (c < 0xF8)
{ {
if (*ptr < 0x90) if (*ptr < 0x90)
return EINVAL; /* overlong */ return EINVAL; /* overlong */
i = 3; /* 0xF0-0xF7: four bytes code point */ i = 3; /* 0xF0-0xF7: four bytes code point */
} }
else else
if (c < 0xFC) if (c < 0xFC)
{ {
if (*ptr < 0x88) if (*ptr < 0x88)
return EINVAL; /* overlong */ return EINVAL; /* overlong */
i = 4; /* 0xF8-0xFB: five bytes code point */ i = 4; /* 0xF8-0xFB: five bytes code point */
} }
else else
if (c < 0xFE) if (c < 0xFE)
{ {
if (*ptr < 0x84) if (*ptr < 0x84)
return EINVAL; /* overlong */ return EINVAL; /* overlong */
i = 5; i = 5;
} }
else else
return EINVAL; /* 0xFE-0xFF: Byte-Order-Mark */ return EINVAL; /* 0xFE-0xFF: Byte-Order-Mark */
*buf++ = c; *buf++ = c;
len--; len--;
if (i > len) if (i > len)
return EINVAL; /* too short */ return EINVAL; /* too short */
while (i > 0) while (i > 0)
{ {
c = *ptr++; c = *ptr++;
len--; len--;
if ((c & 0xC0) != 0x80) if ((c & 0xC0) != 0x80)
return EINVAL; return EINVAL;
*buf++ = c; *buf++ = c;
} }
} }
*buf = '\0'; *buf = '\0';
return 0; return 0;
} }
...@@ -309,95 +309,95 @@ int ...@@ -309,95 +309,95 @@ int
stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type, stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr, socklen_t *restrict addrlen) struct sockaddr *restrict addr, socklen_t *restrict addrlen)
{ {
const uint8_t *ptr; const uint8_t *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (len < 4) if (len < 4)
return EINVAL; return EINVAL;
assert (addrlen != NULL); assert (addrlen != NULL);
switch (ptr[1]) switch (ptr[1])
{ {
case 1: case 1:
{ {
struct sockaddr_in *ip4 = (struct sockaddr_in *)addr; struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
if ((*addrlen < sizeof (*ip4)) || (len != 8)) if ((*addrlen < sizeof (*ip4)) || (len != 8))
{ {
*addrlen = sizeof (*ip4); *addrlen = sizeof (*ip4);
return EINVAL; return EINVAL;
} }
memset (ip4, 0, *addrlen); memset (ip4, 0, *addrlen);
ip4->sin_family = AF_INET; ip4->sin_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
ip4->sin_len = ip4->sin_len =
#endif #endif
*addrlen = sizeof (*ip4); *addrlen = sizeof (*ip4);
memcpy (&ip4->sin_port, ptr + 2, 2); memcpy (&ip4->sin_port, ptr + 2, 2);
memcpy (&ip4->sin_addr, ptr + 4, 4); memcpy (&ip4->sin_addr, ptr + 4, 4);
return 0; return 0;
} }
case 2: case 2:
{ {
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
if ((*addrlen < sizeof (*ip6)) || (len != 20)) if ((*addrlen < sizeof (*ip6)) || (len != 20))
{ {
*addrlen = sizeof (*ip6); *addrlen = sizeof (*ip6);
return EINVAL; return EINVAL;
} }
memset (ip6, 0, *addrlen); memset (ip6, 0, *addrlen);
ip6->sin6_family = AF_INET6; ip6->sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
ip6->sin6_len = ip6->sin6_len =
#endif #endif
*addrlen = sizeof (*ip6); *addrlen = sizeof (*ip6);
memcpy (&ip6->sin6_port, ptr + 2, 2); memcpy (&ip6->sin6_port, ptr + 2, 2);
memcpy (&ip6->sin6_addr, ptr + 4, 16); memcpy (&ip6->sin6_addr, ptr + 4, 16);
return 0; return 0;
} }
} }
return EAFNOSUPPORT; return EAFNOSUPPORT;
} }
int stun_xor_address (const uint8_t *restrict msg, int stun_xor_address (const uint8_t *restrict msg,
struct sockaddr *restrict addr, socklen_t addrlen) struct sockaddr *restrict addr, socklen_t addrlen)
{ {
switch (addr->sa_family) switch (addr->sa_family)
{ {
case AF_INET: case AF_INET:
{ {
struct sockaddr_in *ip4 = (struct sockaddr_in *)addr; struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
if (addrlen < sizeof (*ip4)) if (addrlen < sizeof (*ip4))
return EINVAL; return EINVAL;
ip4->sin_port ^= htons (STUN_COOKIE >> 16); ip4->sin_port ^= htons (STUN_COOKIE >> 16);
ip4->sin_addr.s_addr ^= htonl (STUN_COOKIE); ip4->sin_addr.s_addr ^= htonl (STUN_COOKIE);
return 0; return 0;
} }
case AF_INET6: case AF_INET6:
{ {
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
unsigned short i; unsigned short i;
if (addrlen < sizeof (*ip6)) if (addrlen < sizeof (*ip6))
return EINVAL; return EINVAL;
ip6->sin6_port ^= htons (STUN_COOKIE >> 16); ip6->sin6_port ^= htons (STUN_COOKIE >> 16);
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i]; ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i];
return 0; return 0;
} }
} }
return EAFNOSUPPORT; return EAFNOSUPPORT;
} }
...@@ -406,11 +406,11 @@ stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -406,11 +406,11 @@ stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr, struct sockaddr *restrict addr,
socklen_t *restrict addrlen) socklen_t *restrict addrlen)
{ {
int val = stun_find_addr (msg, type, addr, addrlen); int val = stun_find_addr (msg, type, addr, addrlen);
if (val) if (val)
return val; return val;
return stun_xor_address (msg, addr, *addrlen); return stun_xor_address (msg, addr, *addrlen);
} }
#if 0 #if 0
...@@ -427,14 +427,14 @@ stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -427,14 +427,14 @@ stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type,
int stun_memcmp (const uint8_t *restrict msg, stun_attr_type_t type, int stun_memcmp (const uint8_t *restrict msg, stun_attr_type_t type,
const void *data, size_t len) const void *data, size_t len)
{ {
uint16_t alen; uint16_t alen;
const void *ptr = stun_find (msg, type, &alen); const void *ptr = stun_find (msg, type, &alen);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if ((len != alen) || memcmp (ptr, data, len)) if ((len != alen) || memcmp (ptr, data, len))
return EINVAL; return EINVAL;
return 0; return 0;
} }
...@@ -449,52 +449,52 @@ int stun_memcmp (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -449,52 +449,52 @@ int stun_memcmp (const uint8_t *restrict msg, stun_attr_type_t type,
int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type, int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type,
const char *str) const char *str)
{ {
return stun_memcmp (msg, type, str, strlen (str)); return stun_memcmp (msg, type, str, strlen (str));
} }
#endif #endif
bool stun_has_cookie (const uint8_t *msg) bool stun_has_cookie (const uint8_t *msg)
{ {
uint32_t cookie = htonl (STUN_COOKIE); uint32_t cookie = htonl (STUN_COOKIE);
return memcmp (msg + 4, &cookie, sizeof (cookie)) == 0; return memcmp (msg + 4, &cookie, sizeof (cookie)) == 0;
} }
bool stun_demux (const uint8_t *msg) bool stun_demux (const uint8_t *msg)
{ {
const uint8_t *fpr; const uint8_t *fpr;
uint32_t crc32; uint32_t crc32;
uint16_t fprlen; uint16_t fprlen;
assert (stun_valid (msg)); assert (stun_valid (msg));
/* Checks cookie */ /* Checks cookie */
if (!stun_has_cookie (msg)) if (!stun_has_cookie (msg))
{ {
DBG ("STUN demux error: no cookie!\n"); DBG ("STUN demux error: no cookie!\n");
return 0; return 0;
} }
/* Looks for FINGERPRINT */ /* Looks for FINGERPRINT */
fpr = stun_find (msg, STUN_FINGERPRINT, &fprlen); fpr = stun_find (msg, STUN_FINGERPRINT, &fprlen);
if ((fpr == NULL) || (fprlen != 4)) if ((fpr == NULL) || (fprlen != 4))
{ {
DBG ("STUN demux error: no FINGERPRINT attribute!\n"); DBG ("STUN demux error: no FINGERPRINT attribute!\n");
return 0; return 0;
} }
/* Checks FINGERPRINT */ /* Checks FINGERPRINT */
crc32 = htonl (stun_fingerprint (msg, fpr + 4 - msg)); crc32 = htonl (stun_fingerprint (msg, fpr + 4 - msg));
if (memcmp (fpr, &crc32, 4)) if (memcmp (fpr, &crc32, 4))
{ {
DBG ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!\n", DBG ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!\n",
(fpr[0] << 24) | (fpr[1] << 16) | (fpr[2] << 8) | fpr[3], (fpr[0] << 24) | (fpr[1] << 16) | (fpr[2] << 8) | fpr[3],
stun_fingerprint (msg, fpr + 4 - msg)); stun_fingerprint (msg, fpr + 4 - msg));
return 0; return 0;
} }
DBG ("STUN demux: OK!\n"); DBG ("STUN demux: OK!\n");
return 1; return 1;
} }
...@@ -509,38 +509,38 @@ bool stun_demux (const uint8_t *msg) ...@@ -509,38 +509,38 @@ bool stun_demux (const uint8_t *msg)
int int
stun_verify_key (const uint8_t *msg, const void *key, size_t keylen) stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
{ {
const uint8_t *hash; const uint8_t *hash;
uint8_t sha[20]; uint8_t sha[20];
uint16_t hlen; uint16_t hlen;
assert (msg != NULL); assert (msg != NULL);
assert ((keylen == 0) || (key != NULL)); assert ((keylen == 0) || (key != NULL));
hash = stun_find (msg, STUN_MESSAGE_INTEGRITY, &hlen); hash = stun_find (msg, STUN_MESSAGE_INTEGRITY, &hlen);
if ((hash == NULL) || (hlen != 20)) if ((hash == NULL) || (hlen != 20))
{ {
DBG ("STUN auth error: no MESSAGE-INTEGRITY attribute!\n"); DBG ("STUN auth error: no MESSAGE-INTEGRITY attribute!\n");
return ENOENT; return ENOENT;
} }
stun_sha1 (msg, hash + 20 - msg, sha, key, keylen); stun_sha1 (msg, hash + 20 - msg, sha, key, keylen);
DBG (" Message HMAC-SHA1 fingerprint:" DBG (" Message HMAC-SHA1 fingerprint:"
"\n key : "); "\n key : ");
DBG_bytes (key, keylen); DBG_bytes (key, keylen);
DBG ("\n expected: "); DBG ("\n expected: ");
DBG_bytes (sha, sizeof (sha)); DBG_bytes (sha, sizeof (sha));
DBG ("\n received: "); DBG ("\n received: ");
DBG_bytes (hash, sizeof (sha)); DBG_bytes (hash, sizeof (sha));
DBG ("\n"); DBG ("\n");
if (memcmp (sha, hash, sizeof (sha))) if (memcmp (sha, hash, sizeof (sha)))
{ {
DBG ("STUN auth error: SHA1 fingerprint mismatch!\n"); DBG ("STUN auth error: SHA1 fingerprint mismatch!\n");
return EPERM; return EPERM;
} }
DBG ("STUN auth: OK!\n"); DBG ("STUN auth: OK!\n");
return 0; return 0;
} }
...@@ -553,29 +553,29 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen) ...@@ -553,29 +553,29 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
*/ */
int stun_verify_password (const uint8_t *msg, const char *pw) int stun_verify_password (const uint8_t *msg, const char *pw)
{ {
return stun_verify_key (msg, pw, strlen (pw)); return stun_verify_key (msg, pw, strlen (pw));
} }
static static
int stun_find_errno (const uint8_t *restrict msg, int *restrict code) int stun_find_errno (const uint8_t *restrict msg, int *restrict code)
{ {
uint16_t alen; uint16_t alen;
const uint8_t *ptr = stun_find (msg, STUN_ERROR_CODE, &alen); const uint8_t *ptr = stun_find (msg, STUN_ERROR_CODE, &alen);
uint8_t class, number; uint8_t class, number;
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (alen < 4) if (alen < 4)
return EINVAL; return EINVAL;
class = ptr[2] & 0x7; class = ptr[2] & 0x7;
number = ptr[3]; number = ptr[3];
if ((class < 3) || (class > 6) || (number > 99)) if ((class < 3) || (class > 6) || (number > 99))
return EINVAL; return EINVAL;
*code = (class * 100) + number; *code = (class * 100) + number;
return 0; return 0;
} }
...@@ -596,46 +596,46 @@ stun_match_answer (const uint8_t *msg, stun_method_t method, ...@@ -596,46 +596,46 @@ stun_match_answer (const uint8_t *msg, stun_method_t method,
const uint8_t *id, const uint8_t *key, size_t keylen, const uint8_t *id, const uint8_t *key, size_t keylen,
int *restrict error) int *restrict error)
{ {
assert (stun_valid (msg)); assert (stun_valid (msg));
assert (error != NULL); assert (error != NULL);
if ((stun_get_method (msg) != method) /* wrong request type */ if ((stun_get_method (msg) != method) /* wrong request type */
|| !stun_has_cookie (msg) /* response to old-style request */ || !stun_has_cookie (msg) /* response to old-style request */
|| memcmp (msg + 8, id, 12)) /* wrong transaction ID */ || memcmp (msg + 8, id, 12)) /* wrong transaction ID */
return false; return false;
switch (stun_get_class (msg)) switch (stun_get_class (msg))
{ {
case STUN_REQUEST: case STUN_REQUEST:
case STUN_INDICATION: case STUN_INDICATION:
return false; return false;
case STUN_RESPONSE: case STUN_RESPONSE:
*error = -1; *error = -1;
break; break;
case STUN_ERROR: case STUN_ERROR:
if (stun_find_errno (msg, error) != 0) if (stun_find_errno (msg, error) != 0)
return false; // missing ERROR-CODE: ignore message return false; // missing ERROR-CODE: ignore message
break; break;
} }
/* If a shared secret exists, verify the message hash. /* If a shared secret exists, verify the message hash.
* If there is no shared secret, verify there is no hash at all. */ * If there is no shared secret, verify there is no hash at all. */
if (key != NULL) if (key != NULL)
{ {
/* FIXME: 401 errors do not have MESSAGE-INTEGRITY, so that we /* FIXME: 401 errors do not have MESSAGE-INTEGRITY, so that we
* currently ignore them. */ * currently ignore them. */
if (stun_verify_key (msg, key, keylen) != 0) if (stun_verify_key (msg, key, keylen) != 0)
return false; return false;
} }
else else
{ {
if (stun_present (msg, STUN_MESSAGE_INTEGRITY)) if (stun_present (msg, STUN_MESSAGE_INTEGRITY))
return false; return false;
} }
return true; return true;
} }
...@@ -644,12 +644,12 @@ bool stun_match_messages (const uint8_t *restrict resp, ...@@ -644,12 +644,12 @@ bool stun_match_messages (const uint8_t *restrict resp,
const uint8_t *key, size_t keylen, const uint8_t *key, size_t keylen,
int *restrict error) int *restrict error)
{ {
assert (stun_valid (resp)); assert (stun_valid (resp));
assert (stun_valid (req)); assert (stun_valid (req));
assert ((stun_get_class (req) >> 1) == 0); assert ((stun_get_class (req) >> 1) == 0);
return stun_match_answer (resp, stun_get_method (req), return stun_match_answer (resp, stun_get_method (req),
stun_id (req), key, keylen, error); stun_id (req), key, keylen, error);
} }
...@@ -657,29 +657,29 @@ unsigned ...@@ -657,29 +657,29 @@ unsigned
stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list, stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
unsigned max) unsigned max)
{ {
unsigned count = 0; unsigned count = 0;
uint16_t len = stun_length (msg); uint16_t len = stun_length (msg);
assert (stun_valid (msg)); assert (stun_valid (msg));
msg += 20; msg += 20;
while ((len > 0) && (count < max)) while ((len > 0) && (count < max))
{ {
size_t alen = stun_align (stun_length (msg)); size_t alen = stun_align (stun_length (msg));
uint16_t atype = stun_getw (msg); uint16_t atype = stun_getw (msg);
msg += 4 + alen; msg += 4 + alen;
assert (len >= (4 + alen)); assert (len >= (4 + alen));
len -= 4 + alen; len -= 4 + alen;
if (!stun_optional (atype) && stun_is_unknown (atype)) if (!stun_optional (atype) && stun_is_unknown (atype))
{ {
DBG ("STUN unknown: attribute 0x%04x(%u bytes)\n", DBG ("STUN unknown: attribute 0x%04x(%u bytes)\n",
(unsigned)atype, (unsigned)alen); (unsigned)atype, (unsigned)alen);
list[count++] = atype; list[count++] = atype;
} }
} }
DBG ("STUN unknown: %u mandatory attribute(s)!\n", count); DBG ("STUN unknown: %u mandatory attribute(s)!\n", count);
return count; return count;
} }
...@@ -53,24 +53,24 @@ ...@@ -53,24 +53,24 @@
static inline static inline
void *stun_setw (uint8_t *ptr, uint16_t value) void *stun_setw (uint8_t *ptr, uint16_t value)
{ {
*ptr++ = value >> 8; *ptr++ = value >> 8;
*ptr++ = value & 0xff; *ptr++ = value & 0xff;
return ptr; return ptr;
} }
static inline static inline
void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m) void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
{ {
assert (c < 4); assert (c < 4);
assert (m < (1 << 12)); assert (m < (1 << 12));
h[0] = (c >> 1) | ((m >> 6) & 0x3e); h[0] = (c >> 1) | ((m >> 6) & 0x3e);
h[1] = ((c << 4) & 0x10) | ((m << 1) & 0xe0) | (m & 0x0f); h[1] = ((c << 4) & 0x10) | ((m << 1) & 0xe0) | (m & 0x0f);
assert (stun_getw (h) < (1 << 14)); assert (stun_getw (h) < (1 << 14));
assert (stun_get_class (h) == c); assert (stun_get_class (h) == c);
assert (stun_get_method (h) == m); assert (stun_get_method (h) == m);
} }
...@@ -83,34 +83,34 @@ void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m) ...@@ -83,34 +83,34 @@ void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m, static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m,
const stun_transid_t id) const stun_transid_t id)
{ {
memset (msg, 0, 4); memset (msg, 0, 4);
stun_set_type (msg, c, m); stun_set_type (msg, c, m);
msg += 8; msg += 8;
if (msg != id) if (msg != id)
{ {
uint32_t cookie = htonl (STUN_COOKIE); uint32_t cookie = htonl (STUN_COOKIE);
memcpy (msg - 4, &cookie, sizeof (cookie)); memcpy (msg - 4, &cookie, sizeof (cookie));
memcpy (msg, id, 12); memcpy (msg, id, 12);
} }
} }
void stun_init_request (uint8_t *req, stun_method_t m) void stun_init_request (uint8_t *req, stun_method_t m)
{ {
stun_transid_t id; stun_transid_t id;
stun_make_transid (id); stun_make_transid (id);
stun_init (req, STUN_REQUEST, m, id); stun_init (req, STUN_REQUEST, m, id);
} }
void stun_init_indication (uint8_t *req, stun_method_t m) void stun_init_indication (uint8_t *req, stun_method_t m)
{ {
stun_transid_t id; stun_transid_t id;
stun_make_transid (id); stun_make_transid (id);
stun_init (req, STUN_INDICATION, m, id); stun_init (req, STUN_INDICATION, m, id);
} }
...@@ -128,33 +128,33 @@ void stun_init_indication (uint8_t *req, stun_method_t m) ...@@ -128,33 +128,33 @@ void stun_init_indication (uint8_t *req, stun_method_t m)
void * void *
stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
{ {
uint8_t *a; uint8_t *a;
uint16_t mlen = stun_length (msg); uint16_t mlen = stun_length (msg);
assert (stun_valid (msg)); assert (stun_valid (msg));
assert (stun_padding (mlen) == 0); assert (stun_padding (mlen) == 0);
if (msize > STUN_MAXMSG) if (msize > STUN_MAXMSG)
msize = STUN_MAXMSG; msize = STUN_MAXMSG;
if ((((size_t)mlen) + 24u + length) > msize) if ((((size_t)mlen) + 24u + length) > msize)
return NULL; return NULL;
assert (length < 0xffff); assert (length < 0xffff);
a = msg + 20u + mlen; a = msg + 20u + mlen;
a = stun_setw (a, type); a = stun_setw (a, type);
/* NOTE: If cookie is not present, we need to force the attribute length /* NOTE: If cookie is not present, we need to force the attribute length
* to a multiple of 4 for compatibility with old RFC3489 */ * to a multiple of 4 for compatibility with old RFC3489 */
a = stun_setw (a, stun_has_cookie (msg) ? length : stun_align (length)); a = stun_setw (a, stun_has_cookie (msg) ? length : stun_align (length));
mlen += 4 + length; mlen += 4 + length;
/* Add padding if needed */ /* Add padding if needed */
memset (a + length, ' ', stun_padding (length)); memset (a + length, ' ', stun_padding (length));
mlen += stun_padding (length); mlen += stun_padding (length);
stun_setw (msg + 2, mlen); stun_setw (msg + 2, mlen);
return a; return a;
} }
...@@ -171,18 +171,18 @@ int ...@@ -171,18 +171,18 @@ int
stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const void *data, size_t len) const void *data, size_t len)
{ {
void *ptr = stun_append (msg, msize, type, len); void *ptr = stun_append (msg, msize, type, len);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
memcpy (ptr, data, len); memcpy (ptr, data, len);
return 0; return 0;
} }
int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type) int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type)
{ {
return stun_append_bytes (msg, msize, type, NULL, 0); return stun_append_bytes (msg, msize, type, NULL, 0);
} }
...@@ -190,18 +190,18 @@ int ...@@ -190,18 +190,18 @@ int
stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type, stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint32_t value) uint32_t value)
{ {
value = htonl (value); value = htonl (value);
return stun_append_bytes (msg, msize, type, &value, 4); return stun_append_bytes (msg, msize, type, &value, 4);
} }
int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type, int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint64_t value) uint64_t value)
{ {
uint32_t tab[2]; uint32_t tab[2];
tab[0] = htonl ((uint32_t)(value >> 32)); tab[0] = htonl ((uint32_t)(value >> 32));
tab[1] = htonl ((uint32_t)value); tab[1] = htonl ((uint32_t)value);
return stun_append_bytes (msg, msize, type, tab, 8); return stun_append_bytes (msg, msize, type, tab, 8);
} }
...@@ -209,29 +209,29 @@ int ...@@ -209,29 +209,29 @@ int
stun_append_string (uint8_t *restrict msg, size_t msize, stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str) stun_attr_type_t type, const char *str)
{ {
return stun_append_bytes (msg, msize, type, str, strlen (str)); return stun_append_bytes (msg, msize, type, str, strlen (str));
} }
static int stun_append_server (uint8_t *restrict msg, size_t msize) static int stun_append_server (uint8_t *restrict msg, size_t msize)
{ {
static const char server[] = PACKAGE_STRING; static const char server[] = PACKAGE_STRING;
assert (strlen (server) < 128); assert (strlen (server) < 128);
return stun_append_string (msg, msize, STUN_SERVER, server); return stun_append_string (msg, msize, STUN_SERVER, server);
} }
void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req) void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req)
{ {
assert (stun_valid (req)); assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST); assert (stun_get_class (req) == STUN_REQUEST);
assert (msize >= 20u); assert (msize >= 20u);
stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req)); stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req));
/* For RFC3489 compatibility, we cannot assume the cookie */ /* For RFC3489 compatibility, we cannot assume the cookie */
memcpy (ans + 4, req + 4, 4); memcpy (ans + 4, req + 4, 4);
(void)stun_append_server (ans, msize); (void)stun_append_server (ans, msize);
} }
...@@ -241,53 +241,53 @@ void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req) ...@@ -241,53 +241,53 @@ void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req)
*/ */
static const char *stun_strerror (stun_error_t code) static const char *stun_strerror (stun_error_t code)
{ {
static const struct static const struct
{ {
stun_error_t code; stun_error_t code;
char phrase[32]; char phrase[32];
} tab[] = } tab[] =
{ {
{ STUN_TRY_ALTERNATE, "Try alternate server" }, { STUN_TRY_ALTERNATE, "Try alternate server" },
{ STUN_BAD_REQUEST, "Bad request" }, { STUN_BAD_REQUEST, "Bad request" },
{ STUN_UNAUTHORIZED, "Authorization required" }, { STUN_UNAUTHORIZED, "Authorization required" },
{ STUN_UNKNOWN_ATTRIBUTE, "Unknown attribute" }, { STUN_UNKNOWN_ATTRIBUTE, "Unknown attribute" },
/* /*
{ STUN_STALE_CREDENTIALS, "Authentication expired" }, { STUN_STALE_CREDENTIALS, "Authentication expired" },
{ STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" }, { STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" },
{ STUN_MISSING_USERNAME, "Username required" }, { STUN_MISSING_USERNAME, "Username required" },
{ STUN_USE_TLS, "Secure connection required" }, { STUN_USE_TLS, "Secure connection required" },
{ STUN_MISSING_REALM, "Authentication domain required" }, { STUN_MISSING_REALM, "Authentication domain required" },
{ STUN_MISSING_NONCE, "Authentication token missing" }, { STUN_MISSING_NONCE, "Authentication token missing" },
{ STUN_UNKNOWN_USERNAME, "Unknown user name" }, { STUN_UNKNOWN_USERNAME, "Unknown user name" },
*/ */
{ STUN_NO_BINDING, "Session expired" }, { STUN_NO_BINDING, "Session expired" },
{ STUN_STALE_NONCE, "Authentication token expired" }, { STUN_STALE_NONCE, "Authentication token expired" },
{ STUN_ACT_DST_ALREADY, "Changing remote peer forbidden" }, { STUN_ACT_DST_ALREADY, "Changing remote peer forbidden" },
{ STUN_UNSUPP_TRANSPORT, "Unknown transport protocol" }, { STUN_UNSUPP_TRANSPORT, "Unknown transport protocol" },
{ STUN_INVALID_IP, "Address unavailable" }, { STUN_INVALID_IP, "Address unavailable" },
{ STUN_INVALID_PORT, "Port unavailable" }, { STUN_INVALID_PORT, "Port unavailable" },
{ STUN_OP_TCP_ONLY, "Invalid operation" }, { STUN_OP_TCP_ONLY, "Invalid operation" },
{ STUN_CONN_ALREADY, "Connection already established" }, { STUN_CONN_ALREADY, "Connection already established" },
{ STUN_ALLOC_OVER_QUOTA, "Quota reached" }, { STUN_ALLOC_OVER_QUOTA, "Quota reached" },
{ STUN_ROLE_CONFLICT, "Role conflict" }, { STUN_ROLE_CONFLICT, "Role conflict" },
{ STUN_SERVER_ERROR, "Temporary server error" }, { STUN_SERVER_ERROR, "Temporary server error" },
{ STUN_SERVER_CAPACITY, "Temporary server congestion" }, { STUN_SERVER_CAPACITY, "Temporary server congestion" },
}; };
const char *str = "Unknown error"; const char *str = "Unknown error";
size_t i; size_t i;
for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++) for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
{ {
if (tab[i].code == code) if (tab[i].code == code)
{ {
str = tab[i].phrase; str = tab[i].phrase;
break; break;
} }
} }
/* Maximum allowed error message length */ /* Maximum allowed error message length */
assert (strlen (str) < 128); assert (strlen (str) < 128);
return str; return str;
} }
...@@ -301,63 +301,63 @@ static const char *stun_strerror (stun_error_t code) ...@@ -301,63 +301,63 @@ static const char *stun_strerror (stun_error_t code)
static int static int
stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code) stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code)
{ {
const char *str = stun_strerror (code); const char *str = stun_strerror (code);
size_t len = strlen (str); size_t len = strlen (str);
div_t d = div (code, 100); div_t d = div (code, 100);
uint8_t *ptr = stun_append (msg, msize, STUN_ERROR_CODE, 4 + len); uint8_t *ptr = stun_append (msg, msize, STUN_ERROR_CODE, 4 + len);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
memset (ptr, 0, 2); memset (ptr, 0, 2);
assert (d.quot <= 0x7); assert (d.quot <= 0x7);
ptr[2] = d.quot; ptr[2] = d.quot;
ptr[3] = d.rem; ptr[3] = d.rem;
memcpy (ptr + 4, str, len); memcpy (ptr + 4, str, len);
return 0; return 0;
} }
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req, int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err) stun_error_t err)
{ {
assert (stun_valid (req)); assert (stun_valid (req));
assert (msize >= 20u); assert (msize >= 20u);
assert (stun_get_class (req) == STUN_REQUEST); assert (stun_get_class (req) == STUN_REQUEST);
stun_init (ans, STUN_ERROR, stun_get_method (req), stun_id (req)); stun_init (ans, STUN_ERROR, stun_get_method (req), stun_id (req));
/* For RFC3489 compatibility, we cannot assume the cookie */ /* For RFC3489 compatibility, we cannot assume the cookie */
memcpy (ans + 4, req + 4, 4); memcpy (ans + 4, req + 4, 4);
(void)stun_append_server (ans, msize); (void)stun_append_server (ans, msize);
return stun_append_error (ans, msize, err); return stun_append_error (ans, msize, err);
} }
int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req) int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
{ {
unsigned counter, i; unsigned counter, i;
#ifdef HAVE_C_VARARRAYS #ifdef HAVE_C_VARARRAYS
uint16_t ids[1 + (stun_length (req) / 4)]; uint16_t ids[1 + (stun_length (req) / 4)];
#else #else
uint16_t ids[256]; uint16_t ids[256];
#endif #endif
counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0])); counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0]));
assert (counter > 0); assert (counter > 0);
if (stun_init_error (ans, msize, req, STUN_UNKNOWN_ATTRIBUTE)) if (stun_init_error (ans, msize, req, STUN_UNKNOWN_ATTRIBUTE))
return ENOBUFS; return ENOBUFS;
for (i = 0; i < counter; i++) for (i = 0; i < counter; i++)
ids[i] = htons (ids[i]); ids[i] = htons (ids[i]);
/* NOTE: Old RFC3489 compatibility: /* NOTE: Old RFC3489 compatibility:
* When counter is odd, duplicate one value for 32-bits padding. */ * When counter is odd, duplicate one value for 32-bits padding. */
if (!stun_has_cookie (req) && (counter & 1)) if (!stun_has_cookie (req) && (counter & 1))
ids[counter++] = ids[0]; ids[counter++] = ids[0];
return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids, return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids,
counter * 2); counter * 2);
} }
...@@ -365,53 +365,53 @@ int ...@@ -365,53 +365,53 @@ int
stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const struct sockaddr *restrict addr, socklen_t addrlen) const struct sockaddr *restrict addr, socklen_t addrlen)
{ {
const void *pa; const void *pa;
uint8_t *ptr; uint8_t *ptr;
uint16_t alen, port; uint16_t alen, port;
uint8_t family; uint8_t family;
if (addrlen < sizeof (struct sockaddr)) if (addrlen < sizeof (struct sockaddr))
return EINVAL; return EINVAL;
switch (addr->sa_family) switch (addr->sa_family)
{ {
case AF_INET: case AF_INET:
{ {
const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr; const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
assert (addrlen >= sizeof (*ip4)); assert (addrlen >= sizeof (*ip4));
family = 1; family = 1;
port = ip4->sin_port; port = ip4->sin_port;
alen = 4; alen = 4;
pa = &ip4->sin_addr; pa = &ip4->sin_addr;
break; break;
} }
case AF_INET6: case AF_INET6:
{ {
const struct sockaddr_in6 *ip6 = (const struct sockaddr_in6 *)addr; const struct sockaddr_in6 *ip6 = (const struct sockaddr_in6 *)addr;
if (addrlen < sizeof (*ip6)) if (addrlen < sizeof (*ip6))
return EINVAL; return EINVAL;
family = 2; family = 2;
port = ip6->sin6_port; port = ip6->sin6_port;
alen = 16; alen = 16;
pa = &ip6->sin6_addr; pa = &ip6->sin6_addr;
break; break;
} }
default: default:
return EAFNOSUPPORT; return EAFNOSUPPORT;
} }
ptr = stun_append (msg, msize, type, 4 + alen); ptr = stun_append (msg, msize, type, 4 + alen);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
ptr[0] = 0; ptr[0] = 0;
ptr[1] = family; ptr[1] = family;
memcpy (ptr + 2, &port, 2); memcpy (ptr + 2, &port, 2);
memcpy (ptr + 4, pa, alen); memcpy (ptr + 4, pa, alen);
return 0; return 0;
} }
...@@ -420,20 +420,20 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize, ...@@ -420,20 +420,20 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
const struct sockaddr *restrict addr, const struct sockaddr *restrict addr,
socklen_t addrlen) socklen_t addrlen)
{ {
int val; int val;
/* Must be big enough to hold any supported address: */ /* Must be big enough to hold any supported address: */
struct sockaddr_storage xor; struct sockaddr_storage xor;
if (addrlen > sizeof (xor)) if (addrlen > sizeof (xor))
addrlen = sizeof (xor); addrlen = sizeof (xor);
memcpy (&xor, addr, addrlen); memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen); val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
if (val) if (val)
return val; return val;
return stun_append_addr (msg, msize, type, (struct sockaddr *)&xor, return stun_append_addr (msg, msize, type, (struct sockaddr *)&xor,
addrlen); addrlen);
} }
...@@ -442,63 +442,63 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen, ...@@ -442,63 +442,63 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
const char *realm, const char *username, const char *nonce, const char *realm, const char *username, const char *nonce,
const void *restrict key, size_t keylen) const void *restrict key, size_t keylen)
{ {
size_t len = *plen; size_t len = *plen;
uint8_t *ptr; uint8_t *ptr;
int val = ENOBUFS; int val = ENOBUFS;
uint32_t fpr; uint32_t fpr;
if (realm != NULL) if (realm != NULL)
{ {
val = stun_append_string (msg, len, STUN_REALM, realm); val = stun_append_string (msg, len, STUN_REALM, realm);
if (val) if (val)
return val; return val;
} }
if (username != NULL) if (username != NULL)
{ {
val = stun_append_string (msg, len, STUN_USERNAME, username); val = stun_append_string (msg, len, STUN_USERNAME, username);
if (val) if (val)
return val; return val;
} }
if (nonce != NULL) if (nonce != NULL)
{ {
val = stun_append_string (msg, len, STUN_NONCE, nonce); val = stun_append_string (msg, len, STUN_NONCE, nonce);
if (val) if (val)
return val; return val;
} }
if (key != NULL) if (key != NULL)
{ {
ptr = stun_append (msg, len, STUN_MESSAGE_INTEGRITY, 20); ptr = stun_append (msg, len, STUN_MESSAGE_INTEGRITY, 20);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
stun_sha1 (msg, ptr + 20 - msg, ptr, key, keylen); stun_sha1 (msg, ptr + 20 - msg, ptr, key, keylen);
DBG (" Message HMAC-SHA1 fingerprint:" DBG (" Message HMAC-SHA1 fingerprint:"
"\n key : "); "\n key : ");
DBG_bytes (key, keylen); DBG_bytes (key, keylen);
DBG ("\n sent : "); DBG ("\n sent : ");
DBG_bytes (ptr, 20); DBG_bytes (ptr, 20);
DBG ("\n"); DBG ("\n");
} }
/* /*
* NOTE: we always add a FINGERPRINT, even when it's not needed. * NOTE: we always add a FINGERPRINT, even when it's not needed.
* This is OK, as it is an optional attribute. It also makes my * This is OK, as it is an optional attribute. It also makes my
* software engineer's life easier. * software engineer's life easier.
*/ */
ptr = stun_append (msg, len, STUN_FINGERPRINT, 4); ptr = stun_append (msg, len, STUN_FINGERPRINT, 4);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
*plen = ptr + 4 -msg; *plen = ptr + 4 -msg;
fpr = htonl (stun_fingerprint (msg, *plen)); fpr = htonl (stun_fingerprint (msg, *plen));
memcpy (ptr, &fpr, sizeof (fpr)); memcpy (ptr, &fpr, sizeof (fpr));
return 0; return 0;
} }
...@@ -506,12 +506,12 @@ size_t stun_finish_short (uint8_t *msg, size_t *restrict plen, ...@@ -506,12 +506,12 @@ size_t stun_finish_short (uint8_t *msg, size_t *restrict plen,
const char *username, const char *restrict password, const char *username, const char *restrict password,
const char *nonce) const char *nonce)
{ {
return stun_finish_long (msg, plen, NULL, username, nonce, return stun_finish_long (msg, plen, NULL, username, nonce,
password, password ? strlen (password) : 0); password, password ? strlen (password) : 0);
} }
size_t stun_finish (uint8_t *msg, size_t *restrict plen) size_t stun_finish (uint8_t *msg, size_t *restrict plen)
{ {
return stun_finish_short (msg, plen, NULL, NULL, NULL); return stun_finish_short (msg, plen, NULL, NULL, NULL);
} }
...@@ -55,352 +55,352 @@ ...@@ -55,352 +55,352 @@
static int listen_dgram (void) static int listen_dgram (void)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
int val = -1; int val = -1;
memset (&hints, 0, sizeof (hints)); memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo (NULL, "0", &hints, &res)) if (getaddrinfo (NULL, "0", &hints, &res))
return -1; return -1;
for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next) for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
{ {
int fd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); int fd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (fd == -1) if (fd == -1)
continue; continue;
if (bind (fd, ptr->ai_addr, ptr->ai_addrlen)) if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
{ {
close (fd); close (fd);
continue; continue;
} }
val = fd; val = fd;
break; break;
} }
freeaddrinfo (res); freeaddrinfo (res);
return val; return val;
} }
/** Incorrect socket family test */ /** Incorrect socket family test */
static void bad_family (void) static void bad_family (void)
{ {
struct sockaddr addr, dummy; struct sockaddr addr, dummy;
int val; int val;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_UNSPEC; addr.sa_family = AF_UNSPEC;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr); addr.sa_len = sizeof (addr);
#endif #endif
val = stun_bind_run (-1, &addr, sizeof (addr), val = stun_bind_run (-1, &addr, sizeof (addr),
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val != 0); assert (val != 0);
} }
/** Too small socket address test */ /** Too small socket address test */
static void small_srv_addr (void) static void small_srv_addr (void)
{ {
struct sockaddr addr, dummy; struct sockaddr addr, dummy;
int val; int val;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET; addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr); addr.sa_len = sizeof (addr);
#endif #endif
val = stun_bind_run (-1, &addr, 1, val = stun_bind_run (-1, &addr, 1,
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val == EINVAL); assert (val == EINVAL);
} }
/** Too big socket address test */ /** Too big socket address test */
static void big_srv_addr (void) static void big_srv_addr (void)
{ {
uint8_t buf[sizeof (struct sockaddr_storage) + 16]; uint8_t buf[sizeof (struct sockaddr_storage) + 16];
struct sockaddr dummy; struct sockaddr dummy;
int fd, val; int fd, val;
fd = socket (AF_INET, SOCK_DGRAM, 0); fd = socket (AF_INET, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
memset (buf, 0, sizeof (buf)); memset (buf, 0, sizeof (buf));
val = stun_bind_run (fd, (struct sockaddr *)buf, sizeof (buf), val = stun_bind_run (fd, (struct sockaddr *)buf, sizeof (buf),
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ENOBUFS); assert (val == ENOBUFS);
close (fd); close (fd);
} }
/** Timeout test */ /** Timeout test */
static void timeout (void) static void timeout (void)
{ {
struct sockaddr_storage srv; struct sockaddr_storage srv;
struct sockaddr dummy; struct sockaddr dummy;
socklen_t srvlen = sizeof (srv); socklen_t srvlen = sizeof (srv);
int val; int val;
/* Allocate a local UDP port, so we are 100% sure nobody responds there */ /* Allocate a local UDP port, so we are 100% sure nobody responds there */
int servfd = listen_dgram (); int servfd = listen_dgram ();
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&srv, &srvlen); val = getsockname (servfd, (struct sockaddr *)&srv, &srvlen);
assert (val == 0); assert (val == 0);
val = stun_bind_run (-1, (struct sockaddr *)&srv, srvlen, val = stun_bind_run (-1, (struct sockaddr *)&srv, srvlen,
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ETIMEDOUT); assert (val == ETIMEDOUT);
close (servfd); close (servfd);
} }
/** Malformed responses test */ /** Malformed responses test */
static void bad_responses (void) static void bad_responses (void)
{ {
stun_bind_t *ctx; stun_bind_t *ctx;
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
ssize_t val, len; ssize_t val, len;
uint8_t buf[1000]; uint8_t buf[1000];
/* Allocate a local UDP port */ /* Allocate a local UDP port */
int servfd = listen_dgram (), fd; int servfd = listen_dgram (), fd;
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
fd = socket (addr.ss_family, SOCK_DGRAM, 0); fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
val = stun_bind_start (&ctx, fd, (struct sockaddr *)&addr, addrlen); val = stun_bind_start (&ctx, fd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Send to/receive from our client instance only */ /* Send to/receive from our client instance only */
val = getsockname (fd, (struct sockaddr *)&addr, &addrlen); val = getsockname (fd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = connect (servfd, (struct sockaddr *)&addr, addrlen); val = connect (servfd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Send crap response */ /* Send crap response */
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_process (ctx, "foobar", 6, val = stun_bind_process (ctx, "foobar", 6,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
/* Send request instead of response */ /* Send request instead of response */
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
len = recv (servfd, buf, 1000, MSG_DONTWAIT); len = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (len >= 20); assert (len >= 20);
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
/* Send response with wrong request type */ /* Send response with wrong request type */
buf[0] |= 0x03; buf[0] |= 0x03;
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
buf[0] ^= 0x02; buf[0] ^= 0x02;
/* Send error response without ERROR-CODE */ /* Send error response without ERROR-CODE */
buf[1] |= 0x10; buf[1] |= 0x10;
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
close (fd); close (fd);
close (servfd); close (servfd);
} }
/** Various responses test */ /** Various responses test */
static void responses (void) static void responses (void)
{ {
stun_bind_t *ctx; stun_bind_t *ctx;
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
ssize_t val; ssize_t val;
size_t len; size_t len;
int servfd, fd; int servfd, fd;
stun_msg_t buf; stun_msg_t buf;
/* Allocate a local UDP port for server */ /* Allocate a local UDP port for server */
servfd = listen_dgram (); servfd = listen_dgram ();
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
/* Allocate a client socket and connect to server */ /* Allocate a client socket and connect to server */
fd = socket (addr.ss_family, SOCK_DGRAM, 0); fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
val = connect (fd, (struct sockaddr *)&addr, addrlen); val = connect (fd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Send to/receive from our client instance only */ /* Send to/receive from our client instance only */
val = getsockname (fd, (struct sockaddr *)&addr, &addrlen); val = getsockname (fd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = connect (servfd, (struct sockaddr *)&addr, addrlen); val = connect (servfd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Send error response */ /* Send error response */
val = stun_bind_start (&ctx, fd, NULL, 0); val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0); assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT); val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0); assert (val >= 0);
stun_init_error (buf, sizeof (buf), buf, STUN_SERVER_ERROR); stun_init_error (buf, sizeof (buf), buf, STUN_SERVER_ERROR);
len = sizeof (buf); len = sizeof (buf);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == ECONNREFUSED); assert (val == ECONNREFUSED);
/* Send response with an unknown attribute */ /* Send response with an unknown attribute */
val = stun_bind_start (&ctx, fd, NULL, 0); val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0); assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT); val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0); assert (val >= 0);
stun_init_response (buf, sizeof (buf), buf); stun_init_response (buf, sizeof (buf), buf);
val = stun_append_string (buf, sizeof (buf), 0x6000, val = stun_append_string (buf, sizeof (buf), 0x6000,
"This is an unknown attribute!"); "This is an unknown attribute!");
assert (val == 0); assert (val == 0);
len = sizeof (buf); len = sizeof (buf);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EPROTO); assert (val == EPROTO);
/* Send response with a no mapped address at all */ /* Send response with a no mapped address at all */
val = stun_bind_start (&ctx, fd, NULL, 0); val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0); assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT); val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0); assert (val >= 0);
stun_init_response (buf, sizeof (buf), buf); stun_init_response (buf, sizeof (buf), buf);
len = sizeof (buf); len = sizeof (buf);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == ENOENT); assert (val == ENOENT);
/* Send old-style response */ /* Send old-style response */
val = stun_bind_start (&ctx, fd, NULL, 0); val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0); assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT); val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0); assert (val >= 0);
stun_init_response (buf, sizeof (buf), buf); stun_init_response (buf, sizeof (buf), buf);
val = stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS, val = stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen); (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
len = sizeof (buf); len = sizeof (buf);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_process (ctx, buf, len, val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
/* End */ /* End */
close (servfd); close (servfd);
val = close (fd); val = close (fd);
assert (val == 0); assert (val == 0);
} }
static void keepalive (void) static void keepalive (void)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
size_t len; size_t len;
int val, servfd, fd; int val, servfd, fd;
/* Allocate a local UDP port for server */ /* Allocate a local UDP port for server */
servfd = listen_dgram (); servfd = listen_dgram ();
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
/* Allocate a client socket and connect to server */ /* Allocate a client socket and connect to server */
fd = socket (addr.ss_family, SOCK_DGRAM, 0); fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
/* Keep alive sending smoke test */ /* Keep alive sending smoke test */
val = stun_bind_keepalive (fd, (struct sockaddr *)&addr, addrlen); val = stun_bind_keepalive (fd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Wrong address family test */ /* Wrong address family test */
addr.ss_family = addr.ss_family == AF_INET ? AF_INET6 : AF_INET; addr.ss_family = addr.ss_family == AF_INET ? AF_INET6 : AF_INET;
val = stun_bind_keepalive (fd, (struct sockaddr *)&addr, addrlen); val = stun_bind_keepalive (fd, (struct sockaddr *)&addr, addrlen);
assert (val != 0); assert (val != 0);
/* End */ /* End */
close (servfd); close (servfd);
val = close (fd); val = close (fd);
assert (val == 0); assert (val == 0);
} }
static void test (void (*func) (void), const char *name) static void test (void (*func) (void), const char *name)
{ {
alarm (10); alarm (10);
printf ("%s test... ", name); printf ("%s test... ", name);
func (); func ();
puts ("OK"); puts ("OK");
} }
int main (void) int main (void)
{ {
test (bad_family, "Bad socket family"); test (bad_family, "Bad socket family");
test (small_srv_addr, "Too small server address"); test (small_srv_addr, "Too small server address");
test (big_srv_addr, "Too big server address"); test (big_srv_addr, "Too big server address");
test (bad_responses, "Bad responses"); test (bad_responses, "Bad responses");
test (responses, "Error responses"); test (responses, "Error responses");
test (keepalive, "Keep alives"); test (keepalive, "Keep alives");
test (timeout, "Binding discovery timeout"); test (timeout, "Binding discovery timeout");
return 0; return 0;
} }
...@@ -55,45 +55,45 @@ ...@@ -55,45 +55,45 @@
int main (void) int main (void)
{ {
struct sockaddr_in ip4; struct sockaddr_in ip4;
stun_msg_t buf; stun_msg_t buf;
ssize_t val; ssize_t val;
size_t len; size_t len;
static const uint8_t req[] = static const uint8_t req[] =
"\x00\x01" "\x00\x00" "\x00\x01" "\x00\x00"
"\x00\x01\x02\x03\x04\x05\x06\x07" "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F";
memset (&ip4, 0, sizeof (ip4)); memset (&ip4, 0, sizeof (ip4));
ip4.sin_family = AF_INET; ip4.sin_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
ip4.sin_len = sizeof (addr); ip4.sin_len = sizeof (addr);
#endif #endif
ip4.sin_port = htons (12345); ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001); ip4.sin_addr.s_addr = htonl (0x7f000001);
/* Same with too small response buffer */ /* Same with too small response buffer */
stun_init_request (buf, STUN_BINDING); stun_init_request (buf, STUN_BINDING);
len = sizeof (buf); len = sizeof (buf);
stun_finish (buf, &len); stun_finish (buf, &len);
len = 20 + 12 + 4 + stun_align (strlen (PACKAGE_STRING)) + 7; len = 20 + 12 + 4 + stun_align (strlen (PACKAGE_STRING)) + 7;
val = stun_bind_reply (buf, &len, buf, val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false); (struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == ENOBUFS); assert (val == ENOBUFS);
assert (len == 0); assert (len == 0);
/* Same with too small response buffer */ /* Same with too small response buffer */
stun_init_request (buf, STUN_BINDING); stun_init_request (buf, STUN_BINDING);
stun_append_string (buf, sizeof (buf), 0x666, "Unknown attribute!"); stun_append_string (buf, sizeof (buf), 0x666, "Unknown attribute!");
len = sizeof (buf); len = sizeof (buf);
stun_finish (buf, &len); stun_finish (buf, &len);
len = 20 + 4 + stun_align (strlen (PACKAGE_STRING)) + 7; len = 20 + 4 + stun_align (strlen (PACKAGE_STRING)) + 7;
val = stun_bind_reply (buf, &len, buf, val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false); (struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == ENOBUFS); assert (val == ENOBUFS);
assert (len == 0); assert (len == 0);
return 0; return 0;
} }
...@@ -55,203 +55,203 @@ ...@@ -55,203 +55,203 @@
int main (void) int main (void)
{ {
struct sockaddr_in ip4; struct sockaddr_in ip4;
stun_msg_t req, resp; stun_msg_t req, resp;
const uint64_t tie = 0x8000000000000000LL; const uint64_t tie = 0x8000000000000000LL;
ssize_t val; ssize_t val;
size_t len; size_t len;
static const char name[] = "admin", pass[] = "secret"; static const char name[] = "admin", pass[] = "secret";
char nbuf[STUN_MAX_STR]; char nbuf[STUN_MAX_STR];
int code; int code;
uint16_t alen; uint16_t alen;
bool control = false; bool control = false;
memset (&ip4, 0, sizeof (ip4)); memset (&ip4, 0, sizeof (ip4));
ip4.sin_family = AF_INET; ip4.sin_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
ip4.sin_len = sizeof (addr); ip4.sin_len = sizeof (addr);
#endif #endif
ip4.sin_port = htons (12345); ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001); ip4.sin_addr.s_addr = htonl (0x7f000001);
/* Incorrect message class */ /* Incorrect message class */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
stun_init_response (req, sizeof (req), req); stun_init_response (req, sizeof (req), req);
len = sizeof (req); len = sizeof (req);
val = stun_finish (req, &len); val = stun_finish (req, &len);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EINVAL); assert (val == EINVAL);
assert (len == 0); assert (len == 0);
/* Incorrect message method */ /* Incorrect message method */
stun_init_request (req, 0x666); stun_init_request (req, 0x666);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPROTO); assert (val == EPROTO);
assert (len > 0); assert (len > 0);
/* Unknown attribute */ /* Unknown attribute */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append_string (req, sizeof (req), 0x666, val = stun_append_string (req, sizeof (req), 0x666,
"The evil unknown attribute!"); "The evil unknown attribute!");
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish (req, &len); val = stun_finish (req, &len);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPROTO); assert (val == EPROTO);
assert (len > 0); assert (len > 0);
/* Unauthenticated message */ /* Unauthenticated message */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish (req, &len); val = stun_finish (req, &len);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
assert (stun_match_messages (resp, req, NULL, 0, &code) assert (stun_match_messages (resp, req, NULL, 0, &code)
&& (code == STUN_BAD_REQUEST)); && (code == STUN_BAD_REQUEST));
/* No username */ /* No username */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, NULL, pass, NULL); val = stun_finish_short (req, &len, NULL, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
assert (stun_match_messages (resp, req, NULL, 0, &code) assert (stun_match_messages (resp, req, NULL, 0, &code)
&& (code == STUN_BAD_REQUEST)); && (code == STUN_BAD_REQUEST));
assert (stun_conncheck_username (req, NULL, 0) == NULL); assert (stun_conncheck_username (req, NULL, 0) == NULL);
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL);
assert (stun_conncheck_priority (req) == 0); assert (stun_conncheck_priority (req) == 0);
assert (stun_conncheck_use_candidate (req) == false); assert (stun_conncheck_use_candidate (req) == false);
/* Bad username */ /* Bad username */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, "ab\xff", pass, NULL); val = stun_finish_short (req, &len, "ab\xff", pass, NULL);
assert (val == 0); assert (val == 0);
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL);
/* FIXME: use conncheck_reply */ /* FIXME: use conncheck_reply */
/* Bad integrity */ /* Bad integrity */
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), "bad", &control, tie); sizeof (ip4), "bad", &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
assert (stun_match_messages (resp, req, NULL, 0, &code) assert (stun_match_messages (resp, req, NULL, 0, &code)
&& (code == STUN_UNAUTHORIZED)); && (code == STUN_UNAUTHORIZED));
/* Good message */ /* Good message */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append32 (req, sizeof (req), STUN_PRIORITY, 0x12345678); val = stun_append32 (req, sizeof (req), STUN_PRIORITY, 0x12345678);
assert (val == 0); assert (val == 0);
val = stun_append_flag (req, sizeof (req), STUN_USE_CANDIDATE); val = stun_append_flag (req, sizeof (req), STUN_USE_CANDIDATE);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == 0); assert (val == 0);
assert (len > 0); assert (len > 0);
assert (stun_match_messages (resp, req, (uint8_t *)pass, assert (stun_match_messages (resp, req, (uint8_t *)pass,
strlen (pass), &code) strlen (pass), &code)
&& (code == -1)); && (code == -1));
assert (stun_conncheck_priority (req) == 0x12345678); assert (stun_conncheck_priority (req) == 0x12345678);
assert (stun_conncheck_use_candidate (req) == true); assert (stun_conncheck_use_candidate (req) == true);
/* Error cases for username extraction */ /* Error cases for username extraction */
assert (stun_conncheck_username (req, NULL, 0) == NULL); assert (stun_conncheck_username (req, NULL, 0) == NULL);
assert (stun_conncheck_username (req, nbuf, strlen (name) - 1) == NULL); assert (stun_conncheck_username (req, nbuf, strlen (name) - 1) == NULL);
/* Username extraction */ /* Username extraction */
strcpy (nbuf, "haxor"); strcpy (nbuf, "haxor");
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == nbuf); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == nbuf);
assert (strcmp (nbuf, name) == 0); assert (strcmp (nbuf, name) == 0);
/* Invalid socket address */ /* Invalid socket address */
ip4.sin_family = AF_UNSPEC; ip4.sin_family = AF_UNSPEC;
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EAFNOSUPPORT); assert (val == EAFNOSUPPORT);
ip4.sin_family = AF_INET; ip4.sin_family = AF_INET;
/* Bad CRC32 */ /* Bad CRC32 */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
((uint8_t *)stun_find (req, STUN_FINGERPRINT, &alen))[0] ^= 1; ((uint8_t *)stun_find (req, STUN_FINGERPRINT, &alen))[0] ^= 1;
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EINVAL); assert (val == EINVAL);
assert (len == 0); assert (len == 0);
/* Lost role conflict */ /* Lost role conflict */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLING, tie + 1); val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLING, tie + 1);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
control = true; control = true;
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EACCES); assert (val == EACCES);
assert (len > 0); assert (len > 0);
assert (control == false); assert (control == false);
assert (stun_match_messages (resp, req, (uint8_t *)pass, assert (stun_match_messages (resp, req, (uint8_t *)pass,
strlen (pass), &code) strlen (pass), &code)
&& (code == -1)); && (code == -1));
/* Won role conflict */ /* Won role conflict */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLED, tie - 1); val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLED, tie - 1);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
control = false; control = false;
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == 0); assert (val == 0);
assert (len > 0); assert (len > 0);
assert (control == false); assert (control == false);
assert (stun_match_messages (resp, req, (uint8_t *)pass, assert (stun_match_messages (resp, req, (uint8_t *)pass,
strlen (pass), &code) strlen (pass), &code)
&& (code == STUN_ROLE_CONFLICT)); && (code == STUN_ROLE_CONFLICT));
return 0; return 0;
} }
...@@ -52,168 +52,168 @@ ...@@ -52,168 +52,168 @@
static void fatal (const char *msg, ...) static void fatal (const char *msg, ...)
{ {
va_list ap; va_list ap;
va_start (ap, msg); va_start (ap, msg);
vfprintf (stderr, msg, ap); vfprintf (stderr, msg, ap);
va_end (ap); va_end (ap);
fputc ('\n', stderr); fputc ('\n', stderr);
exit (1); exit (1);
} }
static void static void
dynamic_check (const uint8_t *msg, size_t len) dynamic_check (const uint8_t *msg, size_t len)
{ {
size_t len2 = stun_validate (msg, len); size_t len2 = stun_validate (msg, len);
if ((len != len2) || (len2 & 3)) if ((len != len2) || (len2 & 3))
fatal ("Invalid message (%u, %u)\n", fatal ("Invalid message (%u, %u)\n",
(unsigned)len, (unsigned)len2); (unsigned)len, (unsigned)len2);
if (!stun_demux (msg)) if (!stun_demux (msg))
fatal ("Invalid message multiplexing"); fatal ("Invalid message multiplexing");
printf ("Built message of %u bytes\n", (unsigned)len); printf ("Built message of %u bytes\n", (unsigned)len);
} }
static size_t static size_t
finish_check (uint8_t *msg) finish_check (uint8_t *msg)
{ {
stun_msg_t mshort; stun_msg_t mshort;
size_t len = sizeof (stun_msg_t); size_t len = sizeof (stun_msg_t);
memcpy (mshort, msg, sizeof (mshort)); memcpy (mshort, msg, sizeof (mshort));
if (stun_finish (msg, &len)) if (stun_finish (msg, &len))
fatal ("Cannot finish message"); fatal ("Cannot finish message");
dynamic_check (msg, len); dynamic_check (msg, len);
len = sizeof (mshort); len = sizeof (mshort);
if (stun_verify_password (mshort, "toto") != ENOENT) if (stun_verify_password (mshort, "toto") != ENOENT)
fatal ("Missing HMAC test failed"); fatal ("Missing HMAC test failed");
if (stun_finish_short (mshort, &len, "ABCDE", "admin", "ABC")) if (stun_finish_short (mshort, &len, "ABCDE", "admin", "ABC"))
fatal ("Cannot finish message with short-term creds"); fatal ("Cannot finish message with short-term creds");
dynamic_check (mshort, len); dynamic_check (mshort, len);
if (stun_verify_password (mshort, "admin") != 0) if (stun_verify_password (mshort, "admin") != 0)
fatal ("Valid HMAC test failed"); fatal ("Valid HMAC test failed");
return len; return len;
} }
static void static void
check_af (const char *name, int family, socklen_t addrlen) check_af (const char *name, int family, socklen_t addrlen)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
stun_msg_t msg; stun_msg_t msg;
assert (addrlen <= sizeof (addr)); assert (addrlen <= sizeof (addr));
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS, if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT) (struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family test failed"); fatal ("Unknown address family test failed");
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS, if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT) (struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family xor test failed"); fatal ("Unknown address family xor test failed");
addr.ss_family = family; addr.ss_family = family;
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS, if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen - 1) != EINVAL) (struct sockaddr *)&addr, addrlen - 1) != EINVAL)
fatal ("Too small %s sockaddr test failed", name); fatal ("Too small %s sockaddr test failed", name);
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS, if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen - 1) != EINVAL) (struct sockaddr *)&addr, addrlen - 1) != EINVAL)
fatal ("Too small %s sockaddr xor test failed", name); fatal ("Too small %s sockaddr xor test failed", name);
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS, if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen)) (struct sockaddr *)&addr, addrlen))
fatal ("%s sockaddr test failed", name); fatal ("%s sockaddr test failed", name);
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS, if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen)) (struct sockaddr *)&addr, addrlen))
fatal ("%s sockaddr xor test failed", name); fatal ("%s sockaddr xor test failed", name);
} }
int main (void) int main (void)
{ {
uint8_t msg[STUN_MAXMSG + 8]; uint8_t msg[STUN_MAXMSG + 8];
size_t len; size_t len;
struct sockaddr addr; struct sockaddr addr;
/* Request formatting test */ /* Request formatting test */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
finish_check (msg); finish_check (msg);
if (memcmp (msg, "\x00\x01", 2)) if (memcmp (msg, "\x00\x01", 2))
fatal ("Request formatting test failed"); fatal ("Request formatting test failed");
/* Response formatting test */ /* Response formatting test */
stun_init_response (msg, sizeof (msg), msg); stun_init_response (msg, sizeof (msg), msg);
finish_check (msg); finish_check (msg);
if (memcmp (msg, "\x01\x01", 2)) if (memcmp (msg, "\x01\x01", 2))
fatal ("Response formatting test failed"); fatal ("Response formatting test failed");
/* Error formatting test */ /* Error formatting test */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
if (stun_init_error (msg, sizeof (msg), msg, 400)) if (stun_init_error (msg, sizeof (msg), msg, 400))
fatal ("Error initialization test failed"); fatal ("Error initialization test failed");
finish_check (msg); finish_check (msg);
if (memcmp (msg, "\x01\x11", 2)) if (memcmp (msg, "\x01\x11", 2))
fatal ("Error formatting test failed"); fatal ("Error formatting test failed");
/* Unknown error formatting test */ /* Unknown error formatting test */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
if (stun_init_error (msg, sizeof (msg), msg, 666)) if (stun_init_error (msg, sizeof (msg), msg, 666))
fatal ("Unknown error initialization test failed"); fatal ("Unknown error initialization test failed");
finish_check (msg); finish_check (msg);
if (memcmp (msg, "\x01\x11", 2)) if (memcmp (msg, "\x01\x11", 2))
fatal ("Unknown error formatting test failed"); fatal ("Unknown error formatting test failed");
/* Overflow tests */ /* Overflow tests */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
for (len = 0; for (len = 0;
stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS; stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS;
len += 4) len += 4)
{ {
if (len > 0xffff) if (len > 0xffff)
fatal ("Overflow protection test failed"); fatal ("Overflow protection test failed");
} }
if (stun_append32 (msg, sizeof (msg), 0xffff, 0x12345678) != ENOBUFS) if (stun_append32 (msg, sizeof (msg), 0xffff, 0x12345678) != ENOBUFS)
fatal ("Double-word overflow test failed"); fatal ("Double-word overflow test failed");
if (stun_append64 (msg, sizeof (msg), 0xffff, if (stun_append64 (msg, sizeof (msg), 0xffff,
0x123456789abcdef0) != ENOBUFS) 0x123456789abcdef0) != ENOBUFS)
fatal ("Quad-word overflow test failed"); fatal ("Quad-word overflow test failed");
if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS) if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS)
fatal ("String overflow test failed"); fatal ("String overflow test failed");
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET; addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr); addr.sa_len = sizeof (addr);
#endif #endif
if (stun_append_xor_addr (msg, sizeof (msg), 0xffff, &addr, if (stun_append_xor_addr (msg, sizeof (msg), 0xffff, &addr,
sizeof (addr)) != ENOBUFS) sizeof (addr)) != ENOBUFS)
fatal ("Address overflow test failed"); fatal ("Address overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish (msg, &len) != ENOBUFS) if (stun_finish (msg, &len) != ENOBUFS)
fatal ("Fingerprint overflow test failed"); fatal ("Fingerprint overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", NULL) != ENOBUFS) if (stun_finish_short (msg, &len, NULL, "secret", NULL) != ENOBUFS)
fatal ("Message integrity overflow test failed"); fatal ("Message integrity overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, "login", "secret", NULL) != ENOBUFS) if (stun_finish_short (msg, &len, "login", "secret", NULL) != ENOBUFS)
fatal ("Username overflow test failed"); fatal ("Username overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", "foobar") != ENOBUFS) if (stun_finish_short (msg, &len, NULL, "secret", "foobar") != ENOBUFS)
fatal ("Nonce overflow test failed"); fatal ("Nonce overflow test failed");
/* Address attributes tests */ /* Address attributes tests */
check_af ("IPv4", AF_INET, sizeof (struct sockaddr_in)); check_af ("IPv4", AF_INET, sizeof (struct sockaddr_in));
#ifdef AF_INET6 #ifdef AF_INET6
check_af ("IPv6", AF_INET6, sizeof (struct sockaddr_in6)); check_af ("IPv6", AF_INET6, sizeof (struct sockaddr_in6));
#endif #endif
return 0; return 0;
} }
...@@ -52,313 +52,313 @@ ...@@ -52,313 +52,313 @@
static void fatal (const char *msg, ...) static void fatal (const char *msg, ...)
{ {
va_list ap; va_list ap;
va_start (ap, msg); va_start (ap, msg);
vfprintf (stderr, msg, ap); vfprintf (stderr, msg, ap);
va_end (ap); va_end (ap);
fputc ('\n', stderr); fputc ('\n', stderr);
exit (1); exit (1);
} }
static void validate (const uint8_t *msg, unsigned len) static void validate (const uint8_t *msg, unsigned len)
{ {
unsigned i = 0; unsigned i = 0;
do do
{ {
size_t vlen = stun_validate (msg, i); size_t vlen = stun_validate (msg, i);
if ((vlen & 3) || (vlen != ((i >= len) * len))) if ((vlen & 3) || (vlen != ((i >= len) * len)))
fatal ("%u/%u short message test failed", i, len); fatal ("%u/%u short message test failed", i, len);
} }
while (i++ < (len + 4)); while (i++ < (len + 4));
} }
/* Tests for generic message validation routines */ /* Tests for generic message validation routines */
static void test_message (void) static void test_message (void)
{ {
static const uint8_t simple_resp[] = static const uint8_t simple_resp[] =
"\x15\x55\x00\x00" "\x15\x55\x00\x00"
"\x21\x12\xA4\x42" // cookie "\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xaa\xbb\xcc\xdd"; //extra garbage "\xaa\xbb\xcc\xdd"; //extra garbage
static const uint8_t old_ind[] = static const uint8_t old_ind[] =
"\x14\x55\x00\x00" "\x14\x55\x00\x00"
"\xfe\xdc\xba\x98" // NO cookie "\xfe\xdc\xba\x98" // NO cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"; //extra garbage "\x76\x54\x32\x10"; //extra garbage
static const uint8_t fpr_resp[] = static const uint8_t fpr_resp[] =
"\x15\x55\x00\x10" "\x15\x55\x00\x10"
"\x21\x12\xA4\x42" // cookie "\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\x00\x06\x00\x04" // dummy USERNAME header "\x00\x06\x00\x04" // dummy USERNAME header
"\x41\x42\x43\x44" "\x41\x42\x43\x44"
"\x80\x28\x00\x04" // FINGERPRINT header "\x80\x28\x00\x04" // FINGERPRINT header
"\xdc\x8d\xa7\x74" // CRC32 "\xdc\x8d\xa7\x74" // CRC32
"\xcc\xdd\xee\xff"; // extra garbage "\xcc\xdd\xee\xff"; // extra garbage
static const uint8_t bad1[32] = static const uint8_t bad1[32] =
"\x15\x55\x00\x08" "\x15\x55\x00\x08"
"\x21\x12\xA4\x42" // cookie "\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\x00\x06\x00\x05" // too big attribute for message "\x00\x06\x00\x05" // too big attribute for message
"\x11\x22\x33\x44" "\x11\x22\x33\x44"
"\x55\x66\x77\x88"; "\x55\x66\x77\x88";
static const uint8_t bad2[24] = static const uint8_t bad2[24] =
"\x15\x55\x00\x05" // invalid message length "\x15\x55\x00\x05" // invalid message length
"\x21\x12\xA4\x42" "\x21\x12\xA4\x42"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\x00\x06\x00\x01"; "\x00\x06\x00\x01";
static const uint8_t bad3[27] = static const uint8_t bad3[27] =
"\x15\x55\x00\x08" "\x15\x55\x00\x08"
"\x21\x12\xA4\x42" "\x21\x12\xA4\x42"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\x00\x06\x00\x03" "\x00\x06\x00\x03"
"\x11\x22\x33"; // missing padding "\x11\x22\x33"; // missing padding
static const uint8_t bad_crc[] = static const uint8_t bad_crc[] =
"\x15\x55\x00\x08" "\x15\x55\x00\x08"
"\x21\x12\xA4\x42" "\x21\x12\xA4\x42"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\x80\x28\x00\x04" // FINGERPRINT header "\x80\x28\x00\x04" // FINGERPRINT header
"\x04\x91\xcd\x78"; // CRC32 "\x04\x91\xcd\x78"; // CRC32
static uint8_t bad_crc_offset[] = static uint8_t bad_crc_offset[] =
"\x15\x55\x00\x10" "\x15\x55\x00\x10"
"\x21\x12\xA4\x42" "\x21\x12\xA4\x42"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x20\x67\xc4\x09" "\x20\x67\xc4\x09"
"\x80\x28\x00\x04" // FINGERPRINT header "\x80\x28\x00\x04" // FINGERPRINT header
"\x00\x00\x00\x00" "\x00\x00\x00\x00"
"\x00\x06\x00\x04" "\x00\x06\x00\x04"
"\x41\x42\x43\x44"; "\x41\x42\x43\x44";
if (stun_validate (NULL, 0) != 0) if (stun_validate (NULL, 0) != 0)
fatal ("0 bytes test failed"); fatal ("0 bytes test failed");
if (stun_validate ((uint8_t *)"\xf0", 1) >= 0) if (stun_validate ((uint8_t *)"\xf0", 1) >= 0)
fatal ("1 byte test failed"); fatal ("1 byte test failed");
validate (simple_resp, 20); validate (simple_resp, 20);
validate (old_ind, 20); validate (old_ind, 20);
validate (fpr_resp, 36); validate (fpr_resp, 36);
if (stun_demux (simple_resp)) if (stun_demux (simple_resp))
fatal ("Missing CRC test failed"); fatal ("Missing CRC test failed");
if (stun_demux (old_ind)) if (stun_demux (old_ind))
fatal ("Missing cookie test failed"); fatal ("Missing cookie test failed");
if (!stun_demux (fpr_resp)) if (!stun_demux (fpr_resp))
fatal ("Good CRC test failed"); fatal ("Good CRC test failed");
if (stun_demux (bad_crc)) if (stun_demux (bad_crc))
fatal ("Bad CRC test failed"); fatal ("Bad CRC test failed");
if (stun_demux (bad_crc_offset)) if (stun_demux (bad_crc_offset))
fatal ("Bad CRC offset test failed"); fatal ("Bad CRC offset test failed");
if (stun_validate (bad1, sizeof (bad1)) >= 0) if (stun_validate (bad1, sizeof (bad1)) >= 0)
fatal ("Badness 1 test failed"); fatal ("Badness 1 test failed");
if (stun_validate (bad2, sizeof (bad2)) >= 0) if (stun_validate (bad2, sizeof (bad2)) >= 0)
fatal ("Badness 2 test failed"); fatal ("Badness 2 test failed");
if (stun_validate (bad3, sizeof (bad3)) != 0) if (stun_validate (bad3, sizeof (bad3)) != 0)
fatal ("Badness 3 test failed"); fatal ("Badness 3 test failed");
if (stun_get_class (simple_resp) != 3) if (stun_get_class (simple_resp) != 3)
fatal ("Class test failed"); fatal ("Class test failed");
if (stun_get_method (simple_resp) != 0x525) if (stun_get_method (simple_resp) != 0x525)
fatal ("Method test failed"); fatal ("Method test failed");
} }
/* Tests for message attribute parsing */ /* Tests for message attribute parsing */
static void test_attribute (void) static void test_attribute (void)
{ {
static const uint8_t acme[] = static const uint8_t acme[] =
"\x15\x55\x00\x64" // <-- update message length if needed!! "\x15\x55\x00\x64" // <-- update message length if needed!!
"\x21\x12\xA4\x42" // cookie "\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
"\xfe\xdc\xba\x98" "\xfe\xdc\xba\x98"
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
/* FF01: empty */ /* FF01: empty */
"\xff\x01\x00\x00" "\xff\x01\x00\x00"
/* FF02: address of unknown family, 32-bits */ /* FF02: address of unknown family, 32-bits */
"\xff\x02\x00\x04" "\xff\x02\x00\x04"
"\x41\x42\x43\x44" "\x41\x42\x43\x44"
/* FF03: too short IPv6 address */ /* FF03: too short IPv6 address */
"\xff\x03\x00\x06" "\xff\x03\x00\x06"
"\x00\x02\x12\x34" "\x00\x02\x12\x34"
"\x20\x01\x0d\xb8" "\x20\x01\x0d\xb8"
/* FF04: valid IPv4 address, 64-bits */ /* FF04: valid IPv4 address, 64-bits */
"\xff\x04\x00\x08" "\xff\x04\x00\x08"
"\x00\x01\x12\x34" "\x00\x01\x12\x34"
"\xc0\x00\x02\x01" "\xc0\x00\x02\x01"
/* FF05: too long IPv4 address */ /* FF05: too long IPv4 address */
"\xff\x05\x00\x0A" "\xff\x05\x00\x0A"
"\x00\x01\x12\x34" "\x00\x01\x12\x34"
"\xc0\x00\x02\x01" "\xc0\x00\x02\x01"
"\x66\x60\x00\x00" "\x66\x60\x00\x00"
/* FF06: valid xor'd IPv6 address, 160-bits */ /* FF06: valid xor'd IPv6 address, 160-bits */
"\xff\x06\x00\x14" "\xff\x06\x00\x14"
"\x00\x02\x12\x34" "\x00\x02\x12\x34"
"\x01\x13\xa9\xfa" "\x01\x13\xa9\xfa"
"\xa8\xf9\x8c\xff" "\xa8\xf9\x8c\xff"
"\x20\x26\x74\x48" "\x20\x26\x74\x48"
"\x8c\x9a\xec\xfd" "\x8c\x9a\xec\xfd"
/* MESSAGE-INTEGRITY attribute */ /* MESSAGE-INTEGRITY attribute */
"\x00\x08\x00\x14" "\x00\x08\x00\x14"
"\x42\x95\x4b\x54" "\x42\x95\x4b\x54"
"\x73\x3c\x73\xef" "\x73\x3c\x73\xef"
"\xa9\x75\xad\x6f" "\xa9\x75\xad\x6f"
"\xbe\xd5\x6b\x13" "\xbe\xd5\x6b\x13"
"\x9d\x53\x5f\x57" "\x9d\x53\x5f\x57"
; ;
union union
{ {
struct sockaddr sa; struct sockaddr sa;
struct sockaddr_in6 s6; struct sockaddr_in6 s6;
} addr; } addr;
socklen_t addrlen; socklen_t addrlen;
uint32_t dword; uint32_t dword;
uint64_t qword; uint64_t qword;
char str[STUN_MAX_STR]; char str[STUN_MAX_STR];
printf ("Attribute test message length: %u\n", sizeof (acme)); printf ("Attribute test message length: %u\n", sizeof (acme));
if (stun_validate (acme, sizeof (acme)) <= 0) if (stun_validate (acme, sizeof (acme)) <= 0)
fatal ("Attributes tests message broken"); fatal ("Attributes tests message broken");
if (stun_present (acme, 0xff00)) if (stun_present (acme, 0xff00))
fatal ("Absent attribute test failed"); fatal ("Absent attribute test failed");
if (!stun_present (acme, 0xff01)) if (!stun_present (acme, 0xff01))
fatal ("Present attribute test failed"); fatal ("Present attribute test failed");
if (stun_find_flag (acme, 0xff00) != ENOENT) if (stun_find_flag (acme, 0xff00) != ENOENT)
fatal ("Absent flag test failed"); fatal ("Absent flag test failed");
if (stun_find_flag (acme, 0xff01) != 0) if (stun_find_flag (acme, 0xff01) != 0)
fatal ("Flag test failed"); fatal ("Flag test failed");
if (stun_find_flag (acme, 0xff02) != EINVAL) if (stun_find_flag (acme, 0xff02) != EINVAL)
fatal ("Too big flag test failed"); fatal ("Too big flag test failed");
if (stun_find32 (acme, 0xff00, &dword) != ENOENT) if (stun_find32 (acme, 0xff00, &dword) != ENOENT)
fatal ("Absent dword test failed"); fatal ("Absent dword test failed");
if (stun_find32 (acme, 0xff01, &dword) != EINVAL) if (stun_find32 (acme, 0xff01, &dword) != EINVAL)
fatal ("Bad dword test failed"); fatal ("Bad dword test failed");
if (stun_find32 (acme, 0xff02, &dword) != 0) if (stun_find32 (acme, 0xff02, &dword) != 0)
fatal ("Double-word test failed"); fatal ("Double-word test failed");
if (stun_find64 (acme, 0xff00, &qword) != ENOENT) if (stun_find64 (acme, 0xff00, &qword) != ENOENT)
fatal ("Absent qword test failed"); fatal ("Absent qword test failed");
if (stun_find64 (acme, 0xff01, &qword) != EINVAL) if (stun_find64 (acme, 0xff01, &qword) != EINVAL)
fatal ("Bad qword test failed"); fatal ("Bad qword test failed");
if (stun_find64 (acme, 0xff04, &qword) !=0) if (stun_find64 (acme, 0xff04, &qword) !=0)
fatal ("Quad-word test failed"); fatal ("Quad-word test failed");
if (stun_find_string (acme, 0xff00, str, STUN_MAX_CP) != ENOENT) if (stun_find_string (acme, 0xff00, str, STUN_MAX_CP) != ENOENT)
fatal ("Absent string test failed"); fatal ("Absent string test failed");
if ((stun_find_string (acme, 0xff02, str, STUN_MAX_CP) != 0) if ((stun_find_string (acme, 0xff02, str, STUN_MAX_CP) != 0)
|| strcmp (str, "ABCD")) || strcmp (str, "ABCD"))
fatal ("String test failed"); fatal ("String test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff01, &addr.sa, &addrlen) != EINVAL) if (stun_find_addr (acme, 0xff01, &addr.sa, &addrlen) != EINVAL)
fatal ("Too short addres test failed"); fatal ("Too short addres test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff02, &addr.sa, &addrlen) != EAFNOSUPPORT) if (stun_find_addr (acme, 0xff02, &addr.sa, &addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family test failed"); fatal ("Unknown address family test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff03, &addr.sa, &addrlen) != EINVAL) if (stun_find_addr (acme, 0xff03, &addr.sa, &addrlen) != EINVAL)
fatal ("Too short IPv6 address test failed"); fatal ("Too short IPv6 address test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff04, &addr.sa, &addrlen) != 0) if (stun_find_addr (acme, 0xff04, &addr.sa, &addrlen) != 0)
fatal ("IPv4 address test failed"); fatal ("IPv4 address test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff05, &addr.sa, &addrlen) != EINVAL) if (stun_find_addr (acme, 0xff05, &addr.sa, &addrlen) != EINVAL)
fatal ("Too big IPv4 address test failed"); fatal ("Too big IPv4 address test failed");
addrlen = sizeof (addr); addrlen = sizeof (addr);
if (stun_find_xor_addr (acme, 0xff06, &addr.sa, &addrlen) if (stun_find_xor_addr (acme, 0xff06, &addr.sa, &addrlen)
|| memcmp (&addr.s6.sin6_addr, "\x20\x01\x0d\xb8""\xde\xad\xbe\xef" || memcmp (&addr.s6.sin6_addr, "\x20\x01\x0d\xb8""\xde\xad\xbe\xef"
"\xde\xfa\xce\xd0""\xfa\xce\xde\xed", 16)) "\xde\xfa\xce\xd0""\xfa\xce\xde\xed", 16))
fatal ("IPv6 address test failed"); fatal ("IPv6 address test failed");
if (stun_verify_key (acme, "good_guy", 8) != 0) if (stun_verify_key (acme, "good_guy", 8) != 0)
fatal ("Good secret HMAC test failed"); fatal ("Good secret HMAC test failed");
if (stun_verify_key (acme, "bad__guy", 8) != EPERM) if (stun_verify_key (acme, "bad__guy", 8) != EPERM)
fatal ("Bad secret HMAC test failed"); fatal ("Bad secret HMAC test failed");
} }
static void test_vectors (void) static void test_vectors (void)
{ {
static const char username[] = "evtj:h6vY"; static const char username[] = "evtj:h6vY";
static const char password[] = "VOkJxbRl1RmTxUk/WvJxBt"; static const char password[] = "VOkJxbRl1RmTxUk/WvJxBt";
/* Request message */ /* Request message */
static const unsigned char req[] = static const unsigned char req[] =
"\x00\x01\x00\x44" "\x00\x01\x00\x44"
"\x21\x12\xa4\x42" "\x21\x12\xa4\x42"
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x00\x24\x00\x04" "\x00\x24\x00\x04"
"\x6e\x00\x01\xff" "\x6e\x00\x01\xff"
"\x80\x29\x00\x08" "\x80\x29\x00\x08"
"\x93\x2f\xf9\xb1\x51\x26\x3b\x36" "\x93\x2f\xf9\xb1\x51\x26\x3b\x36"
"\x00\x06\x00\x09" "\x00\x06\x00\x09"
"\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20" "\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20"
"\x00\x08\x00\x14" "\x00\x08\x00\x14"
"\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74\xbf\x85" "\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74\xbf\x85"
"\xd1\xc0\xf5\xde\x36\x87\xbd\x33" "\xd1\xc0\xf5\xde\x36\x87\xbd\x33"
"\x80\x28\x00\x04" "\x80\x28\x00\x04"
"\xad\x8a\x85\xff"; "\xad\x8a\x85\xff";
/* Response message */ /* Response message */
static const unsigned char resp[] = static const unsigned char resp[] =
"\x01\x01\x00\x3c" "\x01\x01\x00\x3c"
"\x21\x12\xa4\x42" "\x21\x12\xa4\x42"
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x80\x22\x00\x0b" "\x80\x22\x00\x0b"
"\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20"
"\x00\x20\x00\x08" "\x00\x20\x00\x08"
"\x00\x01\xa1\x47\x5e\x12\xa4\x43" "\x00\x01\xa1\x47\x5e\x12\xa4\x43"
"\x00\x08\x00\x14" "\x00\x08\x00\x14"
"\xab\x4e\x53\x29\x61\x00\x08\x4c\x89\xf2\x7c\x69" "\xab\x4e\x53\x29\x61\x00\x08\x4c\x89\xf2\x7c\x69"
"\x30\x33\x5c\xa3\x58\x14\xea\x90" "\x30\x33\x5c\xa3\x58\x14\xea\x90"
"\x80\x28\x00\x04" "\x80\x28\x00\x04"
"\xae\x25\x8d\xf2"; "\xae\x25\x8d\xf2";
puts ("Checking test vectors..."); puts ("Checking test vectors...");
if (stun_demux (req) != true) if (stun_demux (req) != true)
fatal ("Request test vector checksum failed"); fatal ("Request test vector checksum failed");
if (stun_verify_password (req, password) != 0) if (stun_verify_password (req, password) != 0)
fatal ("Request test vector authentication failed"); fatal ("Request test vector authentication failed");
if (stun_demux (resp) != true) if (stun_demux (resp) != true)
fatal ("Response test vector checksum failed"); fatal ("Response test vector checksum failed");
if (stun_verify_password (resp, password) != 0) if (stun_verify_password (resp, password) != 0)
fatal ("Response test vector authentication failed"); fatal ("Response test vector authentication failed");
puts ("Done."); puts ("Done.");
} }
int main (void) int main (void)
{ {
test_message (); test_message ();
test_attribute (); test_attribute ();
test_vectors (); test_vectors ();
return 0; return 0;
} }
...@@ -63,80 +63,80 @@ ...@@ -63,80 +63,80 @@
static void stun_gettime (struct timespec *restrict now) static void stun_gettime (struct timespec *restrict now)
{ {
#if (_POSIX_MONOTONIC_CLOCK - 0) >= 0 #if (_POSIX_MONOTONIC_CLOCK - 0) >= 0
if (clock_gettime (CLOCK_MONOTONIC, now)) if (clock_gettime (CLOCK_MONOTONIC, now))
#endif #endif
{ // fallback to wall clock { // fallback to wall clock
struct timeval tv; struct timeval tv;
gettimeofday (&tv, NULL); gettimeofday (&tv, NULL);
now->tv_sec = tv.tv_sec; now->tv_sec = tv.tv_sec;
now->tv_nsec = tv.tv_usec * 1000; now->tv_nsec = tv.tv_usec * 1000;
} }
} }
static inline void add_delay (struct timespec *ts, unsigned delay) static inline void add_delay (struct timespec *ts, unsigned delay)
{ {
div_t d = div (delay, 1000); div_t d = div (delay, 1000);
ts->tv_sec += d.quot; ts->tv_sec += d.quot;
ts->tv_nsec += d.rem * 1000000; ts->tv_nsec += d.rem * 1000000;
while (ts->tv_nsec > 1000000000) while (ts->tv_nsec > 1000000000)
{ {
ts->tv_nsec -= 1000000000; ts->tv_nsec -= 1000000000;
ts->tv_sec++; ts->tv_sec++;
} }
} }
void stun_timer_start (stun_timer_t *timer) void stun_timer_start (stun_timer_t *timer)
{ {
stun_gettime (&timer->deadline); stun_gettime (&timer->deadline);
add_delay (&timer->deadline, timer->delay = STUN_INIT_TIMEOUT); add_delay (&timer->deadline, timer->delay = STUN_INIT_TIMEOUT);
} }
void stun_timer_start_reliable (stun_timer_t *timer) void stun_timer_start_reliable (stun_timer_t *timer)
{ {
stun_gettime (&timer->deadline); stun_gettime (&timer->deadline);
add_delay (&timer->deadline, timer->delay = STUN_RELIABLE_TIMEOUT); add_delay (&timer->deadline, timer->delay = STUN_RELIABLE_TIMEOUT);
} }
unsigned stun_timer_remainder (const stun_timer_t *timer) unsigned stun_timer_remainder (const stun_timer_t *timer)
{ {
unsigned delay; unsigned delay;
struct timespec now; struct timespec now;
stun_gettime (&now); stun_gettime (&now);
if (now.tv_sec > timer->deadline.tv_sec) if (now.tv_sec > timer->deadline.tv_sec)
return 0; return 0;
delay = timer->deadline.tv_sec - now.tv_sec; delay = timer->deadline.tv_sec - now.tv_sec;
if ((delay == 0) && (now.tv_nsec >= timer->deadline.tv_nsec)) if ((delay == 0) && (now.tv_nsec >= timer->deadline.tv_nsec))
return 0; return 0;
delay *= 1000; delay *= 1000;
delay += ((signed)(timer->deadline.tv_nsec - now.tv_nsec)) / 1000000; delay += ((signed)(timer->deadline.tv_nsec - now.tv_nsec)) / 1000000;
return delay; return delay;
} }
int stun_timer_refresh (stun_timer_t *timer) int stun_timer_refresh (stun_timer_t *timer)
{ {
unsigned delay = stun_timer_remainder (timer); unsigned delay = stun_timer_remainder (timer);
if (delay == 0) if (delay == 0)
{ {
#if STUN_RELIABLE_TIMEOUT < STUN_END_TIMEOUT #if STUN_RELIABLE_TIMEOUT < STUN_END_TIMEOUT
/* Reliable timeout MUST be bigger (or equal) to end timeout, so that /* Reliable timeout MUST be bigger (or equal) to end timeout, so that
* retransmissions never happen with reliable transports. */ * retransmissions never happen with reliable transports. */
# error Inconsistent STUN timeout values! # error Inconsistent STUN timeout values!
#endif #endif
if (timer->delay >= STUN_END_TIMEOUT) if (timer->delay >= STUN_END_TIMEOUT)
return -1; return -1;
add_delay (&timer->deadline, timer->delay *= 2); add_delay (&timer->deadline, timer->delay *= 2);
} }
return delay; return delay;
} }
...@@ -46,8 +46,8 @@ ...@@ -46,8 +46,8 @@
typedef struct stun_timer_s typedef struct stun_timer_s
{ {
struct timespec deadline; struct timespec deadline;
unsigned delay; unsigned delay;
} stun_timer_t; } stun_timer_t;
......
...@@ -60,32 +60,32 @@ ...@@ -60,32 +60,32 @@
int stun_trans_init (stun_trans_t *restrict tr, int fd, int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
{ {
int sotype; int sotype;
socklen_t solen = sizeof (sotype); socklen_t solen = sizeof (sotype);
assert (fd != -1); assert (fd != -1);
if (srvlen > sizeof (tr->sock.dst)) if (srvlen > sizeof (tr->sock.dst))
return ENOBUFS; return ENOBUFS;
tr->flags = 0; tr->flags = 0;
tr->msg.offset = 0; tr->msg.offset = 0;
tr->sock.fd = fd; tr->sock.fd = fd;
memcpy (&tr->sock.dst, srv, tr->sock.dstlen = srvlen); memcpy (&tr->sock.dst, srv, tr->sock.dstlen = srvlen);
tr->key.length = 0; tr->key.length = 0;
tr->key.value = NULL; tr->key.value = NULL;
assert (getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen) == 0); assert (getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen) == 0);
(void)getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen); (void)getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen);
switch (sotype) switch (sotype)
{ {
case SOCK_STREAM: case SOCK_STREAM:
case SOCK_SEQPACKET: case SOCK_SEQPACKET:
tr->flags |= TRANS_RELIABLE; tr->flags |= TRANS_RELIABLE;
} }
return 0; return 0;
} }
...@@ -95,83 +95,83 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd, ...@@ -95,83 +95,83 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd,
*/ */
static int stun_socket (int family, int type, int proto) static int stun_socket (int family, int type, int proto)
{ {
int fd = socket (family, type, proto); int fd = socket (family, type, proto);
if (fd == -1) if (fd == -1)
return -1; return -1;
#ifdef FD_CLOEXEC #ifdef FD_CLOEXEC
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC); fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
#endif #endif
#ifdef O_NONBLOCK #ifdef O_NONBLOCK
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK); fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
#endif #endif
#ifdef MSG_ERRQUEUE #ifdef MSG_ERRQUEUE
if (type == SOCK_DGRAM) if (type == SOCK_DGRAM)
{ {
/* Linux specifics for ICMP errors on non-connected sockets */ /* Linux specifics for ICMP errors on non-connected sockets */
int yes = 1; int yes = 1;
switch (family) switch (family)
{ {
case AF_INET: case AF_INET:
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes)); setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
break; break;
case AF_INET6: case AF_INET6:
setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes)); setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
break; break;
} }
} }
#endif #endif
return fd; return fd;
} }
int stun_trans_create (stun_trans_t *restrict tr, int type, int proto, int stun_trans_create (stun_trans_t *restrict tr, int type, int proto,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
{ {
int val, fd; int val, fd;
if (srvlen < sizeof(*srv)) if (srvlen < sizeof(*srv))
return EINVAL; return EINVAL;
fd = stun_socket (srv->sa_family, type, proto); fd = stun_socket (srv->sa_family, type, proto);
if (fd == -1) if (fd == -1)
return errno; return errno;
if (connect (fd, srv, srvlen) && (errno != EINPROGRESS)) if (connect (fd, srv, srvlen) && (errno != EINPROGRESS))
{ {
val = errno; val = errno;
goto error; goto error;
} }
val = stun_trans_init (tr, fd, NULL, 0); val = stun_trans_init (tr, fd, NULL, 0);
if (val) if (val)
goto error; goto error;
tr->flags |= TRANS_OWN_FD; tr->flags |= TRANS_OWN_FD;
return 0; return 0;
error: error:
close (fd); close (fd);
return val; return val;
} }
void stun_trans_deinit (stun_trans_t *tr) void stun_trans_deinit (stun_trans_t *tr)
{ {
int saved = errno; int saved = errno;
assert (tr->sock.fd != -1); assert (tr->sock.fd != -1);
if (tr->flags & TRANS_OWN_FD) if (tr->flags & TRANS_OWN_FD)
close (tr->sock.fd); close (tr->sock.fd);
free (tr->key.value); free (tr->key.value);
#ifndef NDEBUG #ifndef NDEBUG
tr->sock.fd = -1; tr->sock.fd = -1;
#endif #endif
errno = saved; errno = saved;
} }
...@@ -187,41 +187,41 @@ static int stun_trans_send (stun_trans_t *tr); ...@@ -187,41 +187,41 @@ static int stun_trans_send (stun_trans_t *tr);
int stun_trans_start (stun_trans_t *tr) int stun_trans_start (stun_trans_t *tr)
{ {
int val; int val;
tr->msg.offset = 0; tr->msg.offset = 0;
if (tr->flags & TRANS_RELIABLE) if (tr->flags & TRANS_RELIABLE)
/* /*
* FIXME: wait for three-way handshake, somewhere * FIXME: wait for three-way handshake, somewhere
*/ */
stun_timer_start_reliable (&tr->timer); stun_timer_start_reliable (&tr->timer);
else else
stun_timer_start (&tr->timer); stun_timer_start (&tr->timer);
DBG ("STUN transaction @%p started (timeout: %ums)\n", tr, DBG ("STUN transaction @%p started (timeout: %ums)\n", tr,
stun_trans_timeout (tr)); stun_trans_timeout (tr));
val = stun_trans_send (tr); val = stun_trans_send (tr);
if (val) if (val)
return val; return val;
return 0; return 0;
} }
static inline int stun_err_dequeue (int fd) static inline int stun_err_dequeue (int fd)
{ {
#ifdef MSG_ERRQUEUE #ifdef MSG_ERRQUEUE
struct msghdr hdr; struct msghdr hdr;
int saved_errno = errno, ret; int saved_errno = errno, ret;
memset (&hdr, 0, sizeof (hdr)); memset (&hdr, 0, sizeof (hdr));
ret = (recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0); ret = (recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0);
errno = saved_errno; errno = saved_errno;
return ret; return ret;
#else #else
return 0; return 0;
#endif #endif
} }
...@@ -229,19 +229,19 @@ static inline int stun_err_dequeue (int fd) ...@@ -229,19 +229,19 @@ static inline int stun_err_dequeue (int fd)
ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len, ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len,
const struct sockaddr *dst, socklen_t dstlen) const struct sockaddr *dst, socklen_t dstlen)
{ {
static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL; static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
ssize_t val; ssize_t val;
do do
{ {
if (dstlen > 0) if (dstlen > 0)
val = sendto (fd, buf, len, flags, dst, dstlen); val = sendto (fd, buf, len, flags, dst, dstlen);
else else
val = send (fd, buf, len, flags); val = send (fd, buf, len, flags);
} }
while ((val == -1) && stun_err_dequeue (fd)); while ((val == -1) && stun_err_dequeue (fd));
return val; return val;
} }
...@@ -249,46 +249,48 @@ ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen, ...@@ -249,46 +249,48 @@ ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen,
struct sockaddr *restrict dst, struct sockaddr *restrict dst,
socklen_t *restrict dstlen) socklen_t *restrict dstlen)
{ {
static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL; static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
ssize_t val; ssize_t val;
if (dstlen != NULL) if (dstlen != NULL)
val = recvfrom (fd, buf, maxlen, flags, dst, dstlen); val = recvfrom (fd, buf, maxlen, flags, dst, dstlen);
else else
val = recv (fd, buf, maxlen, flags); val = recv (fd, buf, maxlen, flags);
if ((val == -1) && stun_err_dequeue (fd)) if ((val == -1) && stun_err_dequeue (fd))
errno = EAGAIN; errno = EAGAIN;
return val; return val;
} }
unsigned stun_trans_timeout (const stun_trans_t *tr) unsigned stun_trans_timeout (const stun_trans_t *tr)
{ {
assert (tr != NULL); assert (tr != NULL);
assert (tr->sock.fd != -1); assert (tr->sock.fd != -1);
return stun_timer_remainder (&tr->timer); return stun_timer_remainder (&tr->timer);
} }
int stun_trans_fd (const stun_trans_t *tr) int stun_trans_fd (const stun_trans_t *tr)
{ {
assert (tr != NULL); assert (tr != NULL);
assert (tr->sock.fd != -1); assert (tr->sock.fd != -1);
return tr->sock.fd; return tr->sock.fd;
} }
bool stun_trans_reading (const stun_trans_t *tr) bool stun_trans_reading (const stun_trans_t *tr)
{ {
return true; (void)tr;
return true;
} }
bool stun_trans_writing (const stun_trans_t *tr) bool stun_trans_writing (const stun_trans_t *tr)
{ {
return false; (void)tr;
return false;
} }
...@@ -298,43 +300,43 @@ bool stun_trans_writing (const stun_trans_t *tr) ...@@ -298,43 +300,43 @@ bool stun_trans_writing (const stun_trans_t *tr)
static int static int
stun_trans_send (stun_trans_t *tr) stun_trans_send (stun_trans_t *tr)
{ {
const uint8_t *data = tr->msg.buf + tr->msg.offset; const uint8_t *data = tr->msg.buf + tr->msg.offset;
size_t len = tr->msg.length - tr->msg.offset; size_t len = tr->msg.length - tr->msg.offset;
ssize_t val; ssize_t val;
val = stun_sendto (tr->sock.fd, data, len, val = stun_sendto (tr->sock.fd, data, len,
(struct sockaddr *)&tr->sock.dst, tr->sock.dstlen); (struct sockaddr *)&tr->sock.dst, tr->sock.dstlen);
if (val < 0) if (val < 0)
return errno; return errno;
/* Message sent succesfully! */ /* Message sent succesfully! */
tr->msg.offset += val; tr->msg.offset += val;
assert (tr->msg.offset <= tr->msg.length); assert (tr->msg.offset <= tr->msg.length);
return 0; return 0;
} }
int stun_trans_tick (stun_trans_t *tr) int stun_trans_tick (stun_trans_t *tr)
{ {
assert (tr->sock.fd != -1); assert (tr->sock.fd != -1);
switch (stun_timer_refresh (&tr->timer)) switch (stun_timer_refresh (&tr->timer))
{ {
case -1: case -1:
DBG ("STUN transaction @%p failed: time out.\n", tr); DBG ("STUN transaction @%p failed: time out.\n", tr);
return ETIMEDOUT; // fatal error! return ETIMEDOUT; // fatal error!
case 0: case 0:
/* Retransmit can only happen with non reliable transport */ /* Retransmit can only happen with non reliable transport */
assert ((tr->flags & TRANS_RELIABLE) == 0); assert ((tr->flags & TRANS_RELIABLE) == 0);
tr->msg.offset = 0; tr->msg.offset = 0;
stun_trans_send (tr); stun_trans_send (tr);
DBG ("STUN transaction @%p retransmitted (timeout: %ums).\n", tr, DBG ("STUN transaction @%p retransmitted (timeout: %ums).\n", tr,
stun_trans_timeout (tr)); stun_trans_timeout (tr));
} }
return EAGAIN; return EAGAIN;
} }
...@@ -347,54 +349,54 @@ int stun_trans_tick (stun_trans_t *tr) ...@@ -347,54 +349,54 @@ int stun_trans_tick (stun_trans_t *tr)
static int stun_trans_wait (stun_trans_t *tr) static int stun_trans_wait (stun_trans_t *tr)
{ {
#ifdef HAVE_POLL #ifdef HAVE_POLL
int val = 0; int val = 0;
do do
{ {
struct pollfd ufd; struct pollfd ufd;
unsigned delay = stun_trans_timeout (tr); unsigned delay = stun_trans_timeout (tr);
memset (&ufd, 0, sizeof (ufd)); memset (&ufd, 0, sizeof (ufd));
ufd.fd = stun_trans_fd (tr); ufd.fd = stun_trans_fd (tr);
if (stun_trans_writing (tr)) if (stun_trans_writing (tr))
ufd.events |= POLLOUT; ufd.events |= POLLOUT;
if (stun_trans_reading (tr)) if (stun_trans_reading (tr))
ufd.events |= POLLIN; ufd.events |= POLLIN;
if (poll (&ufd, 1, delay) <= 0) if (poll (&ufd, 1, delay) <= 0)
{ {
val = stun_trans_tick (tr); val = stun_trans_tick (tr);
continue; continue;
} }
val = 0; val = 0;
} }
while (val == EAGAIN); while (val == EAGAIN);
return val; return val;
#else #else
(void)tr; (void)tr;
return ENOSYS; return ENOSYS;
#endif #endif
} }
int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen) int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen)
{ {
for (;;) for (;;)
{ {
ssize_t val = stun_trans_wait (tr); ssize_t val = stun_trans_wait (tr);
if (val) if (val)
{ {
errno = val /* = ETIMEDOUT */; errno = val /* = ETIMEDOUT */;
return -1; return -1;
} }
val = stun_recv (tr->sock.fd, buf, buflen); val = stun_recv (tr->sock.fd, buf, buflen);
if (val >= 0) if (val >= 0)
return val; return val;
} }
} }
...@@ -402,72 +404,72 @@ int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen) ...@@ -402,72 +404,72 @@ int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen)
int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode, int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode,
const void *restrict buf, size_t len) const void *restrict buf, size_t len)
{ {
assert (pcode != NULL); assert (pcode != NULL);
/* FIXME: possible infinite loop */ /* FIXME: possible infinite loop */
if (stun_validate (buf, len) <= 0) if (stun_validate (buf, len) <= 0)
return EAGAIN; return EAGAIN;
DBG ("Received %u-bytes STUN message\n", DBG ("Received %u-bytes STUN message\n",
(unsigned)stun_validate (buf, len)); (unsigned)stun_validate (buf, len));
/* NOTE: currently we ignore unauthenticated messages if the context /* NOTE: currently we ignore unauthenticated messages if the context
* is authenticated, for security reasons. */ * is authenticated, for security reasons. */
if (!stun_match_messages (buf, tr->msg.buf, tr->key.value, tr->key.length, if (!stun_match_messages (buf, tr->msg.buf, tr->key.value, tr->key.length,
pcode)) pcode))
return EAGAIN; return EAGAIN;
if (*pcode >= 0) if (*pcode >= 0)
{ {
DBG (" STUN error message received (code: %d)\n", *pcode); DBG (" STUN error message received (code: %d)\n", *pcode);
/* ALTERNATE-SERVER mechanism */ /* ALTERNATE-SERVER mechanism */
if ((tr->key.value != NULL) && ((*pcode / 100) == 3)) if ((tr->key.value != NULL) && ((*pcode / 100) == 3))
{ {
struct sockaddr_storage srv; struct sockaddr_storage srv;
socklen_t slen = sizeof (srv); socklen_t slen = sizeof (srv);
if (stun_find_addr (buf, STUN_ALTERNATE_SERVER, if (stun_find_addr (buf, STUN_ALTERNATE_SERVER,
(struct sockaddr *)&srv, &slen)) (struct sockaddr *)&srv, &slen))
{ {
DBG (" Unexpectedly missing ALTERNATE-SERVER attribute\n"); DBG (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
return ECONNREFUSED; return ECONNREFUSED;
} }
if (tr->sock.dstlen == 0) if (tr->sock.dstlen == 0)
{ {
if (connect (tr->sock.fd, (struct sockaddr *)&srv, slen)) if (connect (tr->sock.fd, (struct sockaddr *)&srv, slen))
{ {
/* This error case includes address family mismatch */ /* This error case includes address family mismatch */
DBG (" Error switching to alternate server: %s\n", DBG (" Error switching to alternate server: %s\n",
strerror (errno)); strerror (errno));
return ECONNREFUSED; return ECONNREFUSED;
} }
} }
else else
{ {
if ((tr->sock.dst.ss_family != srv.ss_family) if ((tr->sock.dst.ss_family != srv.ss_family)
|| (slen > sizeof (tr->sock.dst))) || (slen > sizeof (tr->sock.dst)))
{ {
DBG (" Unsupported alternate server\n"); DBG (" Unsupported alternate server\n");
return ECONNREFUSED; return ECONNREFUSED;
} }
memcpy (&tr->sock.dst, &srv, tr->sock.dstlen = slen); memcpy (&tr->sock.dst, &srv, tr->sock.dstlen = slen);
} }
DBG (" Restarting with alternate server\n"); DBG (" Restarting with alternate server\n");
if (stun_trans_start (tr) == 0) if (stun_trans_start (tr) == 0)
return EAGAIN; return EAGAIN;
DBG (" Restart failed!\n"); DBG (" Restart failed!\n");
} }
return ECONNREFUSED; return ECONNREFUSED;
} }
if (stun_has_unknown (buf)) if (stun_has_unknown (buf))
return EPROTO; return EPROTO;
return 0; return 0;
} }
...@@ -48,27 +48,27 @@ ...@@ -48,27 +48,27 @@
typedef struct stun_trans_s typedef struct stun_trans_s
{ {
stun_timer_t timer; stun_timer_t timer;
unsigned flags; unsigned flags;
struct struct
{ {
size_t length, offset; size_t length, offset;
uint8_t buf[STUN_MAXMSG]; uint8_t buf[STUN_MAXMSG];
} msg; } msg;
struct struct
{ {
int fd; int fd;
socklen_t dstlen; socklen_t dstlen;
struct sockaddr_storage dst; struct sockaddr_storage dst;
} sock; } sock;
struct struct
{ {
size_t length; size_t length;
uint8_t *value; uint8_t *value;
} key; } key;
} stun_trans_t; } stun_trans_t;
...@@ -171,12 +171,12 @@ ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen, ...@@ -171,12 +171,12 @@ ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen,
static inline ssize_t stun_send (int fd, const uint8_t *buf, size_t len) static inline ssize_t stun_send (int fd, const uint8_t *buf, size_t len)
{ {
return stun_sendto (fd, buf, len, NULL, 0); return stun_sendto (fd, buf, len, NULL, 0);
} }
static inline ssize_t stun_recv (int fd, uint8_t *buf, size_t maxlen) static inline ssize_t stun_recv (int fd, uint8_t *buf, size_t maxlen)
{ {
return stun_recvfrom (fd, buf, maxlen, NULL, NULL); return stun_recvfrom (fd, buf, maxlen, NULL, NULL);
} }
# ifdef __cplusplus # ifdef __cplusplus
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment