Commit b9669017 authored by Youness Alaoui's avatar Youness Alaoui

Updating stun to include an agent

parent c07afc9a
...@@ -19,16 +19,18 @@ CLEANFILES += $(BUILT_SOURCES) ...@@ -19,16 +19,18 @@ CLEANFILES += $(BUILT_SOURCES)
noinst_LTLIBRARIES = libstun.la noinst_LTLIBRARIES = libstun.la
dist_noinst_SCRIPTS = build-unknown.sh dist_noinst_SCRIPTS = build-unknown.sh
libstun_la_SOURCES = \ libstun_la_SOURCES = stun.h constants.h \
stun-msg.h stunsend.c stunrecv.c \ stunagent.c stunagent.h \
stun3489bis.c \ stunmessage.c stunmessage.h \
utils.c \ stun3489bis.c stun3489bis.h \
unknown.c \ stuncrc32.c stuncrc32.h \
crc32.c hmac.c \ stunhmac.c stunhmac.h \
usages/timer.h usages/timer.c \ utils.c utils.h
usages/trans.h usages/trans.c \
usages/stun-ice.c usages/stun-ice.h \ # usages/timer.h usages/timer.c \
usages/bind.c usages/bind.h # usages/trans.h usages/trans.c \
# usages/stun-ice.c usages/stun-ice.h \
# usages/bind.c usages/bind.h
libstun_la_LIBADD = $(OPENSSL_LIBS) $(LIBRT) libstun_la_LIBADD = $(OPENSSL_LIBS) $(LIBRT)
......
...@@ -67,9 +67,9 @@ ...@@ -67,9 +67,9 @@
#define STUN_ID_LEN 16 #define STUN_ID_LEN 16
#define STUN_AGENT_MAX_SAVED_IDS 20 #define STUN_AGENT_MAX_SAVED_IDS 20
#define STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES 256
#define STUN_MAGIC_COOKIE 0x2112A442
#define STUN_COOKIE 0x2112A442
#ifndef TRUE #ifndef TRUE
#define TRUE (1 == 1) #define TRUE (1 == 1)
......
...@@ -54,138 +54,12 @@ ...@@ -54,138 +54,12 @@
# define STUN_MAXCHR 127u # define STUN_MAXCHR 127u
# define STUN_MAXSTR ((STUN_MAXCHR * 6u) + 1) # define STUN_MAXSTR ((STUN_MAXCHR * 6u) + 1)
# define STUN_COOKIE 0x2112A442
typedef struct stun_hdr_s
{
uint16_t msg_type;
uint16_t msg_len;
uint32_t msg_cookie;
uint32_t msg_id[3];
} stun_hdr_t;
typedef uint8_t stun_msg_t[STUN_MAXMSG];
/* Message classes */
typedef enum
{
STUN_REQUEST=0,
STUN_INDICATION=1,
STUN_RESPONSE=2,
STUN_ERROR=3
} stun_class_t;
/* Message methods */
typedef enum
{
STUN_BINDING=0x001, /* RFC3489bis-11 */
STUN_OLD_SHARED_SECRET=0x002, /* old RFC3489 */
STUN_ALLOCATE=0x003, /* TURN-04 */
STUN_SET_ACTIVE_DST=0x004, /* TURN-04 */
STUN_CONNECT=0x005, /* TURN-04 */
STUN_IND_SEND=0x006, /* TURN-04 */
STUN_IND_DATA=0x007, /* TURN-04 */
STUN_IND_CONNECT_STATUS=0x008 /* TURN-04 */
} stun_method_t;
/**
* STUN attribute types
* Should be in sync with stun_is_unknown()
*/
typedef enum
{
/* Mandatory attributes */
/* 0x0000 */ /* reserved */
STUN_MAPPED_ADDRESS=0x0001, /* RFC3489bis-11 */
STUN_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */
STUN_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */
STUN_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */
STUN_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */
STUN_USERNAME=0x0006, /* RFC3489bis-11 */
STUN_OLD_PASSWORD=0x0007, /* old RFC3489 */
STUN_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-11 */
STUN_ERROR_CODE=0x0009, /* RFC3489bis-11 */
STUN_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-11 */
STUN_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */
/* 0x000C */ /* reserved */
STUN_LIFETIME=0x000D, /* TURN-04 */
/* 0x000E */ /* reserved */
/* 0x000F */ /* reserved */
STUN_BANDWIDTH=0x0010, /* TURN-04 */
/* 0x0011 */ /* reserved */
STUN_REMOTE_ADDRESS=0x0012, /* TURN-04 */
STUN_DATA=0x0013, /* TURN-04 */
STUN_REALM=0x0014, /* RFC3489bis-11 */
STUN_NONCE=0x0015, /* RFC3489bis-11 */
STUN_RELAY_ADDRESS=0x0016, /* TURN-04 */
STUN_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */
STUN_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */
STUN_REQUESTED_TRANSPORT=0x0019, /* TURN-04 */
/* 0x001A */ /* reserved */
/* 0x001B */ /* reserved */
/* 0x001C */ /* reserved */
/* 0x001D */ /* reserved */
/* 0x001E */ /* reserved */
/* 0x001F */ /* reserved */
STUN_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-11 */
STUN_TIMER_VAL=0x0021, /* TURN-04 */
STUN_REQUESTED_IP=0x0022, /* TURN-04 */
STUN_CONNECT_STAT=0x0023, /* TURN-04 */
STUN_PRIORITY=0x0024, /* ICE-18 */
STUN_USE_CANDIDATE=0x0025, /* ICE-18 */
/* 0x0026 */ /* reserved */
/* 0x0027 */ /* reserved */
/* 0x0028 */ /* reserved */
STUN_XOR_INTERNAL_ADDRESS=0x0029, /* wing-nat-control-04 */
/* 0x002A-0x7fff */ /* reserved */
/* Optional attributes */
/* 0x8000-0x8021 */ /* reserved */
STUN_SERVER=0x8022, /* RFC3489bis-11 */
STUN_ALTERNATE_SERVER=0x8023, /* RFC3489bis-11 */
STUN_REFRESH_INTERVAL=0x8024, /* wing-nat-control-04 */
/* 0x8025 */ /* reserved */
/* 0x8026 */ /* reserved */
/* 0x8027 */ /* reserved */
STUN_FINGERPRINT=0x8028, /* RFC3489bis-11 */
STUN_ICE_CONTROLLED=0x8029, /* ICE-18 */
STUN_ICE_CONTROLLING=0x802A, /* ICE-18 */
/* 0x802B-0xFFFF */ /* reserved */
} stun_attr_type_t;
typedef uint8_t stun_transid_t[12];
/**
* STUN error codes
* Should be in sync with stun_strerror()
*/
typedef enum
{
STUN_TRY_ALTERNATE=300, /* RFC3489bis-11 */
STUN_BAD_REQUEST=400, /* RFC3489bis-11 */
STUN_UNAUTHORIZED=401, /* RFC3489bis-11 */
STUN_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-11 */
STUN_NO_BINDING=437, /* TURN-04 */
STUN_STALE_NONCE=438, /* RFC3489bis-11 */
STUN_ACT_DST_ALREADY=439, /* TURN-04 */
STUN_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */
STUN_UNSUPP_TRANSPORT=442, /* TURN-04 */
STUN_INVALID_IP=443, /* TURN-04 */
STUN_INVALID_PORT=444, /* TURN-04 */
STUN_OP_TCP_ONLY=445, /* TURN-04 */
STUN_CONN_ALREADY=446, /* TURN-04 */
STUN_ALLOC_OVER_QUOTA=486, /* TURN-04 */
STUN_ROLE_CONFLICT=487, /* ICE-18 */
STUN_SERVER_ERROR=500, /* RFC3489bis-11 */
STUN_SERVER_CAPACITY=507, /* TURN-04 */
STUN_ERROR_MAX=699
} stun_error_t;
#include "utils.h" #include "utils.h"
#include "stun3489bis.h" #include "stun3489bis.h"
#include "stunhmac.h"
# ifndef NDEBUG # ifndef NDEBUG
# include <stdio.h> # include <stdio.h>
...@@ -204,31 +78,6 @@ extern "C" { ...@@ -204,31 +78,6 @@ extern "C" {
# endif # endif
/**
* Computes the MESSAGE-INTEGRITY hash of a STUN message.
* @param msg pointer to the STUN message
* @param len size of the message from header (inclusive) and up to
* MESSAGE-INTEGRITY attribute (inclusive)
* @param sha output buffer for SHA1 hash (20 bytes)
* @param key HMAC key
* @param keylen HMAC key bytes length
*
* @return fingerprint value in <b>host</b> byte order.
*/
void stun_sha1 (const uint8_t *msg, size_t len,
uint8_t *sha, const void *key, size_t keylen);
/**
* SIP H(A1) computation
*/
void stun_hash_creds (const char *realm, const char *login, const char *pw,
unsigned char md5[16]);
/**
* Generates a pseudo-random secure STUN transaction ID.
*/
void stun_make_transid (stun_transid_t id);
int stun_xor_address (const uint8_t *msg, int stun_xor_address (const uint8_t *msg,
struct sockaddr *addr, socklen_t addrlen); struct sockaddr *addr, socklen_t addrlen);
...@@ -280,102 +129,6 @@ int stun_verify_key (const uint8_t *msg, const void *key, size_t keylen); ...@@ -280,102 +129,6 @@ int stun_verify_key (const uint8_t *msg, const void *key, size_t keylen);
int stun_verify_password (const uint8_t *msg, const char *pw); int stun_verify_password (const uint8_t *msg, const char *pw);
int stun_verify_username (const uint8_t *msg, const char *local_ufrag, uint32_t compat); int stun_verify_username (const uint8_t *msg, const char *local_ufrag, uint32_t compat);
/**
* 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.
*/
const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen);
/**
* Looks for a flag attribute within a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @return 0 if flag is present, ENOENT if it is not, EINVAL if flag payload
* size is not zero.
*/
int stun_find_flag (const uint8_t *msg, stun_attr_type_t type);
/**
* Extracts a 32-bits attribute from a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param pval [OUT] where to store the host byte ordered value
*
* @return 0 on success, ENOENT if attribute not found,
* EINVAL if attribute payload was not 32-bits.
* In case of error, @a *pval is not modified.
*/
int stun_find32 (const uint8_t *msg, stun_attr_type_t type, uint32_t *pval);
/**
* Extracts a 64-bits attribute from a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param pval [OUT] where to store the host byte ordered value
* @return 0 on success, ENOENT if attribute not found,
* EINVAL if attribute payload was not 64-bits.
* In case of error, @a *pval is not modified.
*/
int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval);
/**
* Extracts an UTF-8 string from a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param buf buffer to store the extracted string
* @param maxcp maximum number of code points allowed
* (@a buf should be (6*maxcp+1) bytes long)
*
* @return 0 on success, ENOENT if attribute not found, EINVAL if attribute
* improperly encoded, ENOBUFS if the buffer size was too small.
*
* @note A nul-byte is appended at the end.
*/
int stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t buflen);
# define STUN_MAX_STR (763u)
# define STUN_MAX_CP (127u)
/**
* Extracts a network address attribute from a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param addr [OUT] where to store the socket address
* @param addrlen [IN/OUT] pointer to the size of the socket address
* buffer upon entry, set to the length of the extracted socket
* address upon return,
* @return 0 on success, ENOENT if attribute not found,
* EINVAL if attribute payload size was wrong or addrlen too small,
* EAFNOSUPPORT if address family is unknown.
*/
int stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr,
socklen_t *restrict addrlen);
/**
* Extracts an obfuscated network address attribute from a valid STUN message.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param addr [OUT] where to store the socket address
* @param addrlen [IN/OUT] pointer to the size of the socket address
* buffer upon entry, set to the length of the extracted socket
* address upon return,
* @return 0 on success, ENOENT if attribute not found,
* EINVAL if attribute payload size was wrong or addrlen too small,
* EAFNOSUPPORT if address family is unknown.
*/
int stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr,
socklen_t *restrict addrlen);
/** /**
* Compares the length and content of an attribute. * Compares the length and content of an attribute.
* *
...@@ -515,85 +268,6 @@ size_t stun_finish_short (uint8_t *msg, size_t *restrict plen, ...@@ -515,85 +268,6 @@ size_t stun_finish_short (uint8_t *msg, size_t *restrict plen,
size_t stun_finish (uint8_t *restrict msg, size_t *restrict plen, int compat); size_t stun_finish (uint8_t *restrict msg, size_t *restrict plen, int compat);
void *stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type,
size_t length);
int stun_append_bytes (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const void *data, size_t len);
/**
* Appends an empty ("flag") attribute to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type);
/**
* Appends an attribute consisting of a 32-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append32 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint32_t value);
/**
* Appends an attribute consisting of a 64-bits value to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param value payload (host byte order)
* @return 0 on success, ENOBUFS on error.
*/
int stun_append64 (uint8_t *msg, size_t msize,
stun_attr_type_t type, uint64_t value);
/**
* Appends an attribute from a nul-terminated string.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param str nul-terminated string
* @return 0 on success, ENOBUFS on error.
*/
int stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str);
/**
* Appends an attribute consisting of a network address to a STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int stun_append_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
socklen_t addrlen);
/**
* Appends an attribute consisting of a xor'ed network address.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribyte type (host byte order)
* @param addr socket address to convert into an attribute
* @param addrlen byte length of socket address
* @return 0 on success, ENOBUFS if the message buffer overflowed,
* EAFNOSUPPORT is the socket address family is not supported,
* EINVAL if the socket address length is too small w.r.t. the address family.
*/
int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
socklen_t addrlen);
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif # endif
......
...@@ -35,19 +35,24 @@ ...@@ -35,19 +35,24 @@
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> /* htons() */ #include <netinet/in.h> /* htons() */
#include <assert.h> #include <string.h>
#include <stdlib.h>
#include "crc32.h" #include "stuncrc32.h"
#include "stun-msg.h" #include "stunmessage.h"
uint32_t stun_fingerprint (const uint8_t *msg, size_t len) uint32_t stun_fingerprint (const uint8_t *msg, size_t len)
{ {
struct iovec iov[3]; struct iovec iov[3];
uint16_t fakelen = htons (len - 20u); uint16_t fakelen = htons (len - 20u);
assert (len >= 28u); // assert (len >= 28u);
iov[0].iov_base = (void *)msg; iov[0].iov_base = (void *)msg;
iov[0].iov_len = 2; iov[0].iov_len = 2;
...@@ -57,5 +62,22 @@ uint32_t stun_fingerprint (const uint8_t *msg, size_t len) ...@@ -57,5 +62,22 @@ uint32_t stun_fingerprint (const uint8_t *msg, size_t len)
/* first 4 bytes done, last 8 bytes not summed */ /* first 4 bytes done, last 8 bytes not summed */
iov[2].iov_len = len - 12u; iov[2].iov_len = len - 12u;
return crc32 (iov, sizeof (iov) / sizeof (iov[0])) ^ 0x5354554e; return htonl (crc32 (iov, sizeof (iov) / sizeof (iov[0])) ^ 0x5354554e);
}
bool stun_has_cookie (const StunMessage *msg)
{
stun_transid_t id;
stun_message_id (msg, id);
uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
return memcmp (id, &cookie, sizeof (cookie)) == 0;
}
int stun_message_append_server (StunMessage *msg)
{
static const char server[] = PACKAGE_STRING;
// assert (strlen (server) < 128);
return stun_message_append_string (msg, STUN_ATTRIBUTE_SERVER, server);
} }
...@@ -54,8 +54,10 @@ ...@@ -54,8 +54,10 @@
*/ */
uint32_t stun_fingerprint (const uint8_t *msg, size_t len); uint32_t stun_fingerprint (const uint8_t *msg, size_t len);
bool stun_has_cookie (const StunMessage *msg);
int stun_message_append_server (StunMessage *msg);
bool stun_has_cookie (const uint8_t *msg);
#endif /* _STUN_3489BIS_H */ #endif /* _STUN_3489BIS_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 <sys/types.h>
#include <sys/socket.h>
#include "stunmessage.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type);
static unsigned stun_agent_find_unknowns (StunAgent *agent,
const StunMessage * msg, uint16_t *list, unsigned max);
void stun_agent_init (StunAgent *agent, const uint16_t *known_attributes,
StunCompatibility compatibility, uint32_t usage_flags)
{
int i;
agent->known_attributes = (uint16_t *) known_attributes;
agent->compatibility = compatibility;
agent->usage_flags = usage_flags;
for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
agent->sent_ids[i].valid = FALSE;
}
}
StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
const uint8_t *buffer, size_t buffer_len,
StunMessageIntegrityValidate validater, void * validater_data)
{
stun_transid_t msg_id;
uint32_t fpr;
uint32_t crc32;
int len;
int error_code;
uint8_t *username = NULL;
uint16_t username_len;
uint8_t *key = NULL;
size_t key_len;
uint8_t *hash;
uint8_t sha[20];
uint16_t hlen;
int sent_id_idx = -1;
len = stun_message_validate_buffer_length (buffer, buffer_len);
if (len == STUN_MESSAGE_BUFFER_INVALID) {
return STUN_VALIDATION_NOT_STUN;
} else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) {
return STUN_VALIDATION_INCOMPLETE_STUN;
} else if (len != (int) buffer_len) {
return STUN_VALIDATION_NOT_STUN;
}
msg->buffer = (uint8_t *) buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
/* TODO: reject it or not ? */
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS &&
!stun_has_cookie (msg)) {
stun_debug ("STUN demux error: no cookie!\n");
return STUN_VALIDATION_BAD_REQUEST;
}
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS &&
agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
/* Looks for FINGERPRINT */
if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) != 0) {
stun_debug ("STUN demux error: no FINGERPRINT attribute!\n");
return STUN_VALIDATION_BAD_REQUEST;
}
/* Checks FINGERPRINT */
crc32 = stun_fingerprint (msg->buffer, stun_message_length (msg) - 8);
if (fpr != crc32) {
stun_debug ("STUN demux error: bad fingerprint: 0x%08x,"
" expected: 0x%08x!\n", fpr, crc32);
return STUN_VALIDATION_BAD_REQUEST;
}
stun_debug ("STUN demux: OK!\n");
}
if (stun_message_get_class (msg) == STUN_RESPONSE ||
stun_message_get_class (msg) == STUN_ERROR) {
stun_message_id (msg, msg_id);
for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) {
if (agent->sent_ids[sent_id_idx].valid == TRUE &&
agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) &&
memcmp (msg_id, agent->sent_ids[sent_id_idx].id,
sizeof(stun_transid_t)) == 0) {
if (stun_message_get_class (msg) == STUN_ERROR &&
stun_message_find_error (msg, &error_code) != 0) {
stun_debug ("STUN demux error: STUN_ERROR with no ERROR_CODE");
return STUN_VALIDATION_BAD_REQUEST;
}
key = agent->sent_ids[sent_id_idx].key;
key_len = agent->sent_ids[sent_id_idx].key_len;
break;
}
}
if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
return STUN_VALIDATION_UNMATCHED_RESPONSE;
}
}
if ((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS &&
(!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
!stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) ||
(agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS &&
stun_message_get_class (msg) != STUN_INDICATION &&
(!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
!stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) ||
!stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) ||
!stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) ||
((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 &&
stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) &&
!stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) {
return STUN_VALIDATION_UNAUTHORIZED;
}
if ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 &&
stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME)) {
username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME,
&username_len);
if (username) {
if (key == NULL) {
if (validater (agent, msg, username, username_len,
&key, &key_len, validater_data) == FALSE) {
return STUN_VALIDATION_UNAUTHORIZED;
}
}
}
}
if (key != NULL && key_len > 0) {
hash = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY,
&hlen);
stun_sha1 (msg->buffer, stun_message_length (msg), sha, key, key_len);
stun_debug (" Message HMAC-SHA1 fingerprint:");
stun_debug ("\nkey : ");
stun_debug_bytes (key, key_len);
stun_debug ("\n expected: ");
stun_debug_bytes (sha, sizeof (sha));
stun_debug ("\n received: ");
stun_debug_bytes (hash, sizeof (sha));
stun_debug ("\n");
if (memcmp (sha, hash, sizeof (sha))) {
stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n");
return STUN_VALIDATION_UNAUTHORIZED;
}
stun_debug ("STUN auth: OK!\n");
}
if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
agent->sent_ids[sent_id_idx].valid = FALSE;
}
return STUN_VALIDATION_SUCCESS;
}
bool stun_agent_init_request (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m)
{
bool ret;
stun_transid_t id;
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
stun_make_transid (id);
ret = stun_message_init (msg, STUN_REQUEST, m, id);
if (ret) {
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS) {
uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
}
}
return ret;
}
bool stun_agent_init_indication (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m)
{
bool ret;
stun_transid_t id;
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
stun_make_transid (id);
ret = stun_message_init (msg, STUN_INDICATION, m, id);
if (ret) {
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS) {
uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
}
}
return ret;
}
bool stun_agent_init_response (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m, StunMessage *request)
{
stun_transid_t id;
if (stun_message_get_class (request) != STUN_REQUEST) {
return FALSE;
}
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
stun_message_id (request, id);
if (stun_message_init (msg, STUN_RESPONSE,
stun_message_get_method (request), id)) {
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS &&
agent->usage_flags & STUN_AGENT_USAGE_ADD_SERVER) {
stun_message_append_server (msg);
}
return TRUE;
}
return FALSE;
}
bool stun_agent_init_error (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, StunMessage *request,
stun_error_t err)
{
stun_transid_t id;
if (stun_message_get_class (request) != STUN_REQUEST) {
return FALSE;
}
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
stun_message_id (request, id);
if (stun_message_init (msg, STUN_ERROR,
stun_message_get_method (request), id)) {
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS &&
agent->usage_flags & STUN_AGENT_USAGE_ADD_SERVER) {
stun_message_append_server (msg);
}
if (stun_message_append_error (msg, err) == 0) {
return TRUE;
}
}
return FALSE;
}
size_t stun_agent_build_unknown_attributes_error (StunAgent *agent,
StunMessage *msg, uint8_t *buffer, size_t buffer_len, StunMessage *request)
{
unsigned counter;
uint16_t ids[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES];
counter = stun_agent_find_unknowns (agent, request,
ids, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES);
if (stun_agent_init_error (agent, msg, buffer, buffer_len,
request, STUN_ERROR_UNKNOWN_ATTRIBUTE) == FALSE) {
return 0;
}
/* NOTE: Old RFC3489 compatibility:
* When counter is odd, duplicate one value for 32-bits padding. */
if (!stun_has_cookie (request) && (counter & 1))
ids[counter++] = ids[0];
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
ids, counter * 2) == 0) {
return stun_agent_finish_message (agent, msg, request->key, request->key_len);
}
return 0;
}
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
uint8_t *key, size_t key_len)
{
uint8_t *ptr;
uint32_t fpr;
int i;
stun_transid_t id;
if (key != NULL) {
ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20);
if (ptr == NULL) {
return 0;
}
stun_sha1 (msg->buffer, stun_message_length (msg), ptr, key, key_len);
stun_debug (" Message HMAC-SHA1 fingerprint:"
"\n key : ");
stun_debug_bytes (key, key_len);
stun_debug ("\n sent : ");
stun_debug_bytes (ptr, 20);
stun_debug ("\n");
}
if (agent->compatibility == STUN_COMPATIBILITY_3489BIS &&
agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4);
if (ptr == NULL) {
return 0;
}
fpr = stun_fingerprint (msg->buffer, stun_message_length (msg));
memcpy (ptr, &fpr, sizeof (fpr));
}
if (stun_message_get_class (msg) == STUN_REQUEST) {
for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
if (agent->sent_ids[i].valid == FALSE) {
stun_message_id (msg, id);
memcpy (agent->sent_ids[i].id, id, sizeof(stun_transid_t));
agent->sent_ids[i].method = stun_message_get_method (msg);
agent->sent_ids[i].key = key;
agent->sent_ids[i].key_len = key_len;
agent->sent_ids[i].valid = TRUE;
return TRUE;
}
}
}
msg->key = key;
msg->key_len = key_len;
return stun_message_length (msg);
}
static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type)
{
uint16_t *known_attr = agent->known_attributes;
while(*known_attr != 0) {
if (*known_attr == type) {
return FALSE;
}
known_attr++;
}
return TRUE;
}
static unsigned
stun_agent_find_unknowns (StunAgent *agent, const StunMessage * msg,
uint16_t *list, unsigned max)
{
unsigned count = 0;
uint16_t len = stun_message_length (msg);
size_t offset = 0;
offset = STUN_MESSAGE_ATTRIBUTES_POS;
while ((offset < len) && (count < max))
{
size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
uint16_t atype = stun_getw (msg->buffer + offset);
offset += STUN_ATTRIBUTE_VALUE_POS + stun_align (alen);
if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype))
{
stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)\n",
(unsigned)atype, (unsigned)alen);
list[count++] = htons (atype);
}
}
stun_debug ("STUN unknown: %u mandatory attribute(s)!\n", count);
return count;
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2008 Collabora Ltd.
* (C) 2008 Nokia Corporation. All rights reserved.
* Contact: Youness Alaoui
*
* 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.
*
*
* 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_AGENT_H
#define _STUN_AGENT_H
#include <stdint.h>
#include <sys/types.h>
#include <stdbool.h>
typedef struct stun_agent_t StunAgent;
#include "stunmessage.h"
typedef enum {
STUN_COMPATIBILITY_RFC3489,
STUN_COMPATIBILITY_3489BIS,
STUN_COMPATIBILITY_LAST = STUN_COMPATIBILITY_3489BIS
} StunCompatibility;
typedef enum {
STUN_VALIDATION_SUCCESS,
STUN_VALIDATION_NOT_STUN,
STUN_VALIDATION_INCOMPLETE_STUN,
STUN_VALIDATION_BAD_REQUEST,
STUN_VALIDATION_UNAUTHORIZED,
STUN_VALIDATION_UNMATCHED_RESPONSE,
STUN_VALIDATION_UNKNOWN_ATTRIBUTE,
STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
} StunValidationStatus;
#define STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS 0x0001
#define STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS 0x0002
#define STUN_AGENT_USAGE_USE_FINGERPRINT 0x0004
#define STUN_AGENT_USAGE_ADD_SERVER 0x0008
#define STUN_AGENT_USAGE_IGNORE_CREDENTIALS 0x000F
typedef struct {
stun_transid_t id;
stun_method_t method;
uint8_t *key;
size_t key_len;
bool valid;
} StunAgentSavedIds;
struct stun_agent_t {
StunCompatibility compatibility;
StunAgentSavedIds sent_ids[STUN_AGENT_MAX_SAVED_IDS];
uint16_t *known_attributes;
uint32_t usage_flags;
};
typedef bool (*StunMessageIntegrityValidate) (StunAgent *agent,
StunMessage *message, uint8_t *username, uint16_t username_len,
uint8_t **password, size_t *password_len, void *user_data);
void stun_agent_init (StunAgent *agent, const uint16_t *known_attributes,
StunCompatibility compatibility, uint32_t usage_flags);
StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
const uint8_t *buffer, size_t buffer_len,
StunMessageIntegrityValidate validater, void * validater_data);
bool stun_agent_init_request (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m);
bool stun_agent_init_indication (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m);
bool stun_agent_init_response (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, stun_method_t m, StunMessage *request);
bool stun_agent_init_error (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, StunMessage *request,
stun_error_t err);
size_t stun_agent_build_unknown_attributes_error (StunAgent *agent,
StunMessage *msg, uint8_t *buffer, size_t buffer_len, StunMessage *request);
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
uint8_t *key, size_t key_len);
#endif /* _STUN_AGENT_H */
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
# include <config.h> # include <config.h>
#endif #endif
#include "crc32.h" #include "stuncrc32.h"
static const uint32_t crc32_tab[] = { static const uint32_t crc32_tab[] = {
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <pthread.h> #include <pthread.h>
#include "stun-msg.h" #include "stunmessage.h"
#include "stunhmac.h" #include "stunhmac.h"
#include <string.h> #include <string.h>
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#ifndef _STUN_HMAC_H #ifndef _STUN_HMAC_H
#define _STUN_HMAC_H #define _STUN_HMAC_H
#include "stunmessage.h"
/** /**
* Computes the MESSAGE-INTEGRITY hash of a STUN message. * Computes the MESSAGE-INTEGRITY hash of a STUN message.
* @param msg pointer to the STUN message * @param msg pointer to the STUN message
...@@ -61,4 +63,4 @@ void stun_hash_creds (const char *realm, const char *login, const char *pw, ...@@ -61,4 +63,4 @@ void stun_hash_creds (const char *realm, const char *login, const char *pw,
void stun_make_transid (stun_transid_t id); void stun_make_transid (stun_transid_t id);
/* _STUN_HMAC_H */ #endif /* _STUN_HMAC_H */
...@@ -61,12 +61,8 @@ bool stun_message_init (StunMessage *msg, stun_class_t c, stun_method_t m, ...@@ -61,12 +61,8 @@ bool stun_message_init (StunMessage *msg, stun_class_t c, stun_method_t m,
memset (msg->buffer, 0, 4); memset (msg->buffer, 0, 4);
stun_set_type (msg->buffer, c, m); stun_set_type (msg->buffer, c, m);
if (msg->buffer != id) memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS,
{ id, sizeof (STUN_MESSAGE_TRANS_ID_LEN));
uint32_t cookie = htonl (STUN_COOKIE);
memcpy (msg->buffer + 4, &cookie, sizeof (cookie));
memcpy (msg->buffer + 8, id, 12);
}
return TRUE; return TRUE;
} }
...@@ -107,12 +103,12 @@ stun_message_find (const StunMessage *msg, stun_attr_type_t type, ...@@ -107,12 +103,12 @@ stun_message_find (const StunMessage *msg, stun_attr_type_t type,
/* Look for and ignore misordered attributes */ /* Look for and ignore misordered attributes */
switch (atype) switch (atype)
{ {
case STUN_MESSAGE_INTEGRITY: case STUN_ATTRIBUTE_MESSAGE_INTEGRITY:
/* Only fingerprint may come after M-I */ /* Only fingerprint may come after M-I */
if (type == STUN_FINGERPRINT) if (type == STUN_ATTRIBUTE_FINGERPRINT)
break; break;
case STUN_FINGERPRINT: case STUN_ATTRIBUTE_FINGERPRINT:
/* Nothing may come after FPR */ /* Nothing may come after FPR */
return NULL; return NULL;
} }
...@@ -273,10 +269,10 @@ stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type, ...@@ -273,10 +269,10 @@ stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type,
return stun_xor_address (msg, addr, *addrlen); return stun_xor_address (msg, addr, *addrlen);
} }
int stun_message_find_errno (const StunMessage *msg, int *restrict code) int stun_message_find_error (const StunMessage *msg, int *restrict code)
{ {
uint16_t alen; uint16_t alen;
const uint8_t *ptr = stun_message_find (msg, STUN_ERROR_CODE, &alen); const uint8_t *ptr = stun_message_find (msg, STUN_ATTRIBUTE_ERROR_CODE, &alen);
uint8_t class, number; uint8_t class, number;
if (ptr == NULL) if (ptr == NULL)
...@@ -319,7 +315,7 @@ stun_message_append (StunMessage *msg, stun_attr_type_t type, size_t length) ...@@ -319,7 +315,7 @@ stun_message_append (StunMessage *msg, stun_attr_type_t type, size_t length)
a = stun_setw (a, type); a = stun_setw (a, type);
/* NOTE: If cookie is not present, we need to force the attribute length /* NOTE: If cookie is not present, we need to force the attribute length
* to a multiple of 4 for compatibility with old RFC3489 */ * to a multiple of 4 for compatibility with old RFC3489 */
a = stun_setw (a, stun_has_cookie (msg->buffer) ? length : stun_align (length)); 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 */
...@@ -473,7 +469,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code) ...@@ -473,7 +469,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code)
size_t len = strlen (str); size_t len = strlen (str);
div_t d = div (code, 100); div_t d = div (code, 100);
uint8_t *ptr = stun_message_append (msg, STUN_ERROR_CODE, 4 + len); uint8_t *ptr = stun_message_append (msg, STUN_ATTRIBUTE_ERROR_CODE, 4 + len);
if (ptr == NULL) if (ptr == NULL)
return ENOBUFS; return ENOBUFS;
...@@ -484,7 +480,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code) ...@@ -484,7 +480,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code)
return 0; return 0;
} }
bool stun_message_is_valid (const uint8_t *msg, size_t length) int stun_message_validate_buffer_length (const uint8_t *msg, size_t length)
{ {
size_t mlen; size_t mlen;
size_t len; size_t len;
...@@ -492,19 +488,19 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length) ...@@ -492,19 +488,19 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length)
if (length < 1) if (length < 1)
{ {
stun_debug ("STUN error: No data!\n"); stun_debug ("STUN error: No data!\n");
return FALSE; return STUN_MESSAGE_BUFFER_INVALID;
} }
if (msg[0] >> 6) if (msg[0] >> 6)
{ {
stun_debug ("STUN error: RTP or other non-protocol packet!\n"); stun_debug ("STUN error: RTP or other non-protocol packet!\n");
return FALSE; // RTP or other non-STUN packet return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
} }
if (length < 4) if (length < 4)
{ {
stun_debug ("STUN error: Incomplete STUN message header!\n"); stun_debug ("STUN error: Incomplete STUN message header!\n");
return FALSE; return STUN_MESSAGE_BUFFER_INCOMPLETE;
} }
mlen = stun_getw (msg + STUN_MESSAGE_LENGTH_POS) + mlen = stun_getw (msg + STUN_MESSAGE_LENGTH_POS) +
...@@ -513,14 +509,14 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length) ...@@ -513,14 +509,14 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length)
if (stun_padding (mlen)) if (stun_padding (mlen))
{ {
stun_debug ("STUN error: Invalid message length: %u!\n", (unsigned)mlen); stun_debug ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
return FALSE; // wrong padding return STUN_MESSAGE_BUFFER_INVALID; // wrong padding
} }
if (length < mlen) if (length < mlen)
{ {
stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n", stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n",
(unsigned)length, (unsigned)mlen); (unsigned)length, (unsigned)mlen);
return FALSE; // partial message return STUN_MESSAGE_BUFFER_INCOMPLETE; // partial message
} }
msg += 20; msg += 20;
...@@ -539,12 +535,54 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length) ...@@ -539,12 +535,54 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length)
{ {
stun_debug ("STUN error: %u instead of %u bytes for attribute!\n", stun_debug ("STUN error: %u instead of %u bytes for attribute!\n",
(unsigned)len, (unsigned)alen); (unsigned)len, (unsigned)alen);
return -1; // no room for attribute value + padding return STUN_MESSAGE_BUFFER_INVALID; // no room for attribute value + padding
} }
len -= alen; len -= alen;
msg += 4 + alen; msg += 4 + alen;
} }
return mlen == length; return mlen;
}
/**
* copies STUN message transaction ID
*/
void stun_message_id (const StunMessage *msg, stun_transid_t id)
{
memcpy (id, msg->buffer + STUN_MESSAGE_TRANS_ID_POS, STUN_MESSAGE_TRANS_ID_LEN);
}
/**
* @return STUN message method (value from 0 to 0xfff)
*/
stun_method_t stun_message_get_method (const StunMessage *msg)
{
uint16_t t = stun_getw (msg->buffer);
return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
(t & 0x000f));
}
/**
* @return STUN message class in host byte order (value from 0 to 3)
*/
stun_class_t stun_message_get_class (const StunMessage *msg)
{
uint16_t t = stun_getw (msg->buffer);
return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
}
/**
* Checks if an attribute is present within a STUN message.
*
* @param msg valid STUN message
* @param type STUN attribute type (host byte order)
*
* @return whether there is a MESSAGE-INTEGRITY attribute
*/
bool stun_message_has_attribute (const StunMessage *msg, stun_attr_type_t type)
{
uint16_t dummy;
return stun_message_find (msg, type, &dummy) != NULL;
} }
...@@ -41,12 +41,7 @@ ...@@ -41,12 +41,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "constants.h" #include "constants.h"
typedef struct { typedef struct stun_message_t StunMessage;
uint8_t *buffer;
size_t buffer_len;
bool message_id_validated;
} StunMessage;
/* Message classes */ /* Message classes */
typedef enum typedef enum
...@@ -78,65 +73,65 @@ typedef enum ...@@ -78,65 +73,65 @@ typedef enum
{ {
/* Mandatory attributes */ /* Mandatory attributes */
/* 0x0000 */ /* reserved */ /* 0x0000 */ /* reserved */
STUN_MAPPED_ADDRESS=0x0001, /* RFC3489bis-11 */ STUN_ATTRIBUTE_MAPPED_ADDRESS=0x0001, /* RFC3489bis-11 */
STUN_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */
STUN_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */
STUN_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */
STUN_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */
STUN_USERNAME=0x0006, /* RFC3489bis-11 */ STUN_ATTRIBUTE_USERNAME=0x0006, /* RFC3489bis-11 */
STUN_OLD_PASSWORD=0x0007, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_PASSWORD=0x0007, /* old RFC3489 */
STUN_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-11 */ STUN_ATTRIBUTE_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-11 */
STUN_ERROR_CODE=0x0009, /* RFC3489bis-11 */ STUN_ATTRIBUTE_ERROR_CODE=0x0009, /* RFC3489bis-11 */
STUN_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-11 */ STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-11 */
STUN_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */ STUN_ATTRIBUTE_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */
/* 0x000C */ /* reserved */ /* 0x000C */ /* reserved */
STUN_LIFETIME=0x000D, /* TURN-04 */ STUN_ATTRIBUTE_LIFETIME=0x000D, /* TURN-04 */
/* 0x000E */ /* reserved */ /* 0x000E */ /* reserved */
/* 0x000F */ /* reserved */ /* 0x000F */ /* reserved */
STUN_BANDWIDTH=0x0010, /* TURN-04 */ STUN_ATTRIBUTE_BANDWIDTH=0x0010, /* TURN-04 */
/* 0x0011 */ /* reserved */ /* 0x0011 */ /* reserved */
STUN_REMOTE_ADDRESS=0x0012, /* TURN-04 */ STUN_ATTRIBUTE_REMOTE_ADDRESS=0x0012, /* TURN-04 */
STUN_DATA=0x0013, /* TURN-04 */ STUN_ATTRIBUTE_DATA=0x0013, /* TURN-04 */
STUN_REALM=0x0014, /* RFC3489bis-11 */ STUN_ATTRIBUTE_REALM=0x0014, /* RFC3489bis-11 */
STUN_NONCE=0x0015, /* RFC3489bis-11 */ STUN_ATTRIBUTE_NONCE=0x0015, /* RFC3489bis-11 */
STUN_RELAY_ADDRESS=0x0016, /* TURN-04 */ STUN_ATTRIBUTE_RELAY_ADDRESS=0x0016, /* TURN-04 */
STUN_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */ STUN_ATTRIBUTE_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */
STUN_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */ STUN_ATTRIBUTE_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */
STUN_REQUESTED_TRANSPORT=0x0019, /* TURN-04 */ STUN_ATTRIBUTE_REQUESTED_TRANSPORT=0x0019, /* TURN-04 */
/* 0x001A */ /* reserved */ /* 0x001A */ /* reserved */
/* 0x001B */ /* reserved */ /* 0x001B */ /* reserved */
/* 0x001C */ /* reserved */ /* 0x001C */ /* reserved */
/* 0x001D */ /* reserved */ /* 0x001D */ /* reserved */
/* 0x001E */ /* reserved */ /* 0x001E */ /* reserved */
/* 0x001F */ /* reserved */ /* 0x001F */ /* reserved */
STUN_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-11 */ STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-11 */
STUN_TIMER_VAL=0x0021, /* TURN-04 */ STUN_ATTRIBUTE_TIMER_VAL=0x0021, /* TURN-04 */
STUN_REQUESTED_IP=0x0022, /* TURN-04 */ STUN_ATTRIBUTE_REQUESTED_IP=0x0022, /* TURN-04 */
STUN_CONNECT_STAT=0x0023, /* TURN-04 */ STUN_ATTRIBUTE_CONNECT_STAT=0x0023, /* TURN-04 */
STUN_PRIORITY=0x0024, /* ICE-18 */ STUN_ATTRIBUTE_PRIORITY=0x0024, /* ICE-18 */
STUN_USE_CANDIDATE=0x0025, /* ICE-18 */ STUN_ATTRIBUTE_USE_CANDIDATE=0x0025, /* ICE-18 */
/* 0x0026 */ /* reserved */ /* 0x0026 */ /* reserved */
/* 0x0027 */ /* reserved */ /* 0x0027 */ /* reserved */
/* 0x0028 */ /* reserved */ /* 0x0028 */ /* reserved */
STUN_XOR_INTERNAL_ADDRESS=0x0029, /* wing-nat-control-04 */ STUN_ATTRIBUTE_XOR_INTERNAL_ADDRESS=0x0029, /* wing-nat-control-04 */
/* 0x002A-0x7fff */ /* reserved */ /* 0x002A-0x7fff */ /* reserved */
/* Optional attributes */ /* Optional attributes */
/* 0x8000-0x8021 */ /* reserved */ /* 0x8000-0x8021 */ /* reserved */
STUN_SERVER=0x8022, /* RFC3489bis-11 */ STUN_ATTRIBUTE_SERVER=0x8022, /* RFC3489bis-11 */
STUN_ALTERNATE_SERVER=0x8023, /* RFC3489bis-11 */ STUN_ATTRIBUTE_ALTERNATE_SERVER=0x8023, /* RFC3489bis-11 */
STUN_REFRESH_INTERVAL=0x8024, /* wing-nat-control-04 */ STUN_ATTRIBUTE_REFRESH_INTERVAL=0x8024, /* wing-nat-control-04 */
/* 0x8025 */ /* reserved */ /* 0x8025 */ /* reserved */
/* 0x8026 */ /* reserved */ /* 0x8026 */ /* reserved */
/* 0x8027 */ /* reserved */ /* 0x8027 */ /* reserved */
STUN_FINGERPRINT=0x8028, /* RFC3489bis-11 */ STUN_ATTRIBUTE_FINGERPRINT=0x8028, /* RFC3489bis-11 */
STUN_ICE_CONTROLLED=0x8029, /* ICE-18 */ STUN_ATTRIBUTE_ICE_CONTROLLED=0x8029, /* ICE-18 */
STUN_ICE_CONTROLLING=0x802A, /* ICE-18 */ STUN_ATTRIBUTE_ICE_CONTROLLING=0x802A, /* ICE-18 */
/* 0x802B-0xFFFF */ /* reserved */ /* 0x802B-0xFFFF */ /* reserved */
} stun_attr_type_t; } stun_attr_type_t;
typedef uint8_t stun_transid_t[12]; typedef uint8_t stun_transid_t[STUN_MESSAGE_TRANS_ID_LEN];
/** /**
...@@ -145,32 +140,41 @@ typedef uint8_t stun_transid_t[12]; ...@@ -145,32 +140,41 @@ typedef uint8_t stun_transid_t[12];
*/ */
typedef enum typedef enum
{ {
STUN_TRY_ALTERNATE=300, /* RFC3489bis-11 */ STUN_ERROR_TRY_ALTERNATE=300, /* RFC3489bis-11 */
STUN_BAD_REQUEST=400, /* RFC3489bis-11 */ STUN_ERROR_BAD_REQUEST=400, /* RFC3489bis-11 */
STUN_UNAUTHORIZED=401, /* RFC3489bis-11 */ STUN_ERROR_UNAUTHORIZED=401, /* RFC3489bis-11 */
STUN_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-11 */ STUN_ERROR_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-11 */
STUN_NO_BINDING=437, /* TURN-04 */ STUN_ERROR_NO_BINDING=437, /* TURN-04 */
STUN_STALE_NONCE=438, /* RFC3489bis-11 */ STUN_ERROR_STALE_NONCE=438, /* RFC3489bis-11 */
STUN_ACT_DST_ALREADY=439, /* TURN-04 */ STUN_ERROR_ACT_DST_ALREADY=439, /* TURN-04 */
STUN_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */ STUN_ERROR_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */
STUN_UNSUPP_TRANSPORT=442, /* TURN-04 */ STUN_ERROR_UNSUPP_TRANSPORT=442, /* TURN-04 */
STUN_INVALID_IP=443, /* TURN-04 */ STUN_ERROR_INVALID_IP=443, /* TURN-04 */
STUN_INVALID_PORT=444, /* TURN-04 */ STUN_ERROR_INVALID_PORT=444, /* TURN-04 */
STUN_OP_TCP_ONLY=445, /* TURN-04 */ STUN_ERROR_OP_TCP_ONLY=445, /* TURN-04 */
STUN_CONN_ALREADY=446, /* TURN-04 */ STUN_ERROR_CONN_ALREADY=446, /* TURN-04 */
STUN_ALLOC_OVER_QUOTA=486, /* TURN-04 */ STUN_ERROR_ALLOC_OVER_QUOTA=486, /* TURN-04 */
STUN_ROLE_CONFLICT=487, /* ICE-18 */ STUN_ERROR_ROLE_CONFLICT=487, /* ICE-18 */
STUN_SERVER_ERROR=500, /* RFC3489bis-11 */ STUN_ERROR_SERVER_ERROR=500, /* RFC3489bis-11 */
STUN_SERVER_CAPACITY=507, /* TURN-04 */ STUN_ERROR_SERVER_CAPACITY=507, /* TURN-04 */
STUN_ERROR_MAX=699 STUN_ERROR_MAX=699
} stun_error_t; } stun_error_t;
#include "stunagent.h"
#include "stunhmac.h" #include "stunhmac.h"
#include "stuncrc32.h" #include "stuncrc32.h"
#include "utils.h" #include "utils.h"
#include "stun3489bis.h" #include "stun3489bis.h"
struct stun_message_t {
StunAgent *agent;
uint8_t *buffer;
size_t buffer_len;
uint8_t *key;
size_t key_len;
};
/** /**
* 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)
...@@ -281,7 +285,7 @@ int stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type, ...@@ -281,7 +285,7 @@ int stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type,
struct sockaddr *restrict addr, socklen_t *restrict addrlen); struct sockaddr *restrict addr, socklen_t *restrict addrlen);
int stun_message_find_errno (const StunMessage *msg, int *restrict code); int stun_message_find_error (const StunMessage *msg, int *restrict code);
void *stun_message_append (StunMessage *msg, stun_attr_type_t type, void *stun_message_append (StunMessage *msg, stun_attr_type_t type,
size_t length); size_t length);
...@@ -368,7 +372,16 @@ int stun_message_append_xor_addr (StunMessage * msg, stun_attr_type_t type, ...@@ -368,7 +372,16 @@ int stun_message_append_xor_addr (StunMessage * msg, stun_attr_type_t type,
*/ */
int stun_message_append_error (StunMessage * msg, stun_error_t code); int stun_message_append_error (StunMessage * msg, stun_error_t code);
bool stun_message_is_valid (const uint8_t *msg, size_t length); #define STUN_MESSAGE_BUFFER_INCOMPLETE 0
#define STUN_MESSAGE_BUFFER_INVALID -1
int stun_message_validate_buffer_length (const uint8_t *msg, size_t length);
void stun_message_id (const StunMessage *msg, stun_transid_t id);
stun_class_t stun_message_get_class (const StunMessage *msg);
stun_method_t stun_message_get_method (const StunMessage *msg);
bool stun_message_has_attribute (const StunMessage *msg, stun_attr_type_t type);
#endif /* _STUN_MESSAGE_H */ #endif /* _STUN_MESSAGE_H */
...@@ -48,446 +48,6 @@ ...@@ -48,446 +48,6 @@
#include <errno.h> #include <errno.h>
#include <netinet/in.h> #include <netinet/in.h>
ssize_t stun_validate (const uint8_t *msg, size_t len)
{
size_t mlen;
if (len < 1)
{
DBG ("STUN error: No data!\n");
return 0;
}
if (msg[0] >> 6)
{
DBG ("STUN error: RTP or other non-protocol packet!\n");
return -1; // RTP or other non-STUN packet
}
if (len < 4)
{
DBG ("STUN error: Incomplete STUN message header!\n");
return 0;
}
mlen = 20u + stun_length (msg);
if (stun_padding (mlen))
{
DBG ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
return -1; // wrong padding
}
if (len < mlen)
{
DBG ("STUN error: Incomplete message: %u of %u bytes!\n",
(unsigned)len, (unsigned)mlen);
return 0; // partial message
}
msg += 20;
len = mlen - 20;
/* from then on, we know we have the entire packet in buffer */
while (len > 0)
{
size_t alen = stun_align (stun_length (msg));
/* thanks to padding check, if (end > msg) then there is not only one
* but at least 4 bytes left */
assert (len >= 4);
len -= 4;
if (len < alen)
{
DBG ("STUN error: %u instead of %u bytes for attribute!\n",
(unsigned)len, (unsigned)alen);
return -1; // no room for attribute value + padding
}
len -= alen;
msg += 4 + alen;
}
return mlen;
}
const void *
stun_find (const uint8_t *restrict msg, stun_attr_type_t type,
uint16_t *restrict palen)
{
size_t length = stun_length (msg);
assert (stun_valid (msg));
assert (palen != NULL);
msg += 20;
while (length > 0)
{
size_t alen = stun_length (msg);
uint16_t atype = stun_getw (msg);
assert (length >= 4);
length -= 4;
msg += 4;
assert (length >= stun_align (alen));
if (atype == type)
{
assert (alen <= 0xffff);
*palen = alen;
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);
length -= alen;
msg += alen;
}
return NULL;
}
int stun_find_flag (const uint8_t *msg, stun_attr_type_t type)
{
const void *ptr;
uint16_t len;
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
return (len == 0) ? 0 : EINVAL;
}
int
stun_find32 (const uint8_t *restrict msg, stun_attr_type_t type,
uint32_t *restrict pval)
{
const void *ptr;
uint16_t len;
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
if (len == 4)
{
uint32_t val;
memcpy (&val, ptr, sizeof (val));
*pval = ntohl (val);
return 0;
}
return EINVAL;
}
int stun_find64 (const uint8_t *msg, stun_attr_type_t type, uint64_t *pval)
{
const void *ptr;
uint16_t len;
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
if (len == 8)
{
uint32_t tab[2];
memcpy (tab, ptr, sizeof (tab));
*pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]);
return 0;
}
return EINVAL;
}
int stun_find_string (const uint8_t *restrict msg, stun_attr_type_t type,
char *buf, size_t buflen)
{
const unsigned char *ptr;
uint16_t len;
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
if (len >= buflen)
return ENOBUFS;
memcpy (buf, ptr, len);
buf[len] = '\0';
return 0;
}
int
stun_find_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr, socklen_t *restrict addrlen)
{
const uint8_t *ptr;
uint16_t len;
ptr = stun_find (msg, type, &len);
if (ptr == NULL)
return ENOENT;
if (len < 4)
return EINVAL;
assert (addrlen != NULL);
switch (ptr[1])
{
case 1:
{
struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
if ((*addrlen < sizeof (*ip4)) || (len != 8))
{
*addrlen = sizeof (*ip4);
return EINVAL;
}
memset (ip4, 0, *addrlen);
ip4->sin_family = AF_INET;
#ifdef HAVE_SA_LEN
ip4->sin_len =
#endif
*addrlen = sizeof (*ip4);
memcpy (&ip4->sin_port, ptr + 2, 2);
memcpy (&ip4->sin_addr, ptr + 4, 4);
return 0;
}
case 2:
{
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
if ((*addrlen < sizeof (*ip6)) || (len != 20))
{
*addrlen = sizeof (*ip6);
return EINVAL;
}
memset (ip6, 0, *addrlen);
ip6->sin6_family = AF_INET6;
#ifdef HAVE_SA_LEN
ip6->sin6_len =
#endif
*addrlen = sizeof (*ip6);
memcpy (&ip6->sin6_port, ptr + 2, 2);
memcpy (&ip6->sin6_addr, ptr + 4, 16);
return 0;
}
}
return EAFNOSUPPORT;
}
int stun_xor_address (const uint8_t *restrict msg,
struct sockaddr *restrict addr, socklen_t addrlen)
{
switch (addr->sa_family)
{
case AF_INET:
{
struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
if (addrlen < sizeof (*ip4))
return EINVAL;
ip4->sin_port ^= htons (STUN_COOKIE >> 16);
ip4->sin_addr.s_addr ^= htonl (STUN_COOKIE);
return 0;
}
case AF_INET6:
{
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
unsigned short i;
if (addrlen < sizeof (*ip6))
return EINVAL;
ip6->sin6_port ^= htons (STUN_COOKIE >> 16);
for (i = 0; i < 16; i++)
ip6->sin6_addr.s6_addr[i] ^= ((uint8_t *)msg)[4 + i];
return 0;
}
}
return EAFNOSUPPORT;
}
int
stun_find_xor_addr (const uint8_t *restrict msg, stun_attr_type_t type,
struct sockaddr *restrict addr,
socklen_t *restrict addrlen)
{
int val = stun_find_addr (msg, type, addr, addrlen);
if (val)
return val;
return stun_xor_address (msg, addr, *addrlen);
}
/**
* Compares the length and content of an attribute.
*
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param data pointer to value to compare with
* @param len byte length of the value
* @return 0 in case of match, ENOENT if attribute was not found,
* EINVAL if it did not match (different length, or same length but
* different content)
*/
int stun_memcmp (const uint8_t *msg, stun_attr_type_t type,
const void *data, size_t len)
{
uint16_t alen;
const void *ptr = stun_find (msg, type, &alen);
if (ptr == NULL)
return ENOENT;
if ((len != alen) || memcmp (ptr, data, len))
return EINVAL;
return 0;
}
/**
* Compares the content of an attribute with a string.
* @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param str string to compare with
* @return 0 in case of match, ENOENT if attribute was not found,
* EINVAL if it did not match
*/
int stun_strcmp (const uint8_t *msg, stun_attr_type_t type, const char *str)
{
return stun_memcmp (msg, type, str, strlen (str));
}
bool stun_has_cookie (const uint8_t *msg)
{
uint32_t cookie = htonl (STUN_COOKIE);
return memcmp (msg + 4, &cookie, sizeof (cookie)) == 0;
}
bool stun_demux (const uint8_t *msg)
{
const uint8_t *fpr;
uint32_t crc32;
uint16_t fprlen;
assert (stun_valid (msg));
/* Checks cookie */
if (!stun_has_cookie (msg))
{
DBG ("STUN demux error: no cookie!\n");
return 0;
}
/* Looks for FINGERPRINT */
fpr = stun_find (msg, STUN_FINGERPRINT, &fprlen);
if ((fpr == NULL) || (fprlen != 4))
{
DBG ("STUN demux error: no FINGERPRINT attribute!\n");
return 0;
}
/* Checks FINGERPRINT */
crc32 = htonl (stun_fingerprint (msg, fpr + 4 - msg));
if (memcmp (fpr, &crc32, 4))
{
DBG ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!\n",
(fpr[0] << 24) | (fpr[1] << 16) | (fpr[2] << 8) | fpr[3],
stun_fingerprint (msg, fpr + 4 - msg));
return 0;
}
DBG ("STUN demux: OK!\n");
return 1;
}
/**
* @param msg valid STUN message
* @param key HMAC shared secret key pointer
* @param keylen HMAC shared secret key byte length
* @return 0 if the message integrity has been successfully verified with the
* specified key. EPERM if the hash was incorrect. ENOENT if there was no
* valid MESSAGE-INTEGRITY attribute.
*/
int
stun_verify_key (const uint8_t *msg, const void *key, size_t keylen)
{
const uint8_t *hash;
uint8_t sha[20];
uint16_t hlen;
assert (msg != NULL);
assert ((keylen == 0) || (key != NULL));
hash = stun_find (msg, STUN_MESSAGE_INTEGRITY, &hlen);
if ((hash == NULL) || (hlen != 20))
{
DBG ("STUN auth error: no MESSAGE-INTEGRITY attribute!\n");
return ENOENT;
}
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)))
{
DBG ("STUN auth error: SHA1 fingerprint mismatch!\n");
return EPERM;
}
DBG ("STUN auth: OK!\n");
return 0;
}
/**
* @param msg valid STUN message
* @param pw nul-terminated HMAC shared secret password
* @return 0 if the message integrity has been successfully verified with the
* specified key. EPERM if the hash was incorrect. ENOENT if there was no
* valid MESSAGE-INTEGRITY attribute.
*/
int stun_verify_password (const uint8_t *msg, const char *pw)
{
return stun_verify_key (msg, pw, strlen (pw));
}
/** /**
* @param msg valid STUN message * @param msg valid STUN message
...@@ -529,130 +89,3 @@ int stun_verify_username (const uint8_t *msg, const char *local_ufrag, uint32_t ...@@ -529,130 +89,3 @@ int stun_verify_username (const uint8_t *msg, const char *local_ufrag, uint32_t
return 0; return 0;
} }
static
int stun_find_errno (const uint8_t *restrict msg, int *restrict code)
{
uint16_t alen;
const uint8_t *ptr = stun_find (msg, STUN_ERROR_CODE, &alen);
uint8_t class, number;
if (ptr == NULL)
return ENOENT;
if (alen < 4)
return EINVAL;
class = ptr[2] & 0x7;
number = ptr[3];
if ((class < 3) || (class > 6) || (number > 99))
return EINVAL;
*code = (class * 100) + number;
return 0;
}
/**
* @param msg valid STUN message
* @param method STUN method number (host byte order)
* @param id STUN transaction id
* @param key HMAC key, or NULL if there 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,
* to the error code ([0..799]) if it is an error response.
*
* @return true if and only if the message is a response or an error response
* with the STUN cookie and specified method and transaction identifier.
*/
static bool
stun_match_answer (const uint8_t *msg, stun_method_t method,
const uint8_t *id, const uint8_t *key, size_t keylen,
int *restrict error)
{
assert (stun_valid (msg));
assert (error != NULL);
if ((stun_get_method (msg) != method) /* wrong request type */
|| !stun_has_cookie (msg) /* response to old-style request */
|| memcmp (msg + 8, id, 12)) /* wrong transaction ID */
return false;
switch (stun_get_class (msg))
{
case STUN_REQUEST:
case STUN_INDICATION:
return false;
case STUN_RESPONSE:
*error = -1;
break;
case STUN_ERROR:
if (stun_find_errno (msg, error) != 0)
return false; // missing ERROR-CODE: ignore message
break;
}
/* If a shared secret exists, verify the message hash.
* 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;
}
bool stun_match_messages (const uint8_t *restrict resp,
const uint8_t *restrict req,
const uint8_t *key, size_t keylen,
int *restrict error)
{
assert (stun_valid (resp));
assert (stun_valid (req));
assert ((stun_get_class (req) >> 1) == 0);
return stun_match_answer (resp, stun_get_method (req),
stun_id (req), key, keylen, error);
}
unsigned
stun_find_unknown (const uint8_t *restrict msg, uint16_t *restrict list,
unsigned max)
{
unsigned count = 0;
uint16_t len = stun_length (msg);
assert (stun_valid (msg));
msg += 20;
while ((len > 0) && (count < max))
{
size_t alen = stun_align (stun_length (msg));
uint16_t atype = stun_getw (msg);
msg += 4 + alen;
assert (len >= (4 + alen));
len -= 4 + alen;
if (!stun_optional (atype) && stun_is_unknown (atype))
{
DBG ("STUN unknown: attribute 0x%04x(%u bytes)\n",
(unsigned)atype, (unsigned)alen);
list[count++] = atype;
}
}
DBG ("STUN unknown: %u mandatory attribute(s)!\n", count);
return count;
}
...@@ -50,484 +50,3 @@ ...@@ -50,484 +50,3 @@
#include <netinet/in.h> #include <netinet/in.h>
static
void *stun_setw (uint8_t *ptr, uint16_t value)
{
*ptr++ = value >> 8;
*ptr++ = value & 0xff;
return ptr;
}
static
void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
{
assert (c < 4);
assert (m < (1 << 12));
h[0] = (c >> 1) | ((m >> 6) & 0x3e);
h[1] = ((c << 4) & 0x10) | ((m << 1) & 0xe0) | (m & 0x0f);
assert (stun_getw (h) < (1 << 14));
assert (stun_get_class (h) == c);
assert (stun_get_method (h) == m);
}
/**
* Initializes a STUN message buffer, with no attributes.
* @param c STUN message class (host byte order)
* @param m STUN message method (host byte order)
* @param id 16-bytes transaction ID
*/
static void stun_init (uint8_t *msg, stun_class_t c, stun_method_t m,
const stun_transid_t id)
{
memset (msg, 0, 4);
stun_set_type (msg, c, m);
msg += 8;
if (msg != id)
{
uint32_t cookie = htonl (STUN_COOKIE);
memcpy (msg - 4, &cookie, sizeof (cookie));
memcpy (msg, id, 12);
}
}
void stun_init_request (uint8_t *req, stun_method_t m)
{
stun_transid_t id;
stun_make_transid (id);
stun_init (req, STUN_REQUEST, m, id);
}
void stun_init_indication (uint8_t *req, stun_method_t m)
{
stun_transid_t id;
stun_make_transid (id);
stun_init (req, STUN_INDICATION, m, id);
}
/**
* Reserves room for appending an attribute to an unfinished STUN message.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type message type (host byte order)
* @param length attribute payload byte length
* @return a pointer to an unitialized buffer of <length> bytes to
* where the attribute payload must be written, or NULL if there is not
* enough room in the STUN message buffer. Return value is always on a
* 32-bits boundary.
*/
void *
stun_append (uint8_t *msg, size_t msize, stun_attr_type_t type, size_t length)
{
uint8_t *a;
uint16_t mlen = stun_length (msg);
assert (stun_valid (msg));
assert (stun_padding (mlen) == 0);
if (msize > STUN_MAXMSG)
msize = STUN_MAXMSG;
if ((((size_t)mlen) + 24u + length) > msize)
return NULL;
assert (length < 0xffff);
a = msg + 20u + mlen;
a = stun_setw (a, type);
/* 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;
/* Add padding if needed */
memset (a + length, ' ', stun_padding (length));
mlen += stun_padding (length);
stun_setw (msg + 2, mlen);
return a;
}
/**
* Appends an attribute from memory.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param type attribute type (host byte order)
* @param data memory address to copy payload from
* @param len attribute payload length
* @return 0 on success, ENOBUFS on error.
*/
int
stun_append_bytes (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const void *data, size_t len)
{
void *ptr = stun_append (msg, msize, type, len);
if (ptr == NULL)
return ENOBUFS;
memcpy (ptr, data, len);
return 0;
}
int stun_append_flag (uint8_t *msg, size_t msize, stun_attr_type_t type)
{
return stun_append_bytes (msg, msize, type, NULL, 0);
}
int
stun_append32 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint32_t value)
{
value = htonl (value);
return stun_append_bytes (msg, msize, type, &value, 4);
}
int stun_append64 (uint8_t *msg, size_t msize, stun_attr_type_t type,
uint64_t value)
{
uint32_t tab[2];
tab[0] = htonl ((uint32_t)(value >> 32));
tab[1] = htonl ((uint32_t)value);
return stun_append_bytes (msg, msize, type, tab, 8);
}
int
stun_append_string (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type, const char *str)
{
return stun_append_bytes (msg, msize, type, str, strlen (str));
}
static int stun_append_server (uint8_t *restrict msg, size_t msize, int compat)
{
static const char server[] = PACKAGE_STRING;
assert (strlen (server) < 128);
if (compat == 1)
return 0;
else
return stun_append_string (msg, msize, STUN_SERVER, server);
}
void stun_init_response (uint8_t *ans, size_t msize, const uint8_t *req, int compat)
{
assert (stun_valid (req));
assert (stun_get_class (req) == STUN_REQUEST);
assert (msize >= 20u);
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, compat);
}
/**
* @param code host-byte order error code
* @return a static pointer to a nul-terminated error message string.
*/
static const char *stun_strerror (stun_error_t code)
{
static const struct
{
stun_error_t code;
char phrase[32];
} tab[] =
{
{ STUN_TRY_ALTERNATE, "Try alternate server" },
{ STUN_BAD_REQUEST, "Bad request" },
{ STUN_UNAUTHORIZED, "Authorization required" },
{ STUN_UNKNOWN_ATTRIBUTE, "Unknown attribute" },
/*
{ STUN_STALE_CREDENTIALS, "Authentication expired" },
{ STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" },
{ STUN_MISSING_USERNAME, "Username required" },
{ STUN_USE_TLS, "Secure connection required" },
{ STUN_MISSING_REALM, "Authentication domain required" },
{ STUN_MISSING_NONCE, "Authentication token missing" },
{ STUN_UNKNOWN_USERNAME, "Unknown user name" },
*/
{ STUN_NO_BINDING, "Session expired" },
{ STUN_STALE_NONCE, "Authentication token expired" },
{ STUN_ACT_DST_ALREADY, "Changing remote peer forbidden" },
{ STUN_UNSUPP_TRANSPORT, "Unknown transport protocol" },
{ STUN_INVALID_IP, "Address unavailable" },
{ STUN_INVALID_PORT, "Port unavailable" },
{ STUN_OP_TCP_ONLY, "Invalid operation" },
{ STUN_CONN_ALREADY, "Connection already established" },
{ STUN_ALLOC_OVER_QUOTA, "Quota reached" },
{ STUN_ROLE_CONFLICT, "Role conflict" },
{ STUN_SERVER_ERROR, "Temporary server error" },
{ STUN_SERVER_CAPACITY, "Temporary server congestion" },
};
const char *str = "Unknown error";
size_t i;
for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
{
if (tab[i].code == code)
{
str = tab[i].phrase;
break;
}
}
/* Maximum allowed error message length */
assert (strlen (str) < 128);
return str;
}
/**
* Appends an ERROR-CODE attribute.
* @param msg STUN message buffer
* @param msize STUN message buffer size
* @param code STUN host-byte order integer error code
* @return 0 on success, or ENOBUFS otherwise
*/
static int
stun_append_error (uint8_t *restrict msg, size_t msize, stun_error_t code)
{
const char *str = stun_strerror (code);
size_t len = strlen (str);
div_t d = div (code, 100);
uint8_t *ptr = stun_append (msg, msize, STUN_ERROR_CODE, 4 + len);
if (ptr == NULL)
return ENOBUFS;
memset (ptr, 0, 2);
assert (d.quot <= 0x7);
ptr[2] = d.quot;
ptr[3] = d.rem;
memcpy (ptr + 4, str, len);
return 0;
}
int stun_init_error (uint8_t *ans, size_t msize, const uint8_t *req,
stun_error_t err, int compat)
{
assert (stun_valid (req));
assert (msize >= 20u);
assert (stun_get_class (req) == STUN_REQUEST);
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, compat);
return stun_append_error (ans, msize, err);
}
int stun_init_error_unknown (uint8_t *ans, size_t msize, const uint8_t *req,
int compat)
{
unsigned counter, i;
#ifdef HAVE_C_VARARRAYS
uint16_t ids[1 + (stun_length (req) / 4)];
#else
uint16_t ids[256];
#endif
counter = stun_find_unknown (req, ids, sizeof (ids) / sizeof (ids[0]));
assert (counter > 0);
if (stun_init_error (ans, msize, req, STUN_UNKNOWN_ATTRIBUTE, compat))
return ENOBUFS;
for (i = 0; i < counter; 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,
counter * 2);
}
int
stun_append_addr (uint8_t *restrict msg, size_t msize, stun_attr_type_t type,
const struct sockaddr *restrict addr, socklen_t addrlen)
{
const void *pa;
uint8_t *ptr;
uint16_t alen, port;
uint8_t family;
if (addrlen < sizeof (struct sockaddr))
return EINVAL;
switch (addr->sa_family)
{
case AF_INET:
{
const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
assert (addrlen >= sizeof (*ip4));
family = 1;
port = ip4->sin_port;
alen = 4;
pa = &ip4->sin_addr;
break;
}
case AF_INET6:
{
const struct sockaddr_in6 *ip6 = (const struct sockaddr_in6 *)addr;
if (addrlen < sizeof (*ip6))
return EINVAL;
family = 2;
port = ip6->sin6_port;
alen = 16;
pa = &ip6->sin6_addr;
break;
}
default:
return EAFNOSUPPORT;
}
ptr = stun_append (msg, msize, type, 4 + alen);
if (ptr == NULL)
return ENOBUFS;
ptr[0] = 0;
ptr[1] = family;
memcpy (ptr + 2, &port, 2);
memcpy (ptr + 4, pa, alen);
return 0;
}
int stun_append_xor_addr (uint8_t *restrict msg, size_t msize,
stun_attr_type_t type,
const struct sockaddr *restrict addr,
socklen_t addrlen)
{
int val;
/* Must be big enough to hold any supported address: */
struct sockaddr_storage xor;
if (addrlen > sizeof (xor))
addrlen = sizeof (xor);
memcpy (&xor, addr, addrlen);
val = stun_xor_address (msg, (struct sockaddr *)&xor, addrlen);
if (val)
return val;
return stun_append_addr (msg, msize, type, (struct sockaddr *)&xor,
addrlen);
}
size_t
stun_finish_long (uint8_t *msg, size_t *restrict plen,
const char *realm, const char *username, const char *nonce,
const void *restrict key, size_t keylen, int compat)
{
size_t len = *plen;
uint8_t *ptr;
int val = ENOBUFS;
uint32_t fpr;
if (realm != NULL)
{
/*if (utf32_strlen (realm) > 127))
return EINVAL;*/
val = stun_append_string (msg, len, STUN_REALM, realm);
if (val)
return val;
}
if (username != NULL)
{
if (strlen (username) >= 513)
return EINVAL;
val = stun_append_string (msg, len, STUN_USERNAME, username);
if (val)
return val;
}
if (nonce != NULL)
{
/*if (utf32_strlen (nonce) > 127))
return EINVAL;*/
val = stun_append_string (msg, len, STUN_NONCE, nonce);
if (val)
return val;
}
if (key != NULL)
{
ptr = stun_append (msg, len, STUN_MESSAGE_INTEGRITY, 20);
if (ptr == NULL)
return ENOBUFS;
stun_sha1 (msg, ptr + 20 - msg, ptr, key, keylen);
DBG (" Message HMAC-SHA1 fingerprint:"
"\n key : ");
DBG_bytes (key, keylen);
DBG ("\n sent : ");
DBG_bytes (ptr, 20);
DBG ("\n");
}
if (compat != 1) {
/*
* 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 = stun_length (msg) + 20;
return 0;
}
size_t stun_finish_short (uint8_t *msg, size_t *restrict plen,
const char *username, const char *restrict password,
const char *nonce, int compat)
{
return stun_finish_long (msg, plen, NULL, username, nonce,
password, password ? strlen (password) : 0, compat);
}
size_t stun_finish (uint8_t *msg, size_t *restrict plen, int compat)
{
return stun_finish_short (msg, plen, NULL, NULL, NULL, compat);
}
...@@ -50,6 +50,9 @@ ...@@ -50,6 +50,9 @@
#include <errno.h> #include <errno.h>
# define STUN_MAX_STR (763u)
# define STUN_MAX_CP (127u)
static void fatal (const char *msg, ...) static void fatal (const char *msg, ...)
{ {
va_list ap; va_list ap;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "stun/usages/bind.h" #include "stunagent.h"
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
......
...@@ -38,9 +38,13 @@ ...@@ -38,9 +38,13 @@
#endif #endif
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <errno.h>
#include "utils.h" #include "utils.h"
/** Compares two socket addresses */ /** Compares two socket addresses */
...@@ -115,97 +119,180 @@ uint16_t stun_getw (const uint8_t *ptr) ...@@ -115,97 +119,180 @@ uint16_t stun_getw (const uint8_t *ptr)
return ((ptr)[0] << 8) | ptr[1]; return ((ptr)[0] << 8) | ptr[1];
} }
uint16_t stun_length (const uint8_t *ptr)
{
return stun_getw (ptr + 2);
}
/** /* /\** */
* @return STUN message class in host byte order (value from 0 to 3) /* * @param msg valid STUN message */
*/ /* * @return true if there is at least one unknown mandatory attribute. */
stun_class_t stun_get_class (const uint8_t *msg) /* *\/ */
/* bool stun_has_unknown (const void *msg) */
/* { */
/* uint16_t dummy; */
/* return stun_find_unknown (msg, &dummy, 1); */
/* } */
void stun_debug (const char *fmt, ...)
{ {
uint16_t t = stun_getw (msg); va_list ap;
return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4)); va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
} }
/** void stun_debug_bytes (const void *data, size_t len)
* @return STUN message method (value from 0 to 0xfff)
*/
stun_method_t stun_get_method (const uint8_t *msg)
{ {
uint16_t t = stun_getw (msg); size_t i;
return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
(t & 0x000f));
}
stun_debug ("0x");
for (i = 0; i < len; i++)
stun_debug ("%02x", ((const unsigned char *)data)[i]);
}
/** int stun_xor_address (const StunMessage *msg,
* @return STUN message transaction ID struct sockaddr *restrict addr, socklen_t addrlen)
*/
const uint8_t *stun_id (const uint8_t *msg)
{ {
//assert (stun_valid (req)); switch (addr->sa_family)
return msg + 8; {
case AF_INET:
{
struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
if (addrlen < sizeof (*ip4))
return EINVAL;
ip4->sin_port ^= htons (STUN_MAGIC_COOKIE >> 16);
ip4->sin_addr.s_addr ^= htonl (STUN_MAGIC_COOKIE);
return 0;
}
case AF_INET6:
{
struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
unsigned short i;
if (addrlen < sizeof (*ip6))
return EINVAL;
ip6->sin6_port ^= htons (STUN_MAGIC_COOKIE >> 16);
for (i = 0; i < 16; i++)
ip6->sin6_addr.s6_addr[i] ^= msg->buffer[4 + i];
return 0;
}
}
return EAFNOSUPPORT;
} }
/** /**
* Checks if an attribute is present within a STUN message. * Compares the length and content of an attribute.
* *
* @param msg valid STUN message * @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order) * @param type STUN attribute type (host byte order)
* * @param data pointer to value to compare with
* @return whether there is a MESSAGE-INTEGRITY attribute * @param len byte length of the value
* @return 0 in case of match, ENOENT if attribute was not found,
* EINVAL if it did not match (different length, or same length but
* different content)
*/ */
bool stun_present (const uint8_t *msg, stun_attr_type_t type) int stun_memcmp (const StunMessage *msg, stun_attr_type_t type,
const void *data, size_t len)
{ {
uint16_t dummy; uint16_t alen;
return stun_find (msg, type, &dummy) != NULL; const void *ptr = stun_message_find (msg, type, &alen);
if (ptr == NULL)
return ENOENT;
if ((len != alen) || memcmp (ptr, data, len))
return EINVAL;
return 0;
} }
/** /**
* @param msg valid STUN message * Compares the content of an attribute with a string.
* @return true if there is at least one unknown mandatory attribute. * @param msg valid STUN message buffer
* @param type STUN attribute type (host byte order)
* @param str string to compare with
* @return 0 in case of match, ENOENT if attribute was not found,
* EINVAL if it did not match
*/ */
bool stun_has_unknown (const void *msg) int stun_strcmp (const StunMessage *msg, stun_attr_type_t type, const char *str)
{ {
uint16_t dummy; return stun_memcmp (msg, type, str, strlen (str));
return stun_find_unknown (msg, &dummy, 1);
} }
# ifndef NDEBUG void *stun_setw (uint8_t *ptr, uint16_t value)
/**
* This function is for debugging only, which is why it is only defined under
* !NDEBUG. It should really only be used in run-time assertions, as it cannot
* detect all possible errors. stun_validate() should be used instead in real
* code.
*
* @param msg pointer to a potential STUN message
* @return whether the pointer refers to a valid STUN message
*/
bool stun_valid (const uint8_t *msg)
{ {
size_t length = 20u + stun_length (msg); *ptr++ = value >> 8;
return stun_validate (msg, length) == (ssize_t)length; *ptr++ = value & 0xff;
return ptr;
} }
# endif
void stun_debug (const char *fmt, ...)
void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m)
{ {
va_list ap; /* assert (c < 4); */
va_start (ap, fmt); /* assert (m < (1 << 12)); */
vfprintf (stderr, fmt, ap);
va_end (ap); h[0] = (c >> 1) | ((m >> 6) & 0x3e);
h[1] = ((c << 4) & 0x10) | ((m << 1) & 0xe0) | (m & 0x0f);
/* assert (stun_getw (h) < (1 << 14)); */
/* assert (stun_get_class (h) == c); */
/* assert (stun_get_method (h) == m); */
} }
void stun_debug_bytes (const void *data, size_t len)
/**
* @param code host-byte order error code
* @return a static pointer to a nul-terminated error message string.
*/
const char *stun_strerror (stun_error_t code)
{ {
static const struct
{
stun_error_t code;
char phrase[32];
} tab[] =
{
{ STUN_ERROR_TRY_ALTERNATE, "Try alternate server" },
{ STUN_ERROR_BAD_REQUEST, "Bad request" },
{ STUN_ERROR_UNAUTHORIZED, "Authorization required" },
{ STUN_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute" },
/*
{ STUN_STALE_CREDENTIALS, "Authentication expired" },
{ STUN_INTEGRITY_CHECK_FAILURE, "Incorrect username/password" },
{ STUN_MISSING_USERNAME, "Username required" },
{ STUN_USE_TLS, "Secure connection required" },
{ STUN_MISSING_REALM, "Authentication domain required" },
{ STUN_MISSING_NONCE, "Authentication token missing" },
{ STUN_UNKNOWN_USERNAME, "Unknown user name" },
*/
{ STUN_ERROR_NO_BINDING, "Session expired" },
{ STUN_ERROR_STALE_NONCE, "Authentication token expired" },
{ STUN_ERROR_ACT_DST_ALREADY, "Changing remote peer forbidden" },
{ STUN_ERROR_UNSUPP_TRANSPORT, "Unknown transport protocol" },
{ STUN_ERROR_INVALID_IP, "Address unavailable" },
{ STUN_ERROR_INVALID_PORT, "Port unavailable" },
{ STUN_ERROR_OP_TCP_ONLY, "Invalid operation" },
{ STUN_ERROR_CONN_ALREADY, "Connection already established" },
{ STUN_ERROR_ALLOC_OVER_QUOTA, "Quota reached" },
{ STUN_ERROR_ROLE_CONFLICT, "Role conflict" },
{ STUN_ERROR_SERVER_ERROR, "Temporary server error" },
{ STUN_ERROR_SERVER_CAPACITY, "Temporary server congestion" },
};
const char *str = "Unknown error";
size_t i; size_t i;
DBG ("0x"); for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
for (i = 0; i < len; i++) {
DBG ("%02x", ((const unsigned char *)data)[i]); if (tab[i].code == code)
{
str = tab[i].phrase;
break;
}
}
/* Maximum allowed error message length */
// assert (strlen (str) < 128);
return str;
} }
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
* @brief STUN client generic utility functions * @brief STUN client generic utility functions
*/ */
#include "stun-msg.h" #include "stunmessage.h"
# ifdef __cplusplus # ifdef __cplusplus
extern "C" { extern "C" {
...@@ -58,25 +58,23 @@ size_t stun_align (size_t l); ...@@ -58,25 +58,23 @@ size_t stun_align (size_t l);
uint16_t stun_getw (const uint8_t *ptr); uint16_t stun_getw (const uint8_t *ptr);
uint16_t stun_length (const uint8_t *ptr); void stun_debug (const char *fmt, ...);
stun_class_t stun_get_class (const uint8_t *msg);
stun_method_t stun_get_method (const uint8_t *msg); void stun_debug_bytes (const void *data, size_t len);
const uint8_t *stun_id (const uint8_t *msg); int stun_xor_address (const StunMessage *msg,
struct sockaddr *restrict addr, socklen_t addrlen);
bool stun_present (const uint8_t *msg, stun_attr_type_t type); int stun_memcmp (const StunMessage *msg, stun_attr_type_t type,
const void *data, size_t len);
bool stun_has_unknown (const void *msg); int stun_strcmp (const StunMessage *msg, stun_attr_type_t type, const char *str);
# ifndef NDEBUG void *stun_setw (uint8_t *ptr, uint16_t value);
bool stun_valid (const uint8_t *msg);
# endif
void stun_debug (const char *fmt, ...); void stun_set_type (uint8_t *h, stun_class_t c, stun_method_t m);
void stun_debug_bytes (const void *data, size_t len); const char *stun_strerror (stun_error_t code);
# ifdef __cplusplus # ifdef __cplusplus
} }
......
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