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

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

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

darcs-hash:20070619075737-77cd4-93e3509da656acdadff0afa36eac3b8569b5ef20.gz
parent f37d8947
......@@ -11,27 +11,30 @@ DIST_SUBDIRS = tests
include $(top_srcdir)/common.mk
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CFLAGS = -std=gnu99 $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)
LIBS = $(GLIB_LIBS)
noinst_LTLIBRARIES = libstun.la
libstun_la_SOURCES = stun.h stun.c \
libstun_la_SOURCES = \
stun-msg.h stunsend.c stunrecv.c crc32.c hmac.c \
timer.h timer.c trans.h trans.c \
bind.h conncheck.h bind.c bindserv.c
bind.h stun-ice.h bind.c bindserv.c \
relay.h relay.c
libstun_la_LIBADD = $(LIBS) $(OPENSSL_LIBS) $(LIBRT)
noinst_PROGRAMS = stun-client
EXTRA_libstun_la_SOURCES = stun.h stun.c
EXTRA_PROGRAMS = stun-client
bin_PROGRAMS = stund stunbdc
check_PROGRAMS = stund
stun_client_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread
stunbdc_LDADD = libstun.la
check_PROGRAMS = \
EXTRA_PROGRAMS += \
test-attribute-pack \
test-attribute-pack-unknown \
test-attribute-dump \
......@@ -57,6 +60,3 @@ test_message_dump_LDADD = libstun.la
test_message_dump_unknown_LDADD = libstun.la
test_message_unpack_LDADD = libstun.la
test_message_find_attribute_LDADD = libstun.la
TESTS = $(check_PROGRAMS)
......@@ -41,6 +41,7 @@
#include <sys/socket.h>
#include "bind.h"
#include "stun-msg.h"
#include <assert.h>
#include <string.h>
......@@ -50,7 +51,9 @@
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/poll.h>
#ifdef HAVE_POLL
# include <sys/poll.h>
#endif
#include <fcntl.h>
/** Blocking mode STUN binding discovery */
......@@ -59,8 +62,11 @@ int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
#ifdef HAVE_POLL
stun_bind_t *ctx;
int val;
uint8_t buf[STUN_MAXMSG];
size_t len = 0;
ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
......@@ -68,30 +74,39 @@ int stun_bind_run (int fd,
do
{
unsigned delay = stun_bind_timeout (ctx);
struct pollfd ufd[1];
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd));
ufd[0].fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN;
poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay);
val = stun_bind_resume (ctx, addr, addrlen);
val = recv (ufd[0].fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
val = stun_bind_elapse (ctx);
else
{
len += val;
val = stun_bind_process (ctx, buf, len, addr, addrlen);
}
}
while (val == EAGAIN);
return val;
#else
(void)fd; (void)srv; (void)srvlen; (void)addr; (void)addrlen;
return ENOSYS;
#endif
}
/** Non-blocking mode STUN binding discovery */
#include "stun-msg.h"
#include "trans.h"
struct stun_bind_s
{
stun_trans_t trans;
size_t keylen;
uint8_t key[0];
};
......@@ -113,22 +128,26 @@ static int
stun_bind_alloc (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
int val;
stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return ENOMEM;
memset (ctx, 0, sizeof (*ctx));
*context = ctx;
int val = stun_trans_init (&ctx->trans, fd, srv, srvlen);
val = (fd != -1)
? stun_trans_init (&ctx->trans, fd, srv, srvlen)
: stun_trans_create (&ctx->trans, SOCK_DGRAM, 0, srv, srvlen);
if (val)
{
free (ctx);
return val;
}
ctx->keylen = (size_t)(-1);
stun_init_request (ctx->trans.msg, STUN_BINDING);
stun_init_request (ctx->trans.msg.buf, STUN_BINDING);
return 0;
}
......@@ -162,8 +181,8 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
ctx = *context;
ctx->trans.msglen = sizeof (ctx->trans.msg);
val = stun_finish (ctx->trans.msg, &ctx->trans.msglen);
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish (ctx->trans.msg.buf, &ctx->trans.msg.length);
if (val)
{
stun_bind_cancel (ctx);
......@@ -205,31 +224,20 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
bool error;
int val = stun_validate (buf, len);
if (val <= 0)
return EAGAIN;
int val;
assert (ctx != NULL);
DBG ("Received %u-bytes STUN message\n", (unsigned)val);
if (!stun_match_messages (buf, ctx->trans.msg,
(ctx->keylen != (size_t)(-1)) ? ctx->key : NULL,
(ctx->keylen != (size_t)(-1)) ? ctx->keylen : 0,
&error))
return EAGAIN;
if (error)
{
stun_bind_cancel (ctx);
return ECONNREFUSED; // FIXME: better error value
}
if (stun_has_unknown (buf))
val = stun_trans_preprocess (&ctx->trans, buf, len);
switch (val)
{
stun_bind_cancel (ctx);
return EPROTO;
case EAGAIN:
return EAGAIN;
case 0:
break;
default:
stun_bind_cancel (ctx);
return val;
}
val = stun_find_xor_addr (buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen);
......@@ -251,24 +259,27 @@ int stun_bind_process (stun_bind_t *restrict ctx,
}
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
stun_msg_t buf;
ssize_t len;
/** ICE keep-alives (Binding discovery indication!) */
assert (context != NULL);
int
stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen)
{
size_t val;
uint8_t buf[28];
len = recv (context->trans.fd, &buf, sizeof (buf), MSG_DONTWAIT);
if (len >= 0)
return stun_bind_process (context, &buf, len, addr, addrlen);
stun_init_indication (buf, STUN_BINDING);
(void)stun_finish (buf, &val);
return stun_bind_elapse (context);
/* NOTE: hopefully, this is only needed for non-stream sockets */
if (sendto (fd, buf, val, MSG_DONTWAIT, srv, srvlen) == -1)
return errno;
return 0;
}
/** Connectivity checks */
#include "conncheck.h"
#include "stun-ice.h"
int
stun_conncheck_start (stun_bind_t **restrict context, int fd,
......@@ -283,43 +294,43 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd,
assert (username != NULL);
assert (password != NULL);
val = stun_bind_alloc (context, fd, srv, srvlen);
val = stun_bind_alloc (&ctx, fd, srv, srvlen);
if (val)
return val;
val = strlen (password);
ctx = realloc (*context, sizeof (*ctx) + val + 1);
if (ctx == NULL)
ctx->trans.key.length = strlen (password);
ctx->trans.key.value = malloc (ctx->trans.key.length);
if (ctx->trans.key.value == NULL)
{
val = ENOMEM;
goto error;
}
*context = ctx;
memcpy (ctx->key, password, val);
ctx->keylen = val;
memcpy (ctx->trans.key.value, password, ctx->trans.key.length);
if (cand_use)
{
val = stun_append_flag (ctx->trans.msg, sizeof (ctx->trans.msg),
val = stun_append_flag (ctx->trans.msg.buf,
sizeof (ctx->trans.msg.buf),
STUN_USE_CANDIDATE);
if (val)
goto error;
}
val = stun_append32 (ctx->trans.msg, sizeof (ctx->trans.msg),
val = stun_append32 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf),
STUN_PRIORITY, priority);
if (val)
goto error;
val = stun_append64 (ctx->trans.msg, sizeof (ctx->trans.msg),
val = stun_append64 (ctx->trans.msg.buf, sizeof (ctx->trans.msg.buf),
controlling ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, tie);
if (val)
goto error;
ctx->trans.msglen = sizeof (ctx->trans.msg);
val = stun_finish_short (ctx->trans.msg, &ctx->trans.msglen,
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length,
username, password, NULL, 0);
if (val)
goto error;
......
......@@ -77,7 +77,7 @@ int stun_bind_run (int fd,
* Starts STUN Binding discovery in non-blocking mode.
*
* @param context pointer to an opaque pointer that will be passed to
* stun_bind_resume() afterward
* other stun_bind_*() functions afterward
* @param fd socket to use for discovery, or -1 to create one
* @param srv STUN server socket address
* @param srvlen STUN server socket address length
......@@ -156,24 +156,15 @@ int stun_bind_process (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen);
/**
* Continues STUN Binding discovery in non-blocking mode:
* Tries to dequeue a data from the network and processes it,
* updates the transaction timer if needed.
*
* @param context binding discovery context (from stun_bind_start())
* @param addr pointer to a socket address structure to hold the discovered
* binding (remember this can be either IPv4 or IPv6 regardless of the socket
* family) [OUT]
* @param addrlen pointer to the byte length of @a addr [IN], set to the byte
* length of the binding socket address on return.
* Sends a STUN Binding indication, aka ICE keep-alive packet.
*
* @return EAGAIN is returned if the discovery has not completed yet.
0 is returned on successful completion, another standard error value
* otherwise. If the return value is not EAGAIN, @a context is freed and must
* not be re-used.
* @param fd socket descriptor to send packet through
* @param srv destination socket address (possibly NULL if connected)
* @param srvlen destination socket address length (possibly 0)
* @return 0 on success, an error code from sendto() otherwise.
*/
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen);
int stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen);
/**
......
......@@ -69,8 +69,6 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen,
bool muxed, const char *restrict pass)
{
assert (plen != NULL);
size_t len = *plen;
int val;
*plen = 0;
......@@ -82,7 +80,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
if (stun_get_class (msg) != STUN_REQUEST)
{
DBG (" Unhandled non-request (class %u) message.\n",
(unsigned)stun_get_class (msg));
stun_get_class (msg));
return EINVAL;
}
......@@ -133,7 +131,7 @@ stun_binding_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
if (stun_get_method (msg) != STUN_BINDING)
{
DBG (" Bad request (method %u) message.\n",
(unsigned)stun_get_method (msg));
stun_get_method (msg));
err (STUN_BAD_REQUEST);
return EPROTO;
}
......@@ -185,7 +183,7 @@ stun_bind_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
/** Connectivity checks **/
#include "conncheck.h"
#include "stun-ice.h"
int
stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
......@@ -229,16 +227,20 @@ stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen)
{
size_t i;
ssize_t len = stun_find_string (msg, STUN_USERNAME, buf, buflen);
if ((len == -1) || ((size_t)len >= buflen))
return NULL;
for (size_t i = 0; i < (size_t)len; i++)
for (i = 0; i < (size_t)len; i++)
{
char c = buf[i];
/* ref ICE sect 7.1.1.4. (ID-16) */
if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z'))
|| ((c >= 'a') && (c <= 'z')) || (c == '+'))
|| ((c >= 'a') && (c <= 'z')) || (c == '+') || (c == ':'))
continue;
return NULL;
}
......@@ -249,6 +251,7 @@ char *stun_conncheck_username (const uint8_t *restrict msg,
uint32_t stun_conncheck_priority (const uint8_t *msg)
{
uint32_t value;
if (stun_find32 (msg, STUN_PRIORITY, &value))
return 0;
return value;
......
......@@ -41,7 +41,6 @@
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <sys/socket.h>
#include "stun-msg.h"
......@@ -55,12 +54,8 @@ static uint32_t crc32 (const void *buf, size_t size);
*/
uint32_t stun_fingerprint (const uint8_t *msg)
{
assert (msg != NULL);
/* Don't hash last 8-bytes (= the FINGERPRINT attribute) */
assert (stun_length (msg) >= 8);
size_t len = 20u + stun_length (msg) - 8;
size_t len = 12u + stun_length (msg); // 20 - 8 = 12
return crc32 (msg, len) ^ 0x5354554e;
}
......
......@@ -38,11 +38,14 @@
#endif
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include "stun-msg.h"
#include <string.h>
#include <assert.h>
void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen)
......@@ -59,3 +62,39 @@ void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen
HMAC (EVP_sha1 (), key, keylen, msg, mlen, sha, NULL);
}
void stun_make_transid (stun_transid_t id)
{
/*
* transid = (HMAC_SHA1 (secret, counter) >> 64)
* This consumes sizeof (secret) bytes of entropy every 2^64 messages.
*/
static struct
{
pthread_mutex_t lock;
uint64_t counter;
uint8_t secret[16];
} store = { PTHREAD_MUTEX_INITIALIZER, 0, "" };
union
{
uint64_t value;
uint8_t bytes[1];
} counter;
uint8_t key[16], sha[20];
pthread_mutex_lock (&store.lock);
counter.value = store.counter++;
if (counter.value == 0)
RAND_pseudo_bytes (store.secret, sizeof (store.secret));
memcpy (key, store.secret, sizeof (key));
pthread_mutex_unlock (&store.lock);
/* Computes hash out of contentious area */
HMAC (EVP_sha1 (), key, sizeof (key), counter.bytes, sizeof (counter),
sha, NULL);
memcpy (id, sha, 12);
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Rémi Denis-Courmont
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Rémi Denis-Courmont, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "relay.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "stun-msg.h"
#include "trans.h"
struct turn_s
{
stun_trans_t trans;
};
/**
* @file relay.c
* @brief STUN relay usage (TURN) implementation
*/
turn_t *turn_socket (int fd, int family, turn_proto_t proto,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
turn_t *ctx;
int val;
if (family != AF_INET)
{
errno = EAFNOSUPPORT;
return NULL;
}
if (proto != TURN_PROTO_UDP)
{
errno = EPROTONOSUPPORT;
return NULL;
}
ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return NULL;
memset (ctx, 0, sizeof (*ctx));
val = (fd != -1)
? stun_trans_init (&ctx->trans, fd, srv, srvlen)
: stun_trans_create (&ctx->trans, SOCK_DGRAM, 0, srv, srvlen);
if (val)
{
free (ctx);
errno = val;
return NULL;
}
stun_init_request (ctx->trans.msg.buf, STUN_ALLOCATE);
return ctx;
}
int turn_connect (turn_t *restrict ctx, const struct sockaddr *restrict dst,
socklen_t len)
{
assert (ctx != NULL);
(void)ctx; (void)dst; (void)len;
errno = ENOSYS;
return -1;
}
ssize_t turn_sendto (turn_t *restrict ctx, const void *data, size_t datalen,
int flags, const struct sockaddr *restrict dst,
socklen_t dstlen)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)datalen; (void)flags; (void)dst; (void)dstlen;
errno = ENOSYS;
return -1;
}
ssize_t turn_send (turn_t *restrict ctx, const void *data, size_t len,
int flags)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)len; (void)flags;
errno = ENOSYS;
return -1;
}
ssize_t turn_recvfrom (turn_t *restrict ctx, void *data, size_t len, int flags,
const struct sockaddr *restrict src, socklen_t *srclen)
{
assert (ctx != NULL);
(void)ctx; (void)data; (void)len; (void)flags; (void)src; (void)srclen;
errno = ENOSYS;
return -1;
}
ssize_t turn_recv (turn_t *restrict ctx, void *data, size_t len, int flags)
{