Commit 94a2d5af authored by Kai Vehmanen's avatar Kai Vehmanen

Fixed bugs STUN fingerprint creation and verification. Major update to the STUN internal APIs.

darcs-hash:20070830144220-77cd4-cc8ab8b16405964ab42ed1d6560ae5a2e770890b.gz
parent d43e9ce2
...@@ -51,67 +51,8 @@ ...@@ -51,67 +51,8 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/time.h> #include <sys/time.h>
#ifdef HAVE_POLL
# include <sys/poll.h>
#endif
#include <fcntl.h> #include <fcntl.h>
/** Blocking mode STUN binding discovery */
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;
uint8_t buf[STUN_MAXMSG];
size_t len = 0;
ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
return val;
do
{
struct pollfd ufd[1];
unsigned delay = stun_bind_timeout (ctx);
memset (ufd, 0, sizeof (ufd));
ufd[0].fd = fd = stun_bind_fd (ctx),
ufd[0].events = POLLIN;
if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), delay) <= 0)
{
val = stun_bind_elapse (ctx);
continue;
}
val = recv (fd, buf + len, sizeof (buf) - len, MSG_DONTWAIT);
if (val == -1)
{
#ifdef MSG_ERRQUEUE
/* FIXME: do proper ICMP error handling? */
struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr));
recvmsg (fd, &hdr, MSG_ERRQUEUE);
#endif
val = EAGAIN;
continue;
}
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 */ /** Non-blocking mode STUN binding discovery */
#include "trans.h" #include "trans.h"
...@@ -164,13 +105,6 @@ stun_bind_alloc (stun_bind_t **restrict context, int fd, ...@@ -164,13 +105,6 @@ stun_bind_alloc (stun_bind_t **restrict context, int fd,
} }
void stun_bind_cancel (stun_bind_t *context)
{
stun_trans_deinit (&context->trans);
free (context);
}
static int static int
stun_bind_launch (stun_bind_t *ctx) stun_bind_launch (stun_bind_t *ctx)
{ {
...@@ -205,6 +139,13 @@ int stun_bind_start (stun_bind_t **restrict context, int fd, ...@@ -205,6 +139,13 @@ int stun_bind_start (stun_bind_t **restrict context, int fd,
} }
void stun_bind_cancel (stun_bind_t *context)
{
stun_trans_deinit (&context->trans);
free (context);
}
/** Timer and retransmission handling */ /** Timer and retransmission handling */
unsigned stun_bind_timeout (const stun_bind_t *context) unsigned stun_bind_timeout (const stun_bind_t *context)
...@@ -225,13 +166,6 @@ int stun_bind_elapse (stun_bind_t *context) ...@@ -225,13 +166,6 @@ int stun_bind_elapse (stun_bind_t *context)
/** Incoming packets handling */ /** Incoming packets handling */
int stun_bind_fd (const stun_bind_t *context)
{
assert (context != NULL);
return stun_trans_fd (&context->trans);
}
int stun_bind_process (stun_bind_t *restrict ctx, int stun_bind_process (stun_bind_t *restrict ctx,
const void *restrict buf, size_t len, const void *restrict buf, size_t len,
struct sockaddr *restrict addr, socklen_t *addrlen) struct sockaddr *restrict addr, socklen_t *addrlen)
...@@ -273,6 +207,37 @@ int stun_bind_process (stun_bind_t *restrict ctx, ...@@ -273,6 +207,37 @@ int stun_bind_process (stun_bind_t *restrict ctx,
} }
/** Blocking mode STUN binding discovery */
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;
uint8_t buf[STUN_MAXMSG];
ssize_t val;
val = stun_bind_start (&ctx, fd, srv, srvlen);
if (val)
return val;
do
{
val = stun_trans_recv (&ctx->trans, buf, sizeof (buf));
if (val == -1)
{
val = errno;
continue;
}
val = stun_bind_process (ctx, buf, val, addr, addrlen);
}
while (val == EAGAIN);
return val;
}
/** ICE keep-alives (Binding discovery indication!) */ /** ICE keep-alives (Binding discovery indication!) */
int int
...@@ -286,6 +251,7 @@ stun_bind_keepalive (int fd, const struct sockaddr *restrict srv, ...@@ -286,6 +251,7 @@ stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
stun_init_indication (buf, STUN_BINDING); stun_init_indication (buf, STUN_BINDING);
val = stun_finish (buf, &len); val = stun_finish (buf, &len);
assert (val == 0); assert (val == 0);
(void)val;
/* NOTE: hopefully, this is only needed for non-stream sockets */ /* NOTE: hopefully, this is only needed for non-stream sockets */
if (stun_sendto (fd, buf, len, srv, srvlen) == -1) if (stun_sendto (fd, buf, len, srv, srvlen) == -1)
...@@ -347,7 +313,7 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd, ...@@ -347,7 +313,7 @@ stun_conncheck_start (stun_bind_t **restrict context, int fd,
ctx->trans.msg.length = sizeof (ctx->trans.msg.buf); ctx->trans.msg.length = sizeof (ctx->trans.msg.buf);
val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length, val = stun_finish_short (ctx->trans.msg.buf, &ctx->trans.msg.length,
username, password, NULL, 0); username, password, NULL);
if (val) if (val)
goto error; goto error;
...@@ -357,3 +323,5 @@ error: ...@@ -357,3 +323,5 @@ error:
stun_bind_cancel (*context); stun_bind_cancel (*context);
return val; return val;
} }
...@@ -120,14 +120,6 @@ unsigned stun_bind_timeout (const stun_bind_t *context); ...@@ -120,14 +120,6 @@ unsigned stun_bind_timeout (const stun_bind_t *context);
*/ */
int stun_bind_elapse (stun_bind_t *context); 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 * Gives data to be processed within the context of a STUN Binding discovery
* or ICE connectivity check. * or ICE connectivity check.
...@@ -168,33 +160,6 @@ int stun_bind_keepalive (int fd, const struct sockaddr *restrict srv, ...@@ -168,33 +160,6 @@ int stun_bind_keepalive (int fd, const struct sockaddr *restrict srv,
socklen_t srvlen); socklen_t srvlen);
/**
* 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 # ifndef STUN_VALIDATE_DECLARATION
# define STUN_VALIDATE_DECLARATION 2 # define STUN_VALIDATE_DECLARATION 2
......
#! /bin/sh
cat << EOF
/* This file is automatically generated! Do not modify! */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "stun-msg.h"
#include <assert.h>
bool stun_is_unknown (uint16_t type)
{
assert (!stun_optional (type));
switch (type)
{
EOF
sed -n -e 's/\s*\(STUN_[A-Z_]*\)=0x[0-7][0-9A-F]\{3\},/\t\tcase \1:/p'
cat << EOF
return false;
default:
return true;
}
}
EOF
...@@ -41,22 +41,31 @@ ...@@ -41,22 +41,31 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h>
#include <sys/uio.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> /* htons() */
#include "stun-msg.h" #include "stun-msg.h"
static uint32_t crc32 (const void *buf, size_t size); static inline uint32_t crc32 (const struct iovec *iov, size_t size);
uint32_t stun_fingerprint (const uint8_t *msg, size_t len)
/**
* Computes the FINGERPRINT of a STUN message.
* @return fingerprint value in <b>host</b> byte order.
*/
uint32_t stun_fingerprint (const uint8_t *msg)
{ {
/* Don't hash last 8-bytes (= the FINGERPRINT attribute) */ struct iovec iov[3];
size_t len = 12u + stun_length (msg); // 20 - 8 = 12 uint16_t fakelen = htons (len - 20u);
return crc32 (msg, len) ^ 0x5354554e;
assert (len >= 28u);
iov[0].iov_base = (void *)msg;
iov[0].iov_len = 2;
iov[1].iov_base = &fakelen;
iov[1].iov_len = 2;
iov[2].iov_base = (void *)(msg + 4);
/* first 4 bytes done, last 8 bytes not summed */
iov[2].iov_len = len - 12u;
return crc32 (iov, sizeof (iov) / sizeof (iov[0])) ^ 0x5354554e;
} }
/*- /*-
...@@ -64,7 +73,7 @@ uint32_t stun_fingerprint (const uint8_t *msg) ...@@ -64,7 +73,7 @@ uint32_t stun_fingerprint (const uint8_t *msg)
* code or tables extracted from it, as desired without restriction. * code or tables extracted from it, as desired without restriction.
* *
* Extracted from FreeBSD CVS (src/sys/libkern/crc32.c) * Extracted from FreeBSD CVS (src/sys/libkern/crc32.c)
* and adapted by Rémi Denis-Courmont, 17 April 2007. * and adapted by Rémi Denis-Courmont, 2007.
*/ */
/* /*
...@@ -153,13 +162,20 @@ static const uint32_t crc32_tab[] = { ...@@ -153,13 +162,20 @@ static const uint32_t crc32_tab[] = {
}; };
static static inline
uint32_t crc32 (const void *buf, size_t size) uint32_t crc32 (const struct iovec *iov, size_t n)
{ {
const uint8_t *p = buf; size_t i;
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
while (size--) for (i = 0; i < n; i++)
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); {
const uint8_t *p = iov[i].iov_base;
size_t size = iov[i].iov_len;
while (size--)
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xffffffff; return crc ^ 0xffffffff;
} }
...@@ -37,30 +37,53 @@ ...@@ -37,30 +37,53 @@
# include <config.h> # include <config.h>
#endif #endif
#include <openssl/evp.h>
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h> #include <pthread.h>
#include "stun-msg.h" #include "stun-msg.h"
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
void stun_sha1 (const uint8_t *msg, uint8_t *sha, const void *key, size_t keylen) void stun_sha1 (const uint8_t *msg, size_t len, uint8_t *sha,
const void *restrict key, size_t keylen)
{ {
size_t mlen = stun_length (msg); HMAC_CTX ctx;
assert (mlen >= 32); uint16_t fakelen = htons (len - 20u);
/* assert (len >= 44u);
* + 20 bytes for STUN header
* - 24 bytes for MESSAGE-INTEGRITY attribute HMAC_CTX_init (&ctx);
* - 8 bytes for FINGERPRINT attribute HMAC_Init_ex (&ctx, key, keylen, EVP_sha1 (), NULL);
*/ HMAC_Update (&ctx, msg, 2);
mlen -= 12; HMAC_Update (&ctx, (const uint8_t *)&fakelen, 2);
/* first 4 bytes done, last 24 bytes not summed */
HMAC_Update (&ctx, msg + 4, len - 28u);
HMAC_Final (&ctx, sha, NULL);
HMAC_CTX_cleanup (&ctx);
}
void stun_hash_creds (const char *realm, const char *login, const char *pw,
unsigned char md5[16])
{
EVP_MD_CTX ctx;
assert (realm && login && pw && md5);
HMAC (EVP_sha1 (), key, keylen, msg, mlen, sha, NULL); EVP_MD_CTX_init (&ctx);
EVP_DigestInit_ex (&ctx, EVP_md5 (), NULL);
EVP_DigestUpdate (&ctx, realm, strlen (realm));
EVP_DigestUpdate (&ctx, ":", 1);
EVP_DigestUpdate (&ctx, login, strlen (login));
EVP_DigestUpdate (&ctx, ":", 1);
EVP_DigestUpdate (&ctx, pw, strlen (pw));
EVP_DigestFinal (&ctx, md5, NULL);
} }
......
/*
* 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 <pthread.h>
#include <openssl/rand.h>
#include <stdint.h>
#define UNIQUE_SIZE 20u
#define NONCE_SIZE 24u
static uint8_t unique_id[UNIQUE_SIZE];
static void generate_unique_id (void)
{
RAND_pseudo_bytes (unique_id, sizeof (unique_id));
}
static void
stun_generate_nonce (uint8_t *nonce, time_t now,
const struct sockaddr_storage *restrict addr)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
HMAC_CTX ctx;
uint32_t stamp = now;
pthread_once (&once, generate_unique_id);
/*
* Nonce are generated from the current time and the client address and
* port number, keyed with a pseudo-random secret.
*/
HMAC_CTX_init (&ctx);
HMAC_Init_ex (&ctx, unique_id, sizeof (unique_id), EVP_sha1 (), NULL);
HMAC_Update (&ctx, &stamp, 4);
HMAC_Update (&ctx, &ss->family, sizeof (ss->family));
switch (addr->ss_family)
{
case AF_INET:
{
const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
HMAC_Update (&ctx, &ip4->sin_addr, 4);
HMAC_Update (&ctx, &ip4->sin_port, 2);
break;
}
case AF_INET6:
{
const struct sockaddr_in6*ip6 = (const struct sockaddr_in6*)addr;
HMAC_Update (&ctx, &ip6->sin6_addr, 16);
HMAC_Update (&ctx, &ip6->sin6_port, 2);
if (IN6_IS_ADDR_LINK_LOCAL (&ip6->sin6_addr))
HMAC_Update (&ctx, &ip6->sin6_scope_id
sizeof (ip6->sin6_scope_id));
break;
}
}
HMAC_Final (&ctx, nonce, NULL);
HMAC_CTX_cleanup (&ctx);
memcpy (nonce + 20, &stamp, 4);
}
static int
stun_append_nonce (uint8_t *buf, size_t buflen,
const struct sockaddr_storage *restrict addr)
{
uint8_t nonce[NONCE_SIZE];
stun_generate_nonce (nonce, time (NULL), addr);
return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce));
}
static int
stun_verify_nonce (const uint8_t *buf, unsigned valid_time,
const struct sockaddr_storage *restrict addr)
{
const uint8_t *
stun_generate_nonce (nonce, time (NULL), addr);
return stun_append_bytes (buf, buflen, STUN_NONCE, nonce, sizeof (nonce));
}
/*
* 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>
/** ICE connectivity checks **/
#include "stun-ice.h"
static int
stun_bind_error (uint8_t *buf, size_t *plen, const uint8_t *req,
stun_error_t code, const char *pass)
{
size_t len = *plen;
int val;
*plen = 0;
DBG ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len);
val = stun_init_error (buf, len, req, code);
if (val)
return val;
val = stun_finish_short (buf, &len, NULL, pass, NULL);
if (val)
return val;
*plen = len;
DBG (" Error response (%u) of %u bytes\n", (unsigned)code,
(unsigned)*plen);
return 0;
}
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)
{
size_t len = *plen;
uint64_t q;
int val, ret = 0;
#define err( code ) \
stun_bind_error (buf, &len, msg, code, pass); \
*plen = len
*plen = 0;
DBG ("STUN Reply (buffer size = %u)...\n", (unsigned)len);
if (stun_get_class (msg) != STUN_REQUEST)
{
DBG (" Unhandled non-request (class %u) message.\n",
stun_get_class (msg));
return EINVAL;
}
if (!stun_demux (msg))
{
DBG (" Incorrectly multiplexed STUN message ignored.\n");
return EINVAL;
}
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);
if (val)
goto failure;
*plen = len;
return EPROTO;
}
/* Short term credentials checking */
val = 0;
if (!stun_present (msg, STUN_MESSAGE_INTEGRITY) // FIXME: wrong!
|| !stun_present (msg, STUN_USERNAME))
{
DBG (" Missing USERNAME or MESSAGE-INTEGRITY.\n");
val = STUN_BAD_REQUEST;
}
else
/* FIXME: verify USERNAME, return STUN_UNAUTHORIZED if wrong */
if (stun_verify_password (msg, pass))
{
DBG (" Integrity check failed.\n");
val = STUN_UNAUTHORIZED;
}
if (val)
{
stun_bind_error (buf, &len, msg, val, NULL);
*plen = len;
return EPERM;
}
if (stun_get_method (msg) != STUN_BINDING)
{
DBG (" Bad request (method %u) message.\n",
stun_get_method (msg));
err (STUN_BAD_REQUEST);
return EPROTO;
}
/* Role conflict handling */
assert (control != NULL);
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;
ret = EACCES;
}
else
{
DBG (" staying \"controll%s\" (sending error)\n",
*control ? "ing" : "ed");
*plen = len;
err (STUN_ROLE_CONFLICT);
return 0;
}
}
#ifndef NDEBUG
else
if (stun_find64 (msg, *control ? STUN_ICE_CONTROLLED
: STUN_ICE_CONTROLLING, &q))
DBG ("STUN Role not specified by peer!\n");
#endif
stun_init_response (buf, len, msg);
val = stun_append_xor_addr (buf, len, STUN_XOR_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);
if (val)
goto failure;
*plen = len;
DBG (" All done (response size: %u)\n", (unsigned)len);
return ret;
failure:
assert (*plen == 0);
DBG (" Fatal error formatting Response: %s\n", strerror (val));
return val;
}
#undef err
/** FIXME: this needs to be merged with stun_conncheck_reply, REALLY! */
char *stun_conncheck_username (const uint8_t *restrict msg,
char *restrict buf, size_t buflen)
{
size_t i;
if ((buflen == 0)
|| stun_find_string (msg, STUN_USERNAME, buf, (buflen - 1) / 6))
return NULL;
for (i = 0; buf[i]; 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 == ':'))
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);
}
...@@ -87,10 +87,15 @@ int stun_conncheck_start (stun_bind_t **restrict context, int fd, ...@@ -87,10 +87,15 @@ int stun_conncheck_start (stun_bind_t **restrict context, int fd,
* @param control [IN/OUT] whether we are controlling ICE or not * @param control [IN/OUT] whether we are controlling ICE or not
* @param tie tie breaker value for ICE role determination * @param tie tie breaker value for ICE role determination
* *
* @return same as stun_bind_reply() with one additionnal error code: * @return 0 if successful (@a rbuf contains a <b>non-error</b> response),
* EACCES: ICE role conflict occured, please recheck the flag at @a control * EINVAL: malformatted request message or socket address,
* EAFNOSUPPORT: unsupported socket address family,
* EPROTO: unsupported request message type or parameter,
* ENOBUFS: insufficient response buffer space.
* EACCES: ICE role conflict occurred, please recheck the flag at @a control
* *
* @note @a buf and @a msg <b>must not</b> collide. * 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 int
stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg, stun_conncheck_reply (uint8_t *buf, size_t *restrict plen, const uint8_t *msg,
......
This diff is collapsed.
...@@ -59,7 +59,15 @@ ...@@ -59,7 +59,15 @@
# define IPV6_RECVPKTINFO IPV6_PKTINFO # define IPV6_RECVPKTINFO IPV6_PKTINFO
#endif #endif
static /** Default port for STUN binding discovery */
#define IPPORT_STUN 3478
#include "stun-msg.h"
#include "stund.h"
/**
* Creates a listening socket
*/
int listen_socket (int fam, int type, int proto, uint16_t port) int listen_socket (int fam, int type, int proto, uint16_t port)
{ {
int yes = 1; int yes = 1;
...@@ -72,8 +80,6 @@ int listen_socket (int fam, int type, int proto, uint16_t port) ...@@ -72,8 +80,6 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
if (fd < 3) if (fd < 3)
goto error; goto error;
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
struct sockaddr_storage addr; struct sockaddr_storage addr;
memset (&addr, 0, sizeof (addr)); memset (&addr, 0, sizeof (addr));
addr.ss_family = fam; addr.ss_family = fam;
...@@ -89,8 +95,7 @@ int listen_socket (int fam, int type, int proto, uint16_t port) ...@@ -89,8 +95,7 @@ int listen_socket (int fam, int type, int proto, uint16_t port)
case AF_INET6: case AF_INET6:
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
if (setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes))) setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof (yes));
goto error;
#endif #endif
((struct sockaddr_in6 *)&addr)->sin6_port = port; ((struct sockaddr_in6 *)&addr)->sin6_port = port;
break; break;
...@@ -139,59 +144,107 @@ error: ...@@ -139,59 +144,107 @@ error:
} }
static int err_dequeue (int fd) /** Dequeue error from a socket if applicable */
static int recv_err (int fd)
{ {
#ifdef MSG_ERRQUEUE #ifdef MSG_ERRQUEUE
struct msghdr hdr; struct msghdr hdr;
memset (&hdr, 0, sizeof (hdr)); memset (&hdr, 0, sizeof (hdr));
return recvmsg (fd, &hdr, MSG_ERRQUEUE) == 0; return recvmsg (fd, &hdr, MSG_ERRQUEUE) >= 0;
#endif #endif
} }
#include "bind.h" /** Receives a message or dequeues an error from a socket */
ssize_t recv_safe (int fd, struct msghdr *msg)
{
ssize_t len = recvmsg (fd, msg, 0);
if (len == -1)
recv_err (fd);
else
if (msg->msg_flags & MSG_TRUNC)
{
errno = EMSGSIZE;
return -1;
}
return len;
}
/** Sends a message through a socket */
ssize_t send_safe (int fd, const struct msghdr *msg)
{
ssize_t len;
do
len = sendmsg (fd, msg, 0);
while ((len == -1) && (recv_err (fd) == 0));
return len;
}
static int dgram_process (int sock) static int dgram_process (int sock)
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
uint8_t buf[1500]; uint8_t buf[STUN_MAXMSG];
char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))]; char ctlbuf[CMSG_SPACE (sizeof (struct in6_pktinfo))];
struct iovec iov = { buf, sizeof (buf) }; struct iovec iov = { buf, sizeof (buf) };
struct msghdr mh; struct msghdr mh =
memset (&mh, 0, sizeof (mh));
mh.msg_name = (struct sockaddr *)&addr;
mh.msg_namelen = sizeof (addr);
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = ctlbuf;
mh.msg_controllen = sizeof (ctlbuf);
ssize_t len = recvmsg (sock, &mh, 0);
if (len < 0)
{ {
err_dequeue (sock); .msg_name = (struct sockaddr *)&addr,
.msg_namelen = sizeof (addr),
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = ctlbuf,
.msg_controllen = sizeof (ctlbuf)
};
size_t len = recv_safe (sock, &mh);
if (len == (size_t)-1)
return -1; return -1;
}
if (mh.msg_flags & MSG_TRUNC) /* Mal-formatted packets */
if ((stun_validate (buf, len) <= 0)
|| (stun_get_class (buf) != STUN_REQUEST))
return -1; return -1;
if (stun_validate (buf, len) <= 0) /* Unknown attributes */
return -1; if (stun_has_unknown (buf))
{
stun_init_error_unknown (buf, sizeof (buf), buf);
goto finish;
}
len = stun_bind_reply (buf, &iov.iov_len, buf, switch (stun_get_method (buf))
mh.msg_name, mh.msg_namelen, false); {
if (iov.iov_len == 0) case STUN_BINDING:
return -1; stun_init_response (buf, sizeof (buf), buf);
if (stun_has_cookie (buf))
stun_append_xor_addr (buf, sizeof (buf),
STUN_XOR_MAPPED_ADDRESS,
mh.msg_name, mh.msg_namelen);
else
stun_append_addr (buf, sizeof (buf), STUN_MAPPED_ADDRESS,
mh.msg_name, mh.msg_namelen);
break;
do case STUN_ALLOCATE:
len = sendmsg (sock, &mh, 0); case STUN_CONNECT:
while ((len == -1) && err_dequeue (sock)); case STUN_SET_ACTIVE_DST:
goto send;
if (len < (ssize_t)iov.iov_len) default:
return -1; stun_init_error (buf, sizeof (buf), buf, STUN_BAD_REQUEST);
return 0; }
finish:
stun_finish (buf, &iov.iov_len);
send:
len = send_safe (sock, &mh);
return (len < iov.iov_len) ? -1 : 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 NICE_STUN_STUND_H
# define NICE_STUN_STUND_H 1
int listen_socket (int fam, int type, int proto, uint16_t port);
ssize_t send_safe (int fd, const struct msghdr *msg);
ssize_t recv_safe (int fd, struct msghdr *msg);
#endif
...@@ -111,15 +111,7 @@ ssize_t stun_validate (const uint8_t *msg, size_t len) ...@@ -111,15 +111,7 @@ ssize_t stun_validate (const uint8_t *msg, size_t len)
} }
/** const void *
* Looks for an attribute in a *valid* STUN message.
* @param msg message buffer
* @param type STUN attribute type (host byte order)
* @param palen [OUT] pointer to store the byte length of the attribute
* @return a pointer to the start of the attribute payload if found,
* otherwise NULL.
*/
static const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type, stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen) uint16_t *restrict palen)
{ {
...@@ -148,6 +140,19 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -148,6 +140,19 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
return msg; return msg;
} }
/* Look for and ignore misordered attributes */
switch (atype)
{
case STUN_MESSAGE_INTEGRITY:
/* Only fingerprint may come after M-I */
if (type == STUN_FINGERPRINT)
break;
case STUN_FINGERPRINT:
/* Nothing may come after FPR */
return NULL;
}
alen = stun_align (alen); alen = stun_align (alen);
length -= alen; length -= alen;
msg += alen; msg += alen;
...@@ -157,13 +162,6 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -157,13 +162,6 @@ stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
} }
bool stun_present (const uint8_t *msg, stun_attr_type_t type)
{
uint16_t dummy;
return stun_find (msg, type, &dummy) != NULL;
}
int stun_find_flag (const uint8_t *msg, stun_attr_type_t type) int stun_find_flag (const uint8_t *msg, stun_attr_type_t type)
{ {
const void *ptr; const void *ptr;
...@@ -220,21 +218,90 @@ int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval) ...@@ -220,21 +218,90 @@ int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
} }
ssize_t stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type, int stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t buflen) char *buf, size_t maxcp)
{ {
const char *ptr; const unsigned char *ptr;
uint16_t len; uint16_t len;
ptr = stun_find (msg, type, &len); ptr = stun_find (msg, type, &len);
if (ptr == NULL) if (ptr == NULL)
return -1; return ENOENT;
/* UTF-8 validation */
while (len > 0)
{
unsigned i;
unsigned char c = *ptr++;
if (maxcp == 0)
return ENOBUFS;
maxcp--;
if (c == 0)
return EINVAL; /* unexpected nul byte */
else
if (c < 0x80)
i = 0; /* 0x01-0x7F: ASCII code point */
else
if (c < 0xC2)
/* 0x80-0xBF: continuation byte */
/* 0xC0-0xC1: overlongs */
return EINVAL;
else
if (c < 0xE0)
i = 1; /* 0xC2-0xDF: two bytes code point */
else
if (c < 0xF0)
{
if (*ptr < 0xA0)
return EINVAL; /* overlong */
i = 2; /* 0xE0-0xEF: three bytes code point */
}
else
if (c < 0xF8)
{
if (*ptr < 0x90)
return EINVAL; /* overlong */
i = 3; /* 0xF0-0xF7: four bytes code point */
}
else
if (c < 0xFC)
{
if (*ptr < 0x88)
return EINVAL; /* overlong */
i = 4; /* 0xF8-0xFB: five bytes code point */
}
else
if (c < 0xFE)
{
if (*ptr < 0x84)
return EINVAL; /* overlong */
i = 5;
}
else
return EINVAL; /* 0xFE-0xFF: Byte-Order-Mark */
*buf++ = c;
len--;
if (i > len)
return EINVAL; /* too short */
while (i > 0)
{
c = *ptr++;
len--;
if ((c & 0xC0) != 0x80)
return EINVAL;
memcpy (buf, ptr, (len < buflen) ? len : buflen); *buf++ = c;
if (len < buflen) }
buf[len] = '\0'; }
return len; *buf = '\0';
return 0;
} }
...@@ -386,49 +453,43 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type, ...@@ -386,49 +453,43 @@ int stun_strcmp (const uint8_t *restrict msg, stun_attr_type_t type,
} }
#endif #endif
bool stun_has_cookie (const uint8_t *msg)
static inline bool check_cookie (const uint8_t *msg)
{ {
uint32_t cookie = htonl (STUN_COOKIE); uint32_t cookie = htonl (STUN_COOKIE);
return memcmp (msg + 4, &cookie, 4) == 0; return memcmp (msg + 4, &cookie, sizeof (cookie)) == 0;
}
static const uint8_t *stun_end (const uint8_t *msg)
{
return msg + 20 + stun_length (msg);
} }
bool stun_demux (const uint8_t *msg) bool stun_demux (const uint8_t *msg)
{ {
const void *fpr; const uint8_t *fpr;
uint32_t crc32; uint32_t crc32;
uint16_t fprlen; uint16_t fprlen;
assert (stun_valid (msg)); assert (stun_valid (msg));
/* Checks cookie */ /* Checks cookie */
if (!check_cookie (msg)) if (!stun_has_cookie (msg))
{ {
DBG ("STUN demux error: no cookie!\n"); DBG ("STUN demux error: no cookie!\n");
return 0; return 0;
} }
/* Looks for FINGERPRINT */ /* Looks for FINGERPRINT */
fpr = stun_end (msg) - 4; fpr = stun_find (msg, STUN_FINGERPRINT, &fprlen);
if ((fpr != stun_find (msg, STUN_FINGERPRINT, &fprlen)) || (fprlen != 4)) if ((fpr == NULL) || (fprlen != 4))
{ {
DBG ("STUN demux error: no FINGERPRINT attribute!\n"); DBG ("STUN demux error: no FINGERPRINT attribute!\n");
return 0; return 0;
} }
/* Checks FINGERPRINT */ /* Checks FINGERPRINT */
crc32 = htonl (stun_fingerprint (msg)); crc32 = htonl (stun_fingerprint (msg, fpr + 4 - msg));
if (memcmp (fpr, &crc32, 4)) if (memcmp (fpr, &crc32, 4))
{ {
DBG ("STUN demux error: bad fingerprint (expected: 0x%08x)!\n", DBG ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!\n",
stun_fingerprint (msg)); (fpr[0] << 24) | (fpr[1] << 16) | (fpr[2] << 8) | fpr[3],
stun_fingerprint (msg, fpr + 4 - msg));
return 0; return 0;
} }
...@@ -455,35 +516,26 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen) ...@@ -455,35 +516,26 @@ stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
assert (msg != NULL); assert (msg != NULL);
assert ((keylen == 0) || (key != NULL)); assert ((keylen == 0) || (key != NULL));
hash = stun_end (msg) - 20; hash = stun_find (msg, STUN_MESSAGE_INTEGRITY, &hlen);
if (stun_demux (msg)) if ((hash == NULL) || (hlen != 20))
hash -= 8; // room for FINGERPRINT at the end
if ((stun_find (msg, STUN_MESSAGE_INTEGRITY, &hlen) != hash)
|| (hlen != 20))
{ {
DBG ("STUN auth error: no MESSAGE-INTEGRITY attribute!\n"); DBG ("STUN auth error: no MESSAGE-INTEGRITY attribute!\n");
return ENOENT; return ENOENT;
} }
stun_sha1 (msg, sha, key, keylen); stun_sha1 (msg, hash + 20 - msg, sha, key, keylen);
DBG (" Message HMAC-SHA1 fingerprint:"
"\n key : ");
DBG_bytes (key, keylen);
DBG ("\n expected: ");
DBG_bytes (sha, sizeof (sha));
DBG ("\n received: ");
DBG_bytes (hash, sizeof (sha));
DBG ("\n");
if (memcmp (sha, hash, sizeof (sha))) if (memcmp (sha, hash, sizeof (sha)))
{ {
#ifndef NDEBUG DBG ("STUN auth error: SHA1 fingerprint mismatch!\n");
unsigned i;
DBG ("STUN auth error: SHA1 fingerprint mismatch!"
"\n key : 0x");
for (i = 0; i < keylen; i++)
DBG ("%02x", ((uint8_t *)key)[i]);
DBG ("\n expected: 0x");
for (i = 0; i < 20; i++)
DBG ("%02x", sha[i]);
DBG ("\n received: 0x");
for (i = 0; i < 20; i++)
DBG ("%02x", hash[i]);
DBG ("\n");
#endif
return EPERM; return EPERM;
} }
...@@ -532,7 +584,7 @@ int stun_find_errno (const uint8_t *restrict msg, int *restrict code) ...@@ -532,7 +584,7 @@ int stun_find_errno (const uint8_t *restrict msg, int *restrict code)
* @param method STUN method number (host byte order) * @param method STUN method number (host byte order)
* @param id STUN transaction id * @param id STUN transaction id
* @param key HMAC key, or NULL if there is no authentication * @param key HMAC key, or NULL if there is no authentication
* @param keylen HMAC key byte length, 0 is no authentication * @param keylen HMAC key byte length, must be 0 if @a key is NULL
* @param error [OUT] set to -1 if the response is not an error, * @param error [OUT] set to -1 if the response is not an error,
* to the error code ([0..799]) if it is an error response. * to the error code ([0..799]) if it is an error response.
* *
...@@ -548,7 +600,7 @@ stun_match_answer (const uint8_t *msg, stun_method_t method, ...@@ -548,7 +600,7 @@ stun_match_answer (const uint8_t *msg, stun_method_t method,
assert (error != NULL); assert (error != NULL);
if ((stun_get_method (msg) != method) /* wrong request type */ if ((stun_get_method (msg) != method) /* wrong request type */
|| !check_cookie (msg) /* response to old-style request */ || !stun_has_cookie (msg) /* response to old-style request */
|| memcmp (msg + 8, id, 12)) /* wrong transaction ID */ || memcmp (msg + 8, id, 12)) /* wrong transaction ID */
return false; return false;
...@@ -563,13 +615,25 @@ stun_match_answer (const uint8_t *msg, stun_method_t method, ...@@ -563,13 +615,25 @@ stun_match_answer (const uint8_t *msg, stun_method_t method,
break; break;
case STUN_ERROR: case STUN_ERROR:
if (stun_find_errno (msg, error)) if (stun_find_errno (msg, error) != 0)
return false; // missing ERROR-CODE: ignore message return false; // missing ERROR-CODE: ignore message
break; break;
} }
if ((key != NULL) && stun_verify_key (msg, key, keylen)) /* If a shared secret exists, verify the message hash.
return false; * If there is no shared secret, verify there is no hash at all. */
if (key != NULL)
{
/* FIXME: 401 errors do not have MESSAGE-INTEGRITY, so that we
* currently ignore them. */
if (stun_verify_key (msg, key, keylen) != 0)
return false;
}
else
{
if (stun_present (msg, STUN_MESSAGE_INTEGRITY))
return false;
}
return true; return true;
} }
...@@ -589,55 +653,6 @@ bool stun_match_messages (const uint8_t *restrict resp, ...@@ -589,55 +653,6 @@ bool stun_match_messages (const uint8_t *restrict resp,
} }
bool stun_is_unknown (uint16_t type)
{
switch (type)
{
/* Mandatory */
case STUN_MAPPED_ADDRESS:
case STUN_OLD_RESPONSE_ADDRESS:
case STUN_OLD_CHANGE_REQUEST:
case STUN_OLD_SOURCE_ADDRESS:
case STUN_OLD_CHANGED_ADDRESS:
case STUN_USERNAME:
case STUN_OLD_PASSWORD:
case STUN_MESSAGE_INTEGRITY:
case STUN_ERROR_CODE:
case STUN_UNKNOWN_ATTRIBUTES:
case STUN_OLD_REFLECTED_FROM:
case STUN_LIFETIME:
case STUN_BANDWIDTH:
case STUN_REMOTE_ADDRESS:
case STUN_DATA:
case STUN_REALM:
case STUN_NONCE:
case STUN_RELAY_ADDRESS:
case STUN_REQUESTED_ADDRESS_TYPE:
case STUN_REQUESTED_PORT_PROPS:
case STUN_REQUESTED_TRANSPORT:
case STUN_XOR_MAPPED_ADDRESS:
case STUN_TIMER_VAL:
case STUN_REQUESTED_IP:
case STUN_CONNECT_STAT:
case STUN_PRIORITY:
case STUN_USE_CANDIDATE:
/* Optional */
case STUN_SERVER:
case STUN_ALTERNATE_SERVER:
case STUN_REFRESH_INTERVAL:
case STUN_FINGERPRINT:
case STUN_ICE_CONTROLLED:
case STUN_ICE_CONTROLLING:
return false;
}
return true;
}
unsigned unsigned
stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list, stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
unsigned max) unsigned max)
...@@ -657,8 +672,7 @@ stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list, ...@@ -657,8 +672,7 @@ stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
assert (len >= (4 + alen)); assert (len >= (4 + alen));
len -= 4 + alen; len -= 4 + alen;
if (!stun_optional (atype) if (!stun_optional (atype) && stun_is_unknown (atype))
&& stun_is_unknown (atype))
{ {
DBG ("STUN unknown: attribute 0x%04x(%u bytes)\n", DBG ("STUN unknown: attribute 0x%04x(%u bytes)\n",
(unsigned)atype, (unsigned)alen); (unsigned)atype, (unsigned)alen);
......
...@@ -78,18 +78,21 @@ void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m) ...@@ -78,18 +78,21 @@ void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
* Initializes a STUN message buffer, with no attributes. * Initializes a STUN message buffer, with no attributes.
* @param c STUN message class (host byte order) * @param c STUN message class (host byte order)
* @param m STUN message method (host byte order) * @param m STUN message method (host byte order)
* @param id 12-bytes transaction ID * @param id 16-bytes transaction ID
*/ */
static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m, static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m,
const stun_transid_t id) const stun_transid_t id)
{ {
static const uint8_t init[8] = { 0, 0, 0, 0, 0x21, 0x12, 0xA4, 0x42 }; memset (msg, 0, 4);
memcpy (msg, init, sizeof (init));
stun_set_type (msg, c, m); stun_set_type (msg, c, m);
msg += 8; msg += 8;
if (msg != id) if (msg != id)
{
uint32_t cookie = htonl (STUN_COOKIE);
memcpy (msg - 4, &cookie, sizeof (cookie));
memcpy (msg, id, 12); memcpy (msg, id, 12);
}
} }
...@@ -122,7 +125,7 @@ void stun_init_indication (uint8_t *req, stun_method_t m) ...@@ -122,7 +125,7 @@ void stun_init_indication (uint8_t *req, stun_method_t m)
* enough room in the STUN message buffer. Return value is always on a * enough room in the STUN message buffer. Return value is always on a
* 32-bits boundary. * 32-bits boundary.
*/ */
static void * void *
stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
{ {
uint8_t *a; uint8_t *a;
...@@ -141,7 +144,9 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) ...@@ -141,7 +144,9 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
a = msg + 20u + mlen; a = msg + 20u + mlen;
a = stun_setw (a, type); a = stun_setw (a, type);
a = stun_setw (a, length); /* NOTE: If cookie is not present, we need to force the attribute length
* to a multiple of 4 for compatibility with old RFC3489 */
a = stun_setw (a, stun_has_cookie (msg) ? length : stun_align (length));
mlen += 4 + length; mlen += 4 + length;
/* Add padding if needed */ /* Add padding if needed */
...@@ -162,7 +167,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length) ...@@ -162,7 +167,7 @@ stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
* @param len attribute payload length * @param len attribute payload length
* @return 0 on success, ENOBUFS on error. * @return 0 on success, ENOBUFS on error.
*/ */
static int int
stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type, stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const void *data, size_t len) const void *data, size_t len)
{ {
...@@ -224,6 +229,8 @@ void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req) ...@@ -224,6 +229,8 @@ void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req)
assert (msize >= 20u); assert (msize >= 20u);
stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req)); stun_init (ans, STUN_RESPONSE, stun_get_method (req), stun_id (req));
/* For RFC3489 compatibility, we cannot assume the cookie */
memcpy (ans + 4, req + 4, 4);
(void)stun_append_server (ans, msize); (void)stun_append_server (ans, msize);
} }
...@@ -246,9 +253,7 @@ static const char *stun_strerror (stun_error_t code) ...@@ -246,9 +253,7 @@ static const char *stun_strerror (stun_error_t code)
{ STUN_UNKNOWN_ATTRIBUTE, "Unknown attribute" }, { STUN_UNKNOWN_ATTRIBUTE, "Unknown attribute" },
/* /*
{ STUN_STALE_CREDENTIALS, "Authentication expired" }, { STUN_STALE_CREDENTIALS, "Authentication expired" },
*/
{ STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" }, { STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" },
/*
{ STUN_MISSING_USERNAME, "Username required" }, { STUN_MISSING_USERNAME, "Username required" },
{ STUN_USE_TLS, "Secure connection required" }, { STUN_USE_TLS, "Secure connection required" },
{ STUN_MISSING_REALM, "Authentication domain required" }, { STUN_MISSING_REALM, "Authentication domain required" },
...@@ -267,12 +272,11 @@ static const char *stun_strerror (stun_error_t code) ...@@ -267,12 +272,11 @@ static const char *stun_strerror (stun_error_t code)
{ STUN_ROLE_CONFLICT, "Role conflict" }, { STUN_ROLE_CONFLICT, "Role conflict" },
{ STUN_SERVER_ERROR, "Temporary server error" }, { STUN_SERVER_ERROR, "Temporary server error" },
{ STUN_SERVER_CAPACITY, "Temporary server congestion" }, { STUN_SERVER_CAPACITY, "Temporary server congestion" },
{ 0, "" }
}; };
const char *str = "Unknown error"; const char *str = "Unknown error";
unsigned i; size_t i;
for (i = 0; tab[i].phrase[0]; i++) for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
{ {
if (tab[i].code == code) if (tab[i].code == code)
{ {
...@@ -315,12 +319,15 @@ stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code) ...@@ -315,12 +319,15 @@ stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code)
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req, int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err) stun_error_t err)
{ {
assert (stun_valid (req)); assert (stun_valid (req));
assert (msize >= 20u); assert (msize >= 20u);
assert (stun_get_class (req) == STUN_REQUEST);
stun_init (ans, STUN_ERROR, stun_get_method (req), stun_id (req)); stun_init (ans, STUN_ERROR, stun_get_method (req), stun_id (req));
/* For RFC3489 compatibility, we cannot assume the cookie */
memcpy (ans + 4, req + 4, 4);
(void)stun_append_server (ans, msize); (void)stun_append_server (ans, msize);
return stun_append_error (ans, msize, err); return stun_append_error (ans, msize, err);
} }
...@@ -330,14 +337,11 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req) ...@@ -330,14 +337,11 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
{ {
unsigned counter, i; unsigned counter, i;
#ifdef HAVE_C_VARARRAYS #ifdef HAVE_C_VARARRAYS
uint16_t ids[stun_length (req) / 4]; uint16_t ids[1 + (stun_length (req) / 4)];
#else #else
uint16_t ids[256]; uint16_t ids[256];
#endif #endif
assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0])); counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0]));
assert (counter > 0); assert (counter > 0);
...@@ -346,6 +350,12 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req) ...@@ -346,6 +350,12 @@ int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req)
for (i = 0; i < counter; i++) for (i = 0; i < counter; i++)
ids[i] = htons (ids[i]); ids[i] = htons (ids[i]);
/* NOTE: Old RFC3489 compatibility:
* When counter is odd, duplicate one value for 32-bits padding. */
if (!stun_has_cookie (req) && (counter & 1))
ids[counter++] = ids[0];
return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids, return stun_append_bytes (ans, msize, STUN_UNKNOWN_ATTRIBUTES, ids,
counter * 2); counter * 2);
} }
...@@ -411,13 +421,11 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize, ...@@ -411,13 +421,11 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
socklen_t addrlen) socklen_t addrlen)
{ {
int val; int val;
union /* Must be big enough to hold any supported address: */
{ struct sockaddr_storage xor;
struct sockaddr addr;
char fill[addrlen];
} xor;
assert (sizeof (xor) >= addrlen); if (addrlen > sizeof (xor))
addrlen = sizeof (xor);
memcpy (&xor, addr, addrlen); memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen); val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
...@@ -429,14 +437,13 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize, ...@@ -429,14 +437,13 @@ int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
} }
static size_t size_t
stun_finish_long (uint8_t *msg, size_t *restrict plen, stun_finish_long (uint8_t *msg, size_t *restrict plen,
const char *realm, const char *username, const char *realm, const char *username, const char *nonce,
const void *key, size_t keylen, const void *restrict key, size_t keylen)
const void *nonce, size_t noncelen)
{ {
size_t len = *plen; size_t len = *plen;
uint8_t *sha = NULL, *crc; uint8_t *ptr;
int val = ENOBUFS; int val = ENOBUFS;
uint32_t fpr; uint32_t fpr;
...@@ -456,58 +463,55 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen, ...@@ -456,58 +463,55 @@ stun_finish_long (uint8_t *msg, size_t *restrict plen,
if (nonce != NULL) if (nonce != NULL)
{ {
val = stun_append_bytes (msg, len, STUN_NONCE, nonce, noncelen); val = stun_append_string (msg, len, STUN_NONCE, nonce);
if (val) if (val)
return val; return val;
} }
if (key != NULL) if (key != NULL)
{ {
sha = stun_append (msg, len, STUN_MESSAGE_INTEGRITY, 20); ptr = stun_append (msg, len, STUN_MESSAGE_INTEGRITY, 20);
if (sha == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
}
crc = stun_append (msg, len, STUN_FINGERPRINT, 4); stun_sha1 (msg, ptr + 20 - msg, ptr, key, keylen);
if (crc == NULL)
return ENOBUFS;
if (sha != NULL)
{
stun_sha1 (msg, sha, key, keylen);
#ifndef NDEBUG
DBG (" Message HMAC-SHA1 fingerprint:" DBG (" Message HMAC-SHA1 fingerprint:"
"\n key : 0x"); "\n key : ");
for (unsigned i = 0; i < keylen; i++) DBG_bytes (key, keylen);
DBG ("%02x", ((uint8_t *)key)[i]); DBG ("\n sent : ");
DBG ("\n sent : 0x"); DBG_bytes (ptr, 20);
for (unsigned i = 0; i < 20; i++)
DBG ("%02x", sha[i]);
DBG ("\n"); DBG ("\n");
#endif
} }
fpr = htonl (stun_fingerprint (msg)); /*
memcpy (crc, &fpr, sizeof (fpr)); * NOTE: we always add a FINGERPRINT, even when it's not needed.
* This is OK, as it is an optional attribute. It also makes my
* software engineer's life easier.
*/
ptr = stun_append (msg, len, STUN_FINGERPRINT, 4);
if (ptr == NULL)
return ENOBUFS;
*plen = ptr + 4 -msg;
fpr = htonl (stun_fingerprint (msg, *plen));
memcpy (ptr, &fpr, sizeof (fpr));
*plen = 20u + stun_length (msg);
return 0; return 0;
} }
size_t stun_finish_short (uint8_t *msg, size_t *restrict plen, size_t stun_finish_short (uint8_t *msg, size_t *restrict plen,
const char *username, const char *password, const char *username, const char *restrict password,
const void *nonce, size_t noncelen) const char *nonce)
{ {
size_t passlen = (password != NULL) ? strlen (password) : 0; return stun_finish_long (msg, plen, NULL, username, nonce,
return stun_finish_long (msg, plen, NULL, username, password, passlen, password, password ? strlen (password) : 0);
nonce, noncelen);
} }
size_t stun_finish (uint8_t *msg, size_t *restrict plen) size_t stun_finish (uint8_t *msg, size_t *restrict plen)
{ {
return stun_finish_short (msg, plen, NULL, NULL, NULL, 0); return stun_finish_short (msg, plen, NULL, NULL, NULL);
} }
...@@ -10,8 +10,11 @@ AM_CPPFLAGS = -I$(top_srcdir) ...@@ -10,8 +10,11 @@ AM_CPPFLAGS = -I$(top_srcdir)
AM_CFLAGS = -std=gnu99 AM_CFLAGS = -std=gnu99
LDADD = ../libstun.la LDADD = ../libstun.la
check_PROGRAMS = test-crc32 test-parse test-format \ check_PROGRAMS = \
test-bind test-bindserv test-conncheck test-parse \
test-format \
test-bind \
test-conncheck
dist_check_SCRIPTS = check-bind.sh dist_check_SCRIPTS = check-bind.sh
......
...@@ -172,18 +172,20 @@ static void bad_responses (void) ...@@ -172,18 +172,20 @@ static void bad_responses (void)
uint8_t buf[1000]; uint8_t buf[1000];
/* Allocate a local UDP port */ /* Allocate a local UDP port */
int servfd = listen_dgram (); int servfd = listen_dgram (), fd;
assert (servfd != -1); assert (servfd != -1);
val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen); val = getsockname (servfd, (struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = stun_bind_start (&ctx, -1, (struct sockaddr *)&addr, addrlen); fd = socket (addr.ss_family, SOCK_DGRAM, 0);
assert (fd != -1);
val = stun_bind_start (&ctx, fd, (struct sockaddr *)&addr, addrlen);
assert (val == 0); assert (val == 0);
/* Send to/receive from our client instance only */ /* Send to/receive from our client instance only */
val = getsockname (stun_bind_fd (ctx), val = getsockname (fd, (struct sockaddr *)&addr, &addrlen);
(struct sockaddr *)&addr, &addrlen);
assert (val == 0); assert (val == 0);
val = connect (servfd, (struct sockaddr *)&addr, addrlen); val = connect (servfd, (struct sockaddr *)&addr, addrlen);
...@@ -219,8 +221,8 @@ static void bad_responses (void) ...@@ -219,8 +221,8 @@ static void bad_responses (void)
(struct sockaddr *)&addr, &addrlen); (struct sockaddr *)&addr, &addrlen);
assert (val == EAGAIN); assert (val == EAGAIN);
stun_bind_cancel (ctx); stun_bind_cancel (ctx);
close (fd);
close (servfd); close (servfd);
} }
......
...@@ -72,19 +72,6 @@ int main (void) ...@@ -72,19 +72,6 @@ int main (void)
ip4.sin_port = htons (12345); ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001); 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));
/* Same with too small response buffer */ /* Same with too small response buffer */
stun_init_request (buf, STUN_BINDING); stun_init_request (buf, STUN_BINDING);
len = sizeof (buf); len = sizeof (buf);
...@@ -96,46 +83,6 @@ int main (void) ...@@ -96,46 +83,6 @@ int main (void)
assert (val == ENOBUFS); assert (val == ENOBUFS);
assert (len == 0); assert (len == 0);
/* Incorrect message class */
stun_init_request (buf, STUN_BINDING);
stun_init_response (buf, sizeof (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);
/* Same with too small response buffer */ /* Same with too small response buffer */
stun_init_request (buf, STUN_BINDING); stun_init_request (buf, STUN_BINDING);
stun_append_string (buf, sizeof (buf), 0x666, "Unknown attribute!"); stun_append_string (buf, sizeof (buf), 0x666, "Unknown attribute!");
...@@ -148,31 +95,5 @@ int main (void) ...@@ -148,31 +95,5 @@ int main (void)
assert (val == ENOBUFS); assert (val == ENOBUFS);
assert (len == 0); assert (len == 0);
/* Non-multiplexed message */
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);
return 0; return 0;
} }
...@@ -61,7 +61,9 @@ int main (void) ...@@ -61,7 +61,9 @@ int main (void)
ssize_t val; ssize_t val;
size_t len; size_t len;
static const char name[] = "admin", pass[] = "secret"; static const char name[] = "admin", pass[] = "secret";
char nbuf[6]; char nbuf[STUN_MAX_STR];
int code;
uint16_t alen;
bool control = false; bool control = false;
memset (&ip4, 0, sizeof (ip4)); memset (&ip4, 0, sizeof (ip4));
...@@ -72,6 +74,46 @@ int main (void) ...@@ -72,6 +74,46 @@ int main (void)
ip4.sin_port = htons (12345); ip4.sin_port = htons (12345);
ip4.sin_addr.s_addr = htonl (0x7f000001); ip4.sin_addr.s_addr = htonl (0x7f000001);
/* Incorrect message class */
stun_init_request (req, STUN_BINDING);
stun_init_response (req, sizeof (req), req);
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 == EINVAL);
assert (len == 0);
/* Incorrect message method */
stun_init_request (req, 0x666);
len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0);
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EPROTO);
assert (len > 0);
/* Unknown attribute */
stun_init_request (req, STUN_BINDING);
val = stun_append_string (req, sizeof (req), 0x666,
"The evil unknown attribute!");
assert (val == 0);
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 == EPROTO);
assert (len > 0);
/* Unauthenticated message */ /* Unauthenticated message */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
...@@ -83,12 +125,13 @@ int main (void) ...@@ -83,12 +125,13 @@ int main (void)
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
// FIXME: check error code assert (stun_match_messages (resp, req, NULL, 0, &code)
&& (code == STUN_BAD_REQUEST));
/* No username */ /* No username */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, NULL, pass, NULL, 0); val = stun_finish_short (req, &len, NULL, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
...@@ -96,32 +139,30 @@ int main (void) ...@@ -96,32 +139,30 @@ int main (void)
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
assert (stun_get_class (resp) == STUN_ERROR); assert (stun_match_messages (resp, req, NULL, 0, &code)
assert (!stun_present (resp, STUN_MESSAGE_INTEGRITY)); && (code == STUN_BAD_REQUEST));
assert (stun_conncheck_username (req, NULL, 0) == NULL); assert (stun_conncheck_username (req, NULL, 0) == NULL);
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL);
assert (stun_conncheck_priority (req) == 0); assert (stun_conncheck_priority (req) == 0);
assert (stun_conncheck_use_candidate (req) == false); assert (stun_conncheck_use_candidate (req) == false);
/* Bad username */ /* Bad username */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, "ab\xff", pass, NULL, 0); val = stun_finish_short (req, &len, "ab\xff", pass, NULL);
assert (val == 0); assert (val == 0);
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == NULL);
/* FIXME: use conncheck_reply */ /* FIXME: use conncheck_reply */
/* Bad fingerprint */ /* Bad integrity */
len = sizeof (resp); len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4, val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), "bad", &control, tie); sizeof (ip4), "bad", &control, tie);
assert (val == EPERM); assert (val == EPERM);
assert (len > 0); assert (len > 0);
assert (stun_get_class (resp) == STUN_ERROR); assert (stun_match_messages (resp, req, NULL, 0, &code)
assert (!stun_present (resp, STUN_MESSAGE_INTEGRITY)); && (code == STUN_UNAUTHORIZED));
/* Good message */ /* Good message */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
...@@ -130,7 +171,7 @@ int main (void) ...@@ -130,7 +171,7 @@ int main (void)
val = stun_append_flag (req, sizeof (req), STUN_USE_CANDIDATE); val = stun_append_flag (req, sizeof (req), STUN_USE_CANDIDATE);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
...@@ -138,9 +179,9 @@ int main (void) ...@@ -138,9 +179,9 @@ int main (void)
sizeof (ip4), pass, &control, tie); sizeof (ip4), pass, &control, tie);
assert (val == 0); assert (val == 0);
assert (len > 0); assert (len > 0);
assert (stun_get_class (resp) == STUN_RESPONSE); assert (stun_match_messages (resp, req, (uint8_t *)pass,
assert (stun_present (resp, STUN_MESSAGE_INTEGRITY)); strlen (pass), &code)
&& (code == -1));
assert (stun_conncheck_priority (req) == 0x12345678); assert (stun_conncheck_priority (req) == 0x12345678);
assert (stun_conncheck_use_candidate (req) == true); assert (stun_conncheck_use_candidate (req) == true);
...@@ -152,13 +193,34 @@ int main (void) ...@@ -152,13 +193,34 @@ int main (void)
assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == nbuf); assert (stun_conncheck_username (req, nbuf, sizeof (nbuf)) == nbuf);
assert (strcmp (nbuf, name) == 0); assert (strcmp (nbuf, name) == 0);
/* Invalid socket address */
ip4.sin_family = AF_UNSPEC;
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EAFNOSUPPORT);
ip4.sin_family = AF_INET;
/* Bad CRC32 */
stun_init_request (req, STUN_BINDING);
len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0);
((uint8_t *)stun_find (req, STUN_FINGERPRINT, &alen))[0] ^= 1;
len = sizeof (resp);
val = stun_conncheck_reply (resp, &len, req, (struct sockaddr *)&ip4,
sizeof (ip4), pass, &control, tie);
assert (val == EINVAL);
assert (len == 0);
/* Lost role conflict */ /* Lost role conflict */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLING, tie + 1); val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLING, tie + 1);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
...@@ -168,15 +230,16 @@ int main (void) ...@@ -168,15 +230,16 @@ int main (void)
assert (val == EACCES); assert (val == EACCES);
assert (len > 0); assert (len > 0);
assert (control == false); assert (control == false);
assert (stun_get_class (resp) == STUN_RESPONSE); assert (stun_match_messages (resp, req, (uint8_t *)pass,
assert (stun_present (resp, STUN_MESSAGE_INTEGRITY)); strlen (pass), &code)
&& (code == -1));
/* Won role conflict */ /* Won role conflict */
stun_init_request (req, STUN_BINDING); stun_init_request (req, STUN_BINDING);
val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLED, tie - 1); val = stun_append64 (req, sizeof (req), STUN_ICE_CONTROLLED, tie - 1);
assert (val == 0); assert (val == 0);
len = sizeof (req); len = sizeof (req);
val = stun_finish_short (req, &len, name, pass, NULL, 0); val = stun_finish_short (req, &len, name, pass, NULL);
assert (val == 0); assert (val == 0);
len = sizeof (resp); len = sizeof (resp);
...@@ -186,8 +249,9 @@ int main (void) ...@@ -186,8 +249,9 @@ int main (void)
assert (val == 0); assert (val == 0);
assert (len > 0); assert (len > 0);
assert (control == false); assert (control == false);
assert (stun_get_class (resp) == STUN_ERROR); assert (stun_match_messages (resp, req, (uint8_t *)pass,
assert (stun_present (resp, STUN_MESSAGE_INTEGRITY)); strlen (pass), &code)
&& (code == STUN_ROLE_CONFLICT));
return 0; return 0;
} }
...@@ -89,7 +89,7 @@ finish_check (uint8_t *msg) ...@@ -89,7 +89,7 @@ finish_check (uint8_t *msg)
len = sizeof (mshort); len = sizeof (mshort);
if (stun_verify_password (mshort, "toto") != ENOENT) if (stun_verify_password (mshort, "toto") != ENOENT)
fatal ("Missing HMAC test failed"); fatal ("Missing HMAC test failed");
if (stun_finish_short (mshort, &len, "ABCDE", "admin", "ABC", 3)) if (stun_finish_short (mshort, &len, "ABCDE", "admin", "ABC"))
fatal ("Cannot finish message with short-term creds"); fatal ("Cannot finish message with short-term creds");
dynamic_check (mshort, len); dynamic_check (mshort, len);
if (stun_verify_password (mshort, "admin") != 0) if (stun_verify_password (mshort, "admin") != 0)
...@@ -155,6 +155,7 @@ int main (void) ...@@ -155,6 +155,7 @@ int main (void)
fatal ("Response formatting test failed"); fatal ("Response formatting test failed");
/* Error formatting test */ /* Error formatting test */
stun_init_request (msg, STUN_BINDING);
if (stun_init_error (msg, sizeof (msg), msg, 400)) if (stun_init_error (msg, sizeof (msg), msg, 400))
fatal ("Error initialization test failed"); fatal ("Error initialization test failed");
finish_check (msg); finish_check (msg);
...@@ -162,6 +163,7 @@ int main (void) ...@@ -162,6 +163,7 @@ int main (void)
fatal ("Error formatting test failed"); fatal ("Error formatting test failed");
/* Unknown error formatting test */ /* Unknown error formatting test */
stun_init_request (msg, STUN_BINDING);
if (stun_init_error (msg, sizeof (msg), msg, 666)) if (stun_init_error (msg, sizeof (msg), msg, 666))
fatal ("Unknown error initialization test failed"); fatal ("Unknown error initialization test failed");
finish_check (msg); finish_check (msg);
...@@ -198,13 +200,13 @@ int main (void) ...@@ -198,13 +200,13 @@ int main (void)
if (stun_finish (msg, &len) != ENOBUFS) if (stun_finish (msg, &len) != ENOBUFS)
fatal ("Fingerprint overflow test failed"); fatal ("Fingerprint overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", NULL, 0) != ENOBUFS) if (stun_finish_short (msg, &len, NULL, "secret", NULL) != ENOBUFS)
fatal ("Message integrity overflow test failed"); fatal ("Message integrity overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, "login", "secret", NULL, 0) != ENOBUFS) if (stun_finish_short (msg, &len, "login", "secret", NULL) != ENOBUFS)
fatal ("Username overflow test failed"); fatal ("Username overflow test failed");
len = sizeof (msg); len = sizeof (msg);
if (stun_finish_short (msg, &len, NULL, "secret", "foobar", 6) != ENOBUFS) if (stun_finish_short (msg, &len, NULL, "secret", "foobar") != ENOBUFS)
fatal ("Nonce overflow test failed"); fatal ("Nonce overflow test failed");
/* Address attributes tests */ /* Address attributes tests */
......
...@@ -220,11 +220,11 @@ static void test_attribute (void) ...@@ -220,11 +220,11 @@ static void test_attribute (void)
/* MESSAGE-INTEGRITY attribute */ /* MESSAGE-INTEGRITY attribute */
"\x00\x08\x00\x14" "\x00\x08\x00\x14"
"\x20\x10\xee\x8d" "\x42\x95\x4b\x54"
"\x61\xc9\x3e\x46" "\x73\x3c\x73\xef"
"\xbe\x41\xad\x5c" "\xa9\x75\xad\x6f"
"\xad\x38\xaa\x4c" "\xbe\xd5\x6b\x13"
"\xe8\xf1\xaf\x07" "\x9d\x53\x5f\x57"
; ;
union union
...@@ -235,7 +235,7 @@ static void test_attribute (void) ...@@ -235,7 +235,7 @@ static void test_attribute (void)
socklen_t addrlen; socklen_t addrlen;
uint32_t dword; uint32_t dword;
uint64_t qword; uint64_t qword;
char str[5]; char str[STUN_MAX_STR];
printf ("Attribute test message length: %u\n", sizeof (acme)); printf ("Attribute test message length: %u\n", sizeof (acme));
...@@ -268,11 +268,9 @@ static void test_attribute (void) ...@@ -268,11 +268,9 @@ static void test_attribute (void)
if (stun_find64 (acme, 0xff04, &qword) !=0) if (stun_find64 (acme, 0xff04, &qword) !=0)
fatal ("Quad-word test failed"); fatal ("Quad-word test failed");
if (stun_find_string (acme, 0xff00, str, sizeof (str)) != -1) if (stun_find_string (acme, 0xff00, str, STUN_MAX_CP) != ENOENT)
fatal ("Absent string test failed"); fatal ("Absent string test failed");
if ((stun_find_string (acme, 0xff02, str, 1) != 4) || (str[0] != 'A')) if ((stun_find_string (acme, 0xff02, str, STUN_MAX_CP) != 0)
fatal ("String buffer underflow test failed");
if ((stun_find_string (acme, 0xff02, str, sizeof (str)) != 4)
|| strcmp (str, "ABCD")) || strcmp (str, "ABCD"))
fatal ("String test failed"); fatal ("String test failed");
...@@ -304,9 +302,63 @@ static void test_attribute (void) ...@@ -304,9 +302,63 @@ static void test_attribute (void)
} }
static void test_vectors (void)
{
static const char username[] = "evtj:h6vY";
static const char password[] = "VOkJxbRl1RmTxUk/WvJxBt";
/* Request message */
static const unsigned char req[] =
"\x00\x01\x00\x44"
"\x21\x12\xa4\x42"
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x00\x24\x00\x04"
"\x6e\x00\x01\xff"
"\x80\x29\x00\x08"
"\x93\x2f\xf9\xb1\x51\x26\x3b\x36"
"\x00\x06\x00\x09"
"\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20"
"\x00\x08\x00\x14"
"\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74\xbf\x85"
"\xd1\xc0\xf5\xde\x36\x87\xbd\x33"
"\x80\x28\x00\x04"
"\xad\x8a\x85\xff";
/* Response message */
static const unsigned char resp[] =
"\x01\x01\x00\x3c"
"\x21\x12\xa4\x42"
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x80\x22\x00\x0b"
"\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20"
"\x00\x20\x00\x08"
"\x00\x01\xa1\x47\x5e\x12\xa4\x43"
"\x00\x08\x00\x14"
"\xab\x4e\x53\x29\x61\x00\x08\x4c\x89\xf2\x7c\x69"
"\x30\x33\x5c\xa3\x58\x14\xea\x90"
"\x80\x28\x00\x04"
"\xae\x25\x8d\xf2";
puts ("Checking test vectors...");
if (stun_demux (req) != true)
fatal ("Request test vector checksum failed");
if (stun_verify_password (req, password) != 0)
fatal ("Request test vector authentication failed");
if (stun_demux (resp) != true)
fatal ("Response test vector checksum failed");
if (stun_verify_password (resp, password) != 0)
fatal ("Response test vector authentication failed");
puts ("Done.");
}
int main (void) int main (void)
{ {
test_message (); test_message ();
test_attribute (); test_attribute ();
test_vectors ();
return 0; return 0;
} }
...@@ -127,7 +127,9 @@ int stun_timer_refresh (stun_timer_t *timer) ...@@ -127,7 +127,9 @@ int stun_timer_refresh (stun_timer_t *timer)
unsigned delay = stun_timer_remainder (timer); unsigned delay = stun_timer_remainder (timer);
if (delay == 0) if (delay == 0)
{ {
#if STUN_END_TIMEOUT > STUN_RELIABLE_TIMEOUT #if STUN_RELIABLE_TIMEOUT < STUN_END_TIMEOUT
/* Reliable timeout MUST be bigger (or equal) to end timeout, so that
* retransmissions never happen with reliable transports. */
# error Inconsistent STUN timeout values! # error Inconsistent STUN timeout values!
#endif #endif
if (timer->delay >= STUN_END_TIMEOUT) if (timer->delay >= STUN_END_TIMEOUT)
......
...@@ -46,12 +46,16 @@ ...@@ -46,12 +46,16 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <netinet/in.h> #include <netinet/in.h>
#ifdef HAVE_POLL
# include <poll.h>
#endif
#include "stun-msg.h" #include "stun-msg.h"
#include "trans.h" #include "trans.h"
#define TRANS_OWN_FD 0x1 /* descriptor belongs to us */ #define TRANS_OWN_FD 0x1 /* descriptor belongs to us */
#define TRANS_RELIABLE 0x2 /* reliable transport */ #define TRANS_RELIABLE 0x2 /* reliable transport */
#define TRANS_FGPRINT 0x4 /* whether to use FINGERPRINT */
int stun_trans_init (stun_trans_t *restrict tr, int fd, int stun_trans_init (stun_trans_t *restrict tr, int fd,
const struct sockaddr *restrict srv, socklen_t srvlen) const struct sockaddr *restrict srv, socklen_t srvlen)
...@@ -85,17 +89,15 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd, ...@@ -85,17 +89,15 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd,
} }
int stun_trans_create (stun_trans_t *restrict tr, int type, int proto, /**
const struct sockaddr *restrict srv, socklen_t srvlen) * Creates and connects a socket. This is useful when a socket is to be used
* for multiple consecutive transactions (e.g. TURN).
*/
static int stun_socket (int family, int type, int proto)
{ {
int fd, val; int fd = socket (family, type, proto);
if (srvlen < sizeof (struct sockaddr))
return EINVAL;
fd = socket (srv->sa_family, type, proto);
if (fd == -1) if (fd == -1)
return errno; return -1;
#ifdef FD_CLOEXEC #ifdef FD_CLOEXEC
fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC); fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
...@@ -109,7 +111,7 @@ int stun_trans_create (stun_trans_t *restrict tr, int type, int proto, ...@@ -109,7 +111,7 @@ int stun_trans_create (stun_trans_t *restrict tr, int type, int proto,
{ {
/* Linux specifics for ICMP errors on non-connected sockets */ /* Linux specifics for ICMP errors on non-connected sockets */
int yes = 1; int yes = 1;
switch (srv->sa_family) switch (family)
{ {
case AF_INET: case AF_INET:
setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes)); setsockopt (fd, SOL_IP, IP_RECVERR, &yes, sizeof (yes));
...@@ -121,25 +123,47 @@ int stun_trans_create (stun_trans_t *restrict tr, int type, int proto, ...@@ -121,25 +123,47 @@ int stun_trans_create (stun_trans_t *restrict tr, int type, int proto,
} }
#endif #endif
return fd;
}
int stun_trans_create (stun_trans_t *restrict tr, int type, int proto,
const struct sockaddr *restrict srv, socklen_t srvlen)
{
int val, fd;
if (srvlen < sizeof(*srv))
return EINVAL;
fd = stun_socket (srv->sa_family, type, proto);
if (fd == -1)
return errno;
if (connect (fd, srv, srvlen) && (errno != EINPROGRESS)) if (connect (fd, srv, srvlen) && (errno != EINPROGRESS))
{
val = errno; val = errno;
else goto error;
val = stun_trans_init (tr, fd, NULL, 0); }
val = stun_trans_init (tr, fd, NULL, 0);
if (val) if (val)
{ goto error;
close (fd);
return val;
}
tr->flags |= TRANS_OWN_FD; tr->flags |= TRANS_OWN_FD;
return 0; return 0;
error:
close (fd);
return val;
} }
void stun_trans_deinit (stun_trans_t *tr) void stun_trans_deinit (stun_trans_t *tr)
{ {
int saved = errno; int saved = errno;
assert (tr->sock.fd != -1);
if (tr->flags & TRANS_OWN_FD) if (tr->flags & TRANS_OWN_FD)
close (tr->sock.fd); close (tr->sock.fd);
free (tr->key.value); free (tr->key.value);
...@@ -165,19 +189,23 @@ int stun_trans_start (stun_trans_t *tr) ...@@ -165,19 +189,23 @@ int stun_trans_start (stun_trans_t *tr)
{ {
int val; int val;
assert (tr->msg.offset == 0); tr->msg.offset = 0;
val = stun_trans_send (tr);
if (val)
return val;
if (tr->flags & TRANS_RELIABLE) if (tr->flags & TRANS_RELIABLE)
/*
* FIXME: wait for three-way handshake, somewhere
*/
stun_timer_start_reliable (&tr->timer); stun_timer_start_reliable (&tr->timer);
else else
stun_timer_start (&tr->timer); stun_timer_start (&tr->timer);
DBG ("STUN transaction @%p started (timeout: %ums)\n", tr, DBG ("STUN transaction @%p started (timeout: %ums)\n", tr,
stun_trans_timeout (tr)); stun_trans_timeout (tr));
val = stun_trans_send (tr);
if (val)
return val;
return 0; return 0;
} }
...@@ -216,6 +244,54 @@ ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len, ...@@ -216,6 +244,54 @@ ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len,
return val; return val;
} }
ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen,
struct sockaddr *restrict dst,
socklen_t *restrict dstlen)
{
static const int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
ssize_t val;
if (dstlen != NULL)
val = recvfrom (fd, buf, maxlen, flags, dst, dstlen);
else
val = recv (fd, buf, maxlen, flags);
if ((val == -1) && stun_err_dequeue (fd))
errno = EAGAIN;
return val;
}
unsigned stun_trans_timeout (const stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->sock.fd != -1);
return stun_timer_remainder (&tr->timer);
}
int stun_trans_fd (const stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->sock.fd != -1);
return tr->sock.fd;
}
bool stun_trans_reading (const stun_trans_t *tr)
{
return true;
}
bool stun_trans_writing (const stun_trans_t *tr)
{
return false;
}
/** /**
* Try to send STUN request/indication * Try to send STUN request/indication
*/ */
...@@ -234,14 +310,15 @@ stun_trans_send (stun_trans_t *tr) ...@@ -234,14 +310,15 @@ stun_trans_send (stun_trans_t *tr)
/* Message sent succesfully! */ /* Message sent succesfully! */
tr->msg.offset += val; tr->msg.offset += val;
assert (tr->msg.offset <= tr->msg.length); assert (tr->msg.offset <= tr->msg.length);
if (tr->msg.offset == tr->msg.length)
tr->msg.offset = 0; // FIXME: temporary hack, may break TCP
return 0; return 0;
} }
int stun_trans_tick (stun_trans_t *tr) int stun_trans_tick (stun_trans_t *tr)
{ {
assert (tr->sock.fd != -1);
switch (stun_timer_refresh (&tr->timer)) switch (stun_timer_refresh (&tr->timer))
{ {
case -1: case -1:
...@@ -249,6 +326,10 @@ int stun_trans_tick (stun_trans_t *tr) ...@@ -249,6 +326,10 @@ int stun_trans_tick (stun_trans_t *tr)
return ETIMEDOUT; // fatal error! return ETIMEDOUT; // fatal error!
case 0: case 0:
/* Retransmit can only happen with non reliable transport */
assert ((tr->flags & TRANS_RELIABLE) == 0);
tr->msg.offset = 0;
stun_trans_send (tr); stun_trans_send (tr);
DBG ("STUN transaction @%p retransmitted (timeout: %ums).\n", tr, DBG ("STUN transaction @%p retransmitted (timeout: %ums).\n", tr,
stun_trans_timeout (tr)); stun_trans_timeout (tr));
...@@ -257,11 +338,73 @@ int stun_trans_tick (stun_trans_t *tr) ...@@ -257,11 +338,73 @@ int stun_trans_tick (stun_trans_t *tr)
} }
/**
* Waits for a response or timeout to occur.
*
* @return ETIMEDOUT if the transaction has timed out, or 0 if an incoming
* message needs to be processed.
*/
static int stun_trans_wait (stun_trans_t *tr)
{
#ifdef HAVE_POLL
int val = 0;
do
{
struct pollfd ufd;
unsigned delay = stun_trans_timeout (tr);
memset (&ufd, 0, sizeof (ufd));
ufd.fd = stun_trans_fd (tr);
if (stun_trans_writing (tr))
ufd.events |= POLLOUT;
if (stun_trans_reading (tr))
ufd.events |= POLLIN;
if (poll (&ufd, 1, delay) <= 0)
{
val = stun_trans_tick (tr);
continue;
}
val = 0;
}
while (val == EAGAIN);
return val;
#else
(void)tr;
return ENOSYS;
#endif
}
int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen)
{
for (;;)
{
ssize_t val = stun_trans_wait (tr);
if (val)
{
errno = val /* = ETIMEDOUT */;
return -1;
}
val = stun_recv (tr->sock.fd, buf, buflen);
if (val >= 0)
return val;
}
}
int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode, int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode,
const void *restrict buf, size_t len) const void *restrict buf, size_t len)
{ {
assert (pcode != NULL); assert (pcode != NULL);
/* FIXME: possible infinite loop */
if (stun_validate (buf, len) <= 0) if (stun_validate (buf, len) <= 0)
return EAGAIN; return EAGAIN;
...@@ -277,6 +420,49 @@ int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode, ...@@ -277,6 +420,49 @@ int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode,
if (*pcode >= 0) if (*pcode >= 0)
{ {
DBG (" STUN error message received (code: %d)\n", *pcode); DBG (" STUN error message received (code: %d)\n", *pcode);
/* ALTERNATE-SERVER mechanism */
if ((tr->key.value != NULL) && ((*pcode / 100) == 3))
{
struct sockaddr_storage srv;
socklen_t slen = sizeof (srv);
if (stun_find_addr (buf, STUN_ALTERNATE_SERVER,
(struct sockaddr *)&srv, &slen))
{
DBG (" Unexpectedly missing ALTERNATE-SERVER attribute\n");
return ECONNREFUSED;
}
if (tr->sock.dstlen == 0)
{
if (connect (tr->sock.fd, (struct sockaddr *)&srv, slen))
{
/* This error case includes address family mismatch */
DBG (" Error switching to alternate server: %s\n",
strerror (errno));
return ECONNREFUSED;
}
}
else
{
if ((tr->sock.dst.ss_family != srv.ss_family)
|| (slen > sizeof (tr->sock.dst)))
{
DBG (" Unsupported alternate server\n");
return ECONNREFUSED;
}
memcpy (&tr->sock.dst, &srv, tr->sock.dstlen = slen);
}
DBG (" Restarting with alternate server\n");
if (stun_trans_start (tr) == 0)
return EAGAIN;
DBG (" Restart failed!\n");
}
return ECONNREFUSED; return ECONNREFUSED;
} }
...@@ -285,19 +471,3 @@ int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode, ...@@ -285,19 +471,3 @@ int stun_trans_preprocess (stun_trans_t *restrict tr, int *pcode,
return 0; return 0;
} }
unsigned stun_trans_timeout (const stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->sock.fd != -1);
return stun_timer_remainder (&tr->timer);
}
int stun_trans_fd (const stun_trans_t *tr)
{
assert (tr != NULL);
assert (tr->sock.fd != -1);
return tr->sock.fd;
}
...@@ -92,19 +92,20 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd, ...@@ -92,19 +92,20 @@ int stun_trans_init (stun_trans_t *restrict tr, int fd,
* *
* @param tr pointer to an unused STUN transaction struct * @param tr pointer to an unused STUN transaction struct
* @param sotype socket type (as in socket() second parameter) * @param sotype socket type (as in socket() second parameter)
* @param proto socket protocol (as is socket() third parameter) * @param proto socket protocol (as in socket() third parameter)
* @param srv STUN server socket address (ignored if @a srvlen is 0) * @param srv STUN server socket address (ignored if @a srvlen is 0)
* @param srvlen STUN server socket address length (or 0 @a fd is connected) * @param srvlen STUN server socket address length (or 0 @a fd is connected)
*/ */
int stun_trans_create (stun_trans_t *restrict tr, int sotype, int proto, int stun_trans_create (stun_trans_t *restrict tr, int sotype, int proto,
const struct sockaddr *restrict srv, socklen_t srvlen); const struct sockaddr *restrict srv, socklen_t srvlen);
/**
* Releases resources allocated by stun_trans_init() or stun_trans_create(),
* and cancel the transaction if still pending.
*/
void stun_trans_deinit (stun_trans_t *restrict tr); void stun_trans_deinit (stun_trans_t *restrict tr);
int stun_trans_start (stun_trans_t *restrict tr); int stun_trans_start (stun_trans_t *restrict tr);
int stun_trans_tick (stun_trans_t *tr);
int stun_trans_preprocess (stun_trans_t *restrict tr, int *code,
const void *restrict buf, size_t len);
/** /**
* This is meant to integrate with I/O pooling loops and event frameworks. * This is meant to integrate with I/O pooling loops and event frameworks.
...@@ -117,14 +118,67 @@ unsigned stun_trans_timeout (const stun_trans_t *tr); ...@@ -117,14 +118,67 @@ unsigned stun_trans_timeout (const stun_trans_t *tr);
/** /**
* This is meant to integrate with I/O polling loops and event frameworks. * This is meant to integrate with I/O polling loops and event frameworks.
* *
* @return file descriptor used by the STUN Binding discovery context. * @return file descriptor the transaction is waiting for.
* Always succeeds. * Always succeeds.
*/ */
int stun_trans_fd (const stun_trans_t *tr); int stun_trans_fd (const stun_trans_t *tr);
/**
* This is meant to integrate with I/O polling loops and event frameworks.
*
* @return whether the transaction waits for input (from the network).
*/
bool stun_trans_reading (const stun_trans_t *tr);
/**
* This is meant to integrate with I/O polling loops and event frameworks.
*
* @return whether the transaction waits for output (to the network).
*/
bool stun_trans_writing (const stun_trans_t *tr);
/**
* Refreshes a STUN request transaction state according to current time,
* retransmits request if needed. This function should be called when
* stun_trans_timeout() reaches zero
*
* @return ETIMEDOUT if the transaction has timed out, or EAGAIN if it is
* still pending.
*/
int stun_trans_tick (stun_trans_t *tr);
int stun_trans_recv (stun_trans_t *tr, uint8_t *buf, size_t buflen);
int stun_trans_preprocess (stun_trans_t *restrict tr, int *code,
const void *restrict buf, size_t len);
/**
* Safe wrapper around sendto()
* - returns EPIPE, but never yields SIGPIPE.
* - non blocking regardless of file descriptor blocking-ness.
* - drops incoming ICMP errors. FIXME: this is actually quite bad;
* we should process ICMP errors, not ignore them.
*
* This can be used to send non-requests message, i.e. whenever the
* transaction is not used.
*/
ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len, ssize_t stun_sendto (int fd, const uint8_t *buf, size_t len,
const struct sockaddr *dst, socklen_t dstlen); const struct sockaddr *dst, socklen_t dstlen);
ssize_t stun_recvfrom (int fd, uint8_t *buf, size_t maxlen,
struct sockaddr *restrict src,
socklen_t *restrict srclen);
static inline ssize_t stun_send (int fd, const uint8_t *buf, size_t len)
{
return stun_sendto (fd, buf, len, NULL, 0);
}
static inline ssize_t stun_recv (int fd, uint8_t *buf, size_t maxlen)
{
return stun_recvfrom (fd, buf, maxlen, NULL, NULL);
}
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif # endif
......
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