Commit 307d62f7 authored by Kai Vehmanen's avatar Kai Vehmanen

Major STUN update. Completed support for ICE connectivity checks. Added...

Major STUN update. Completed support for ICE connectivity checks. Added NICEAPI_EXPORT attributes. Added initial code for TURN relay support. The old STUN API is still present but not compiled.

darcs-hash:20070619075737-77cd4-93e3509da656acdadff0afa36eac3b8569b5ef20.gz
parent f37d8947
......@@ -11,27 +11,30 @@ DIST_SUBDIRS = tests
include $(top_srcdir)/common.mk
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CFLAGS = -std=gnu99 $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)
LIBS = $(GLIB_LIBS)
noinst_LTLIBRARIES = libstun.la
libstun_la_SOURCES = stun.h stun.c \
libstun_la_SOURCES = \
stun-msg.h stunsend.c stunrecv.c crc32.c hmac.c \
timer.h timer.c trans.h trans.c \
bind.h conncheck.h bind.c bindserv.c
bind.h stun-ice.h bind.c bindserv.c \
relay.h relay.c
libstun_la_LIBADD = $(LIBS) $(OPENSSL_LIBS) $(LIBRT)
noinst_PROGRAMS = stun-client
EXTRA_libstun_la_SOURCES = stun.h stun.c
EXTRA_PROGRAMS = stun-client
bin_PROGRAMS = stund stunbdc
check_PROGRAMS = stund
stun_client_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread
stunbdc_LDADD = libstun.la
check_PROGRAMS = \
EXTRA_PROGRAMS += \
test-attribute-pack \
test-attribute-pack-unknown \
test-attribute-dump \
......@@ -57,6 +60,3 @@ test_message_dump_LDADD = libstun.la
test_message_dump_unknown_LDADD = libstun.la
test_message_unpack_LDADD = libstun.la
test_message_find_attribute_LDADD = libstun.la
TESTS = $(check_PROGRAMS)
......@@ -41,6 +41,7 @@
#include <sys/socket.h>
#include "bind.h"
#include "stun-msg.h"
#include <assert.h>
#include <string.h>
......@@ -50,7 +51,9 @@
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/poll.h>
#ifdef HAVE_POLL
# include <sys/poll.h>
#endif
#include <fcntl.h>
/** Blocking mode STUN binding discovery */
......@@ -59,8 +62,11 @@ int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
#ifdef HAVE_POLL
stun_bind_t *ctx;
int val;
uint8_t buf[STUN_MAXMSG];
size_t len = 0;
ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
......@@ -68,30 +74,39 @@ int stun_bind_run (int fd,
do
{
unsigned delay = stun_bind_timeout (ctx);
struct pollfd ufd[1];
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd));
ufd[0].fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN;
poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay);
val = stun_bind_resume (ctx, addr, addrlen);
val = recv (ufd[0].fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
val = stun_bind_elapse (ctx);
else
{
len += val;
val = stun_bind_process (ctx, buf, len, addr, addrlen);
}
}
while (val == EAGAIN);
return val;
#else
(void)fd; (void)srv; (void)srvlen; (void)addr; (void)addrlen;
return ENOSYS;
#endif
}
/** Non-blocking mode STUN binding discovery */
#include "stun-msg.h"
#include "trans.h"
struct stun_bind_s
{
stun_trans_t trans;
size_t keylen;
uint8_t key[0];
};
......@@ -113,22 +128,26 @@ static int
stun_bind_alloc (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
int val;
stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return ENOMEM;
memset (ctx, 0, sizeof (*ctx));
*context = ctx;
int val = stun_trans_init (&ctx->trans, fd, 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;
}
ctx->keylen = (size_t)(-1);
stun_init_request (ctx->trans.msg, STUN_BINDING);
stun_init_request (ctx->trans.msg.buf, STUN_BINDING);
return 0;
}
......@@ -162,8 +181,8 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
ctx = *context;
ctx->trans.msglen = sizeof (ctx->trans.msg);
val = stun_finish (ctx->trans.msg, &ctx->trans.msglen);
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);
......@@ -205,31 +224,20 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
bool error;
int val = stun_validate (buf, len);
if (val <= 0)
return EAGAIN;
int val;
assert (ctx != NULL);
DBG ("Received %u-bytes STUN message\n", (unsigned)val);
if (!stun_match_messages (buf, ctx->trans.msg,
(ctx->keylen != (size_t)(-1)) ? ctx->key : NULL,
(ctx->keylen != (size_t)(-1)) ? ctx->keylen : 0,
&error))
return EAGAIN;
if (error)
{
stun_bind_cancel (ctx);
return ECONNREFUSED; // FIXME: better error value
}
if (stun_has_unknown (buf))
val = stun_trans_preprocess (&ctx->trans, buf, len);
switch (val)
{
case EAGAIN:
return EAGAIN;
case 0:
break;
default:
stun_bind_cancel (ctx);
return EPROTO;
return val;
}
val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen);
......@@ -251,24 +259,27 @@ int stun_bind_process (stun_bind_t *restrict ctx,
}
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
stun_msg_t buf;
ssize_t len;
/** ICE keep-alives (Binding discovery indication!) */
assert (context != NULL);
int
stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen)
{
size_t val;
uint8_t buf[28];
len = recv (context->trans.fd, &buf, sizeof (buf), MSG_DONTWAIT);
if (len >= 0)
return stun_bind_process (context, &buf, len, addr, addrlen);
stun_init_indication (buf, STUN_BINDING);
(void)stun_finish (buf, &val);
return stun_bind_elapse (context);
/* NOTE: hopefully, this is only needed for non-stream sockets */
if (sendto (fd, buf, val, MSG_DONTWAIT, srv, srvlen) == -1)
return errno;
return 0;
}
/** Connectivity checks */
#include "conncheck.h"
#include "stun-ice.h"
int
stun_conncheck_start (stun_bind_t **restrict context, int fd,
......@@ -283,43 +294,43 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd,
assert (username != NULL);
assert (password != NULL);
val = stun_bind_alloc (context, fd, srv, srvlen);
val = stun_bind_alloc (&ctx, fd, srv, srvlen);
if (val)
return val;
val = strlen (password);
ctx = realloc (*context, sizeof (*ctx) + val + 1);
if (ctx == NULL)
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->key, password, val);
ctx->keylen = val;
memcpy (ctx->trans.key.value, password, ctx->trans.key.length);
if (cand_use)
{
val = stun_append_flag (ctx->trans.msg, sizeof (ctx->trans.msg),
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, sizeof (ctx->trans.msg),
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, sizeof (ctx->trans.msg),
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.msglen = sizeof (ctx->trans.msg);
val = stun_finish_short (ctx->trans.msg, &ctx->trans.msglen,
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length,
username, password, NULL, 0);
if (val)
goto error;
......
......@@ -77,7 +77,7 @@ int stun_bind_run (int fd,
* Starts STUN Binding discovery in non-blocking mode.
*
* @param context pointer to an opaque pointer that will be passed to
* stun_bind_resume() afterward
* other stun_bind_*() functions afterward
* @param fd socket to use for discovery, or -1 to create one
* @param srv STUN server socket address
* @param srvlen STUN server socket address length
......@@ -156,24 +156,15 @@ int stun_bind_process (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen);
/**
* Continues STUN Binding discovery in non-blocking mode:
* Tries to dequeue a data from the network and processes it,
* updates the transaction timer if needed.
*
* @param context binding discovery context (from stun_bind_start())
* @param addr pointer to a socket address structure to hold the discovered
* binding (remember this can be either IPv4 or IPv6 regardless of the socket
* family) [OUT]
* @param addrlen pointer to the byte length of @a addr [IN], set to the byte
* length of the binding socket address on return.
* Sends a STUN Binding indication, aka ICE keep-alive packet.
*
* @return EAGAIN is returned if the discovery has not completed yet.
0 is returned on successful completion, another standard error value
* otherwise. If the return value is not EAGAIN, @a context is freed and must
* not be re-used.
* @param fd socket descriptor to send packet through
* @param srv destination socket address (possibly NULL if connected)
* @param srvlen destination socket address length (possibly 0)
* @return 0 on success, an error code from sendto() otherwise.
*/
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen);
int stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen);
/**
......
......@@ -69,8 +69,6 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen,
bool muxed, const char *restrict pass)
{
assert (plen != NULL);
size_t len = *plen;
int val;
*plen = 0;
......@@ -82,7 +80,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
if (stun_get_class (msg) != STUN_REQUEST)
{
DBG (" Unhandled non-request (class %u) message.\n",
(unsigned)stun_get_class (msg));
stun_get_class (msg));
return EINVAL;
}
......@@ -133,7 +131,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
if (stun_get_method (msg) != STUN_BINDING)
{
DBG (" Bad request (method %u) message.\n",
(unsigned)stun_get_method (msg));
stun_get_method (msg));
err (STUN_BAD_REQUEST);
return EPROTO;
}
......@@ -185,7 +183,7 @@ stun_bind_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
/** Connectivity checks **/
#include "conncheck.h"
#include "stun-ice.h"
int
stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
......@@ -229,16 +227,20 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen)
{
size_t i;
ssize_t len = stun_find_string (msg, STUN_USERNAME, buf, buflen);
if ((len == -1) || ((size_t)len >= buflen))
return NULL;
for (size_t i = 0; i < (size_t)len; i++)
for (i = 0; i < (size_t)len; 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 >= 'a') && (c <= 'z')) || (c == '+') || (c == ':'))
continue;
return NULL;
}
......@@ -249,6 +251,7 @@ char *stun_conncheck_username (const uint8_t *restrict msg,
uint32_t stun_conncheck_priority (const uint8_t *msg)
{
uint32_t value;
if (stun_find32 (msg, STUN_PRIORITY, &value))
return 0;
return value;
......
......@@ -41,7 +41,6 @@
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <sys/socket.h>
#include "stun-msg.h"
......@@ -55,12 +54,8 @@ static uint32_t crc32 (const void *buf, size_t size);
*/
uint32_t stun_fingerprint (const uint8_t *msg)
{
assert (msg != NULL);
/* Don't hash last 8-bytes (= the FINGERPRINT attribute) */
assert (stun_length (msg) >= 8);
size_t len = 20u + stun_length (msg) - 8;
size_t len = 12u + stun_length (msg); // 20 - 8 = 12
return crc32 (msg, len) ^ 0x5354554e;
}
......
......@@ -38,11 +38,14 @@
#endif
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include "stun-msg.h"
#include <string.h>
#include <assert.h>
void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen)
......@@ -59,3 +62,39 @@ void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen
HMAC (EVP_sha1 (), key, keylen, msg, mlen, sha, 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);
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Rémi Denis-Courmont
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Rémi Denis-Courmont, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "relay.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "stun-msg.h"
#include "trans.h"
struct turn_s
{
stun_trans_t trans;
};
/**
* @file relay.c
* @brief STUN relay usage (TURN) implementation
*/
turn_t *turn_socket (int fd, int family, turn_proto_t proto,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
turn_t *ctx;
int val;
if (family != AF_INET)
{
errno = EAFNOSUPPORT;
return NULL;
}
if (proto != TURN_PROTO_UDP)
{
errno = EPROTONOSUPPORT;
return NULL;
}
ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return NULL;
memset (ctx, 0, sizeof (*ctx));
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);
errno = val;
return NULL;
}
stun_init_request (ctx->trans.msg.buf, STUN_ALLOCATE);
return ctx;
}
int turn_connect (turn_t *restrict ctx, const struct sockaddr *restrict dst,
socklen_t len)
{
assert (ctx != NULL);
(void)ctx; (void)dst; (void)len;
errno = ENOSYS;
return -1;
}
ssize_t turn_sendto (turn_t *restrict ctx, const void *data, size_t datalen,
int flags, const struct sockaddr *restrict dst,
socklen_t dstlen)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)datalen; (void)flags; (void)dst; (void)dstlen;
errno = ENOSYS;
return -1;
}
ssize_t turn_send (turn_t *restrict ctx, const void *data, size_t len,
int flags)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)len; (void)flags;
errno = ENOSYS;
return -1;
}
ssize_t turn_recvfrom (turn_t *restrict ctx, void *data, size_t len, int flags,
const struct sockaddr *restrict src, socklen_t *srclen)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)len; (void)flags; (void)src; (void)srclen;
errno = ENOSYS;
return -1;
}
ssize_t turn_recv (turn_t *restrict ctx, void *data, size_t len, int flags)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)len; (void)flags;
errno = ENOSYS;
return -1;
}
int turn_getsockname (turn_t *restrict ctx,
const struct sockaddr *restrict name, socklen_t *len)
{
assert (ctx != NULL);
(void)ctx; (void)name; (void)len;
errno = ENOSYS;
return -1;
}
int turn_getpeername (turn_t *restrict ctx,
const struct sockaddr *restrict name, socklen_t *len)
{
assert (ctx != NULL);
(void)ctx; (void)name; (void)len;
errno = ENOSYS;
return -1;
}
int turn_close (turn_t *restrict ctx)
{
assert (ctx != NULL);
stun_trans_deinit (&ctx->trans);
free (ctx);
return 0;
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Rémi Denis-Courmont
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Rémi Denis-Courmont, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifndef STUN_RELAY_H
# define STUN_RELAY_H 1
/**
* @file relay.h
* @brief STUN relay usage (TURN)
*/
typedef struct turn_s turn_t;
typedef enum
{
TURN_PROTO_TCP=6,
TURN_PROTO_UDP=17
} turn_proto_t;
# ifdef __cplusplus
extern "C" {
# endif
turn_t *turn_socket (int fd, int family, turn_proto_t proto,
const struct sockaddr *restrict srv, socklen_t srvlen);
int turn_setbandwidth (turn_t *ctx, unsigned kbits);
int turn_setrealm (turn_t *restrict ctx, const char *realm);
int turn_setusername (turn_t *restrict ctx, const char *username);
int turn_setpassword (turn_t *restrict ctx, const char *password);
int turn_connect (turn_t *restrict ctx, const struct sockaddr *restrict dst,
socklen_t len);
ssize_t turn_sendto (turn_t *restrict ctx, const void *data, size_t datalen,
int flags, const struct sockaddr *restrict dst,
socklen_t dstlen);
ssize_t turn_send (turn_t *restrict ctx, const void *data, size_t len,
int flags);
ssize_t turn_recvfrom (turn_t *restrict ctx, void *data, size_t len, int flags,
const struct sockaddr *restrict src, socklen_t *srclen);
ssize_t turn_recv (turn_t *restrict ctx, void *data, size_t len, int flags);
int turn_getsockname (turn_t *restrict ctx,
const struct sockaddr *restrict name, socklen_t *len);
int turn_getpeername (turn_t *restrict ctx,
const struct sockaddr *restrict name, socklen_t *len);
int turn_close (turn_t *ctx);
# ifdef __cplusplus
}
# endif
#endif
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h>
#include <netdb.h>
......
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Rémi Denis-Courmont
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Rémi Denis-Courmont, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifndef STUN_CONNCHECK_H
# define STUN_CONNCHECK_H 1
/**
* @file stun-ice.h
* @brief STUN/ICE connectivity checks
*/
# include "stun/bind.h"
# ifdef __cplusplus
extern "C" {
# endif
/**
* Starts a connectivity check using STUN Binding discovery.
*
* @param context pointer to an opaque pointer that will be passed to
* stun_bind_resume() afterward
* @param fd socket to use for discovery, or -1 to create one
* @param srv STUN server socket address
* @param srvlen STUN server socket address length
* @param username nul-terminated username for authentication
* (need not be kept valid after return)
* @param password nul-terminated shared secret (ICE password)
* (need not be kept valid after return)
* @param cand_use whether to include a USE-CANDIDATE flag
* @param priority host-byte order PRIORITY value
* @param controlling whether we are in controlling (true) or
* controlled (false) state
* @param tie control tie breaker value (host-byte order)
*
* @return 0 on success, a standard error value otherwise.
*/
int stun_conncheck_start (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
const char *username, const char *password,
bool cand_use, bool controlling, uint32_t priority,
uint64_t tie);
/**
* Tries to parse a STUN connectivity check (Binding request) and format a
* response accordingly.
*
* @param buf [OUT] output buffer to write a Binding response to. May refer
* to the same buffer space as the request message.
* @param plen [IN/OUT] output buffer size on entry, response length on exit
* @param msg pointer to the first byte of the binding request
* @param src socket address the message was received from
* @param srclen byte length of the socket address
* @param password HMAC secret password
* @param control [IN/OUT] whether we are controlling ICE or not
* @param tie tie breaker value for ICE role determination
*
* @return same as stun_bind_reply() with one additionnal error code:
* EACCES: ICE role conflict occured, please recheck the flag at @a control
*
* @note @a buf and @a msg <b>must not</b> collide.
*/
int
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);
/**
* Extracts the username from a STUN message.
* @param msg pointer to the first byte of the binding request
* @param buf where to store the username as a nul-terminated string
* @param buflen byte length of @a buf buffer
*
* @return @a buf on success, NULL on error
*/
char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen);
/**
* Extracts the priority from a STUN message.
* @param msg valid STUN message.
* @return host byte order priority, or 0 if not specified.
*/
uint32_t stun_conncheck_priority (const uint8_t *msg);
/**
* Extracts the "use candidate" flag from a STUN message.
* @param msg valid STUN message.
* @return true if the flag is set, false if not.
*/
bool stun_conncheck_use_candidate (const uint8_t *msg);
# ifdef __cplusplus
}
# endif
#endif
......@@ -38,6 +38,12 @@
#ifndef STUN_MSG_H
# define STUN_MSG_H 1
/**
* @file stun-msg.h
* @brief STUN low-level message formatting and parsing
*/
# ifndef NDEBUG
# include <stdio.h>
# include <stdarg.h>
......@@ -85,7 +91,8 @@ typedef enum
typedef enum
{
STUN_BINDING=0x001,
STUN_SHARED_SECRET=0x002
STUN_SHARED_SECRET=0x002,
STUN_ALLOCATE=0x003
} stun_method_t;
/* Attribute types */
......@@ -187,19 +194,20 @@ static inline uint16_t stun_length (const uint8_t *ptr)
/**
* @return STUN message class in host byte order (value from 0 to 3)
*/
static inline uint16_t stun_get_class (const uint8_t *msg)
static inline stun_class_t stun_get_class (const uint8_t *msg)
{
uint16_t t = stun_getw (msg);
return ((t & 0x0100) >> 7) | ((t & 0x0010) >> 4);
return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
}
/**
* @return STUN message method (value from 0 to 0xfff)
*/
static inline uint16_t stun_get_method (const uint8_t *msg)
static inline stun_method_t stun_get_method (const uint8_t *msg)
{
uint16_t t = stun_getw (msg);
return ((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) | (t & 0x000f);
return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
(t & 0x000f));
}
/**
......@@ -220,6 +228,11 @@ uint32_t stun_fingerprint (const uint8_t *msg);
void stun_sha1 (const uint8_t *msg, uint8_t *sha,
const void *key, size_t keylen);
/**
* Generates a pseudo-random secure STUN transaction ID.
*/
void stun_make_transid (stun_transid_t id);
int stun_xor_address (const uint8_t *msg,
struct sockaddr *addr, socklen_t addrlen);
......@@ -357,11 +370,64 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type,
bool stun_is_unknown (uint16_t type);
unsigned stun_find_unknown (const uint8_t *msg, uint16_t *list, unsigned max);
/* Message formatting functions */
/**
* @section stunsend
* @brief Message formatting functions
*/
/**
* Initializes a STUN request message buffer, with no attributes.
* @param m STUN message method (host byte order)
*/
void stun_init_request (uint8_t *msg, stun_method_t m);
/**
* Initializes a STUN indication message buffer, with no attributes.
* @param m STUN message method (host byte order)
*/
void stun_init_indication (uint8_t *msg, stun_method_t m);
/**
* Initializes a STUN message buffer with no attributes,
* in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param req STUN message query
*
* ans == req is allowed.
*/
void stun_init_response (uint8_t *ans, const uint8_t *req);
/**
* Initializes a STUN error response message buffer with an ERROR-CODE
* attribute, in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
* @param req STUN message to copy method and transaction ID from
* @param err host-byte order STUN integer error code
*
* @return 0 on success, ENOBUFS on error
*
* ans == req is allowed.
*/
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err);
/**
* Initializes a STUN error response message buffer, in response to a valid
* STUN request messsage with unknown attributes. STUN method, transaction ID
* and unknown attribute IDs are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
* @param req STUN request message
* @return 0 on success, ENOBUFS otherwise
*
* ans == req is allowed.
*/
int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req);
/**
......@@ -394,17 +460,75 @@ size_t stun_finish_short (uint8_t *msg, size_t *restrict plen,
*/
size_t stun_finish (uint8_t *restrict msg, size_t *restrict plen);
/**
* Appends an empty ("flag") attribute to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type);
/**
* Appends an attribute consisting of a 32-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append32 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint32_t value);
/**
* Appends an attribute consisting of a 64-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append64 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint64_t value);
/**
* Appends an attribute from a nul-terminated string.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param str nul-terminated string
* @return 0 on success, ENOBUFS on error.
*/
int stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str);
/**
* Appends an attribute consisting of a network address to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int stun_append_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
socklen_t addrlen);
/**
* Appends an attribute consisting of a xor'ed network address.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -69,7 +69,8 @@ printaddr (const char *str, const struct sockaddr *addr, socklen_t addrlen)
static int run (int family, const char *hostname, const char *service)
{
struct addrinfo hints, *res;
int retval = -1;
const struct addrinfo *ptr;
int ret = -1;
memset (&hints, 0, sizeof (hints));
hints.ai_family = family;
......@@ -77,18 +78,19 @@ static int run (int family, const char *hostname, const char *service)
if (service == NULL)
service = "3478";
int val = getaddrinfo (hostname, service, &hints, &res);
if (val)
ret = getaddrinfo (hostname, service, &hints, &res);
if (ret)
{
fprintf (stderr, "%s (port %s): %s\n", hostname, service,
gai_strerror (val));
gai_strerror (ret));
return -1;
}
for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
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);
......@@ -99,12 +101,12 @@ static int run (int family, const char *hostname, const char *service)
else
{
printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen);
retval = 0;
ret = 0;
}
}
freeaddrinfo (res);
return retval;
return ret;
}
......@@ -115,7 +117,8 @@ int main (int argc, char *argv[])
{ "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
const char *server = NULL, *port = NULL;
int family = AF_UNSPEC;
......@@ -149,6 +152,9 @@ int main (int argc, char *argv[])
printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n",
PACKAGE, VERSION);
return 0;
default:
return 2;
}
}
......
......@@ -108,11 +108,17 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
{
case AF_INET:
setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes));
#ifdef IP_RECVERR
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
#endif
break;
case AF_INET6:
setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes,
sizeof (yes));
#ifdef IPV6_RECVERR
setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
#endif
break;
}
}
......@@ -133,6 +139,16 @@ error:
}
static inline int err_dequeue (int fd)
{
#ifdef MSG_ERRQUEUE
struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr));
return recvmsg (fd, &hdr, MSG_ERRQUEUE) == 0;
#endif
}
#include "stun-msg.h" // FIXME: remove
#include "bind.h"
......@@ -156,45 +172,41 @@ static int dgram_process (int sock)
if (len < 0)
{
DBG ("Receive error: %s\n", strerror (errno));
return errno;
return -1;
}
if (mh.msg_flags & MSG_TRUNC)
{
DBG ("Truncated datagram ignored.\n");
return EMSGSIZE;
return -1;
}
if (stun_validate (buf, len) <= 0)
return EINVAL;
return -1;
len = stun_bind_reply (buf, &iov.iov_len, buf,
mh.msg_name, mh.msg_namelen, false);
if (iov.iov_len == 0)
return len;
return -1;
do
len = sendmsg (sock, &mh, 0);
if (len == -1)
return errno;
if ((size_t)len < iov.iov_len)
return EMSGSIZE;
while ((len == -1) && err_dequeue (sock));
if (len < (ssize_t)iov.iov_len)
return -1;
return 0;
}
static int run (int family, int protocol)
static int run (int family, int protocol, unsigned port)
{
int sock = listen_socket (family, SOCK_DGRAM, protocol,
htons (IPPORT_STUN));
int sock = listen_socket (family, SOCK_DGRAM, protocol, htons (port));
if (sock == -1)
return -1;
for (;;)
{
int val = dgram_process (sock);
if (val)
DBG ("stund: %s\n", strerror (val));
}
dgram_process (sock);
}
......@@ -210,6 +222,7 @@ static void exit_handler (int signum)
int main (int argc, char *argv[])
{
int family = AF_INET;
unsigned port = IPPORT_STUN;
for (;;)
{
......@@ -229,7 +242,10 @@ int main (int argc, char *argv[])
}
}
if (optind < argc)
port = atoi (argv[optind++]);
signal (SIGINT, exit_handler);
signal (SIGTERM, exit_handler);
return -run (family, IPPROTO_UDP);
return run (family, IPPROTO_UDP, port) ? EXIT_FAILURE : EXIT_SUCCESS;
}
......@@ -58,6 +58,8 @@ int stun_valid (const uint8_t *msg)
ssize_t stun_validate (const uint8_t *msg, size_t len)
{
size_t mlen;
if (len < 1)
{
DBG ("STUN error: No data!\n");
......@@ -76,7 +78,7 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
return 0;
}
size_t mlen = 20u + stun_length (msg);
mlen = 20u + stun_length (msg);
if (stun_padding (mlen))
{
DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
......@@ -85,29 +87,33 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
if (len < mlen)
{
DBG ("STUN error: Incomplete message: %u of %u bytes!\n", len,
(unsigned)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 */
for (const uint8_t *end = msg + (mlen - 20); end > msg;)
while (len > 0)
{
msg += 4;
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 ((end - msg) >= 0);
assert (len >= 4);
len -= 4;
size_t alen = stun_align (stun_getw (msg - 2));
msg += alen;
if ((end - msg) < 0)
if (len < alen)
{
DBG ("STUN error: %u instead of %u bytes for attribute!\n",
(unsigned)(end - (msg - alen)), (unsigned)alen);
(unsigned)len, (unsigned)alen);
return -1; // no room for attribute value + padding
}
len -= alen;
msg += 4 + alen;
}
return mlen;
......@@ -126,18 +132,19 @@ static const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen)
{
assert (msg != NULL);
size_t length = stun_length (msg);
assert (stun_valid (msg));
assert (palen != NULL);
size_t length = stun_length (msg);
msg += 20;
while (length > 0)
{
assert (length >= 4);
size_t alen = stun_length (msg);
uint16_t atype = stun_getw (msg);
unsigned alen = stun_length (msg);
assert (length >= 4);
length -= 4;
msg += 4;
......@@ -161,15 +168,17 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
bool stun_present (const uint8_t *msg, stun_attr_type_t type)
{
return stun_find (msg, type, &(uint16_t){ 0 }) != NULL;
uint16_t dummy;
return stun_find (msg, type, &dummy) != NULL;
}
int stun_find_flag (const uint8_t *msg, stun_attr_type_t type)
{
const void *ptr;
uint16_t len;
const void *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
return (len == 0) ? 0 : EINVAL;
......@@ -180,14 +189,17 @@ int
stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type,
uint32_t *restrict pval)
{
const void *ptr;
uint16_t len;
const void *ptr = stun_find (msg, type, &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;
......@@ -198,14 +210,17 @@ stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type,
int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
{
const void *ptr;
uint16_t len;
const void *ptr = stun_find (msg, type, &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;
......@@ -217,8 +232,10 @@ int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
ssize_t stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t buflen)
{
const char *ptr;
uint16_t len;
const char *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return -1;
......@@ -234,8 +251,10 @@ 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;
const uint8_t *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
......@@ -309,11 +328,13 @@ int stun_xor_address (const uint8_t *restrict msg,
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 (unsigned i = 0; i < 16; i++)
for (i = 0; i < 16; i++)
ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i];
return 0;
}
......@@ -376,7 +397,8 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type,
static inline bool check_cookie (const uint8_t *msg)
{
return memcmp (msg + 4, &(uint32_t){ htonl (STUN_COOKIE) }, 4) == 0;
uint32_t cookie = htonl (STUN_COOKIE);
return memcmp (msg + 4, &cookie, 4) == 0;
}
......@@ -388,6 +410,10 @@ static const uint8_t *stun_end (const uint8_t *msg)
bool stun_demux (const uint8_t *msg)
{
const void *fpr;
uint32_t crc32;
uint16_t fprlen;
assert (stun_valid (msg));
DBG ("Demultiplexing STUN message @%p\n", msg);
......@@ -400,8 +426,7 @@ bool stun_demux (const uint8_t *msg)
}
/* Looks for FINGERPRINT */
uint16_t fprlen;
const void *fpr = stun_end (msg) - 4;
fpr = stun_end (msg) - 4;
if ((fpr != stun_find (msg, STUN_FINGERPRINT, &fprlen)) || (fprlen != 4))
{
DBG (" No FINGERPRINT attribute!\n");
......@@ -409,7 +434,7 @@ bool stun_demux (const uint8_t *msg)
}
/* Checks FINGERPRINT */
uint32_t crc32 = htonl (stun_fingerprint (msg));
crc32 = htonl (stun_fingerprint (msg));
if (memcmp (fpr, &crc32, 4))
{
DBG (" Incorrect message fingerprint (expected: 0x%08x)!\n",
......@@ -433,6 +458,7 @@ 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;
......@@ -441,7 +467,7 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
DBG ("Authenticating STUN message @%p\n", msg);
const uint8_t *hash = stun_end (msg) - 20;
hash = stun_end (msg) - 20;
if (stun_demux (msg))
hash -= 8; // room for FINGERPRINT at the end
......@@ -455,17 +481,21 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
stun_sha1 (msg, sha, key, keylen);
if (memcmp (sha, hash, sizeof (sha)))
{
#ifndef NDEBUG
unsigned i;
DBG (" Message HMAC-SHA1 fingerprint mismatch!"
"\n key : 0x");
for (unsigned i = 0; i < keylen; i++)
for (i = 0; i < keylen; i++)
DBG ("%02x", ((uint8_t *)key)[i]);
DBG ("\n expected: 0x");
for (unsigned i = 0; i < 20; i++)
for (i = 0; i < 20; i++)
DBG ("%02x", sha[i]);
DBG ("\n received: 0x");
for (unsigned i = 0; i < 20; i++)
for (i = 0; i < 20; i++)
DBG ("%02x", hash[i]);
DBG ("\n");
#endif
return EPERM;
}
......@@ -623,18 +653,19 @@ stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
while ((len > 0) && (count < max))
{
uint16_t type = stun_getw (msg);
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 (type)
&& stun_is_unknown (type))
if (!stun_optional (atype)
&& stun_is_unknown (atype))
{
DBG (" found unknown attribute: 0x%04x (%u bytes)\n",
(unsigned)type, (unsigned)alen);
list[count++] = type;
(unsigned)atype, (unsigned)alen);
list[count++] = atype;
}
}
......
......@@ -48,7 +48,7 @@
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
static inline
void *stun_setw (uint8_t *ptr, uint16_t value)
......@@ -74,25 +74,6 @@ void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
}
static void stun_make_transid (stun_transid_t id)
{
static struct
{
pthread_mutex_t lock;
uint64_t counter;
} store = { PTHREAD_MUTEX_INITIALIZER, 0 };
uint64_t counter;
pthread_mutex_lock (&store.lock);
counter = store.counter++;
pthread_mutex_unlock (&store.lock);
/* FIXME: generate a random key and use HMAC or something... */
memset (id, 0, 4);
memcpy (id + 4, &counter, 8);
}
/**
* Initializes a STUN message buffer, with no attributes.
* @param c STUN message class (host byte order)
......@@ -112,10 +93,6 @@ static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m,
}
/**
* Initializes a STUN request message buffer, with no attributes.
* @param m STUN message method (host byte order)
*/
void stun_init_request (uint8_t *req, stun_method_t m)
{
stun_transid_t id;
......@@ -125,16 +102,15 @@ void stun_init_request (uint8_t *req, stun_method_t m)
}
/**
* Initializes a STUN message buffer with no attributes,
* in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param req STUN message query
*
* ans == req is allowed.
*/
void stun_init_indication (uint8_t *req, stun_method_t m)
{
stun_transid_t id;
stun_make_transid (id);
stun_init (req, STUN_INDICATION, m, id);
}
void stun_init_response (uint8_t *ans, const uint8_t *req)
{
//assert (stun_valid (req));
......@@ -158,7 +134,9 @@ void stun_init_response (uint8_t *ans, const uint8_t *req)
static 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);
assert (stun_padding (mlen) == 0);
if (msize > STUN_MAXMSG)
......@@ -169,7 +147,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
assert (length < 0xffff);
uint8_t *a = msg + 20u + mlen;
a = msg + 20u + mlen;
a = stun_setw (a, type);
a = stun_setw (a, length);
......@@ -205,27 +183,12 @@ stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
}
/**
* Appends an empty ("flag") attribute to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type)
{
return stun_append_bytes (msg, msize, type, NULL, 0);
}
/**
* Appends an attribute consisting of a 32-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int
stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint32_t value)
......@@ -235,14 +198,6 @@ stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type,
}
/**
* Appends an attribute consisting of a 64-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint64_t value)
{
......@@ -253,14 +208,6 @@ int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type,
}
/**
* Appends an attribute from a nul-terminated string.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param str nul-terminated string
* @return 0 on success, ENOBUFS on error.
*/
int
stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str)
......@@ -298,12 +245,14 @@ static const char *stun_strerror (stun_error_t code)
{ STUN_GLOBAL_FAILURE, "Unrecoverable failure" },
{ 0, "" }
};
unsigned i;
for (unsigned i = 0; tab[i].phrase[0]; i++)
for (i = 0; tab[i].phrase[0]; i++)
{
if (tab[i].code == code)
return tab[i].phrase;
}
return "Unknown error";
}
......@@ -335,20 +284,6 @@ stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code)
}
/**
* Initializes a STUN error response message buffer with an ERROR-CODE
* attribute, in response to a given valid STUN request messsage.
* STUN method and transaction ID are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
* @param req STUN message to copy method and transaction ID from
* @param err host-byte order STUN integer error code
*
* @return 0 on success, ENOBUFS on error
*
* ans == req is allowed.
*/
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err)
{
......@@ -359,22 +294,14 @@ int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
}
/**
* Initializes a STUN error response message buffer, in response to a valid
* STUN request messsage with unknown attributes. STUN method, transaction ID
* and unknown attribute IDs are copied from the request message.
*
* @param ans [OUT] STUN message buffer
* @param msize STUN message buffer size
* @param req STUN request message
* @return 0 on success, ENOBUFS otherwise
*
* ans == req is allowed.
*/
int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
{
unsigned counter, i;
#ifdef HAVE_C_VARARRAYS
uint16_t ids[stun_length (req) / 4];
unsigned counter;
#else
uint16_t ids[256];
#endif
//assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
......@@ -385,35 +312,25 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
if (stun_init_error (ans, msize, req, STUN_UNKNOWN_ATTRIBUTE))
return ENOBUFS;
for (unsigned i = 0; i < counter; i++)
for (i = 0; i < counter; i++)
ids[i] = htons (ids[i]);
return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids,
counter * 2);
}
/**
* Appends an attribute consisting of a network address to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int
stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const struct sockaddr *restrict addr, socklen_t addrlen)
{
if (addrlen < sizeof (struct sockaddr))
return EINVAL;
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:
......@@ -444,7 +361,7 @@ stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
return EAFNOSUPPORT;
}
uint8_t *ptr = stun_append (msg, msize, type, 4 + alen);
ptr = stun_append (msg, msize, type, 4 + alen);
if (ptr == NULL)
return ENOBUFS;
......@@ -456,35 +373,24 @@ stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
}
/**
* Appends an attribute consisting of a xor'ed network address.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
socklen_t addrlen)
{
union
{
struct sockaddr addr;
char buf[addrlen];
} xor;
struct sockaddr_storage xor;
int val;
memcpy (xor.buf, addr, addrlen);
val = stun_xor_address (msg, &xor.addr, addrlen);
if (addrlen > sizeof (xor))
return ENOBUFS;
memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
if (val)
return val;
return stun_append_addr (msg, msize, type, &xor.addr, addrlen);
return stun_append_addr (msg, msize, type, (struct sockaddr *)&xor,
addrlen);
}
......@@ -494,11 +400,10 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
const void *key, size_t keylen,
const void *nonce, size_t noncelen)
{
assert (plen != NULL);
size_t len = *plen;
uint8_t *sha = NULL;
uint8_t *sha = NULL, *crc;
int val = ENOBUFS;
uint32_t fpr;
if (realm != NULL)
{
......@@ -528,7 +433,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
return ENOBUFS;
}
void *crc = stun_append (msg, len, STUN_FINGERPRINT, 4);
crc = stun_append (msg, len, STUN_FINGERPRINT, 4);
if (crc == NULL)
return ENOBUFS;
......@@ -547,7 +452,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
#endif
}
uint32_t fpr = htonl (stun_fingerprint (msg));
fpr = htonl (stun_fingerprint (msg));
memcpy (crc, &fpr, sizeof (fpr));
*plen = 20u + stun_length (msg);
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h"
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h"
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h"
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h"
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h"
......
......@@ -7,6 +7,7 @@
include $(top_srcdir)/common.mk
AM_CPPFLAGS = -I$(top_srcdir)
AM_CFLAGS = -std=gnu99
LDADD = ../libstun.la
check_PROGRAMS = test-crc32 test-parse test-format \
......@@ -14,4 +15,5 @@ check_PROGRAMS = test-crc32 test-parse test-format \
dist_check_SCRIPTS = check-bind.sh
TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS)
TESTS = $(check_PROGRAMS)
#$(dist_check_SCRIPTS)
......@@ -14,29 +14,35 @@ $STUNC -V
! $STUNC -4 127.0.0.1 1
! $STUNC -6 ::1 1
# Real tests
# Allocate a likely unused port number
PORT=$((32768+$$))
if test $PORT -le 1024; then
PORT=$(($PORT+1024))
fi
# Start the STUN test daemon if needed
rm -f stund-*.err stunc-*.log
echo "Using local UDP port number $PORT ..."
exit 77
# FIXME: kill daemons properly
# FIXME: use custom port number
# Start the STUN test daemon if needed
rm -f stund?.pid stund?.fail stunc?.log
( $STUND -4 || echo ABORT > stund-IPv4.err ) &
( $STUND -6 || echo ABORT > stund-IPv6.err ) &
for v in 4 6; do
(($SHELL -c "echo \$\$ > stund$v.pid ; exec $STUND -$v $PORT") || \
touch stund$v.fail) &
done
# Run the test client
$STUNC -4 > stunc-IPv4.log || test -f stund-IPv4.err
$STUNC -6 > stunc-IPv6.log || test -f stund-IPv6.err
$STUNC -4 127.0.0.1 $PORT > stunc4.log || test -f stund4.fail
$STUNC -6 ::1 $PORT > stunc6.log || test -f stund6.fail
# Terminate the test daemon
kill -INT %1 2>/dev/null || true
kill -INT %2 2>/dev/null || true
for v in 4 6; do kill -INT $(cat stund$v.pid) || true; done
wait
# Check client results
if test -f stund-IPv4.err; then exit 77; fi
grep -e "^Mapped address: 127.0.0.1" stunc-IPv4.log || exit 4
if test -f stund4.fail; then exit 77; fi
grep -e "^Mapped address: 127.0.0.1" stunc4.log || exit 4
if test -f stund6.fail; then exit 77; fi
grep -e "^Mapped address: ::1" stunc6.log || exit 6
if test -f stund-IPv6.err; then exit 77; fi
grep -e "^Mapped address: ::1" stunc-IPv6.log || exit 6
rm -f stund?.fail stund?.pid stunc?.log
......@@ -56,13 +56,14 @@
static int listen_dgram (void)
{
struct addrinfo hints, *res;
int val = -1;
memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo (NULL, "0", &hints, &res))
return -1;
int val = -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);
......@@ -88,6 +89,7 @@ static int listen_dgram (void)
static void bad_family (void)
{
struct sockaddr addr, dummy;
int val;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_UNSPEC;
......@@ -95,7 +97,7 @@ static void bad_family (void)
addr.sa_len = sizeof (addr);
#endif
int val = stun_bind_run (-1, &addr, sizeof (addr),
val = stun_bind_run (-1, &addr, sizeof (addr),
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val != 0);
}
......@@ -105,6 +107,7 @@ static void bad_family (void)
static void small_srv_addr (void)
{
struct sockaddr addr, dummy;
int val;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET;
......@@ -112,7 +115,7 @@ static void small_srv_addr (void)
addr.sa_len = sizeof (addr);
#endif
int val = stun_bind_run (-1, &addr, 1,
val = stun_bind_run (-1, &addr, 1,
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val == EINVAL);
}
......@@ -123,12 +126,13 @@ static void big_srv_addr (void)
{
uint8_t buf[sizeof (struct sockaddr_storage) + 16];
struct sockaddr dummy;
int fd, val;
int fd = socket (AF_INET, SOCK_DGRAM, 0);
fd = socket (AF_INET, SOCK_DGRAM, 0);
assert (fd != -1);
memset (buf, 0, sizeof (buf));
int val = stun_bind_run (fd, (struct sockaddr *)buf, sizeof (buf),
val = stun_bind_run (fd, (struct sockaddr *)buf, sizeof (buf),
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ENOBUFS);
close (fd);
......@@ -138,10 +142,10 @@ static void big_srv_addr (void)
/** Timeout test */
static void timeout (void)
{
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 ();
......@@ -161,10 +165,10 @@ static void timeout (void)
/** Malformed responses test */
static void bad_responses (void)
{
ssize_t val;
stun_bind_t *ctx;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx;
ssize_t val;
uint8_t buf[1000];
/* Allocate a local UDP port */
......@@ -186,16 +190,20 @@ static void bad_responses (void)
assert (val == 0);
/* Send crap response */
send (servfd, "foobar", 6, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
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 non-matching message (request instead of response) */
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
send (servfd, buf, val, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_process (ctx, buf, val,
(struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN);
stun_bind_cancel (ctx);
......@@ -206,22 +214,23 @@ static void bad_responses (void)
/** Various responses test */
static void responses (void)
{
ssize_t val;
size_t len;
stun_bind_t *ctx;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx;
ssize_t val;
size_t len;
int servfd, fd;
stun_msg_t buf;
/* Allocate a local UDP port for server */
int servfd = listen_dgram ();
servfd = listen_dgram ();
assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
/* Allocate a client socket and connect to server */
int fd = socket (addr.ss_family, SOCK_DGRAM, 0);
fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1);
val = connect (fd, (struct sockaddr *)&addr, addrlen);
......@@ -246,8 +255,10 @@ static void responses (void)
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
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 */
......@@ -265,8 +276,11 @@ static void responses (void)
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
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 */
......@@ -281,8 +295,11 @@ static void responses (void)
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
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 */
......@@ -300,8 +317,11 @@ static void responses (void)
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
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 */
......
......@@ -59,6 +59,10 @@ int main (void)
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;
......@@ -122,9 +126,6 @@ int main (void)
assert (len > 0);
/* Non-multiplexed message */
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";
len = sizeof (buf);
val = stun_bind_reply (buf, &len, req,
(struct sockaddr *)&ip4, sizeof (ip4), false);
......
......@@ -39,7 +39,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include "stun/conncheck.h"
#include "stun/stun-ice.h"
#include "stun/stun-msg.h"
#include <stdlib.h>
......@@ -57,10 +57,11 @@ int main (void)
{
struct sockaddr_in ip4;
stun_msg_t req, resp;
const uint64_t tie = 0x8000000000000000LL;
ssize_t val;
size_t len;
const uint64_t tie = 0x8000000000000000LL;
static const char name[] = "admin", pass[] = "secret";
char nbuf[6];
bool control = false;
memset (&ip4, 0, sizeof (ip4));
......@@ -95,9 +96,17 @@ int main (void)
sizeof (ip4), pass, &control, tie);
assert (val == EPERM);
assert (len > 0);
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);
/* 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, 0);
assert (val == 0);
......@@ -108,6 +117,25 @@ int main (void)
assert (val == 0);
assert (len > 0);
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);
/* Bad username */
stun_init_request (req, STUN_BINDING);
len = sizeof (req);
val = stun_finish_short (req, &len, "ab\xff", pass, NULL, 0);
assert (val == 0);
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL);
/* Bad fingerprint */
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), "bad", &control, tie);
......
......@@ -66,5 +66,3 @@ int main (void)
return 0;
}
......@@ -140,6 +140,7 @@ int main (void)
{
uint8_t msg[STUN_MAXMSG + 8];
size_t len;
struct sockaddr addr;
/* Request formatting test */
stun_init_request (msg, STUN_BINDING);
......@@ -169,11 +170,11 @@ int main (void)
/* Overflow tests */
stun_init_request (msg, STUN_BINDING);
for (unsigned i = 0;
for (len = 0;
stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS;
i++)
len += 4)
{
if ((i << 2) > 0xffff)
if (len > 0xffff)
fatal ("Overflow protection test failed");
}
......@@ -185,7 +186,6 @@ int main (void)
if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS)
fatal ("String overflow test failed");
struct sockaddr addr;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN
......
......@@ -179,7 +179,7 @@ static void test_message (void)
/* Tests for message attribute parsing */
static void test_attribute (void)
{
uint8_t acme[] =
static const uint8_t acme[] =
"\x15\x55\x00\x4c" // <-- update message length if needed!!
"\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10"
......@@ -286,7 +286,6 @@ static void test_attribute (void)
}
int main (void)
{
test_message ();
......
......@@ -54,6 +54,8 @@
#define STUN_INIT_TIMEOUT 600
#define STUN_END_TIMEOUT 4800
#define STUN_RELIABLE_TIMEOUT 7900
/**
* Clock used throughout the STUN code.
* STUN requires a monotonic 1kHz clock to operate properly.
......@@ -86,10 +88,6 @@ static inline void add_delay (struct timespec *ts, unsigned delay)
}
/**
* Starts a STUN transaction retransmission timer.
* @param timer structure for internal timer state
*/
void stun_timer_start (stun_timer_t *timer)
{
stun_gettime (&timer->deadline);
......@@ -97,6 +95,14 @@ void stun_timer_start (stun_timer_t *timer)
}
void stun_timer_start_reliable (stun_timer_t *timer)
{
stun_gettime (&timer->deadline);
add_delay (&timer->deadline, timer->delay = STUN_RELIABLE_TIMEOUT);
}
unsigned stun_timer_remainder (const stun_timer_t *timer)
{
unsigned delay;
......@@ -116,18 +122,14 @@ unsigned stun_timer_remainder (const stun_timer_t *timer)
}
/**
* Updates a STUN transaction retransmission timer.
* @param timer internal timer state
* @return -1 if the transaction timed out,
* 0 if the transaction should be retransmitted,
* otherwise milliseconds left until next time out or retransmit.
*/
int stun_timer_refresh (stun_timer_t *timer)
{
unsigned delay = stun_timer_remainder (timer);
if (delay == 0)
{
#if STUN_END_TIMEOUT > STUN_RELIABLE_TIMEOUT
# error Inconsistent STUN timeout values!
#endif
if (timer->delay >= STUN_END_TIMEOUT)
return -1;
......
......@@ -36,6 +36,11 @@
#ifndef STUN_TIMER_H
# define STUN_TIMER_H 1
/**
* @file timer.h
* @brief STUN retransmission timer
*/
# include <sys/types.h>
# include <time.h>
......@@ -50,7 +55,20 @@ typedef struct stun_timer_s
extern "C" {
# endif
/**
* Starts a STUN transaction retransmission timer.
* @param timer structure for internal timer state
*/
void stun_timer_start (stun_timer_t *timer);
void stun_timer_start_reliable (stun_timer_t *timer);
/**
* Updates a STUN transaction retransmission timer.
* @param timer internal timer state
* @return -1 if the transaction timed out,
* 0 if the transaction should be retransmitted,
* otherwise milliseconds left until next time out or retransmit.
*/
int stun_timer_refresh (stun_timer_t *timer);
unsigned stun_timer_remainder (const stun_timer_t *timer);
......
......@@ -45,22 +45,55 @@
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include "stun-msg.h"
#include "trans.h"
/**
* Initializes a new STUN request transaction
*/
#define TRANS_OWN_FD 0x1 /* descriptor belongs to us */
#define TRANS_RELIABLE 0x2 /* reliable transport */
int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
if (fd == -1)
int sotype;
socklen_t solen = sizeof (sotype);
assert (fd != -1);
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;
if (getsockopt (fd, SOL_SOCKET, SO_TYPE, &sotype, &solen))
return errno;
switch (sotype)
{
case SOCK_STREAM:
case SOCK_SEQPACKET:
tr->flags |= TRANS_RELIABLE;
}
return 0;
}
int stun_trans_create (stun_trans_t *restrict tr, int type, int proto,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
int fd, val;
if (srvlen < sizeof (struct sockaddr))
return EINVAL;
fd = socket (srv->sa_family, SOCK_DGRAM, 0);
fd = socket (srv->sa_family, type, proto);
if (fd == -1)
return errno;
......@@ -71,47 +104,35 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd,
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
#endif
if (connect (fd, srv, srvlen))
#ifdef MSG_ERRQUEUE
if (type == SOCK_DGRAM)
{
close (fd);
return errno;
/* Linux specifics for ICMP errors on non-connected sockets */
int yes = 1;
switch (srv->sa_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;
}
tr->ownfd = true;
tr->srvlen = 0;
}
#endif
if (connect (fd, srv, srvlen) && (errno != EINPROGRESS))
val = errno;
else
{
if (srvlen > sizeof (tr->srv))
return ENOBUFS;
val = stun_trans_init (tr, fd, NULL, 0);
tr->ownfd = false;
memcpy (&tr->srv, srv, tr->srvlen = srvlen);
if (val)
{
close (fd);
return val;
}
tr->fd = fd;
return 0;
}
/**
* Sends a STUN request
*/
static int
stun_trans_send (stun_trans_t *tr)
{
/* FIXME: support for TCP */
ssize_t val;
if (tr->srvlen > 0)
val = sendto (tr->fd, tr->msg, tr->msglen, 0,
(struct sockaddr *)&tr->srv, tr->srvlen);
else
val = send (tr->fd, tr->msg, tr->msglen, 0);
if (val == -1)
return errno;
if (val < (ssize_t)tr->msglen)
return EMSGSIZE;
tr->flags |= TRANS_OWN_FD;
return 0;
}
......@@ -119,53 +140,91 @@ stun_trans_send (stun_trans_t *tr)
void stun_trans_deinit (stun_trans_t *tr)
{
int saved = errno;
if (tr->ownfd)
close (tr->fd);
if (tr->flags & TRANS_OWN_FD)
close (tr->sock.fd);
free (tr->key.value);
#ifndef NDEBUG
tr->fd = -1;
tr->sock.fd = -1;
#endif
errno = saved;
}
#ifndef MSG_DONTWAIT
# define MSG_DONTWAIT 0
#endif
#ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL 0
#endif
static int stun_trans_send (stun_trans_t *tr);
int stun_trans_start (stun_trans_t *tr)
{
int val = stun_trans_send (tr);
int val;
assert (tr->msg.offset == 0);
val = stun_trans_send (tr);
if (val)
return val;
if (tr->flags & TRANS_RELIABLE)
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));
return 0;
}
/**
* This is meant to integrate with I/O pooling loops and event frameworks.
*
* @return recommended maximum delay (in milliseconds) to wait for a
* response.
*/
unsigned stun_trans_timeout (const stun_trans_t *tr)
static inline int stun_err_dequeue (int fd)
{
assert (tr != NULL);
assert (tr->fd != -1);
return stun_timer_remainder (&tr->timer);
#ifdef MSG_ERRQUEUE
struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr));
return recvmsg (fd, &hdr, MSG_ERRQUEUE) == 0;
#else
return 0;
#endif
}
/**
* This is meant to integrate with I/O polling loops and event frameworks.
*
* @return file descriptor used by the STUN Binding discovery context.
* Always succeeds.
* Try to send STUN request/indication
*/
int stun_trans_fd (const stun_trans_t *tr)
static int
stun_trans_send (stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->fd != -1);
return tr->fd;
const uint8_t *data = tr->msg.buf + tr->msg.offset;
size_t len = tr->msg.length - tr->msg.offset;
ssize_t val;
int errval;
do
{
if (tr->sock.dstlen > 0)
val = sendto (tr->sock.fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL,
(struct sockaddr *)&tr->sock.dst, tr->sock.dstlen);
else
val = send (tr->sock.fd, data, len, MSG_DONTWAIT | MSG_NOSIGNAL);
if (val >= 0)
{
/* Message sent succesfully! */
tr->msg.offset += val;
return 0;
}
errval = errno;
}
while (stun_err_dequeue (tr->sock.fd));
return errval;
}
......@@ -184,3 +243,45 @@ int stun_trans_tick (stun_trans_t *tr)
}
return EAGAIN;
}
int stun_trans_preprocess (stun_trans_t *restrict tr,
const void *restrict buf, size_t len)
{
bool code;
if (stun_validate (buf, len) <= 0)
return EAGAIN;
DBG ("Received %u-bytes STUN message\n",
(unsigned)stun_validate (buf, len));
/* FIXME: some error messages cannot be authenticated!! */
if (!stun_match_messages (buf, tr->msg.buf, tr->key.value, tr->key.length,
&code))
return EAGAIN;
if (code)
return ECONNREFUSED; // FIXME: better error value
if (stun_has_unknown (buf))
return EPROTO;
return 0;
}
unsigned stun_trans_timeout (const stun_trans_t *tr)
{
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;
}
......@@ -36,23 +36,39 @@
#ifndef STUN_TRANS_H
# define STUN_TRANS_H 1
/**
* @file trans.h
* @brief STUN client generic transaction layer
*/
# include <sys/types.h>
# include <sys/socket.h>
# include <stdbool.h>
# include "timer.h"
typedef struct stun_trans_s
{
stun_timer_t timer;
size_t msglen;
stun_msg_t msg;
unsigned flags;
bool ownfd;
struct
{
size_t length, offset;
uint8_t buf[STUN_MAXMSG];
} msg;
struct
{
int fd;
socklen_t srvlen;
struct sockaddr_storage srv;
socklen_t dstlen;
struct sockaddr_storage dst;
} sock;
struct
{
size_t length;
uint8_t *value;
} key;
} stun_trans_t;
......@@ -60,14 +76,50 @@ typedef struct stun_trans_s
extern "C" {
# endif
/**
* Initializes a new STUN request transaction
*
* @param tr pointer to an unused STUN transaction struct
* @param fd socket descriptor to use
* @param srv STUN server socket address (ignored if @a srvlen is 0)
* @param srvlen STUN server socket address length (or 0 @a fd is connected)
*/
int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen);
/**
* Initializes a new STUN request transaction with its dedicated socket
*
* @param tr pointer to an unused STUN transaction struct
* @param sotype socket type (as in socket() second parameter)
* @param proto socket protocol (as is socket() third parameter)
* @param srv STUN server socket address (ignored if @a srvlen is 0)
* @param srvlen STUN server socket address length (or 0 @a fd is connected)
*/
int stun_trans_create (stun_trans_t *restrict tr, int sotype, int proto,
const struct sockaddr *restrict srv, socklen_t srvlen);
void stun_trans_deinit (stun_trans_t *restrict tr);
int stun_trans_start (stun_trans_t *restrict tr);
int stun_trans_tick (stun_trans_t *tr);
int stun_trans_preprocess (stun_trans_t *restrict tr,
const void *restrict buf, size_t len);
/**
* This is meant to integrate with I/O pooling loops and event frameworks.
*
* @return recommended maximum delay (in milliseconds) to wait for a
* response.
*/
unsigned stun_trans_timeout (const stun_trans_t *tr);
/**
* This is meant to integrate with I/O polling loops and event frameworks.
*
* @return file descriptor used by the STUN Binding discovery context.
* Always succeeds.
*/
int stun_trans_fd (const stun_trans_t *tr);
# 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