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

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

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

darcs-hash:20070619075737-77cd4-93e3509da656acdadff0afa36eac3b8569b5ef20.gz
parent f37d8947
......@@ -11,27 +11,30 @@ DIST_SUBDIRS = tests
include $(top_srcdir)/common.mk
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CFLAGS = -std=gnu99 $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)
LIBS = $(GLIB_LIBS)
noinst_LTLIBRARIES = libstun.la
libstun_la_SOURCES = stun.h stun.c \
libstun_la_SOURCES = \
stun-msg.h stunsend.c stunrecv.c crc32.c hmac.c \
timer.h timer.c trans.h trans.c \
bind.h conncheck.h bind.c bindserv.c
bind.h stun-ice.h bind.c bindserv.c \
relay.h relay.c
libstun_la_LIBADD = $(LIBS) $(OPENSSL_LIBS) $(LIBRT)
noinst_PROGRAMS = stun-client
EXTRA_libstun_la_SOURCES = stun.h stun.c
EXTRA_PROGRAMS = stun-client
bin_PROGRAMS = stund stunbdc
check_PROGRAMS = stund
stun_client_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread
stunbdc_LDADD = libstun.la
check_PROGRAMS = \
EXTRA_PROGRAMS += \
test-attribute-pack \
test-attribute-pack-unknown \
test-attribute-dump \
......@@ -57,6 +60,3 @@ test_message_dump_LDADD = libstun.la
test_message_dump_unknown_LDADD = libstun.la
test_message_unpack_LDADD = libstun.la
test_message_find_attribute_LDADD = libstun.la
TESTS = $(check_PROGRAMS)
......@@ -41,6 +41,7 @@
#include <sys/socket.h>
#include "bind.h"
#include "stun-msg.h"
#include <assert.h>
#include <string.h>
......@@ -50,7 +51,9 @@
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/poll.h>
#ifdef HAVE_POLL
# include <sys/poll.h>
#endif
#include <fcntl.h>
/** Blocking mode STUN binding discovery */
......@@ -59,8 +62,11 @@ int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
#ifdef HAVE_POLL
stun_bind_t *ctx;
int val;
uint8_t buf[STUN_MAXMSG];
size_t len = 0;
ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
......@@ -68,30 +74,39 @@ int stun_bind_run (int fd,
do
{
unsigned delay = stun_bind_timeout (ctx);
struct pollfd ufd[1];
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd));
ufd[0].fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN;
poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay);
val = stun_bind_resume (ctx, addr, addrlen);
val = recv (ufd[0].fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
val = stun_bind_elapse (ctx);
else
{
len += val;
val = stun_bind_process (ctx, buf, len, addr, addrlen);
}
}
while (val == EAGAIN);
return val;
#else
(void)fd; (void)srv; (void)srvlen; (void)addr; (void)addrlen;
return ENOSYS;
#endif
}
/** Non-blocking mode STUN binding discovery */
#include "stun-msg.h"
#include "trans.h"
struct stun_bind_s
{
stun_trans_t trans;
size_t keylen;
uint8_t key[0];
};
......@@ -113,22 +128,26 @@ static int
stun_bind_alloc (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
int val;
stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return ENOMEM;
memset (ctx, 0, sizeof (*ctx));
*context = ctx;
int val = stun_trans_init (&ctx->trans, fd, srv, srvlen);
val = (fd != -1)
? stun_trans_init (&ctx->trans, fd, srv, srvlen)
: stun_trans_create (&ctx->trans, SOCK_DGRAM, 0, srv, srvlen);
if (val)
{
free (ctx);
return val;
}
ctx->keylen = (size_t)(-1);
stun_init_request (ctx->trans.msg, STUN_BINDING);
stun_init_request (ctx->trans.msg.buf, STUN_BINDING);
return 0;
}
......@@ -162,8 +181,8 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
ctx = *context;
ctx->trans.msglen = sizeof (ctx->trans.msg);
val = stun_finish (ctx->trans.msg, &ctx->trans.msglen);
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish (ctx->trans.msg.buf, &ctx->trans.msg.length);
if (val)
{
stun_bind_cancel (ctx);
......@@ -205,31 +224,20 @@ int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
bool error;
int val = stun_validate (buf, len);
if (val <= 0)
return EAGAIN;
int val;
assert (ctx != NULL);
DBG ("Received %u-bytes STUN message\n", (unsigned)val);
if (!stun_match_messages (buf, ctx->trans.msg,
(ctx->keylen != (size_t)(-1)) ? ctx->key : NULL,
(ctx->keylen != (size_t)(-1)) ? ctx->keylen : 0,
&error))
return EAGAIN;
if (error)
{
stun_bind_cancel (ctx);
return ECONNREFUSED; // FIXME: better error value
}
if (stun_has_unknown (buf))
val = stun_trans_preprocess (&ctx->trans, buf, len);
switch (val)
{
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)
{
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,