Commit 91d4e0a1 authored by Kai Vehmanen's avatar Kai Vehmanen

Major update to the new STUN API: support for binding discoveries, fingerprint...

Major update to the new STUN API: support for binding discoveries, fingerprint support, message-integrity support, asynchronous public API without glib dependency. Test cases for most of the functionality. Unit tests moved to a separate folder.

darcs-hash:20070521152838-77cd4-17a0eed0493917f9125713ebc5d333bd74af5a4f.gz
parent 2aee6d44
......@@ -7,10 +7,12 @@
# Licensed under MPL 1.1/LGPL 2.1. See file COPYING.
SUBDIRS = . tests
DIST_SUBDIRS = tests
include $(top_srcdir)/common.mk
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS)
AM_CFLAGS = $(ERROR_CFLAGS) $(GLIB_CFLAGS) $(OPENSSL_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)
LIBS = $(GLIB_LIBS)
......@@ -18,14 +20,16 @@ noinst_LTLIBRARIES = libstun.la
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)
timer.h timer.c trans.h trans.c \
bind.h conncheck.h bind.c bindserv.c
libstun_la_LIBADD = $(LIBS) $(OPENSSL_LIBS) $(LIBRT)
noinst_PROGRAMS = stun-client stund
noinst_PROGRAMS = stun-client
bin_PROGRAMS = stund stunbdc
stun_client_LDADD = libstun.la
stund_LDADD = libstun.la -lpthread
stunbdc_LDADD = libstun.la
check_PROGRAMS = \
test-attribute-pack \
......
This diff is collapsed.
......@@ -36,23 +36,178 @@
#ifndef STUN_BIND_H
# define STUN_BIND_H 1
/**
* @file bind.h
* @brief STUN binding discovery
*/
# ifndef IPPORT_STUN
/** Default port for STUN binding discovery */
# define IPPORT_STUN 3478
# endif
typedef struct stun_bind_s stun_bind_t;
# include <stdbool.h>
# include <stdint.h>
# ifdef __cplusplus
extern "C" {
# endif
/**
* 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 [OUT] pointer to a socket address structure to hold
* discovered binding (Remember that it can be an IPv6 even if the socket
* local family is IPv4, so you should use a sockaddr_storage buffer)
* @param addrlen [IN/OUT] pointer to the byte length of addr, 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);
/**
* 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);
const struct sockaddr *restrict srv, socklen_t srvlen);
/**
* Aborts a running STUN Binding discovery.
* @param context binding discovery (or conncheck) context pointer
* to be released.
*/
void stun_bind_cancel (stun_bind_t *context);
/**
* This is meant to integrate with I/O pooling loops and event frameworks.
*
* @param context binding discovery (or conncheck) context pointer
* @return recommended maximum delay (in milliseconds) to wait for a
* response.
*/
unsigned stun_bind_timeout (const stun_bind_t *context);
/**
* Handles retransmission timeout, and sends request retransmit if needed.
* This should be called whenever event polling indicates that
* stun_bind_timeout() has elapsed. It is however safe to call this earlier
* (in which case retransmission will not occur) or later (in which case
* late retransmission will be done).
*
* @param context binding discovery (or conncheck) context pointer
*
* @return ETIMEDOUT if the transaction has timed out, or EAGAIN if it is
* still pending.
*
* If anything except EAGAIN (but including zero) is returned, the context
* is free'd and must no longer be used.
*/
int stun_bind_elapse (stun_bind_t *context);
/**
* 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_bind_fd (const stun_bind_t *context);
/**
* Gives data to be processed within the context of a STUN Binding discovery
* or ICE connectivity check.
*
* @param context context (from stun_bind_start() or stun_conncheck_start())
* @param buf pointer to received data to be processed
* @param len byte length of data at @a buf
* @param addr socket address pointer to receive mapped address in case of
* successful processing
* @param addrlen [IN/OUT] pointer to the size of the socket address buffer
* at @a addr upon entry, set to the useful size on success
*
* @return 0 on success, an error code otherwise:
* - EAGAIN: ignored invalid message (non-fatal error)
* - ECONNREFUSED: error message from server
* - EPROTO: unsupported message from server
* - ENOENT: no mapped address in message from server
* - EAFNOSUPPORT: unsupported mapped address family from server
* - EINVAL: invalid mapped address from server
*
* If anything except EAGAIN (but including zero) is returned, the context
* is free'd and must no longer be used.
*/
int stun_bind_process (stun_bind_t *restrict context,
const void *restrict buf, size_t len,
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.
*
* @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.
*/
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);
/**
* Tries to parse a STUN Binding request and format a Binding 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 a valid STUN message (check message
* validity with stun_validate() first)
* @param src socket address the message was received from
* @param srclen byte length of the socket address
* @param muxed If true, non-multiplexed (old RFC3489-style) messages will be
* considered as invalid.
*
* @return 0 if successful (@a rbuf contains a <b>non-error</b> response),
* EINVAL: malformatted request message or socket address,
* EAFNOSUPPORT: unsupported socket address family,
* EPROTO: unsupported request message type or parameter,
* ENOBUFS: insufficient response buffer space.
*
* In case of error, the value at @a plen is set to the size of an error
* response, or 0 if no error response should be sent.
*/
int
stun_bind_reply (uint8_t *buf, size_t *plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen,
bool muxed);
# ifndef STUN_VALIDATE_DECLARATION
# define STUN_VALIDATE_DECLARATION 2
ssize_t stun_validate (const uint8_t *msg, size_t len);
# endif
# ifdef __cplusplus
}
......
/*
* 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 <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "bind.h"
#include "stun-msg.h"
#include <errno.h>
static int
stun_bind_error (uint8_t *buf, size_t *plen, const uint8_t *req,
stun_error_t code, const char *pass)
{
int val = stun_init_error (buf, *plen, req, code);
if (val)
return val;
val = stun_finish_short (buf, plen, NULL, pass, NULL, 0);
if (val)
return val;
DBG (" Error response (%u) of %u bytes\n", (unsigned)code,
(unsigned)*plen);
return 0;
}
static int
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;
#define err( code ) \
stun_bind_error (buf, &len, msg, code, pass); \
*plen = len
if (stun_get_class (msg) != STUN_REQUEST)
{
DBG (" Unhandled non-request (class %u) message.\n",
(unsigned)stun_get_class (msg));
return EINVAL;
}
if (muxed)
{
if (!stun_demux (msg))
{
DBG (" Incorrectly multiplexed STUN message ignored.\n");
return EINVAL;
}
}
else
muxed = stun_demux (msg);
DBG (" %s-style STUN message.\n", muxed ? "New" : "Old");
if (pass != NULL)
{
if (!stun_has_integrity (msg))
{
DBG (" Message Authentication Code missing.\n");
err (STUN_UNAUTHORIZED);
return EPERM;
}
if (!stun_present (msg, STUN_USERNAME))
{
DBG (" Username missing.\n");
err (STUN_MISSING_USERNAME);
return EPERM;
}
/* NOTE: should check in that order:
* - missing realm
* - missing nonce
* - stale nonce
* - unknown user / stale credentials
*/
if (stun_verify_password (msg, pass))
{
DBG (" Integrity check failed.\n");
err (STUN_INTEGRITY_CHECK_FAILURE);
return EPERM;
}
}
if (stun_get_method (msg) != STUN_BINDING)
{
DBG (" Bad request (method %u) message.\n",
(unsigned)stun_get_method (msg));
err (STUN_BAD_REQUEST);
return EPROTO;
}
if (stun_has_unknown (msg))
{
DBG (" Unknown mandatory attributes in message.\n");
val = stun_init_error_unknown (buf, len, msg);
if (!val)
val = stun_finish_short (buf, &len, NULL, pass, NULL, 0);
if (val)
goto failure;
*plen = len;
return EPROTO;
}
stun_init_response (buf, msg);
val = muxed
? stun_append_xor_addr (buf, len, STUN_XOR_MAPPED_ADDRESS, src, srclen)
: stun_append_addr (buf, len, STUN_MAPPED_ADDRESS, src, srclen);
if (val)
{
DBG (" Mapped address problem: %s\n", strerror (val));
goto failure;
}
val = stun_finish_short (buf, &len, NULL, pass, NULL, 0);
if (val)
goto failure;
*plen = len;
return 0;
failure:
err (STUN_GLOBAL_FAILURE);
return val;
}
#undef err
int
stun_bind_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
const struct sockaddr *restrict src, socklen_t srclen,
bool muxed)
{
return stun_binding_reply (buf, plen, msg, src, srclen, muxed, NULL);
}
/** Connectivity checks **/
#include "conncheck.h"
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)
{
uint64_t q;
int val = stun_binding_reply (buf, plen, msg, src, srclen, true, pass);
if (val)
return val;
/* Role conflict handling */
// FIXME: breaks if buf == msg
assert (val == 0);
if (!stun_find64 (msg, *control ? STUN_ICE_CONTROLLING
: STUN_ICE_CONTROLLED, &q))
{
DBG ("STUN Role Conflict detected:\n");
if (tie < q)
{
DBG (" switching role from \"controll%s\" to \"controll%s\"\n",
*control ? "ing" : "ed", *control ? "ed" : "ing");
*control = !*control;
val = EACCES;
}
else
{
DBG (" staying \"controll%s\" (sending error)\n",
*control ? "ing" : "ed");
stun_bind_error (buf, plen, msg, STUN_ROLE_CONFLICT, pass);
}
}
return val;
}
char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen)
{
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++)
{
char c = buf[i];
if (((c >= '/') && (c <= '9')) || ((c >= 'A') && (c <= 'Z'))
|| ((c >= 'a') && (c <= 'z')) || (c == '+'))
continue;
return NULL;
}
return buf;
}
uint32_t stun_conncheck_priority (const uint8_t *msg)
{
uint32_t value;
if (stun_find32 (msg, STUN_PRIORITY, &value))
return 0;
return value;
}
bool stun_conncheck_use_candidate (const uint8_t *msg)
{
return !stun_find_flag (msg, STUN_USE_CANDIDATE);
}
/*
* 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
# include "stun/bind.h"
# ifdef __cplusplus
extern "C" {
# endif
/**
* @file conncheck.h
* @brief STUN/ICE connectivity checks
*/
/**
* 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
......@@ -53,15 +53,13 @@ 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)
uint32_t stun_fingerprint (const uint8_t *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;
assert (stun_length (msg) >= 8);
size_t len = 20u + stun_length (msg) - 8;
return crc32 (msg, len) ^ 0x5354554e;
}
......
......@@ -45,7 +45,7 @@
#include <assert.h>
void stun_sha1 (const void *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)
{
size_t mlen = stun_length (msg);
assert (mlen >= 32);
......
This diff is collapsed.
/*
* 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 "stun/bind.h"
#include <unistd.h>
#include <getopt.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static void
printaddr (const char *str, const struct sockaddr *addr, socklen_t addrlen)
{
char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV];
int val = getnameinfo (addr, addrlen, hostbuf, sizeof (hostbuf),
servbuf, sizeof (servbuf),
NI_NUMERICHOST | NI_NUMERICSERV);
if (val)
printf ("%s: %s\n", str, gai_strerror (val));
else
printf ("%s: %s port %s\n", str, hostbuf, servbuf);
}
static int run (int family, const char *hostname, const char *service)
{
struct addrinfo hints, *res;
int retval = -1;
memset (&hints, 0, sizeof (hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
if (service == NULL)
service = "3478";
int val = getaddrinfo (hostname, service, &hints, &res);
if (val)
{
fprintf (stderr, "%s (port %s): %s\n", hostname, service,
gai_strerror (val));
return -1;
}
for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
{
struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr);
printaddr ("Server address", ptr->ai_addr, ptr->ai_addrlen);
val = stun_bind_run (-1, ptr->ai_addr, ptr->ai_addrlen,
(struct sockaddr *)&addr, &addrlen);
if (val)
fprintf (stderr, "%s\n", strerror (val));
else
{
printaddr ("Mapped address", (struct sockaddr *)&addr, addrlen);
retval = 0;
}
}
freeaddrinfo (res);
return retval;
}
int main (int argc, char *argv[])
{
static const struct option opts[] =
{
{ "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' }
};
const char *server = NULL, *port = NULL;
int family = AF_UNSPEC;
for (;;)
{
int val = getopt_long (argc, argv, "46hV", opts, NULL);
if (val == EOF)
break;
switch (val)
{
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'h':
printf ("Usage: %s [-4|-6] <server> [port number]\n"
"Performs STUN Binding Discovery\n"
"\n"
" -4, --ipv4 Force IP version 4\n"
" -6, --ipv6 Force IP version 6\n"
"\n", argv[0]);
return 0;
case 'V':
printf ("stunbcd: STUN Binding Discovery client (%s v%s)\n",
PACKAGE, VERSION);
return 0;
}
}
if (optind < argc)
server = argv[optind++];
if (optind < argc)
port = argv[optind++];
if (optind < argc)
{
fprintf (stderr, "%s: extra parameter `%s'\n", argv[0], argv[optind]);
return 2;
}
return run (family, server, port) ? 1 : 0;
}
......@@ -62,6 +62,7 @@
static
int listen_socket (int fam, int type, int proto, uint16_t port)
{
int yes = 1;
int fd = socket (fam, type, proto);
if (fd == -1)
{
......@@ -88,7 +89,8 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
case AF_INET6:
#ifdef IPV6_V6ONLY
setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int));
if (setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes)))
goto error;
#endif
((struct sockaddr_in6 *)&addr)->sin6_port = port;
break;
......@@ -105,13 +107,12 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
switch (fam)
{
case AF_INET:
setsockopt (fd, SOL_IP, IP_PKTINFO, &(int){ 1 },
sizeof (int));
setsockopt (fd, SOL_IP, IP_PKTINFO, &yes, sizeof (yes));
break;
case AF_INET6:
setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &(int){ 1 },
sizeof (int));
setsockopt (fd, SOL_IPV6, IPV6_RECVPKTINFO, &yes,
sizeof (yes));
break;
}
}
......@@ -132,14 +133,15 @@ error:
}
#include "stun-msg.h"
#include "stun-msg.h" // FIXME: remove
#include "bind.h"
static int dgram_process (int sock)
{
struct sockaddr_storage addr;
stun_msg_t buf;
char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))];
struct iovec iov = { &buf, sizeof (buf) };
struct iovec iov = { buf, sizeof (buf) };
struct msghdr mh;
memset (&mh, 0, sizeof (mh));
......@@ -163,37 +165,13 @@ static int dgram_process (int sock)
return EMSGSIZE;
}
len = stun_validate (&buf, len);
if (len <= 0)
{
DBG ("Invalid STUN message ignored.\n");
if (stun_validate (buf, len) <= 0)
return EINVAL;
}
DBG ("%u-bytes STUN message received.\n", (unsigned)len);
if (stun_get_class (&buf) != STUN_REQUEST)
return 0;
if (stun_get_method (&buf) != STUN_BINDING)
return 0; // FIXME: SPEC: return an error response
// FIXME: check for unsupported attributes et al
bool compat = !stun_demux (&buf);
DBG (" %s-style STUN message.\n", compat ? "Old" : "New");
stun_init_response (&buf, &buf);
len = compat
? stun_append_addr (&buf, STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, mh.msg_namelen)
: stun_append_xor_addr (&buf, STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, mh.msg_namelen);
if (len)
return len; // FIXME: send 600 error
iov.iov_len = len = stun_finish (&buf);
if (len == 0)
return ENOBUFS; // FIXME: send 600 error
len = stun_bind_reply (buf, &iov.iov_len, buf,
mh.msg_name, mh.msg_namelen, false);
if (iov.iov_len == 0)
return len;
len = sendmsg (sock, &mh, 0);
if (len == -1)
......
This diff is collapsed.
This diff is collapsed.
......@@ -6,17 +6,12 @@
# Licensed under MPL 1.1/LGPL 2.1. See file COPYING.
include $(top_srcdir)/common.mk
AM_CPPFLAGS = -I$(srcdir)/..
AM_CPPFLAGS = -I$(top_srcdir)
LDADD = ../libstun.la
check_PROGRAMS = test-crc32 test-parse test-format test-bind
test_crc32_SOURCES = crc32.c
test_parse_SOURCES = parse.c
test_parse_LDADD = ../libstun.la
test_format_SOURCES = format.c
test_format_LDADD = ../libstun.la
test_bind_SOURCES = bind.c
test_bind_LDADD = ../libstun.la
check_PROGRAMS = test-crc32 test-parse test-format \
test-bind test-bindserv test-conncheck
dist_check_SCRIPTS = check-bind.sh
TESTS = test-crc32 test-parse test-format check-bind.sh
TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS)
#! /bin/sh
LOG=test-bind.log
rm -f "$LOG"
STUNC=../stunbdc
STUND=../stund
set -xe
# Dummy command line parsing tests
$STUNC -h
$STUNC -V
! $STUNC server port dummy
# Timeout tests
! $STUNC -4 127.0.0.1 1
! $STUNC -6 ::1 1
# Real tests
# Start the STUN test daemon if needed
../stund -4 &
../stund -6 &
rm -f stund-*.err stunc-*.log
exit 77
# FIXME: kill daemons properly
# FIXME: use custom port number
( $STUND -4 || echo ABORT > stund-IPv4.err ) &
( $STUND -6 || echo ABORT > stund-IPv6.err ) &
# Run the test client
{
./test-bind
echo "test-bind returned $?"
} | tee "$LOG"
$STUNC -4 > stunc-IPv4.log || test -f stund-IPv4.err
$STUNC -6 > stunc-IPv6.log || test -f stund-IPv6.err
# Terminate the test daemon
kill -INT %1
kill -INT %2
if ! grep "test-bind returned 0" "$LOG"; then
echo "test-bind failed" >&2
exit 1
fi
for a in 127.0.0.1 ::1; do
for t in Auto UDP; do
if ! grep -e "^$t discovery *: $a port " "$LOG"; then
echo "Unexpected mapping from test-bind" >&2
exit 1
fi
done
done
rm -f "$LOG"
exit 0
kill -INT %1 2>/dev/null || true
kill -INT %2 2>/dev/null || true
# Check client results
if test -f stund-IPv4.err; then exit 77; fi
grep -e "^Mapped address: 127.0.0.1" stunc-IPv4.log || exit 4
if test -f stund-IPv6.err; then exit 77; fi
grep -e "^Mapped address: ::1" stunc-IPv6.log || exit 6
/*
* 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 "stun/bind.h"
#include "stun/stun-msg.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#undef NDEBUG /* ensure assertions are built-in */
#include <assert.h>
static int listen_dgram (void)
{
struct addrinfo hints, *res;
memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo (NULL, "0", &hints, &res))
return -1;
int val = -1;
for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
{
int fd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (fd == -1)
continue;
if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
{
close (fd);
continue;
}
val = fd;
break;
}
freeaddrinfo (res);
return val;
}
/** Incorrect socket family test */
static void bad_family (void)
{
struct sockaddr addr, dummy;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_UNSPEC;
#ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr);
#endif
int val = stun_bind_run (-1, &addr, sizeof (addr),
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val != 0);
}
/** Too small socket address test */
static void small_srv_addr (void)
{
struct sockaddr addr, dummy;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr);
#endif
int val = stun_bind_run (-1, &addr, 1,
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val == EINVAL);
}
/** Too big socket address test */
static void big_srv_addr (void)
{
uint8_t buf[sizeof (struct sockaddr_storage) + 16];
struct sockaddr dummy;
int fd = socket (AF_INET, SOCK_DGRAM, 0);
assert (fd != -1);
memset (buf, 0, sizeof (buf));
int val = stun_bind_run (fd, (struct sockaddr *)buf, sizeof (buf),
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ENOBUFS);
close (fd);
}
/** Timeout test */
static void timeout (void)
{
int val;
struct sockaddr_storage srv;
struct sockaddr dummy;
socklen_t srvlen = sizeof (srv);
/* Allocate a local UDP port, so we are 100% sure nobody responds there */
int servfd = listen_dgram ();
assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&srv, &srvlen);
assert (val == 0);
val = stun_bind_run (-1, (struct sockaddr *)&srv, srvlen,
&dummy, &(socklen_t){ sizeof (dummy) });
assert (val == ETIMEDOUT);
close (servfd);
}
/** Malformed responses test */
static void bad_responses (void)
{
ssize_t val;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx;
uint8_t buf[1000];
/* Allocate a local UDP port */
int servfd = listen_dgram ();
assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
val = stun_bind_start (&ctx, -1, (struct sockaddr *)&addr, addrlen);
assert (val == 0);
/* Send to/receive from our client instance only */
val = getsockname (stun_bind_fd (ctx),
(struct sockaddr *)&addr, &addrlen);
assert (val == 0);
val = connect (servfd, (struct sockaddr *)&addr, addrlen);
assert (val == 0);
/* Send crap response */
send (servfd, "foobar", 6, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN);
/* Send non-matching message (request instead of response) */
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
send (servfd, buf, val, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN);
stun_bind_cancel (ctx);
close (servfd);
}
/** Various responses test */
static void responses (void)
{
ssize_t val;
size_t len;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof (addr);
stun_bind_t *ctx;
stun_msg_t buf;
/* Allocate a local UDP port for server */
int servfd = listen_dgram ();
assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
/* Allocate a client socket and connect to server */
int fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1);
val = connect (fd, (struct sockaddr *)&addr, addrlen);
assert (val == 0);
/* Send to/receive from our client instance only */
val = getsockname (fd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
val = connect (servfd, (struct sockaddr *)&addr, addrlen);
assert (val == 0);
/* Send error response */
val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
stun_init_error (buf, sizeof (buf), buf, STUN_GLOBAL_FAILURE);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == ECONNREFUSED);
/* Send response with an unknown attribute */
val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
stun_init_response (buf, buf);
val = stun_append_string (buf, sizeof (buf), 0x6000,
"This is an unknown attribute!");
assert (val == 0);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == EPROTO);
/* Send response with a no mapped address at all */
val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
stun_init_response (buf, buf);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == ENOENT);
/* Send old-style response */
val = stun_bind_start (&ctx, fd, NULL, 0);
assert (val == 0);
val = recv (servfd, buf, 1000, MSG_DONTWAIT);
assert (val >= 0);
stun_init_response (buf, buf);
val = stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen);
assert (val == 0);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
send (servfd, buf, len, 0);
val = stun_bind_resume (ctx, (struct sockaddr *)&addr, &addrlen);
assert (val == 0);
/* End */
close (servfd);
val = close (fd);
assert (val == 0);
}
static void test (void (*func) (void), const char *name)
{
alarm (10);
printf ("%s test... ", name);
func ();
puts ("OK");
}
int main (void)
{
test (bad_family, "Bad socket family");
test (small_srv_addr, "Too small server address");
test (big_srv_addr, "Too big server address");
test (timeout, "Binding discovery timeout");
test (bad_responses, "Bad responses");
test (responses, "Error responses");
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "stun/bind.h"
#include "stun/stun-msg.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#undef NDEBUG /* ensure assertions are built-in */
#include <assert.h>
int main (void)
{
struct sockaddr_in ip4;
stun_msg_t buf;
ssize_t val;
size_t len;
memset (&ip4, 0, sizeof (ip4));
ip4.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
ip4.sin_len = sizeof (addr);
#endif
ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001);
/* Good message test */
stun_init_request (buf, STUN_BINDING);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
len = sizeof (buf);
val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == 0);
assert (len > 0);
assert (stun_present (buf, STUN_XOR_MAPPED_ADDRESS));
/* Incorrect message class */
stun_init_request (buf, STUN_BINDING);
stun_init_response (buf, buf);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
len = sizeof (buf);
val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == EINVAL);
assert (len == 0);
/* Incorrect message method */
stun_init_request (buf, 0x666);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
len = sizeof (buf);
val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == EPROTO);
assert (len > 0);
/* Unknown attribute */
stun_init_request (buf, STUN_BINDING);
val = stun_append_string (buf, sizeof (buf), 0x666,
"The evil unknown attribute!");
assert (val == 0);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
len = sizeof (buf);
val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == EPROTO);
assert (len > 0);
/* Non-multiplexed message */
static const uint8_t req[] =
"\x00\x01" "\x00\x00"
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F";
len = sizeof (buf);
val = stun_bind_reply (buf, &len, req,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == 0);
assert (len > 0);
assert (stun_present (buf, STUN_MAPPED_ADDRESS));
len = sizeof (buf);
val = stun_bind_reply (buf, &len, req,
(struct sockaddr *)&ip4, sizeof (ip4), true);
assert (val == EINVAL);
assert (len == 0);
/* Invalid socket address */
stun_init_request (buf, STUN_BINDING);
len = sizeof (buf);
val = stun_finish (buf, &len);
assert (val == 0);
ip4.sin_family = AF_UNSPEC;
len = sizeof (buf);
val = stun_bind_reply (buf, &len, buf,
(struct sockaddr *)&ip4, sizeof (ip4), false);
assert (val == EAFNOSUPPORT);
assert (len > 0);
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "stun/conncheck.h"
#include "stun/stun-msg.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#undef NDEBUG /* ensure assertions are built-in */
#include <assert.h>
int main (void)
{
struct sockaddr_in ip4;
stun_msg_t req, resp;
ssize_t val;
size_t len;
const uint64_t tie = 0x8000000000000000LL;
static const char name[] = "admin", pass[] = "secret";
bool control = false;
memset (&ip4, 0, sizeof (ip4));
ip4.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
ip4.sin_len = sizeof (addr);
#endif
ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001);
/* Unauthenticated message */
stun_init_request (req, STUN_BINDING);
len = sizeof (req);
val = stun_finish (req, &len);
assert (val == 0);
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EPERM);
assert (len > 0);
// FIXME: check error code
/* No username */
stun_init_request (req, STUN_BINDING);
len = sizeof (req);
val = stun_finish_short (req, &len, NULL, pass, NULL, 0);
assert (val == 0);
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EPERM);
assert (len > 0);
/* Good message */
stun_init_request (req, STUN_BINDING);
len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0);
assert (val == 0);
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == 0);
assert (len > 0);
/* Bad fingerprint */
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), "bad", &control, tie);
assert (val == EPERM);
assert (len > 0);
/* Lost role conflict */
stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLING, tie + 1);
assert (val == 0);
len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0);
assert (val == 0);
len = sizeof (resp);
control = true;
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EACCES);
assert (len > 0);
assert (control == false);
/* Won role conflict */
stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLED, tie - 1);
assert (val == 0);
len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0);
assert (val == 0);
len = sizeof (resp);
control = false;
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == 0);
assert (len > 0);
assert (control == false);
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "../crc32.c"
static void test (const void *in, size_t n, uint32_t out)
{
static unsigned num = 0;
num++;
if (crc32 (in, n) != out)
{
fprintf (stderr, "Test %u failed: %08x instead of %08x\n",
num, crc32 (in, n), out);
exit (1);
}
}
int main (void)
{
test (NULL, 0, 0);
test (&(uint32_t ){ 0 }, 0, 0);
test ("foo", 3, 0x8c736521);
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "stun/stun-msg.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
static void fatal (const char *msg, ...)
{
va_list ap;
va_start (ap, msg);
vfprintf (stderr, msg, ap);
va_end (ap);
fputc ('\n', stderr);
exit (1);
}
static void
dynamic_check (const uint8_t *msg, size_t len)
{
size_t len2 = stun_validate (msg, len);
if ((len != len2) || (len2 & 3))
fatal ("Invalid message (%u, %u)\n",
(unsigned)len, (unsigned)len2);
if (!stun_demux (msg))
fatal ("Invalid message multiplexing");
printf ("Built message of %u bytes\n", (unsigned)len);
}
static size_t
finish_check (uint8_t *msg)
{
stun_msg_t mshort;
size_t len = sizeof (stun_msg_t);
memcpy (mshort, msg, sizeof (mshort));
if (stun_finish (msg, &len))
fatal ("Cannot finish message");
dynamic_check (msg, len);
len = sizeof (mshort);
if (stun_verify_password (mshort, "toto") != ENOENT)
fatal ("Missing HMAC test failed");
if (stun_finish_short (mshort, &len, "ABCDE", "admin", "ABC", 3))
fatal ("Cannot finish message with short-term creds");
dynamic_check (mshort, len);
if (stun_verify_password (mshort, "admin") != 0)
fatal ("Valid HMAC test failed");
return len;
}
static void
check_af (const char *name, int family, socklen_t addrlen)
{
struct sockaddr_storage addr;
stun_msg_t msg;
assert (addrlen <= sizeof (addr));
memset (&addr, 0, sizeof (addr));
stun_init_request (msg, STUN_BINDING);
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family test failed");
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family xor test failed");
addr.ss_family = family;
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen - 1) != EINVAL)
fatal ("Too small %s sockaddr test failed", name);
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen - 1) != EINVAL)
fatal ("Too small %s sockaddr xor test failed", name);
if (stun_append_addr (msg, sizeof (msg), STUN_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen))
fatal ("%s sockaddr test failed", name);
if (stun_append_xor_addr (msg, sizeof (msg), STUN_XOR_MAPPED_ADDRESS,
(struct sockaddr *)&addr, addrlen))
fatal ("%s sockaddr xor test failed", name);
}
int main (void)
{
uint8_t msg[STUN_MAXMSG + 8];
size_t len;
/* Request formatting test */
stun_init_request (msg, STUN_BINDING);
finish_check (msg);
if (memcmp (msg, "\x00\x01", 2))
fatal ("Request formatting test failed");
/* Response formatting test */
stun_init_response (msg, msg);
finish_check (msg);
if (memcmp (msg, "\x01\x01", 2))
fatal ("Response formatting test failed");
/* Error formatting test */
if (stun_init_error (msg, sizeof (msg), msg, 400))
fatal ("Error initialization test failed");
finish_check (msg);
if (memcmp (msg, "\x01\x11", 2))
fatal ("Error formatting test failed");
/* Unknown error formatting test */
if (stun_init_error (msg, sizeof (msg), msg, 666))
fatal ("Unknown error initialization test failed");
finish_check (msg);
if (memcmp (msg, "\x01\x11", 2))
fatal ("Unknown error formatting test failed");
/* Overflow tests */
stun_init_request (msg, STUN_BINDING);
for (unsigned i = 0;
stun_append_flag (msg, sizeof (msg), 0xffff) != ENOBUFS;
i++)
{
if ((i << 2) > 0xffff)
fatal ("Overflow protection test failed");
}
if (stun_append32 (msg, sizeof (msg), 0xffff, 0x12345678) != ENOBUFS)
fatal ("Double-word overflow test failed");
if (stun_append64 (msg, sizeof (msg), 0xffff,
0x123456789abcdef0) != ENOBUFS)
fatal ("Quad-word overflow test failed");
if (stun_append_string (msg, sizeof (msg), 0xffff, "foobar") != ENOBUFS)
fatal ("String overflow test failed");
struct sockaddr addr;
memset (&addr, 0, sizeof (addr));
addr.sa_family = AF_INET;
#ifdef HAVE_SA_LEN
addr.sa_len = sizeof (addr);
#endif
if (stun_append_xor_addr (msg, sizeof (msg), 0xffff, &addr,
sizeof (addr)) != ENOBUFS)
fatal ("Address overflow test failed");
len = sizeof (msg);
if (stun_finish (msg, &len) != ENOBUFS)
fatal ("Fingerprint overflow test failed");
len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", NULL, 0) != ENOBUFS)
fatal ("Message integrity overflow test failed");
len = sizeof (msg);
if (stun_finish_short (msg, &len, "login", "secret", NULL, 0) != ENOBUFS)
fatal ("Username overflow test failed");
len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", "foobar", 6) != ENOBUFS)
fatal ("Nonce overflow test failed");
/* Address attributes tests */
check_af ("IPv4", AF_INET, sizeof (struct sockaddr_in));
#ifdef AF_INET6
check_af ("IPv6", AF_INET6, sizeof (struct sockaddr_in6));
#endif
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include "stun/stun-msg.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
static void fatal (const char *msg, ...)
{
va_list ap;
va_start (ap, msg);
vfprintf (stderr, msg, ap);
va_end (ap);
fputc ('\n', stderr);
exit (1);
}
static void validate (const uint8_t *msg, unsigned len)
{
unsigned i = 0;
do
{
size_t vlen = stun_validate (msg, i);
if ((vlen & 3) || (vlen != ((i >= len) * len)))
fatal ("%u/%u short message test failed", i, len);
}
while (i++ < (len + 4));
}
/* Tests for generic message validation routines */
static void test_message (void)
{
static const uint8_t simple_resp[] =
"\x15\x55\x00\x00"
"\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\xaa\xbb\xcc\xdd"; //extra garbage
static const uint8_t old_ind[] =
"\x14\x55\x00\x00"
"\xfe\xdc\xba\x98" // NO cookie
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"; //extra garbage
static const uint8_t fpr_resp[] =
"\x15\x55\x00\x10"
"\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\x00\x06\x00\x04" // dummy USERNAME header
"\x41\x42\x43\x44"
"\x80\x28\x00\x04" // FINGERPRINT header
"\xdc\x8d\xa7\x74" // CRC32
"\xcc\xdd\xee\xff"; // extra garbage
static const uint8_t bad1[32] =
"\x15\x55\x00\x08"
"\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\x00\x06\x00\x05" // too big attribute for message
"\x11\x22\x33\x44"
"\x55\x66\x77\x88";
static const uint8_t bad2[24] =
"\x15\x55\x00\x05" // invalid message length
"\x21\x12\xA4\x42"
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\x00\x06\x00\x01";
static const uint8_t bad3[27] =
"\x15\x55\x00\x08"
"\x21\x12\xA4\x42"
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\x00\x06\x00\x03"
"\x11\x22\x33"; // missing padding
static const uint8_t bad_crc[] =
"\x15\x55\x00\x08"
"\x21\x12\xA4\x42"
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
"\x80\x28\x00\x04" // FINGERPRINT header
"\x04\x91\xcd\x78"; // CRC32
static uint8_t bad_crc_offset[] =
"\x15\x55\x00\x10"
"\x21\x12\xA4\x42"
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x20\x67\xc4\x09"
"\x80\x28\x00\x04" // FINGERPRINT header
"\x00\x00\x00\x00"
"\x00\x06\x00\x04"
"\x41\x42\x43\x44";
if (stun_validate (NULL, 0) != 0)
fatal ("0 bytes test failed");
if (stun_validate ((uint8_t *)"\xf0", 1) >= 0)
fatal ("1 byte test failed");
validate (simple_resp, 20);
validate (old_ind, 20);
validate (fpr_resp, 36);
if (stun_demux (simple_resp))
fatal ("Missing CRC test failed");
if (stun_demux (old_ind))
fatal ("Missing cookie test failed");
if (!stun_demux (fpr_resp))
fatal ("Good CRC test failed");
if (stun_demux (bad_crc))
fatal ("Bad CRC test failed");
if (stun_demux (bad_crc_offset))
fatal ("Bad CRC offset test failed");
if (stun_validate (bad1, sizeof (bad1)) >= 0)
fatal ("Badness 1 test failed");
if (stun_validate (bad2, sizeof (bad2)) >= 0)
fatal ("Badness 2 test failed");
if (stun_validate (bad3, sizeof (bad3)) != 0)
fatal ("Badness 3 test failed");
if (stun_get_class (simple_resp) != 3)
fatal ("Class test failed");
if (stun_get_method (simple_resp) != 0x525)
fatal ("Method test failed");
}
/* Tests for message attribute parsing */
static void test_attribute (void)
{
uint8_t acme[] =
"\x15\x55\x00\x4c" // <-- update message length if needed!!
"\x21\x12\xA4\x42" // cookie
"\x76\x54\x32\x10"
"\xfe\xdc\xba\x98"
"\x76\x54\x32\x10"
/* FF01: empty */
"\xff\x01\x00\x00"
/* FF02: address of unknown family, 32-bits */
"\xff\x02\x00\x04"
"\x41\x42\x43\x44"
/* FF03: too short IPv6 address */
"\xff\x03\x00\x06"
"\x00\x02\x12\x34"
"\x20\x01\x0d\xb8"
/* FF04: valid IPv4 address, 64-bits */
"\xff\x04\x00\x08"
"\x00\x01\x12\x34"
"\xc0\x00\x02\x01"
/* FF05: too long IPv4 address */
"\xff\x05\x00\x0A"
"\x00\x01\x12\x34"
"\xc0\x00\x02\x01"
"\x66\x60\x00\x00"
/* MESSAGE-INTEGRITY attribute */
"\x00\x08\x00\x14"
"\xd2\x0c\x85\xcd"
"\x43\x3b\xec\x9e"
"\x4d\x84\x2d\x87"
"\x82\x80\x5b\x3b"
"\xd5\x54\xd8\xa8"
;
struct sockaddr addr;
socklen_t addrlen;
uint32_t dword;
uint64_t qword;
char str[5];
printf ("Attribute test message length: %u\n", sizeof (acme));
if (stun_validate (acme, sizeof (acme)) <= 0)
fatal ("Attributes tests message broken");
if (stun_present (acme, 0xff00))
fatal ("Absent attribute test failed");
if (!stun_present (acme, 0xff01))
fatal ("Present attribute test failed");
if (stun_find_flag (acme, 0xff00) != ENOENT)
fatal ("Absent flag test failed");
if (stun_find_flag (acme, 0xff01) != 0)
fatal ("Flag test failed");
if (stun_find_flag (acme, 0xff02) != EINVAL)
fatal ("Too big flag test failed");
if (stun_find32 (acme, 0xff00, &dword) != ENOENT)
fatal ("Absent dword test failed");
if (stun_find32 (acme, 0xff01, &dword) != EINVAL)
fatal ("Bad dword test failed");
if (stun_find32 (acme, 0xff02, &dword) != 0)
fatal ("Double-word test failed");
if (stun_find64 (acme, 0xff00, &qword) != ENOENT)
fatal ("Absent qword test failed");
if (stun_find64 (acme, 0xff01, &qword) != EINVAL)
fatal ("Bad qword test failed");
if (stun_find64 (acme, 0xff04, &qword) !=0)
fatal ("Quad-word test failed");
if (stun_find_string (acme, 0xff00, str, sizeof (str)) != -1)
fatal ("Absent string test failed");
if ((stun_find_string (acme, 0xff02, str, 1) != 4) || (str[0] != 'A'))
fatal ("String buffer underflow test failed");
if ((stun_find_string (acme, 0xff02, str, sizeof (str)) != 4)
|| strcmp (str, "ABCD"))
fatal ("String test failed");
addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff01, &addr, &addrlen) != EINVAL)
fatal ("Too short addres test failed");
addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff02, &addr, &addrlen) != EAFNOSUPPORT)
fatal ("Unknown address family test failed");
addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff03, &addr, &addrlen) != EINVAL)
fatal ("Too short IPv6 address test failed");
addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff04, &addr, &addrlen) != 0)
fatal ("IPv4 address test failed");
addrlen = sizeof (addr);
if (stun_find_addr (acme, 0xff05, &addr, &addrlen) != EINVAL)
fatal ("Too big IPv4 address test failed");
if (stun_verify_key (acme, "good_guy", 8) != 0)
fatal ("Good secret HMAC test failed");
if (stun_verify_key (acme, "bad__guy", 8) != EPERM)
fatal ("Bad secret HMAC test failed");
}
int main (void)
{
test_message ();
test_attribute ();
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "timer.h"
#include <stdlib.h> /* div() */
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.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
/**
* Clock used throughout the STUN code.
* STUN requires a monotonic 1kHz clock to operate properly.
*/
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;
}
}
static inline void add_delay (struct timespec *ts, unsigned delay)
{
div_t d = div (delay, 1000);
ts->tv_sec += d.quot;
ts->tv_nsec += d.rem * 1000000;
while (ts->tv_nsec > 1000000000)
{
ts->tv_nsec -= 1000000000;
ts->tv_sec++;
}
}
/**
* Starts a STUN transaction retransmission timer.
* @param timer structure for internal timer state
*/
void stun_timer_start (stun_timer_t *timer)
{
stun_gettime (&timer->deadline);
add_delay (&timer->deadline, timer->delay = STUN_INIT_TIMEOUT);
}
unsigned stun_timer_remainder (const stun_timer_t *timer)
{
unsigned delay;
struct timespec now;
stun_gettime (&now);
if (now.tv_sec > timer->deadline.tv_sec)
return 0;
delay = timer->deadline.tv_sec - now.tv_sec;
if ((delay == 0) && (now.tv_nsec >= timer->deadline.tv_nsec))
return 0;
delay *= 1000;
delay += ((signed)(timer->deadline.tv_nsec - now.tv_nsec)) / 1000000;
return delay;
}
/**
* Updates a STUN transaction retransmission timer.
* @param timer internal timer state
* @return -1 if the transaction timed out,
* 0 if the transaction should be retransmitted,
* otherwise milliseconds left until next time out or retransmit.
*/
int stun_timer_refresh (stun_timer_t *timer)
{
unsigned delay = stun_timer_remainder (timer);
if (delay == 0)
{
if (timer->delay >= STUN_END_TIMEOUT)
return -1;
add_delay (&timer->deadline, timer->delay *= 2);
}
return delay;
}
/*
* 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_TIMER_H
# define STUN_TIMER_H 1
# include <sys/types.h>
# include <time.h>
typedef struct stun_timer_s
{
struct timespec deadline;
unsigned delay;
} stun_timer_t;
# ifdef __cplusplus
extern "C" {
# endif
void stun_timer_start (stun_timer_t *timer);
int stun_timer_refresh (stun_timer_t *timer);
unsigned stun_timer_remainder (const stun_timer_t *timer);
# ifdef __cplusplus
}
# endif
#endif /* !STUN_TIMER_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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include "stun-msg.h"
#include "trans.h"
/**
* Initializes a new STUN request transaction
*/
int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
if (fd == -1)
{
if (srvlen < sizeof (struct sockaddr))
return EINVAL;
fd = socket (srv->sa_family, SOCK_DGRAM, 0);
if (fd == -1)
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
if (connect (fd, srv, srvlen))
{
close (fd);
return errno;
}
tr->ownfd = true;
tr->srvlen = 0;
}
else
{
if (srvlen > sizeof (tr->srv))
return ENOBUFS;
tr->ownfd = false;
memcpy (&tr->srv, srv, tr->srvlen = srvlen);
}
tr->fd = fd;
return 0;
}
/**
* Sends a STUN request
*/
static int
stun_trans_send (stun_trans_t *tr)
{
/* FIXME: support for TCP */
ssize_t val;
if (tr->srvlen > 0)
val = sendto (tr->fd, tr->msg, tr->msglen, 0,
(struct sockaddr *)&tr->srv, tr->srvlen);
else
val = send (tr->fd, tr->msg, tr->msglen, 0);
if (val == -1)
return errno;
if (val < (ssize_t)tr->msglen)
return EMSGSIZE;
return 0;
}
void stun_trans_deinit (stun_trans_t *tr)
{
int saved = errno;
if (tr->ownfd)
close (tr->fd);
#ifndef NDEBUG
tr->fd = -1;
#endif
errno = saved;
}
int stun_trans_start (stun_trans_t *tr)
{
int val = stun_trans_send (tr);
if (val)
return val;
stun_timer_start (&tr->timer);
DBG ("STUN transaction @%p started (timeout: %ums)\n", tr,
stun_trans_timeout (tr));
return 0;
}
/**
* This is meant to integrate with I/O pooling loops and event frameworks.
*
* @return recommended maximum delay (in milliseconds) to wait for a
* response.
*/
unsigned stun_trans_timeout (const stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->fd != -1);
return stun_timer_remainder (&tr->timer);
}
/**
* 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)
{
assert (tr != NULL);
assert (tr->fd != -1);
return tr->fd;
}
int stun_trans_tick (stun_trans_t *tr)
{
switch (stun_timer_refresh (&tr->timer))
{
case -1:
DBG ("STUN transaction @%p failed: time out.\n", tr);
return ETIMEDOUT; // fatal error!
case 0:
stun_trans_send (tr);
DBG ("STUN transaction @%p retransmitted (timeout: %ums).\n", tr,
stun_trans_timeout (tr));
}
return EAGAIN;
}
/*
* 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_TRANS_H
# define STUN_TRANS_H 1
# include <sys/types.h>
# include <sys/socket.h>
# include <stdbool.h>
# include "timer.h"
typedef struct stun_trans_s
{
stun_timer_t timer;
size_t msglen;
stun_msg_t msg;
bool ownfd;
int fd;
socklen_t srvlen;
struct sockaddr_storage srv;
} stun_trans_t;
# ifdef __cplusplus
extern "C" {
# endif
int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen);
void stun_trans_deinit (stun_trans_t *restrict tr);
int stun_trans_start (stun_trans_t *restrict tr);
int stun_trans_tick (stun_trans_t *tr);
unsigned stun_trans_timeout (const stun_trans_t *tr);
int stun_trans_fd (const stun_trans_t *tr);
# ifdef __cplusplus
}
# endif
#endif /* !STUN_TRANS_H */
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