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