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 ...@@ -11,27 +11,30 @@ DIST_SUBDIRS = tests
include $(top_srcdir)/common.mk 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) AM_CPPFLAGS = -I$(top_srcdir)
LIBS = $(GLIB_LIBS) LIBS = $(GLIB_LIBS)
noinst_LTLIBRARIES = libstun.la 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 \ stun-msg.h stunsend.c stunrecv.c crc32.c hmac.c \
timer.h timer.c trans.h trans.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) 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 bin_PROGRAMS = stund stunbdc
check_PROGRAMS = stund
stun_client_LDADD = libstun.la stun_client_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread stund_LDADD = libstun.la -lpthread
stunbdc_LDADD = libstun.la stunbdc_LDADD = libstun.la
check_PROGRAMS = \ EXTRA_PROGRAMS += \
test-attribute-pack \ test-attribute-pack \
test-attribute-pack-unknown \ test-attribute-pack-unknown \
test-attribute-dump \ test-attribute-dump \
...@@ -57,6 +60,3 @@ test_message_dump_LDADD = libstun.la ...@@ -57,6 +60,3 @@ test_message_dump_LDADD = libstun.la
test_message_dump_unknown_LDADD = libstun.la test_message_dump_unknown_LDADD = libstun.la
test_message_unpack_LDADD = libstun.la test_message_unpack_LDADD = libstun.la
test_message_find_attribute_LDADD = libstun.la test_message_find_attribute_LDADD = libstun.la
TESTS = $(check_PROGRAMS)
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include "bind.h" #include "bind.h"
#include "stun-msg.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
...@@ -50,7 +51,9 @@ ...@@ -50,7 +51,9 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/poll.h> #ifdef HAVE_POLL
# include <sys/poll.h>
#endif
#include <fcntl.h> #include <fcntl.h>
/** Blocking mode STUN binding discovery */ /** Blocking mode STUN binding discovery */
...@@ -59,8 +62,11 @@ int stun_bind_run (int fd, ...@@ -59,8 +62,11 @@ int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen, const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen) struct sockaddr *restrict addr, socklen_t *addrlen)
{ {
#ifdef HAVE_POLL
stun_bind_t *ctx; 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); val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val) if (val)
...@@ -68,30 +74,39 @@ int stun_bind_run (int fd, ...@@ -68,30 +74,39 @@ int stun_bind_run (int fd,
do do
{ {
unsigned delay = stun_bind_timeout (ctx);
struct pollfd ufd[1]; struct pollfd ufd[1];
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd)); memset (ufd, 0, sizeof (ufd));
ufd[0].fd = stun_bind_fd (ctx), ufd[0].fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN; ufd[0].events = POLLIN;
poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay); 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); while (val == EAGAIN);
return val; return val;
#else
(void)fd; (void)srv; (void)srvlen; (void)addr; (void)addrlen;
return ENOSYS;
#endif
} }
/** Non-blocking mode STUN binding discovery */ /** Non-blocking mode STUN binding discovery */
#include "stun-msg.h"
#include "trans.h" #include "trans.h"
struct stun_bind_s struct stun_bind_s
{ {
stun_trans_t trans; stun_trans_t trans;
size_t keylen;
uint8_t key[0];
}; };
...@@ -113,22 +128,26 @@ static int ...@@ -113,22 +128,26 @@ static int
stun_bind_alloc (stun_bind_t **restrict context, int fd, stun_bind_alloc (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
{ {
int val;
stun_bind_t *ctx = malloc (sizeof (*ctx)); stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL) if (ctx == NULL)
return ENOMEM; return ENOMEM;
memset (ctx, 0, sizeof (*ctx)); memset (ctx, 0, sizeof (*ctx));
*context = ctx; *context = ctx;
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) if (val)
{ {
free (ctx); free (ctx);
return val; return val;
} }
ctx->keylen = (size_t)(-1); stun_init_request (ctx->trans.msg.buf, STUN_BINDING);
stun_init_request (ctx->trans.msg, STUN_BINDING);
return 0; return 0;
} }
...@@ -162,8 +181,8 @@ int stun_bind_start (stun_bind_t **restrict context, int fd, ...@@ -162,8 +181,8 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
ctx = *context; ctx = *context;
ctx->trans.msglen = sizeof (ctx->trans.msg); ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish (ctx->trans.msg, &ctx->trans.msglen); val = stun_finish (ctx->trans.msg.buf, &ctx->trans.msg.length);
if (val) if (val)
{ {
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
...@@ -205,31 +224,20 @@ int stun_bind_process (stun_bind_t *restrict ctx, ...@@ -205,31 +224,20 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len, const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen) struct sockaddr *restrict addr, socklen_t *addrlen)
{ {
bool error; int val;
int val = stun_validate (buf, len);
if (val <= 0)
return EAGAIN;
assert (ctx != NULL); assert (ctx != NULL);
DBG ("Received %u-bytes STUN message\n", (unsigned)val); val = stun_trans_preprocess (&ctx->trans, buf, len);
switch (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))
{ {
stun_bind_cancel (ctx); case EAGAIN:
return EPROTO; return EAGAIN;
case 0:
break;
default:
stun_bind_cancel (ctx);
return val;
} }
val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen); val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen);
...@@ -251,24 +259,27 @@ int stun_bind_process (stun_bind_t *restrict ctx, ...@@ -251,24 +259,27 @@ int stun_bind_process (stun_bind_t *restrict ctx,
} }
int stun_bind_resume (stun_bind_t *restrict context, /** ICE keep-alives (Binding discovery indication!) */
struct sockaddr *restrict addr, socklen_t *addrlen)
{
stun_msg_t buf;
ssize_t len;
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); stun_init_indication (buf, STUN_BINDING);
if (len >= 0) (void)stun_finish (buf, &val);
return stun_bind_process (context, &buf, len, addr, addrlen);
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 */ /** Connectivity checks */
#include "conncheck.h" #include "stun-ice.h"
int int
stun_conncheck_start (stun_bind_t **restrict context, int fd, stun_conncheck_start (stun_bind_t **restrict context, int fd,
...@@ -283,43 +294,43 @@ 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 (username != NULL);
assert (password != NULL); assert (password != NULL);
val = stun_bind_alloc (context, fd, srv, srvlen); val = stun_bind_alloc (&ctx, fd, srv, srvlen);
if (val) if (val)
return val; return val;
val = strlen (password); ctx->trans.key.length = strlen (password);
ctx = realloc (*context, sizeof (*ctx) + val + 1); ctx->trans.key.value = malloc (ctx->trans.key.length);
if (ctx == NULL) if (ctx->trans.key.value == NULL)
{ {
val = ENOMEM; val = ENOMEM;
goto error; goto error;
} }
*context = ctx; *context = ctx;
memcpy (ctx->key, password, val); memcpy (ctx->trans.key.value, password, ctx->trans.key.length);
ctx->keylen = val;
if (cand_use) 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); STUN_USE_CANDIDATE);
if (val) if (val)
goto error; 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); STUN_PRIORITY, priority);
if (val) if (val)
goto error; 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 controlling ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, tie); : STUN_ICE_CONTROLLED, tie);
if (val) if (val)
goto error; goto error;
ctx->trans.msglen = sizeof (ctx->trans.msg); ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish_short (ctx->trans.msg, &ctx->trans.msglen, val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length,
username, password, NULL, 0); username, password, NULL, 0);
if (val) if (val)
goto error; goto error;
......
...@@ -77,7 +77,7 @@ int stun_bind_run (int fd, ...@@ -77,7 +77,7 @@ int stun_bind_run (int fd,
* Starts STUN Binding discovery in non-blocking mode. * Starts STUN Binding discovery in non-blocking mode.
* *
* @param context pointer to an opaque pointer that will be passed to * @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 fd socket to use for discovery, or -1 to create one
* @param srv STUN server socket address * @param srv STUN server socket address
* @param srvlen STUN server socket address length * @param srvlen STUN server socket address length
...@@ -156,24 +156,15 @@ int stun_bind_process (stun_bind_t *restrict context, ...@@ -156,24 +156,15 @@ int stun_bind_process (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen); struct sockaddr *restrict addr, socklen_t *addrlen);
/** /**
* Continues STUN Binding discovery in non-blocking mode: * Sends a STUN Binding indication, aka ICE keep-alive packet.
* 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.
* *
* @return EAGAIN is returned if the discovery has not completed yet. * @param fd socket descriptor to send packet through
0 is returned on successful completion, another standard error value * @param srv destination socket address (possibly NULL if connected)
* otherwise. If the return value is not EAGAIN, @a context is freed and must * @param srvlen destination socket address length (possibly 0)
* not be re-used. * @return 0 on success, an error code from sendto() otherwise.
*/ */
int stun_bind_resume (stun_bind_t *restrict context, int stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
struct sockaddr *restrict addr, socklen_t *addrlen); socklen_t srvlen);
/** /**
......
...@@ -69,8 +69,6 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, ...@@ -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, const struct sockaddr *restrict src, socklen_t srclen,
bool muxed, const char *restrict pass) bool muxed, const char *restrict pass)
{ {
assert (plen != NULL);
size_t len = *plen; size_t len = *plen;
int val; int val;
*plen = 0; *plen = 0;
...@@ -82,7 +80,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, ...@@ -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) if (stun_get_class (msg) != STUN_REQUEST)
{ {
DBG (" Unhandled non-request (class %u) message.\n", DBG (" Unhandled non-request (class %u) message.\n",
(unsigned)stun_get_class (msg)); stun_get_class (msg));
return EINVAL; return EINVAL;
} }
...@@ -133,7 +131,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, ...@@ -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) if (stun_get_method (msg) != STUN_BINDING)
{ {
DBG (" Bad request (method %u) message.\n", DBG (" Bad request (method %u) message.\n",
(unsigned)stun_get_method (msg)); stun_get_method (msg));
err (STUN_BAD_REQUEST); err (STUN_BAD_REQUEST);
return EPROTO; return EPROTO;
} }
...@@ -185,7 +183,7 @@ stun_bind_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, ...@@ -185,7 +183,7 @@ stun_bind_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
/** Connectivity checks **/ /** Connectivity checks **/
#include "conncheck.h" #include "stun-ice.h"
int int
stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, 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, ...@@ -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 *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen) char *restrict buf, size_t buflen)
{ {
size_t i;
ssize_t len = stun_find_string (msg, STUN_USERNAME, buf, buflen); ssize_t len = stun_find_string (msg, STUN_USERNAME, buf, buflen);
if ((len == -1) || ((size_t)len >= buflen)) if ((len == -1) || ((size_t)len >= buflen))
return NULL; return NULL;
for (size_t i = 0; i < (size_t)len; i++) for (i = 0; i < (size_t)len; i++)
{ {
char c = buf[i]; char c = buf[i];
/* ref ICE sect 7.1.1.4. (ID-16) */
if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z'))
|| ((c >= 'a') && (c <= 'z')) || (c == '+')) || ((c >= 'a') && (c <= 'z')) || (c == '+') || (c == ':'))
continue; continue;
return NULL; return NULL;
} }
...@@ -249,6 +251,7 @@ char *stun_conncheck_username (const uint8_t *restrict msg, ...@@ -249,6 +251,7 @@ char *stun_conncheck_username (const uint8_t *restrict msg,
uint32_t stun_conncheck_priority (const uint8_t *msg) uint32_t stun_conncheck_priority (const uint8_t *msg)
{ {
uint32_t value; uint32_t value;
if (stun_find32 (msg, STUN_PRIORITY, &value)) if (stun_find32 (msg, STUN_PRIORITY, &value))
return 0; return 0;
return value; return value;
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "stun-msg.h" #include "stun-msg.h"
...@@ -55,12 +54,8 @@ static uint32_t crc32 (const void *buf, size_t size); ...@@ -55,12 +54,8 @@ static uint32_t crc32 (const void *buf, size_t size);
*/ */
uint32_t stun_fingerprint (const uint8_t *msg) uint32_t stun_fingerprint (const uint8_t *msg)
{ {
assert (msg != NULL);
/* Don't hash last 8-bytes (= the FINGERPRINT attribute) */ /* Don't hash last 8-bytes (= the FINGERPRINT attribute) */
assert (stun_length (msg) >= 8); size_t len = 12u + stun_length (msg); // 20 - 8 = 12
size_t len = 20u + stun_length (msg) - 8;
return crc32 (msg, len) ^ 0x5354554e; return crc32 (msg, len) ^ 0x5354554e;
} }
......
...@@ -38,11 +38,14 @@ ...@@ -38,11 +38,14 @@
#endif #endif
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <openssl/rand.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <pthread.h>
#include "stun-msg.h" #include "stun-msg.h"
#include <string.h>
#include <assert.h> #include <assert.h>
void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen) 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 ...@@ -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); 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 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h> #include <errno.h>
#include <netdb.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 @@ ...@@ -38,6 +38,12 @@
#ifndef STUN_MSG_H #ifndef STUN_MSG_H
# define STUN_MSG_H 1 # define STUN_MSG_H 1
/**
* @file stun-msg.h
* @brief STUN low-level message formatting and parsing
*/
# ifndef NDEBUG # ifndef NDEBUG
# include <stdio.h> # include <stdio.h>
# include <stdarg.h> # include <stdarg.h>
...@@ -85,7 +91,8 @@ typedef enum ...@@ -85,7 +91,8 @@ typedef enum
typedef enum typedef enum
{ {
STUN_BINDING=0x001, STUN_BINDING=0x001,
STUN_SHARED_SECRET=0x002 STUN_SHARED_SECRET=0x002,
STUN_ALLOCATE=0x003
} stun_method_t; } stun_method_t;
/* Attribute types */ /* Attribute types */
...@@ -113,7 +120,7 @@ typedef enum ...@@ -113,7 +120,7 @@ typedef enum
STUN_USE_CANDIDATE=0x0025, /* ICE-15 */ STUN_USE_CANDIDATE=0x0025, /* ICE-15 */
/* Optional attributes */ /* Optional attributes */
STUN_SERVER=0x8022, STUN_SERVER=0x8022,
STUN_ALTERNATE_SERVER=0x8023, STUN_ALTERNATE_SERVER=0x8023,
STUN_REFRESH_INTERVAL=0x8024, STUN_REFRESH_INTERVAL=0x8024,
...@@ -187,19 +194,20 @@ static inline uint16_t stun_length (const uint8_t *ptr) ...@@ -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) * @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); 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) * @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); 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); ...@@ -220,6 +228,11 @@ uint32_t stun_fingerprint (const uint8_t *msg);
void stun_sha1 (const uint8_t *msg, uint8_t *sha, void stun_sha1 (const uint8_t *msg, uint8_t *sha,
const void *key, size_t keylen); 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, int stun_xor_address (const uint8_t *msg,
struct sockaddr *addr, socklen_t addrlen); struct sockaddr *addr, socklen_t addrlen);
...@@ -357,11 +370,64 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -357,11 +370,64 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type,
bool stun_is_unknown (uint16_t type); bool stun_is_unknown (uint16_t type);
unsigned stun_find_unknown (const uint8_t *msg, uint16_t *list, unsigned max); 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); 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); 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, int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err); 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); 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, ...@@ -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); 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); 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, int stun_append32 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint32_t value); 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, int stun_append64 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint64_t value); 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, int stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str); stun_attr_type_t type, const char *str);
/**
* 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, int stun_append_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, stun_attr_type_t type,
const struct sockaddr *restrict addr, const struct sockaddr *restrict addr,
socklen_t addrlen); 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, int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, stun_attr_type_t type,
const struct sockaddr *restrict addr, const struct sockaddr *restrict addr,
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -69,7 +69,8 @@ printaddr (const char *str, const struct sockaddr *addr, socklen_t addrlen) ...@@ -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) static int run (int family, const char *hostname, const char *service)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
int retval = -1; const struct addrinfo *ptr;
int ret = -1;
memset (&hints, 0, sizeof (hints)); memset (&hints, 0, sizeof (hints));
hints.ai_family = family; hints.ai_family = family;
...@@ -77,18 +78,19 @@ static int run (int family, const char *hostname, const char *service) ...@@ -77,18 +78,19 @@ static int run (int family, const char *hostname, const char *service)
if (service == NULL) if (service == NULL)
service = "3478"; service = "3478";
int val = getaddrinfo (hostname, service, &hints, &res); ret = getaddrinfo (hostname, service, &hints, &res);
if (val) if (ret)
{ {
fprintf (stderr, "%s (port %s): %s\n", hostname, service, fprintf (stderr, "%s (port %s): %s\n", hostname, service,
gai_strerror (val)); gai_strerror (ret));
return -1; 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; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
int val;
printaddr ("Server address", ptr->ai_addr, ptr->ai_addrlen); printaddr ("Server address", ptr->ai_addr, ptr->ai_addrlen);
...@@ -99,12 +101,12 @@ static int run (int family, const char *hostname, const char *service) ...@@ -99,12 +101,12 @@ static int run (int family, const char *hostname, const char *service)
else else
{ {
printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen); printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen);
retval = 0; ret = 0;
} }
} }
freeaddrinfo (res); freeaddrinfo (res);
return retval; return ret;
} }
...@@ -115,7 +117,8 @@ int main (int argc, char *argv[]) ...@@ -115,7 +117,8 @@ int main (int argc, char *argv[])
{ "ipv4", no_argument, NULL, '4' }, { "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' }, { "ipv6", no_argument, NULL, '6' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' } { "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
}; };
const char *server = NULL, *port = NULL; const char *server = NULL, *port = NULL;
int family = AF_UNSPEC; int family = AF_UNSPEC;
...@@ -149,6 +152,9 @@ int main (int argc, char *argv[]) ...@@ -149,6 +152,9 @@ int main (int argc, char *argv[])
printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n", printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n",
PACKAGE, VERSION); PACKAGE, VERSION);
return 0; return 0;
default:
return 2;
} }
} }
......
...@@ -108,11 +108,17 @@ int listen_socket (int fam, int type, int proto, uint16_t port) ...@@ -108,11 +108,17 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
{ {
case AF_INET: case AF_INET:
setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes)); setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes));
#ifdef IP_RECVERR
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
#endif
break; break;
case AF_INET6: case AF_INET6:
setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes, setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes,
sizeof (yes)); sizeof (yes));
#ifdef IPV6_RECVERR
setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &yes, sizeof (yes));
#endif
break; break;
} }
} }
...@@ -133,6 +139,16 @@ error: ...@@ -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 "stun-msg.h" // FIXME: remove
#include "bind.h" #include "bind.h"
...@@ -156,45 +172,41 @@ static int dgram_process (int sock) ...@@ -156,45 +172,41 @@ static int dgram_process (int sock)
if (len < 0) if (len < 0)
{ {
DBG ("Receive error: %s\n", strerror (errno)); DBG ("Receive error: %s\n", strerror (errno));
return errno; return -1;
} }
if (mh.msg_flags & MSG_TRUNC) if (mh.msg_flags & MSG_TRUNC)
{ {
DBG ("Truncated datagram ignored.\n"); DBG ("Truncated datagram ignored.\n");
return EMSGSIZE; return -1;
} }
if (stun_validate (buf, len) <= 0) if (stun_validate (buf, len) <= 0)
return EINVAL; return -1;
len = stun_bind_reply (buf, &iov.iov_len, buf, len = stun_bind_reply (buf, &iov.iov_len, buf,
mh.msg_name, mh.msg_namelen, false); mh.msg_name, mh.msg_namelen, false);
if (iov.iov_len == 0) if (iov.iov_len == 0)
return len; return -1;
do
len = sendmsg (sock, &mh, 0);
while ((len == -1) && err_dequeue (sock));
len = sendmsg (sock, &mh, 0); if (len < (ssize_t)iov.iov_len)
if (len == -1) return -1;
return errno;
if ((size_t)len < iov.iov_len)
return EMSGSIZE;
return 0; 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, int sock = listen_socket (family, SOCK_DGRAM, protocol, htons (port));
htons (IPPORT_STUN));
if (sock == -1) if (sock == -1)
return -1; return -1;
for (;;) for (;;)
{ dgram_process (sock);
int val = dgram_process (sock);
if (val)
DBG ("stund: %s\n", strerror (val));
}
} }
...@@ -210,6 +222,7 @@ static void exit_handler (int signum) ...@@ -210,6 +222,7 @@ static void exit_handler (int signum)
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
int family = AF_INET; int family = AF_INET;
unsigned port = IPPORT_STUN;
for (;;) for (;;)
{ {
...@@ -229,7 +242,10 @@ int main (int argc, char *argv[]) ...@@ -229,7 +242,10 @@ int main (int argc, char *argv[])
} }
} }
if (optind < argc)
port = atoi (argv[optind++]);
signal (SIGINT, exit_handler); signal (SIGINT, exit_handler);
signal (SIGTERM, exit_handler); signal (SIGTERM, exit_handler);
return -run (family, IPPROTO_UDP); return run (family, IPPROTO_UDP, port) ? EXIT_FAILURE : EXIT_SUCCESS;
} }
...@@ -58,6 +58,8 @@ int stun_valid (const uint8_t *msg) ...@@ -58,6 +58,8 @@ int stun_valid (const uint8_t *msg)
ssize_t stun_validate (const uint8_t *msg, size_t len) ssize_t stun_validate (const uint8_t *msg, size_t len)
{ {
size_t mlen;
if (len < 1) if (len < 1)
{ {
DBG ("STUN error: No data!\n"); DBG ("STUN error: No data!\n");
...@@ -76,7 +78,7 @@ ssize_t stun_validate (const uint8_t *msg, size_t len) ...@@ -76,7 +78,7 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
return 0; return 0;
} }
size_t mlen = 20u + stun_length (msg); mlen = 20u + stun_length (msg);
if (stun_padding (mlen)) if (stun_padding (mlen))
{ {
DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen); DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
...@@ -85,29 +87,33 @@ ssize_t stun_validate (const uint8_t *msg, size_t len) ...@@ -85,29 +87,33 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
if (len < mlen) if (len < mlen)
{ {
DBG ("STUN error: Incomplete message: %u of %u bytes!\n", len, DBG ("STUN error: Incomplete message: %u of %u bytes!\n",
(unsigned)mlen); (unsigned)len, (unsigned)mlen);
return 0; // partial message return 0; // partial message
} }
msg += 20; msg += 20;
len = mlen - 20;
/* from then on, we know we have the entire packet in buffer */ /* from then on, we know we have the entire packet in buffer */
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 /* thanks to padding check, if (end > msg) then there is not only one
* but at least 4 bytes left */ * but at least 4 bytes left */
assert ((end - msg) >= 0); assert (len >= 4);
len -= 4;
size_t alen = stun_align (stun_getw (msg - 2)); if (len < alen)
msg += alen;
if ((end - msg) < 0)
{ {
DBG ("STUN error: %u instead of %u bytes for attribute!\n", 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 return -1; // no room for attribute value + padding
} }
len -= alen;
msg += 4 + alen;
} }
return mlen; return mlen;
...@@ -124,20 +130,21 @@ ssize_t stun_validate (const uint8_t *msg, size_t len) ...@@ -124,20 +130,21 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
*/ */
static const void * static const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type, stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen) uint16_t *restrict palen)
{ {
assert (msg != NULL); size_t length = stun_length (msg);
assert (stun_valid (msg)); assert (stun_valid (msg));
assert (palen != NULL); assert (palen != NULL);
size_t length = stun_length (msg);
msg += 20; msg += 20;
while (length > 0) while (length > 0)
{ {
assert (length >= 4); size_t alen = stun_length (msg);
uint16_t atype = stun_getw (msg); uint16_t atype = stun_getw (msg);
unsigned alen = stun_length (msg);
assert (length >= 4);
length -= 4; length -= 4;
msg += 4; msg += 4;
...@@ -161,15 +168,17 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -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) 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) int stun_find_flag (const uint8_t *msg, stun_attr_type_t type)
{ {
const void *ptr;
uint16_t len; uint16_t len;
const void *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
return (len == 0) ? 0 : EINVAL; return (len == 0) ? 0 : EINVAL;
...@@ -180,14 +189,17 @@ int ...@@ -180,14 +189,17 @@ int
stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type, stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type,
uint32_t *restrict pval) uint32_t *restrict pval)
{ {
const void *ptr;
uint16_t len; uint16_t len;
const void *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (len == 4) if (len == 4)
{ {
uint32_t val; uint32_t val;
memcpy (&val, ptr, sizeof (val)); memcpy (&val, ptr, sizeof (val));
*pval = ntohl (val); *pval = ntohl (val);
return 0; return 0;
...@@ -198,14 +210,17 @@ stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -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) int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
{ {
const void *ptr;
uint16_t len; uint16_t len;
const void *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
if (len == 8) if (len == 8)
{ {
uint32_t tab[2]; uint32_t tab[2];
memcpy (tab, ptr, sizeof (tab)); memcpy (tab, ptr, sizeof (tab));
*pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]); *pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]);
return 0; return 0;
...@@ -217,8 +232,10 @@ int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval) ...@@ -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, ssize_t stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t buflen) char *buf, size_t buflen)
{ {
const char *ptr;
uint16_t len; uint16_t len;
const char *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return -1; return -1;
...@@ -234,8 +251,10 @@ int ...@@ -234,8 +251,10 @@ int
stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type, stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr, socklen_t *restrict addrlen) struct sockaddr *restrict addr, socklen_t *restrict addrlen)
{ {
const uint8_t *ptr;
uint16_t len; uint16_t len;
const uint8_t *ptr = stun_find (msg, type, &len);
ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return ENOENT; return ENOENT;
...@@ -309,11 +328,13 @@ int stun_xor_address (const uint8_t *restrict msg, ...@@ -309,11 +328,13 @@ int stun_xor_address (const uint8_t *restrict msg,
case AF_INET6: case AF_INET6:
{ {
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
unsigned short i;
if (addrlen < sizeof (*ip6)) if (addrlen < sizeof (*ip6))
return EINVAL; return EINVAL;
ip6->sin6_port ^= htons (STUN_COOKIE >> 16); ip6->sin6_port ^= htons (STUN_COOKIE >> 16);
for (unsigned i = 0; i < 16; i++) for (i = 0; i < 16; i++)
ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i]; ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i];
return 0; return 0;
} }
...@@ -376,7 +397,8 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -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) 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) ...@@ -388,6 +410,10 @@ static const uint8_t *stun_end (const uint8_t *msg)
bool stun_demux (const uint8_t *msg) bool stun_demux (const uint8_t *msg)
{ {
const void *fpr;
uint32_t crc32;
uint16_t fprlen;
assert (stun_valid (msg)); assert (stun_valid (msg));
DBG ("Demultiplexing STUN message @%p\n", msg); DBG ("Demultiplexing STUN message @%p\n", msg);
...@@ -400,8 +426,7 @@ bool stun_demux (const uint8_t *msg) ...@@ -400,8 +426,7 @@ bool stun_demux (const uint8_t *msg)
} }
/* Looks for FINGERPRINT */ /* Looks for FINGERPRINT */
uint16_t fprlen; fpr = stun_end (msg) - 4;
const void *fpr = stun_end (msg) - 4;
if ((fpr != stun_find (msg, STUN_FINGERPRINT, &fprlen)) || (fprlen != 4)) if ((fpr != stun_find (msg, STUN_FINGERPRINT, &fprlen)) || (fprlen != 4))
{ {
DBG (" No FINGERPRINT attribute!\n"); DBG (" No FINGERPRINT attribute!\n");
...@@ -409,7 +434,7 @@ bool stun_demux (const uint8_t *msg) ...@@ -409,7 +434,7 @@ bool stun_demux (const uint8_t *msg)
} }
/* Checks FINGERPRINT */ /* Checks FINGERPRINT */
uint32_t crc32 = htonl (stun_fingerprint (msg)); crc32 = htonl (stun_fingerprint (msg));
if (memcmp (fpr, &crc32, 4)) if (memcmp (fpr, &crc32, 4))
{ {
DBG (" Incorrect message fingerprint (expected: 0x%08x)!\n", DBG (" Incorrect message fingerprint (expected: 0x%08x)!\n",
...@@ -433,6 +458,7 @@ bool stun_demux (const uint8_t *msg) ...@@ -433,6 +458,7 @@ bool stun_demux (const uint8_t *msg)
int int
stun_verify_key (const uint8_t *msg, const void *key, size_t keylen) stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
{ {
const uint8_t *hash;
uint8_t sha[20]; uint8_t sha[20];
uint16_t hlen; uint16_t hlen;
...@@ -441,7 +467,7 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen) ...@@ -441,7 +467,7 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
DBG ("Authenticating STUN message @%p\n", msg); DBG ("Authenticating STUN message @%p\n", msg);
const uint8_t *hash = stun_end (msg) - 20; hash = stun_end (msg) - 20;
if (stun_demux (msg)) if (stun_demux (msg))
hash -= 8; // room for FINGERPRINT at the end 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) ...@@ -455,17 +481,21 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
stun_sha1 (msg, sha, key, keylen); stun_sha1 (msg, sha, key, keylen);
if (memcmp (sha, hash, sizeof (sha))) if (memcmp (sha, hash, sizeof (sha)))
{ {
#ifndef NDEBUG
unsigned i;
DBG (" Message HMAC-SHA1 fingerprint mismatch!" DBG (" Message HMAC-SHA1 fingerprint mismatch!"
"\n key : 0x"); "\n key : 0x");
for (unsigned i = 0; i < keylen; i++) for (i = 0; i < keylen; i++)
DBG ("%02x", ((uint8_t *)key)[i]); DBG ("%02x", ((uint8_t *)key)[i]);
DBG ("\n expected: 0x"); DBG ("\n expected: 0x");
for (unsigned i = 0; i < 20; i++) for (i = 0; i < 20; i++)
DBG ("%02x", sha[i]); DBG ("%02x", sha[i]);
DBG ("\n received: 0x"); DBG ("\n received: 0x");
for (unsigned i = 0; i < 20; i++) for (i = 0; i < 20; i++)
DBG ("%02x", hash[i]); DBG ("%02x", hash[i]);
DBG ("\n"); DBG ("\n");
#endif
return EPERM; return EPERM;
} }
...@@ -623,18 +653,19 @@ stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list, ...@@ -623,18 +653,19 @@ stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
while ((len > 0) && (count < max)) while ((len > 0) && (count < max))
{ {
uint16_t type = stun_getw (msg);
size_t alen = stun_align (stun_length (msg)); size_t alen = stun_align (stun_length (msg));
uint16_t atype = stun_getw (msg);
msg += 4 + alen; msg += 4 + alen;
assert (len >= (4 + alen)); assert (len >= (4 + alen));
len -= 4 + alen; len -= 4 + alen;
if (!stun_optional (type) if (!stun_optional (atype)
&& stun_is_unknown (type)) && stun_is_unknown (atype))
{ {
DBG (" found unknown attribute: 0x%04x (%u bytes)\n", DBG (" found unknown attribute: 0x%04x (%u bytes)\n",
(unsigned)type, (unsigned)alen); (unsigned)atype, (unsigned)alen);
list[count++] = type; list[count++] = atype;
} }
} }
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
#include <errno.h> #include <errno.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <pthread.h>
static inline static inline
void *stun_setw (uint8_t *ptr, uint16_t value) 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) ...@@ -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. * Initializes a STUN message buffer, with no attributes.
* @param c STUN message class (host byte order) * @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, ...@@ -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) void stun_init_request (uint8_t *req, stun_method_t m)
{ {
stun_transid_t id; stun_transid_t id;
...@@ -125,16 +102,15 @@ void stun_init_request (uint8_t *req, stun_method_t m) ...@@ -125,16 +102,15 @@ void stun_init_request (uint8_t *req, stun_method_t m)
} }
/** void stun_init_indication (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_transid_t id;
* STUN method and transaction ID are copied from the request message.
* stun_make_transid (id);
* @param ans [OUT] STUN message buffer stun_init (req, STUN_INDICATION, m, id);
* @param req STUN message query }
*
* ans == req is allowed.
*/
void stun_init_response (uint8_t *ans, const uint8_t *req) void stun_init_response (uint8_t *ans, const uint8_t *req)
{ {
//assert (stun_valid (req)); //assert (stun_valid (req));
...@@ -158,7 +134,9 @@ void stun_init_response (uint8_t *ans, const uint8_t *req) ...@@ -158,7 +134,9 @@ void stun_init_response (uint8_t *ans, const uint8_t *req)
static void * static void *
stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
{ {
uint8_t *a;
uint16_t mlen = stun_length (msg); uint16_t mlen = stun_length (msg);
assert (stun_padding (mlen) == 0); assert (stun_padding (mlen) == 0);
if (msize > STUN_MAXMSG) if (msize > STUN_MAXMSG)
...@@ -169,7 +147,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) ...@@ -169,7 +147,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
assert (length < 0xffff); assert (length < 0xffff);
uint8_t *a = msg + 20u + mlen; a = msg + 20u + mlen;
a = stun_setw (a, type); a = stun_setw (a, type);
a = stun_setw (a, length); a = stun_setw (a, length);
...@@ -205,27 +183,12 @@ stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, ...@@ -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) 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);
} }
/**
* 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 int
stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type, stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint32_t value) uint32_t value)
...@@ -235,14 +198,6 @@ stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type, ...@@ -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, int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint64_t value) uint64_t value)
{ {
...@@ -253,14 +208,6 @@ int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type, ...@@ -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 int
stun_append_string (uint8_t *restrict msg, size_t msize, stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str) stun_attr_type_t type, const char *str)
...@@ -298,12 +245,14 @@ static const char *stun_strerror (stun_error_t code) ...@@ -298,12 +245,14 @@ static const char *stun_strerror (stun_error_t code)
{ STUN_GLOBAL_FAILURE, "Unrecoverable failure" }, { STUN_GLOBAL_FAILURE, "Unrecoverable failure" },
{ 0, "" } { 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) if (tab[i].code == code)
return tab[i].phrase; return tab[i].phrase;
} }
return "Unknown error"; return "Unknown error";
} }
...@@ -335,20 +284,6 @@ stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code) ...@@ -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, int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err) stun_error_t err)
{ {
...@@ -359,22 +294,14 @@ int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req, ...@@ -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) 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]; uint16_t ids[stun_length (req) / 4];
unsigned counter; #else
uint16_t ids[256];
#endif
//assert (stun_valid (req)); //assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST); 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) ...@@ -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)) if (stun_init_error (ans, msize, req, STUN_UNKNOWN_ATTRIBUTE))
return ENOBUFS; return ENOBUFS;
for (unsigned i = 0; i < counter; i++) for (i = 0; i < counter; i++)
ids[i] = htons (ids[i]); ids[i] = htons (ids[i]);
return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids, return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids,
counter * 2); 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 int
stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const struct sockaddr *restrict addr, socklen_t addrlen) const struct sockaddr *restrict addr, socklen_t addrlen)
{ {
if (addrlen < sizeof (struct sockaddr))
return EINVAL;
const void *pa; const void *pa;
uint8_t *ptr;
uint16_t alen, port; uint16_t alen, port;
uint8_t family; uint8_t family;
if (addrlen < sizeof (struct sockaddr))
return EINVAL;
switch (addr->sa_family) switch (addr->sa_family)
{ {
case AF_INET: case AF_INET:
...@@ -444,7 +361,7 @@ stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, ...@@ -444,7 +361,7 @@ stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
return EAFNOSUPPORT; return EAFNOSUPPORT;
} }
uint8_t *ptr = stun_append (msg, msize, type, 4 + alen); ptr = stun_append (msg, msize, type, 4 + alen);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
...@@ -456,35 +373,24 @@ stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, ...@@ -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, int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, stun_attr_type_t type,
const struct sockaddr *restrict addr, const struct sockaddr *restrict addr,
socklen_t addrlen) socklen_t addrlen)
{ {
union struct sockaddr_storage xor;
{
struct sockaddr addr;
char buf[addrlen];
} xor;
int val; int val;
memcpy (xor.buf, addr, addrlen); if (addrlen > sizeof (xor))
val = stun_xor_address (msg, &xor.addr, addrlen); return ENOBUFS;
memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
if (val) if (val)
return val; return val;
return stun_append_addr (msg, msize, type, &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, ...@@ -494,11 +400,10 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
const void *key, size_t keylen, const void *key, size_t keylen,
const void *nonce, size_t noncelen) const void *nonce, size_t noncelen)
{ {
assert (plen != NULL);
size_t len = *plen; size_t len = *plen;
uint8_t *sha = NULL; uint8_t *sha = NULL, *crc;
int val = ENOBUFS; int val = ENOBUFS;
uint32_t fpr;
if (realm != NULL) if (realm != NULL)
{ {
...@@ -528,7 +433,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen, ...@@ -528,7 +433,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
return ENOBUFS; return ENOBUFS;
} }
void *crc = stun_append (msg, len, STUN_FINGERPRINT, 4); crc = stun_append (msg, len, STUN_FINGERPRINT, 4);
if (crc == NULL) if (crc == NULL)
return ENOBUFS; return ENOBUFS;
...@@ -547,7 +452,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen, ...@@ -547,7 +452,7 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
#endif #endif
} }
uint32_t fpr = htonl (stun_fingerprint (msg)); fpr = htonl (stun_fingerprint (msg));
memcpy (crc, &fpr, sizeof (fpr)); memcpy (crc, &fpr, sizeof (fpr));
*plen = 20u + stun_length (msg); *plen = 20u + stun_length (msg);
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h" #include "stun.h"
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h" #include "stun.h"
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h" #include "stun.h"
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h" #include "stun.h"
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h> #include <string.h>
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this * not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "stun.h" #include "stun.h"
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
include $(top_srcdir)/common.mk include $(top_srcdir)/common.mk
AM_CPPFLAGS = -I$(top_srcdir) AM_CPPFLAGS = -I$(top_srcdir)
AM_CFLAGS = -std=gnu99
LDADD = ../libstun.la LDADD = ../libstun.la
check_PROGRAMS = test-crc32 test-parse test-format \ check_PROGRAMS = test-crc32 test-parse test-format \
...@@ -14,4 +15,5 @@ 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 dist_check_SCRIPTS = check-bind.sh
TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS) TESTS = $(check_PROGRAMS)
#$(dist_check_SCRIPTS)
...@@ -14,29 +14,35 @@ $STUNC -V ...@@ -14,29 +14,35 @@ $STUNC -V
! $STUNC -4 127.0.0.1 1 ! $STUNC -4 127.0.0.1 1
! $STUNC -6 ::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 echo "Using local UDP port number $PORT ..."
rm -f stund-*.err stunc-*.log
exit 77 # Start the STUN test daemon if needed
# FIXME: kill daemons properly rm -f stund?.pid stund?.fail stunc?.log
# FIXME: use custom port number
( $STUND -4 || echo ABORT > stund-IPv4.err ) & for v in 4 6; do
( $STUND -6 || echo ABORT > stund-IPv6.err ) & (($SHELL -c "echo \$\$ > stund$v.pid ; exec $STUND -$v $PORT") || \
touch stund$v.fail) &
done
# Run the test client # Run the test client
$STUNC -4 > stunc-IPv4.log || test -f stund-IPv4.err $STUNC -4 127.0.0.1 $PORT > stunc4.log || test -f stund4.fail
$STUNC -6 > stunc-IPv6.log || test -f stund-IPv6.err $STUNC -6 ::1 $PORT > stunc6.log || test -f stund6.fail
# Terminate the test daemon # Terminate the test daemon
kill -INT %1 2>/dev/null || true for v in 4 6; do kill -INT $(cat stund$v.pid) || true; done
kill -INT %2 2>/dev/null || true wait
# Check client results # Check client results
if test -f stund-IPv4.err; then exit 77; fi if test -f stund4.fail; then exit 77; fi
grep -e "^Mapped address: 127.0.0.1" stunc-IPv4.log || exit 4 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 rm -f stund?.fail stund?.pid stunc?.log
grep -e "^Mapped address: ::1" stunc-IPv6.log || exit 6
...@@ -56,13 +56,14 @@ ...@@ -56,13 +56,14 @@
static int listen_dgram (void) static int listen_dgram (void)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
int val = -1;
memset (&hints, 0, sizeof (hints)); memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo (NULL, "0", &hints, &res)) if (getaddrinfo (NULL, "0", &hints, &res))
return -1; return -1;
int val = -1;
for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next) for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
{ {
int fd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); int fd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
...@@ -88,6 +89,7 @@ static int listen_dgram (void) ...@@ -88,6 +89,7 @@ static int listen_dgram (void)
static void bad_family (void) static void bad_family (void)
{ {
struct sockaddr addr, dummy; struct sockaddr addr, dummy;
int val;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_UNSPEC; addr.sa_family = AF_UNSPEC;
...@@ -95,8 +97,8 @@ static void bad_family (void) ...@@ -95,8 +97,8 @@ static void bad_family (void)
addr.sa_len = sizeof (addr); addr.sa_len = sizeof (addr);
#endif #endif
int val = stun_bind_run (-1, &addr, sizeof (addr), val = stun_bind_run (-1, &addr, sizeof (addr),
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val != 0); assert (val != 0);
} }
...@@ -105,6 +107,7 @@ static void bad_family (void) ...@@ -105,6 +107,7 @@ static void bad_family (void)
static void small_srv_addr (void) static void small_srv_addr (void)
{ {
struct sockaddr addr, dummy; struct sockaddr addr, dummy;
int val;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET; addr.sa_family = AF_INET;
...@@ -112,8 +115,8 @@ static void small_srv_addr (void) ...@@ -112,8 +115,8 @@ static void small_srv_addr (void)
addr.sa_len = sizeof (addr); addr.sa_len = sizeof (addr);
#endif #endif
int val = stun_bind_run (-1, &addr, 1, val = stun_bind_run (-1, &addr, 1,
&dummy, &(socklen_t){ sizeof (dummy) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val == EINVAL); assert (val == EINVAL);
} }
...@@ -123,13 +126,14 @@ static void big_srv_addr (void) ...@@ -123,13 +126,14 @@ static void big_srv_addr (void)
{ {
uint8_t buf[sizeof (struct sockaddr_storage) + 16]; uint8_t buf[sizeof (struct sockaddr_storage) + 16];
struct sockaddr dummy; struct sockaddr dummy;
int fd, val;
int fd = socket (AF_INET, SOCK_DGRAM, 0); fd = socket (AF_INET, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
memset (buf, 0, sizeof (buf)); memset (buf, 0, sizeof (buf));
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) }); &dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ENOBUFS); assert (val == ENOBUFS);
close (fd); close (fd);
} }
...@@ -138,10 +142,10 @@ static void big_srv_addr (void) ...@@ -138,10 +142,10 @@ static void big_srv_addr (void)
/** Timeout test */ /** Timeout test */
static void timeout (void) static void timeout (void)
{ {
int val;
struct sockaddr_storage srv; struct sockaddr_storage srv;
struct sockaddr dummy; struct sockaddr dummy;
socklen_t srvlen = sizeof (srv); socklen_t srvlen = sizeof (srv);
int val;
/* Allocate a local UDP port, so we are 100% sure nobody responds there */ /* Allocate a local UDP port, so we are 100% sure nobody responds there */
int servfd = listen_dgram (); int servfd = listen_dgram ();
...@@ -161,10 +165,10 @@ static void timeout (void) ...@@ -161,10 +165,10 @@ static void timeout (void)
/** Malformed responses test */ /** Malformed responses test */
static void bad_responses (void) static void bad_responses (void)
{ {
ssize_t val; stun_bind_t *ctx;
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx; ssize_t val;
uint8_t buf[1000]; uint8_t buf[1000];
/* Allocate a local UDP port */ /* Allocate a local UDP port */
...@@ -186,16 +190,20 @@ static void bad_responses (void) ...@@ -186,16 +190,20 @@ static void bad_responses (void)
assert (val == 0); assert (val == 0);
/* Send crap response */ /* Send crap response */
send (servfd, "foobar", 6, 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); assert (val == 0);
val = stun_bind_process (ctx, "foobar", 6,
(struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
/* Send non-matching message (request instead of response) */ /* 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); val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0); assert (val >= 0);
send (servfd, buf, val, 0); val = stun_bind_process (ctx, buf, val,
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
...@@ -206,22 +214,23 @@ static void bad_responses (void) ...@@ -206,22 +214,23 @@ static void bad_responses (void)
/** Various responses test */ /** Various responses test */
static void responses (void) static void responses (void)
{ {
ssize_t val; stun_bind_t *ctx;
size_t len;
struct sockaddr_storage addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr); socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx; ssize_t val;
size_t len;
int servfd, fd;
stun_msg_t buf; stun_msg_t buf;
/* Allocate a local UDP port for server */ /* Allocate a local UDP port for server */
int servfd = listen_dgram (); servfd = listen_dgram ();
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
/* Allocate a client socket and connect to server */ /* Allocate a client socket and connect to server */
int fd = socket (addr.ss_family, SOCK_DGRAM, 0); fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1); assert (fd != -1);
val = connect (fd, (struct sockaddr *)&addr, addrlen); val = connect (fd, (struct sockaddr *)&addr, addrlen);
...@@ -246,8 +255,10 @@ static void responses (void) ...@@ -246,8 +255,10 @@ static void responses (void)
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
send (servfd, buf, len, 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); assert (val == 0);
val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen);
assert (val == ECONNREFUSED); assert (val == ECONNREFUSED);
/* Send response with an unknown attribute */ /* Send response with an unknown attribute */
...@@ -265,8 +276,11 @@ static void responses (void) ...@@ -265,8 +276,11 @@ static void responses (void)
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
send (servfd, buf, len, 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); assert (val == 0);
val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen);
assert (val == EPROTO); assert (val == EPROTO);
/* Send response with a no mapped address at all */ /* Send response with a no mapped address at all */
...@@ -281,8 +295,11 @@ static void responses (void) ...@@ -281,8 +295,11 @@ static void responses (void)
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
send (servfd, buf, len, 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); assert (val == 0);
val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen);
assert (val == ENOENT); assert (val == ENOENT);
/* Send old-style response */ /* Send old-style response */
...@@ -300,8 +317,11 @@ static void responses (void) ...@@ -300,8 +317,11 @@ static void responses (void)
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
send (servfd, buf, len, 0); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen); assert (val == 0);
val = stun_bind_process (ctx, buf, len,
(struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
/* End */ /* End */
......
...@@ -59,6 +59,10 @@ int main (void) ...@@ -59,6 +59,10 @@ int main (void)
stun_msg_t buf; stun_msg_t buf;
ssize_t val; ssize_t val;
size_t len; size_t len;
static const uint8_t req[] =
"\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)); memset (&ip4, 0, sizeof (ip4));
ip4.sin_family = AF_INET; ip4.sin_family = AF_INET;
...@@ -122,9 +126,6 @@ int main (void) ...@@ -122,9 +126,6 @@ int main (void)
assert (len > 0); assert (len > 0);
/* Non-multiplexed message */ /* 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); len = sizeof (buf);
val = stun_bind_reply (buf, &len, req, val = stun_bind_reply (buf, &len, req,
(struct sockaddr *)&ip4, sizeof (ip4), false); (struct sockaddr *)&ip4, sizeof (ip4), false);
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "stun/conncheck.h" #include "stun/stun-ice.h"
#include "stun/stun-msg.h" #include "stun/stun-msg.h"
#include <stdlib.h> #include <stdlib.h>
...@@ -57,10 +57,11 @@ int main (void) ...@@ -57,10 +57,11 @@ int main (void)
{ {
struct sockaddr_in ip4; struct sockaddr_in ip4;
stun_msg_t req, resp; stun_msg_t req, resp;
const uint64_t tie = 0x8000000000000000LL;
ssize_t val; ssize_t val;
size_t len; size_t len;
const uint64_t tie = 0x8000000000000000LL;
static const char name[] = "admin", pass[] = "secret"; static const char name[] = "admin", pass[] = "secret";
char nbuf[6];
bool control = false; bool control = false;
memset (&ip4, 0, sizeof (ip4)); memset (&ip4, 0, sizeof (ip4));
...@@ -95,9 +96,17 @@ int main (void) ...@@ -95,9 +96,17 @@ int main (void)
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); 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 */ /* Good message */
stun_init_request (req, STUN_BINDING); 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); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0); val = stun_finish_short (req, &len, name, pass, NULL, 0);
assert (val == 0); assert (val == 0);
...@@ -108,6 +117,25 @@ int main (void) ...@@ -108,6 +117,25 @@ int main (void)
assert (val == 0); assert (val == 0);
assert (len > 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 */ /* Bad fingerprint */
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), "bad", &control, tie); sizeof (ip4), "bad", &control, tie);
......
...@@ -66,5 +66,3 @@ int main (void) ...@@ -66,5 +66,3 @@ int main (void)
return 0; return 0;
} }
...@@ -140,6 +140,7 @@ int main (void) ...@@ -140,6 +140,7 @@ int main (void)
{ {
uint8_t msg[STUN_MAXMSG + 8]; uint8_t msg[STUN_MAXMSG + 8];
size_t len; size_t len;
struct sockaddr addr;
/* Request formatting test */ /* Request formatting test */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
...@@ -169,11 +170,11 @@ int main (void) ...@@ -169,11 +170,11 @@ int main (void)
/* Overflow tests */ /* Overflow tests */
stun_init_request (msg, STUN_BINDING); stun_init_request (msg, STUN_BINDING);
for (unsigned i = 0; for (len = 0;
stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS; stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS;
i++) len += 4)
{ {
if ((i << 2) > 0xffff) if (len > 0xffff)
fatal ("Overflow protection test failed"); fatal ("Overflow protection test failed");
} }
...@@ -185,7 +186,6 @@ int main (void) ...@@ -185,7 +186,6 @@ int main (void)
if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS) if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS)
fatal ("String overflow test failed"); fatal ("String overflow test failed");
struct sockaddr addr;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET; addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
......
...@@ -179,7 +179,7 @@ static void test_message (void) ...@@ -179,7 +179,7 @@ static void test_message (void)
/* Tests for message attribute parsing */ /* Tests for message attribute parsing */
static void test_attribute (void) static void test_attribute (void)
{ {
uint8_t acme[] = static const uint8_t acme[] =
"\x15\x55\x00\x4c" // <-- update message length if needed!! "\x15\x55\x00\x4c" // <-- update message length if needed!!
"\x21\x12\xA4\x42" // cookie "\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10" "\x76\x54\x32\x10"
...@@ -286,7 +286,6 @@ static void test_attribute (void) ...@@ -286,7 +286,6 @@ static void test_attribute (void)
} }
int main (void) int main (void)
{ {
test_message (); test_message ();
......
...@@ -54,6 +54,8 @@ ...@@ -54,6 +54,8 @@
#define STUN_INIT_TIMEOUT 600 #define STUN_INIT_TIMEOUT 600
#define STUN_END_TIMEOUT 4800 #define STUN_END_TIMEOUT 4800
#define STUN_RELIABLE_TIMEOUT 7900
/** /**
* Clock used throughout the STUN code. * Clock used throughout the STUN code.
* STUN requires a monotonic 1kHz clock to operate properly. * STUN requires a monotonic 1kHz clock to operate properly.
...@@ -86,10 +88,6 @@ static inline void add_delay (struct timespec *ts, unsigned delay) ...@@ -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) void stun_timer_start (stun_timer_t *timer)
{ {
stun_gettime (&timer->deadline); stun_gettime (&timer->deadline);
...@@ -97,6 +95,14 @@ void stun_timer_start (stun_timer_t *timer) ...@@ -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 stun_timer_remainder (const stun_timer_t *timer)
{ {
unsigned delay; unsigned delay;
...@@ -116,18 +122,14 @@ unsigned stun_timer_remainder (const stun_timer_t *timer) ...@@ -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) int stun_timer_refresh (stun_timer_t *timer)
{ {
unsigned delay = stun_timer_remainder (timer); unsigned delay = stun_timer_remainder (timer);
if (delay == 0) if (delay == 0)
{ {
#if STUN_END_TIMEOUT > STUN_RELIABLE_TIMEOUT
# error Inconsistent STUN timeout values!
#endif
if (timer->delay >= STUN_END_TIMEOUT) if (timer->delay >= STUN_END_TIMEOUT)
return -1; return -1;
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
#ifndef STUN_TIMER_H #ifndef STUN_TIMER_H
# define STUN_TIMER_H 1 # define STUN_TIMER_H 1
/**
* @file timer.h
* @brief STUN retransmission timer
*/
# include <sys/types.h> # include <sys/types.h>
# include <time.h> # include <time.h>
...@@ -50,7 +55,20 @@ typedef struct stun_timer_s ...@@ -50,7 +55,20 @@ typedef struct stun_timer_s
extern "C" { extern "C" {
# endif # 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 (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); int stun_timer_refresh (stun_timer_t *timer);
unsigned stun_timer_remainder (const stun_timer_t *timer); unsigned stun_timer_remainder (const stun_timer_t *timer);
......
...@@ -45,73 +45,94 @@ ...@@ -45,73 +45,94 @@
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <netinet/in.h>
#include "stun-msg.h" #include "stun-msg.h"
#include "trans.h" #include "trans.h"
/** #define TRANS_OWN_FD 0x1 /* descriptor belongs to us */
* Initializes a new STUN request transaction #define TRANS_RELIABLE 0x2 /* reliable transport */
*/
int stun_trans_init (stun_trans_t *restrict tr, int fd, int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
{ {
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)
{ {
if (srvlen < sizeof (struct sockaddr)) case SOCK_STREAM:
return EINVAL; case SOCK_SEQPACKET:
tr->flags |= TRANS_RELIABLE;
}
fd = socket (srv->sa_family, SOCK_DGRAM, 0); return 0;
if (fd == -1) }
return errno;
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, type, proto);
if (fd == -1)
return errno;
#ifdef FD_CLOEXEC #ifdef FD_CLOEXEC
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC); fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
#endif #endif
#ifdef O_NONBLOCK #ifdef O_NONBLOCK
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK); fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
#endif #endif
if (connect (fd, srv, srvlen)) #ifdef MSG_ERRQUEUE
if (type == SOCK_DGRAM)
{
/* Linux specifics for ICMP errors on non-connected sockets */
int yes = 1;
switch (srv->sa_family)
{ {
close (fd); case AF_INET:
return errno; 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 else
{ val = stun_trans_init (tr, fd, NULL, 0);
if (srvlen > sizeof (tr->srv))
return ENOBUFS;
tr->ownfd = false; if (val)
memcpy (&tr->srv, srv, tr->srvlen = srvlen); {
close (fd);
return val;
} }
tr->fd = fd; tr->flags |= TRANS_OWN_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;
return 0; return 0;
} }
...@@ -119,53 +140,91 @@ stun_trans_send (stun_trans_t *tr) ...@@ -119,53 +140,91 @@ stun_trans_send (stun_trans_t *tr)
void stun_trans_deinit (stun_trans_t *tr) void stun_trans_deinit (stun_trans_t *tr)
{ {
int saved = errno; int saved = errno;
if (tr->ownfd) if (tr->flags & TRANS_OWN_FD)
close (tr->fd); close (tr->sock.fd);
free (tr->key.value);
#ifndef NDEBUG #ifndef NDEBUG
tr->fd = -1; tr->sock.fd = -1;
#endif #endif
errno = saved; 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 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) if (val)
return val; return val;
stun_timer_start (&tr->timer); 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, DBG ("STUN transaction @%p started (timeout: %ums)\n", tr,
stun_trans_timeout (tr)); stun_trans_timeout (tr));
return 0; return 0;
} }
/** static inline int stun_err_dequeue (int fd)
* 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)
{ {
assert (tr != NULL); #ifdef MSG_ERRQUEUE
assert (tr->fd != -1); struct msghdr hdr;
return stun_timer_remainder (&tr->timer); 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. * Try to send STUN request/indication
*
* @return file descriptor used by the STUN Binding discovery context.
* Always succeeds.
*/ */
int stun_trans_fd (const stun_trans_t *tr) static int
stun_trans_send (stun_trans_t *tr)
{ {
assert (tr != NULL); const uint8_t *data = tr->msg.buf + tr->msg.offset;
assert (tr->fd != -1); size_t len = tr->msg.length - tr->msg.offset;
return tr->fd; 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) ...@@ -184,3 +243,45 @@ int stun_trans_tick (stun_trans_t *tr)
} }
return EAGAIN; 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 @@ ...@@ -36,23 +36,39 @@
#ifndef STUN_TRANS_H #ifndef STUN_TRANS_H
# define STUN_TRANS_H 1 # define STUN_TRANS_H 1
/**
* @file trans.h
* @brief STUN client generic transaction layer
*/
# include <sys/types.h> # include <sys/types.h>
# include <sys/socket.h> # include <sys/socket.h>
# include <stdbool.h>
# include "timer.h" # include "timer.h"
typedef struct stun_trans_s typedef struct stun_trans_s
{ {
stun_timer_t timer; stun_timer_t timer;
size_t msglen; unsigned flags;
stun_msg_t msg;
struct
{
size_t length, offset;
uint8_t buf[STUN_MAXMSG];
} msg;
bool ownfd; struct
int fd; {
socklen_t srvlen; int fd;
struct sockaddr_storage srv; socklen_t dstlen;
struct sockaddr_storage dst;
} sock;
struct
{
size_t length;
uint8_t *value;
} key;
} stun_trans_t; } stun_trans_t;
...@@ -60,14 +76,50 @@ typedef struct stun_trans_s ...@@ -60,14 +76,50 @@ typedef struct stun_trans_s
extern "C" { extern "C" {
# endif # 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, int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen); const struct sockaddr *restrict srv, socklen_t srvlen);
/**
* 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); void stun_trans_deinit (stun_trans_t *restrict tr);
int stun_trans_start (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_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); 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); int stun_trans_fd (const stun_trans_t *tr);
# ifdef __cplusplus # ifdef __cplusplus
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment