Commit c8309418 authored by Kai Vehmanen's avatar Kai Vehmanen
Browse files

Added a new STUN API with full binding discovery functionality. Allows use of both old and new API.

darcs-hash:20070502142111-77cd4-c6af7764a0fd09c2829c468363671476d56bce29.gz
parent 89ca3b9d
......@@ -6,6 +6,8 @@
#
# Licensed under MPL 1.1/LGPL 2.1. See file COPYING.
SUBDIRS = . tests
include $(top_srcdir)/common.mk
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS)
......@@ -14,12 +16,16 @@ LIBS = $(GLIB_LIBS)
noinst_LTLIBRARIES = libstun.la
libstun_la_SOURCES = stun.h stun.c
libstun_la_SOURCES = stun.h stun.c \
stun-msg.h stunsend.c stunrecv.c crc32.c hmac.c \
bind.h bind.c
libstun_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS)
libstun_la_LIBADD = $(LIBS) $(OPENSSL_LIBS) $(LIBRT)
noinst_PROGRAMS = stun-client stun-server
noinst_PROGRAMS = stun-client stund
stun_client_LDADD = libstun.la
stun_server_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread
check_PROGRAMS = \
test-attribute-pack \
......
/*
* 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 "bind.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <fcntl.h>
/**
* Initial STUN timeout (milliseconds). The spec says it should be 100ms,
* but that's way too short for most types of wireless Internet access.
*/
#define STUN_INIT_TIMEOUT 600
#define STUN_END_TIMEOUT 4800
/**
* Performs STUN Binding discovery in blocking mode.
*
* @param fd socket to use for binding discovery, or -1 to create one
* @param srv STUN server socket address
* @param srvlen STUN server socket address byte length
* @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 addr [IN], set to the byte
* length of the binding socket address on return.
*
* @return 0 on success, a standard error value in case of error.
* In case of error, addr and addrlen are undefined.
*/
int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
stun_bind_t *ctx;
int val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
return val;
do
{
unsigned delay = stun_bind_timeout (ctx);
struct pollfd ufd[1];
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);
}
while (val == EAGAIN);
return val;
}
#include "stun-msg.h"
struct stun_bind_s
{
struct sockaddr_storage srv;
socklen_t srvlen;
struct timespec deadline;
unsigned delay;
int fd;
bool ownfd;
stun_transid_t transid;
};
static int
stun_bind_req (stun_bind_t *ctx)
{
/* FIXME: support for TCP */
stun_msg_t msg;
stun_init (&msg, STUN_REQUEST, STUN_BINDING, ctx->transid);
size_t len = stun_finish (&msg);
if (!len)
return errno;
ssize_t val = sendto (ctx->fd, &msg, len, 0,
(struct sockaddr *)&ctx->srv, ctx->srvlen);
if (val == -1)
return errno;
if (val < (ssize_t)len)
return EMSGSIZE;
return 0;
}
static void stun_gettime (struct timespec *restrict now)
{
#if (_POSIX_MONOTONIC_CLOCK - 0) >= 0
if (clock_gettime (CLOCK_MONOTONIC, now))
#endif
{ // fallback to wall clock
struct timeval tv;
gettimeofday (&tv, NULL);
now->tv_sec = tv.tv_sec;
now->tv_nsec = tv.tv_usec * 1000;
}
}
/**
* Sets deadline = now + delay
*/
static void
stun_setto (struct timespec *restrict deadline, unsigned delay)
{
div_t d = div (delay, 1000);
stun_gettime (deadline);
// add delay to current time
deadline->tv_sec += d.quot;
deadline->tv_nsec += d.rem * 1000000;
DBG ("New STUN timeout is %ums\n", delay);
}
/**
* @return Remaining delay = deadline - now, or 0 if behind schedule.
*/
static unsigned
stun_getto (const struct timespec *restrict deadline)
{
unsigned delay;
struct timespec now;
stun_gettime (&now);
if (now.tv_sec > deadline->tv_sec)
return 0;
delay = deadline->tv_sec - now.tv_sec;
if ((delay == 0) && (now.tv_nsec >= deadline->tv_nsec))
return 0;
delay *= 1000;
delay += ((signed)(deadline->tv_nsec - now.tv_nsec)) / 1000000;
DBG ("Current STUN timeout is %ums\n", delay);
return delay;
}
/**
* Aborts a running STUN Binding dicovery.
*/
void stun_bind_cancel (stun_bind_t *restrict context)
{
int val = errno;
if (context->ownfd)
close (context->fd);
#ifndef NDEBUG
context->fd = -1;
#endif
free (context);
errno = val;
}
/**
* Starts STUN Binding discovery in non-blocking mode.
*
* @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
*
* @return 0 on success, a standard error value otherwise.
*/
int stun_bind_start (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv,
socklen_t srvlen)
{
stun_bind_t *ctx = malloc (sizeof (*ctx));
if (ctx == NULL)
return errno;
memset (ctx, 0, sizeof (*ctx));
*context = ctx;
if (srvlen > sizeof (ctx->srv))
{
stun_bind_cancel (ctx);
return ENOBUFS;
}
memcpy (&ctx->srv, srv, ctx->srvlen = srvlen);
if (fd == -1)
{
if (srvlen < sizeof (struct sockaddr))
{
stun_bind_cancel (ctx);
return EINVAL;
}
fd = socket (ctx->srv.ss_family, SOCK_DGRAM, 0);
if (fd == -1)
{
stun_bind_cancel (ctx);
return errno;
}
#ifdef FD_CLOEXEC
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
#endif
#ifdef O_NONBLOCK
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
#endif
ctx->ownfd = true;
}
ctx->fd = fd;
stun_setto (&ctx->deadline, ctx->delay = STUN_INIT_TIMEOUT);
stun_make_transid (ctx->transid);
int val = stun_bind_req (ctx);
if (val)
{
stun_bind_cancel (ctx);
return val;
}
return 0;
}
/**
* Continues STUN Binding discovery in non-blocking mode.
*
* @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 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.
0 is returned on successful completion, another standard error value
* otherwise. If the return value is not EAGAIN, <context> is freed and must
* not be re-used.
*
* FIXME: document file descriptor closure semantics.
*/
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen)
{
stun_msg_t buf;
ssize_t len;
bool error;
assert (context != NULL);
assert (context->fd != -1);
// FIXME: should we only accept packet from server IP:port ?
// FIXME: write a function to wrap this?
len = recv (context->fd, &buf, sizeof (buf), MSG_DONTWAIT);
if (len < 0)
goto skip;
len = stun_validate (&buf, len);
if (len <= 0)
goto skip;
DBG ("Received %u-bytes STUN message\n", (unsigned)len);
if (!stun_match_answer (&buf, STUN_BINDING, context->transid, &error))
goto skip;
if (error)
{
stun_bind_cancel (context);
return ECONNREFUSED; // FIXME: better error value
}
if (stun_has_unknown (&buf))
{
stun_bind_cancel (context);
return EPROTO;
}
len = stun_find_xor_addr (&buf, STUN_XOR_MAPPED_ADDRESS, addr, addrlen);
if (len)
{
DBG (" No XOR-MAPPED-ADDRESS: %s\n", strerror (len));
len = stun_find_addr (&buf, STUN_MAPPED_ADDRESS, addr, addrlen);
if (len)
{
DBG (" No MAPPED-ADDRESS: %s\n", strerror (len));
stun_bind_cancel (context);
return len;
}
}
DBG (" Mapped address found!\n");
stun_bind_cancel (context);
return 0;
skip:
// FIXME: we call gettimeofday() twice here (minor problem)
if (!stun_getto (&context->deadline))
{
if (context->delay >= STUN_END_TIMEOUT)
{
DBG ("Received no valid responses. STUN transaction failed.\n");
stun_bind_cancel (context);
return ETIMEDOUT; // fatal error!
}
context->delay *= 2;
DBG ("Retrying with longer timeout... %ums\n", context->delay);
stun_bind_req (context);
stun_setto (&context->deadline, context->delay);
}
return EAGAIN;
}
/**
* @return recommended maximum delay (in milliseconds) to wait for a
* response.
* This is meant to integrate with I/O pooling loops and event frameworks.
*/
unsigned stun_bind_timeout (const stun_bind_t *restrict context)
{
assert (context != NULL);
assert (context->fd != -1);
return stun_getto (&context->deadline);
}
/**
* @return file descriptor used by the STUN Binding discovery context.
* Always succeeds.
* This is meant to integrate with I/O polling loops and event frameworks.
*/
int stun_bind_fd (const stun_bind_t *restrict context)
{
assert (context != NULL);
return context->fd;
}
/*
* 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_BIND_H
# define STUN_BIND_H 1
typedef struct stun_bind_s stun_bind_t;
# ifdef __cplusplus
extern "C" {
# endif
int stun_bind_run (int fd,
const struct sockaddr *restrict srv, socklen_t srvlen,
struct sockaddr *restrict addr, socklen_t *addrlen);
int stun_bind_start (stun_bind_t **restrict context, int fd,
const struct sockaddr *restrict srv,
socklen_t srvlen);
int stun_bind_resume (stun_bind_t *restrict context,
struct sockaddr *restrict addr, socklen_t *addrlen);
void stun_bind_cancel (stun_bind_t *restrict context);
int stun_bind_fd (const stun_bind_t *restrict context);
unsigned stun_bind_timeout (const stun_bind_t *restrict context);
# ifdef __cplusplus
}
# endif
#endif
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Rémi Denis-Courmont
* COPYRIGHT (C) 1986 Gary S. Brown
* See documentation of the function crc32() below.
*
* 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 <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <sys/socket.h>
#include "stun-msg.h"
static uint32_t crc32 (const void *buf, size_t size);
/**
* Computes the FINGERPRINT of a STUN message.
* @return fingerprint value in <b>host</b> byte order.
*/
uint32_t stun_fingerprint (const void *msg)
{
const uint8_t *ptr = msg;
assert (msg != NULL);
/* Don't hash last 8-bytes (= the FINGERPRINT attribute) */
assert (stun_length (ptr) >= 8);
size_t len = 20 + stun_length (ptr) - 8;
return crc32 (msg, len) ^ 0x5354554e;
}
/*-
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
* code or tables extracted from it, as desired without restriction.
*
* Extracted from FreeBSD CVS (src/sys/libkern/crc32.c)
* and adapted by Rémi Denis-Courmont, 17 April 2007.
*/
/*
* First, the polynomial itself and its table of feedback terms. The
* polynomial is
* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
*
* Note that we take it "backwards" and put the highest-order term in
* the lowest-order bit. The X^32 term is "implied"; the LSB is the
* X^31 term, etc. The X^0 term (usually shown as "+1") results in
* the MSB being 1
*
* Note that the usual hardware shift register implementation, which
* is what we're using (we're merely optimizing it by doing eight-bit
* chunks at a time) shifts bits into the lowest-order term. In our
* implementation, that means shifting towards the right. Why do we
* do it this way? Because the calculated CRC must be transmitted in
* order from highest-order term to lowest-order term. UARTs transmit
* characters in order from LSB to MSB. By storing the CRC this way
* we hand it to the UART in the order low-byte to high-byte; the UART
* sends each low-bit to hight-bit; and the result is transmission bit
* by bit from highest- to lowest-order term without requiring any bit
* shuffling on our part. Reception works similarly
*
* The feedback terms table consists of 256, 32-bit entries. Notes
*
* The table can be generated at runtime if desired; code to do so
* is shown later. It might not be obvious, but the feedback
* terms simply represent the results of eight shift/xor opera
* tions for all combinations of data and CRC register values
*
* The values must be right-shifted by eight bits by the "updcrc
* logic; the shift must be unsigned (bring in zeroes). On some
* hardware you could probably optimize the shift in assembler by
* using byte-swap instructions
* polynomial $edb88320
*
*
* CRC32 code derived from work by Gary S. Brown.
*/
static const uint32_t crc32_tab[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
</