diff options
-rw-r--r-- | stun/Makefile.am | 22 | ||||
-rw-r--r-- | stun/constants.h | 4 | ||||
-rw-r--r-- | stun/stun-msg.h | 328 | ||||
-rw-r--r-- | stun/stun3489bis.c | 32 | ||||
-rw-r--r-- | stun/stun3489bis.h | 4 | ||||
-rw-r--r-- | stun/stunagent.c | 461 | ||||
-rw-r--r-- | stun/stunagent.h | 111 | ||||
-rw-r--r-- | stun/stuncrc32.c | 2 | ||||
-rw-r--r-- | stun/stunhmac.c | 2 | ||||
-rw-r--r-- | stun/stunhmac.h | 4 | ||||
-rw-r--r-- | stun/stunmessage.c | 80 | ||||
-rw-r--r-- | stun/stunmessage.h | 133 | ||||
-rw-r--r-- | stun/stunrecv.c | 567 | ||||
-rw-r--r-- | stun/stunsend.c | 481 | ||||
-rw-r--r-- | stun/tests/test-parse.c | 3 | ||||
-rw-r--r-- | stun/tools/stunbdc.c | 2 | ||||
-rw-r--r-- | stun/utils.c | 205 | ||||
-rw-r--r-- | stun/utils.h | 24 |
18 files changed, 915 insertions, 1550 deletions
diff --git a/stun/Makefile.am b/stun/Makefile.am index 5e7ba52..1a1c03c 100644 --- a/stun/Makefile.am +++ b/stun/Makefile.am @@ -19,16 +19,18 @@ CLEANFILES += $(BUILT_SOURCES) noinst_LTLIBRARIES = libstun.la dist_noinst_SCRIPTS = build-unknown.sh -libstun_la_SOURCES = \ - stun-msg.h stunsend.c stunrecv.c \ - stun3489bis.c \ - utils.c \ - unknown.c \ - crc32.c hmac.c \ - usages/timer.h usages/timer.c \ - usages/trans.h usages/trans.c \ - usages/stun-ice.c usages/stun-ice.h \ - usages/bind.c usages/bind.h +libstun_la_SOURCES = stun.h constants.h \ + stunagent.c stunagent.h \ + stunmessage.c stunmessage.h \ + stun3489bis.c stun3489bis.h \ + stuncrc32.c stuncrc32.h \ + stunhmac.c stunhmac.h \ + utils.c utils.h + +# usages/timer.h usages/timer.c \ +# 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) diff --git a/stun/constants.h b/stun/constants.h index 4a13b53..f5f1e2d 100644 --- a/stun/constants.h +++ b/stun/constants.h @@ -67,9 +67,9 @@ #define STUN_ID_LEN 16 #define STUN_AGENT_MAX_SAVED_IDS 20 +#define STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES 256 - -#define STUN_COOKIE 0x2112A442 +#define STUN_MAGIC_COOKIE 0x2112A442 #ifndef TRUE #define TRUE (1 == 1) diff --git a/stun/stun-msg.h b/stun/stun-msg.h index fc759c7..0fded73 100644 --- a/stun/stun-msg.h +++ b/stun/stun-msg.h @@ -54,138 +54,12 @@ # define STUN_MAXCHR 127u # 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 "stun3489bis.h" +#include "stunhmac.h" # ifndef NDEBUG # include <stdio.h> @@ -204,31 +78,6 @@ extern "C" { # 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, struct sockaddr *addr, socklen_t addrlen); @@ -281,102 +130,6 @@ 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); /** - * 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. * * @param msg valid STUN message buffer @@ -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); -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 } # endif diff --git a/stun/stun3489bis.c b/stun/stun3489bis.c index 11e0809..53518da 100644 --- a/stun/stun3489bis.c +++ b/stun/stun3489bis.c @@ -35,19 +35,24 @@ * file under either the MPL or the LGPL. */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + #include <sys/socket.h> #include <netinet/in.h> /* htons() */ -#include <assert.h> +#include <string.h> +#include <stdlib.h> -#include "crc32.h" -#include "stun-msg.h" +#include "stuncrc32.h" +#include "stunmessage.h" uint32_t stun_fingerprint (const uint8_t *msg, size_t len) { struct iovec iov[3]; uint16_t fakelen = htons (len - 20u); - assert (len >= 28u); + // assert (len >= 28u); iov[0].iov_base = (void *)msg; iov[0].iov_len = 2; @@ -57,5 +62,22 @@ uint32_t stun_fingerprint (const uint8_t *msg, size_t len) /* first 4 bytes done, last 8 bytes not summed */ 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); } diff --git a/stun/stun3489bis.h b/stun/stun3489bis.h index 6f18a51..1562839 100644 --- a/stun/stun3489bis.h +++ b/stun/stun3489bis.h @@ -54,8 +54,10 @@ */ 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 */ diff --git a/stun/stunagent.c b/stun/stunagent.c new file mode 100644 index 0000000..c7e62f6 --- /dev/null +++ b/stun/stunagent.c @@ -0,0 +1,461 @@ +/* + * 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; +} diff --git a/stun/stunagent.h b/stun/stunagent.h new file mode 100644 index 0000000..78ebcc1 --- /dev/null +++ b/stun/stunagent.h @@ -0,0 +1,111 @@ +/* + * 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 */ diff --git a/stun/stuncrc32.c b/stun/stuncrc32.c index 0d11475..3d1e253 100644 --- a/stun/stuncrc32.c +++ b/stun/stuncrc32.c @@ -87,7 +87,7 @@ # include <config.h> #endif -#include "crc32.h" +#include "stuncrc32.h" static const uint32_t crc32_tab[] = { diff --git a/stun/stunhmac.c b/stun/stunhmac.c index 7aa5f96..edfec86 100644 --- a/stun/stunhmac.c +++ b/stun/stunhmac.c @@ -45,7 +45,7 @@ #include <sys/socket.h> #include <netinet/in.h> #include <pthread.h> -#include "stun-msg.h" +#include "stunmessage.h" #include "stunhmac.h" #include <string.h> diff --git a/stun/stunhmac.h b/stun/stunhmac.h index 1ece62f..b4fa833 100644 --- a/stun/stunhmac.h +++ b/stun/stunhmac.h @@ -35,6 +35,8 @@ #ifndef _STUN_HMAC_H #define _STUN_HMAC_H +#include "stunmessage.h" + /** * Computes the MESSAGE-INTEGRITY hash of a 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, void stun_make_transid (stun_transid_t id); -/* _STUN_HMAC_H */ +#endif /* _STUN_HMAC_H */ diff --git a/stun/stunmessage.c b/stun/stunmessage.c index ba1de5d..eac9f4a 100644 --- a/stun/stunmessage.c +++ b/stun/stunmessage.c @@ -61,12 +61,8 @@ bool stun_message_init (StunMessage *msg, stun_class_t c, stun_method_t m, memset (msg->buffer, 0, 4); stun_set_type (msg->buffer, c, m); - if (msg->buffer != id) - { - uint32_t cookie = htonl (STUN_COOKIE); - memcpy (msg->buffer + 4, &cookie, sizeof (cookie)); - memcpy (msg->buffer + 8, id, 12); - } + memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, + id, sizeof (STUN_MESSAGE_TRANS_ID_LEN)); return TRUE; } @@ -107,12 +103,12 @@ stun_message_find (const StunMessage *msg, stun_attr_type_t type, /* Look for and ignore misordered attributes */ switch (atype) { - case STUN_MESSAGE_INTEGRITY: + case STUN_ATTRIBUTE_MESSAGE_INTEGRITY: /* Only fingerprint may come after M-I */ - if (type == STUN_FINGERPRINT) + if (type == STUN_ATTRIBUTE_FINGERPRINT) break; - case STUN_FINGERPRINT: + case STUN_ATTRIBUTE_FINGERPRINT: /* Nothing may come after FPR */ return NULL; } @@ -273,10 +269,10 @@ stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type, 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; - 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; if (ptr == NULL) @@ -319,7 +315,7 @@ stun_message_append (StunMessage *msg, stun_attr_type_t type, size_t length) 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->buffer) ? length : stun_align (length)); + a = stun_setw (a, stun_has_cookie (msg) ? length : stun_align (length)); mlen += 4 + length; /* Add padding if needed */ @@ -473,7 +469,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code) size_t len = strlen (str); 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) return ENOBUFS; @@ -484,7 +480,7 @@ stun_message_append_error (StunMessage *msg, stun_error_t code) 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 len; @@ -492,19 +488,19 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length) if (length < 1) { stun_debug ("STUN error: No data!\n"); - return FALSE; + return STUN_MESSAGE_BUFFER_INVALID; } if (msg[0] >> 6) { 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) { stun_debug ("STUN error: Incomplete STUN message header!\n"); - return FALSE; + return STUN_MESSAGE_BUFFER_INCOMPLETE; } mlen = stun_getw (msg + STUN_MESSAGE_LENGTH_POS) + @@ -513,14 +509,14 @@ bool stun_message_is_valid (const uint8_t *msg, size_t length) if (stun_padding (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) { stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n", (unsigned)length, (unsigned)mlen); - return FALSE; // partial message + return STUN_MESSAGE_BUFFER_INCOMPLETE; // partial message } msg += 20; @@ -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", (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; 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; } diff --git a/stun/stunmessage.h b/stun/stunmessage.h index fdcc8c7..8428d32 100644 --- a/stun/stunmessage.h +++ b/stun/stunmessage.h @@ -41,12 +41,7 @@ #include <stdbool.h> #include "constants.h" -typedef struct { - uint8_t *buffer; - size_t buffer_len; - bool message_id_validated; -} StunMessage; - +typedef struct stun_message_t StunMessage; /* Message classes */ typedef enum @@ -78,65 +73,65 @@ 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 */ + STUN_ATTRIBUTE_MAPPED_ADDRESS=0x0001, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_OLD_RESPONSE_ADDRESS=0x0002, /* old RFC3489 */ + STUN_ATTRIBUTE_OLD_CHANGE_REQUEST=0x0003, /* old RFC3489 */ + STUN_ATTRIBUTE_OLD_SOURCE_ADDRESS=0x0004, /* old RFC3489 */ + STUN_ATTRIBUTE_OLD_CHANGED_ADDRESS=0x0005, /* old RFC3489 */ + STUN_ATTRIBUTE_USERNAME=0x0006, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_OLD_PASSWORD=0x0007, /* old RFC3489 */ + STUN_ATTRIBUTE_MESSAGE_INTEGRITY=0x0008, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_ERROR_CODE=0x0009, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES=0x000A, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_OLD_REFLECTED_FROM=0x000B, /* old RFC3489 */ /* 0x000C */ /* reserved */ - STUN_LIFETIME=0x000D, /* TURN-04 */ + STUN_ATTRIBUTE_LIFETIME=0x000D, /* TURN-04 */ /* 0x000E */ /* reserved */ /* 0x000F */ /* reserved */ - STUN_BANDWIDTH=0x0010, /* TURN-04 */ + STUN_ATTRIBUTE_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 */ + STUN_ATTRIBUTE_REMOTE_ADDRESS=0x0012, /* TURN-04 */ + STUN_ATTRIBUTE_DATA=0x0013, /* TURN-04 */ + STUN_ATTRIBUTE_REALM=0x0014, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_NONCE=0x0015, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_RELAY_ADDRESS=0x0016, /* TURN-04 */ + STUN_ATTRIBUTE_REQUESTED_ADDRESS_TYPE=0x0017, /* TURN-IPv6-03 */ + STUN_ATTRIBUTE_REQUESTED_PORT_PROPS=0x0018, /* TURN-04 */ + STUN_ATTRIBUTE_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 */ + STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS=0x0020, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_TIMER_VAL=0x0021, /* TURN-04 */ + STUN_ATTRIBUTE_REQUESTED_IP=0x0022, /* TURN-04 */ + STUN_ATTRIBUTE_CONNECT_STAT=0x0023, /* TURN-04 */ + STUN_ATTRIBUTE_PRIORITY=0x0024, /* ICE-18 */ + STUN_ATTRIBUTE_USE_CANDIDATE=0x0025, /* ICE-18 */ /* 0x0026 */ /* reserved */ /* 0x0027 */ /* 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 */ /* Optional attributes */ /* 0x8000-0x8021 */ /* reserved */ - STUN_SERVER=0x8022, /* RFC3489bis-11 */ - STUN_ALTERNATE_SERVER=0x8023, /* RFC3489bis-11 */ - STUN_REFRESH_INTERVAL=0x8024, /* wing-nat-control-04 */ + STUN_ATTRIBUTE_SERVER=0x8022, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_ALTERNATE_SERVER=0x8023, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_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 */ + STUN_ATTRIBUTE_FINGERPRINT=0x8028, /* RFC3489bis-11 */ + STUN_ATTRIBUTE_ICE_CONTROLLED=0x8029, /* ICE-18 */ + STUN_ATTRIBUTE_ICE_CONTROLLING=0x802A, /* ICE-18 */ /* 0x802B-0xFFFF */ /* reserved */ } 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]; */ 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_TRY_ALTERNATE=300, /* RFC3489bis-11 */ + STUN_ERROR_BAD_REQUEST=400, /* RFC3489bis-11 */ + STUN_ERROR_UNAUTHORIZED=401, /* RFC3489bis-11 */ + STUN_ERROR_UNKNOWN_ATTRIBUTE=420, /* RFC3489bis-11 */ + STUN_ERROR_NO_BINDING=437, /* TURN-04 */ + STUN_ERROR_STALE_NONCE=438, /* RFC3489bis-11 */ + STUN_ERROR_ACT_DST_ALREADY=439, /* TURN-04 */ + STUN_ERROR_UNSUPP_FAMILY=440, /* TURN-IPv6-03 */ + STUN_ERROR_UNSUPP_TRANSPORT=442, /* TURN-04 */ + STUN_ERROR_INVALID_IP=443, /* TURN-04 */ + STUN_ERROR_INVALID_PORT=444, /* TURN-04 */ + STUN_ERROR_OP_TCP_ONLY=445, /* TURN-04 */ + STUN_ERROR_CONN_ALREADY=446, /* TURN-04 */ + STUN_ERROR_ALLOC_OVER_QUOTA=486, /* TURN-04 */ + STUN_ERROR_ROLE_CONFLICT=487, /* ICE-18 */ + STUN_ERROR_SERVER_ERROR=500, /* RFC3489bis-11 */ + STUN_ERROR_SERVER_CAPACITY=507, /* TURN-04 */ STUN_ERROR_MAX=699 } stun_error_t; +#include "stunagent.h" #include "stunhmac.h" #include "stuncrc32.h" #include "utils.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. * @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, 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, size_t length); @@ -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); -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 */ diff --git a/stun/stunrecv.c b/stun/stunrecv.c index 771ec19..7ca097d 100644 --- a/stun/stunrecv.c +++ b/stun/stunrecv.c @@ -48,446 +48,6 @@ #include <errno.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 @@ -529,130 +89,3 @@ int stun_verify_username (const uint8_t *msg, const char *local_ufrag, uint32_t 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; -} diff --git a/stun/stunsend.c b/stun/stunsend.c index d245b9e..6b08691 100644 --- a/stun/stunsend.c +++ b/stun/stunsend.c @@ -50,484 +50,3 @@ #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); -} diff --git a/stun/tests/test-parse.c b/stun/tests/test-parse.c index 03c9c34..a4d94b2 100644 --- a/stun/tests/test-parse.c +++ b/stun/tests/test-parse.c @@ -50,6 +50,9 @@ #include <errno.h> +# define STUN_MAX_STR (763u) +# define STUN_MAX_CP (127u) + static void fatal (const char *msg, ...) { va_list ap; diff --git a/stun/tools/stunbdc.c b/stun/tools/stunbdc.c index 56e7ffd..4ba0056 100644 --- a/stun/tools/stunbdc.c +++ b/stun/tools/stunbdc.c @@ -39,7 +39,7 @@ #include <sys/types.h> #include <sys/socket.h> -#include "stun/usages/bind.h" +#include "stunagent.h" #include <unistd.h> #include <getopt.h> diff --git a/stun/utils.c b/stun/utils.c index 300d8df..ca3db3a 100644 --- a/stun/utils.c +++ b/stun/utils.c @@ -38,9 +38,13 @@ #endif #include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> #include <sys/socket.h> #include <netinet/in.h> +#include <errno.h> #include "utils.h" /** Compares two socket addresses */ @@ -115,97 +119,180 @@ uint16_t stun_getw (const uint8_t *ptr) 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) - */ -stun_class_t stun_get_class (const uint8_t *msg) +/* /\** */ +/* * @param msg valid STUN message */ +/* * @return true if there is at least one unknown mandatory attribute. */ +/* *\/ */ +/* 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); - return (stun_class_t)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4)); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); } -/** - * @return STUN message method (value from 0 to 0xfff) - */ -stun_method_t stun_get_method (const uint8_t *msg) +void stun_debug_bytes (const void *data, size_t len) { - uint16_t t = stun_getw (msg); - return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) | - (t & 0x000f)); -} + size_t i; + stun_debug ("0x"); + for (i = 0; i < len; i++) + stun_debug ("%02x", ((const unsigned char *)data)[i]); +} -/** - * @return STUN message transaction ID - */ -const uint8_t *stun_id (const uint8_t *msg) +int stun_xor_address (const StunMessage *msg, + struct sockaddr *restrict addr, socklen_t addrlen) { - //assert (stun_valid (req)); - return msg + 8; + 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_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) - * - * @return whether there is a MESSAGE-INTEGRITY attribute + * @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) */ -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; - return stun_find (msg, type, &dummy) != NULL; + uint16_t alen; + 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 - * @return true if there is at least one unknown mandatory attribute. + * 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 */ -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_find_unknown (msg, &dummy, 1); + return stun_memcmp (msg, type, str, strlen (str)); } -# ifndef NDEBUG -/** - * 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) +void *stun_setw (uint8_t *ptr, uint16_t value) { - size_t length = 20u + stun_length (msg); - return stun_validate (msg, length) == (ssize_t)length; + *ptr++ = value >> 8; + *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; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); +/* 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); */ } -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; - DBG ("0x"); - for (i = 0; i < len; i++) - DBG ("%02x", ((const unsigned char *)data)[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; } diff --git a/stun/utils.h b/stun/utils.h index 688ddd6..ef17a03 100644 --- a/stun/utils.h +++ b/stun/utils.h @@ -41,7 +41,7 @@ * @brief STUN client generic utility functions */ -#include "stun-msg.h" +#include "stunmessage.h" # ifdef __cplusplus extern "C" { @@ -58,25 +58,23 @@ size_t stun_align (size_t l); uint16_t stun_getw (const uint8_t *ptr); -uint16_t stun_length (const uint8_t *ptr); - -stun_class_t stun_get_class (const uint8_t *msg); +void stun_debug (const char *fmt, ...); -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 -bool stun_valid (const uint8_t *msg); -# endif +void *stun_setw (uint8_t *ptr, uint16_t value); -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 } |