From 48792eb5fca60586115abd95c6897a60cf41c660 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 4 Apr 2006 16:08:30 +0000 Subject: [PATCH] a stunning new change git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@1037 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_stun.h | 220 +++++++++++++++++++++++++++++++ src/switch_stun.c | 268 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 488 insertions(+) create mode 100644 src/include/switch_stun.h create mode 100644 src/switch_stun.c diff --git a/src/include/switch_stun.h b/src/include/switch_stun.h new file mode 100644 index 0000000000..2aaf587d3e --- /dev/null +++ b/src/include/switch_stun.h @@ -0,0 +1,220 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * switch_stun.h STUN (Simple Traversal of UDP over NAT) + * + */ +/*! + \defgroup stun1 STUN code + \ingroup FREESWITCH + \{ +*/ +#ifndef _SWITCH_STUN_PARSER_H +#define _SWITCH_STUN_PARSER_H + +#define SWITCH_STUN_PACKET_MIN_LEN 20 + +typedef enum { + SWITCH_STUN_BINDING_REQUEST = 0x0001, + SWITCH_STUN_BINDING_RESPONSE = 0x0101, + SWITCH_STUN_BINDING_ERROR_RESPONSE = 0x0111, + SWITCH_STUN_SHARED_SECRET_REQUEST = 0x0002, + SWITCH_STUN_SHARED_SECRET_RESPONSE = 0x0102, + SWITCH_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112, + SWITCH_STUN_ALLOCATE_REQUEST = 0x0003, + SWITCH_STUN_ALLOCATE_RESPONSE = 0x0103, + SWITCH_STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, + SWITCH_STUN_SEND_REQUEST = 0x0004, + SWITCH_STUN_SEND_RESPONSE = 0x0104, + SWITCH_STUN_SEND_ERROR_RESPONSE = 0x0114, + SWITCH_STUN_DATA_INDICATION = 0x0115 +} switch_stun_message_t; + +typedef enum { + SWITCH_STUN_ATTR_MAPPED_ADDRESS = 0x0001, /* Address */ + SWITCH_STUN_ATTR_RESPONSE_ADDRESS = 0x0002, /* Address */ + SWITCH_STUN_ATTR_CHANGE_REQUEST = 0x0003, /* UInt32 */ + SWITCH_STUN_ATTR_SOURCE_ADDRESS = 0x0004, /* Address */ + SWITCH_STUN_ATTR_CHANGED_ADDRESS = 0x0005, /* Address */ + SWITCH_STUN_ATTR_USERNAME = 0x0006, /* ByteString, multiple of 4 bytes */ + SWITCH_STUN_ATTR_PASSWORD = 0x0007, /* ByteString, multiple of 4 bytes */ + SWITCH_STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, /* ByteString, 20 bytes */ + SWITCH_STUN_ATTR_ERROR_CODE = 0x0009, /* ErrorCode */ + SWITCH_STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, /* UInt16List */ + SWITCH_STUN_ATTR_REFLECTED_FROM = 0x000b, /* Address */ + SWITCH_STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, /* TransportPrefs */ + SWITCH_STUN_ATTR_LIFETIME = 0x000d, /* UInt32 */ + SWITCH_STUN_ATTR_ALTERNATE_SERVER = 0x000e, /* Address */ + SWITCH_STUN_ATTR_MAGIC_COOKIE = 0x000f, /* ByteString, 4 bytes */ + SWITCH_STUN_ATTR_BANDWIDTH = 0x0010, /* UInt32 */ + SWITCH_STUN_ATTR_DESTINATION_ADDRESS = 0x0011, /* Address */ + SWITCH_STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, /* Address */ + SWITCH_STUN_ATTR_DATA = 0x0013, /* ByteString */ + SWITCH_STUN_ATTR_OPTIONS = 0x8001 /* UInt32 */ +} switch_stun_attribute_t; + +typedef enum { + SWITCH_STUN_ERROR_BAD_REQUEST = 400, + SWITCH_STUN_ERROR_UNAUTHORIZED = 401, + SWITCH_STUN_ERROR_UNKNOWN_ATTRIBUTE = 420, + SWITCH_STUN_ERROR_STALE_CREDENTIALS = 430, + SWITCH_STUN_ERROR_INTEGRITY_CHECK_FAILURE = 431, + SWITCH_STUN_ERROR_MISSING_USERNAME = 432, + SWITCH_STUN_ERROR_USE_TLS = 433, + SWITCH_STUN_ERROR_SERVER_ERROR = 500, + SWITCH_STUN_ERROR_GLOBAL_FAILURE = 600 +} switch_stun_error_t; + +typedef enum { + SWITCH_STUN_TYPE_PACKET_TYPE, + SWITCH_STUN_TYPE_ATTRIBUTE, + SWITCH_STUN_TYPE_ERROR +} switch_stun_type_t; + +typedef struct { + int16_t type; + int16_t length; + char id[16]; +} switch_stun_packet_header_t; + +typedef struct { + int16_t type; + uint16_t length; + char value[0]; +} switch_stun_packet_attribute_t; + +typedef struct { + switch_stun_packet_header_t header; + switch_stun_packet_attribute_t first_attribute; +} switch_stun_packet_t; + +typedef struct { + int8_t wasted; + int8_t family; + int16_t port; + int32_t address; +} switch_stun_ip_t; + + +/*! + \brief Writes random characters into a buffer + \param buf the buffer + \param len the length of the data + \param set the set of chars to use (NULL for auto) +*/ +SWITCH_DECLARE(void) switch_stun_random_string(char *buf, uint16_t len, char *set); + +/*! + \brief Prepare a raw packet for parsing + \param buf the raw data + \param len the length of the data + \return a stun packet pointer to buf to use as an access point +*/ +SWITCH_DECLARE(switch_stun_packet_t *)switch_stun_packet_parse(uint8_t *buf, uint32_t len); + +/*! + \brief Obtain a printable string form of a given value + \param type the type of message + \param value the value to look up + \return a sring version of value +*/ +SWITCH_DECLARE(const char *)switch_stun_value_to_name(int32_t type, int32_t value); + + +/*! + \brief Extract a mapped address (IP:PORT) from a packet attribute + \param attribute the attribute from which to extract + \param ipstr a buffer to write the string representation of the ip + \param port the port + \return true or false +*/ +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_get_mapped_address(switch_stun_packet_attribute_t *attribute, char *ipstr, uint16_t *port); + +/*! + \brief Extract a username from a packet attribute + \param attribute the attribute from which to extract + \param username a buffer to write the string representation of the username + \param len the maximum size of the username buffer + \return a pointer to the username or NULL +*/ +SWITCH_DECLARE(char *)switch_stun_packet_attribute_get_username(switch_stun_packet_attribute_t *attribute, char *username, uint16_t len); + + +/*! + \brief Prepare a new outbound packet of a certian type and id + \param id id to use (NULL for an auto generated id) + \param type the stun packet type + \param buf a pointer to data to use for the packet + \return a pointer to a ready-to-use stun packet +*/ +SWITCH_DECLARE(switch_stun_packet_t *)switch_stun_packet_build_header(switch_stun_message_t type, + char *id, + uint8_t *buf + ); + +/*! + \brief Add a username packet attribute + \param packet the packet to add the attribute to + \param username the string representation of the username + \param ulen the length of the username + \return true or false +*/ +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_username(switch_stun_packet_t *packet, char *username, uint16_t ulen); + + +/*! + \brief Add a binded address packet attribute + \param packet the packet to add the attribute to + \param ipstr the string representation of the ip + \param port the port of the mapped address + \return true or false +*/ +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port); + +/*! + \brief set a switch_stun_packet_attribute_t pointer to point at the first attribute in a packet + \param packet the packet in question + \param attribute the pointer to set up +*/ +#define switch_stun_packet_first_attribute(packet, attribute) attribute = &packet->first_attribute; + +/*! + \brief Increment an attribute pointer to the next attribute in it's packet + \param attribute the pointer to increment + \return true or false depending on if there are any more attributes +*/ +#define switch_stun_packet_next_attribute(attribute) (attribute = (switch_stun_packet_attribute_t *) (attribute->value + attribute->length)) && attribute->length + +/*! + \brief Obtain the correct length in bytes of a stun packet + \param packet the packet in question + \return the size in bytes (host order) of the entire packet +*/ +#define switch_stun_packet_length(packet) ntohs(packet->header.length) + sizeof(switch_stun_packet_header_t) +///\} +#endif diff --git a/src/switch_stun.c b/src/switch_stun.c new file mode 100644 index 0000000000..c7850d2484 --- /dev/null +++ b/src/switch_stun.c @@ -0,0 +1,268 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * switch_stun.c STUN (Simple Traversal of UDP over NAT) + * + */ +#include + +struct value_mapping { + const uint32_t value; + const char *name; +}; + +static const struct value_mapping PACKET_TYPES[] = { + { SWITCH_STUN_BINDING_REQUEST, "BINDING_REQUEST" }, + { SWITCH_STUN_BINDING_RESPONSE, "BINDING_RESPONSE" }, + { SWITCH_STUN_BINDING_ERROR_RESPONSE, "BINDING_ERROR_RESPONSE" }, + { SWITCH_STUN_SHARED_SECRET_REQUEST, "SHARED_SECRET_REQUEST" }, + { SWITCH_STUN_SHARED_SECRET_RESPONSE, "SHARED_SECRET_RESPONSE" }, + { SWITCH_STUN_SHARED_SECRET_ERROR_RESPONSE, "SHARED_SECRET_ERROR_RESPONSE" }, + { SWITCH_STUN_ALLOCATE_REQUEST, "ALLOCATE_REQUEST" }, + { SWITCH_STUN_ALLOCATE_RESPONSE, "ALLOCATE_RESPONSE" }, + { SWITCH_STUN_ALLOCATE_ERROR_RESPONSE, "ALLOCATE_ERROR_RESPONSE" }, + { SWITCH_STUN_SEND_REQUEST, "SEND_REQUEST" }, + { SWITCH_STUN_SEND_RESPONSE, "SEND_RESPONSE" }, + { SWITCH_STUN_SEND_ERROR_RESPONSE, "SEND_ERROR_RESPONSE" }, + { SWITCH_STUN_DATA_INDICATION , "DATA_INDICATION"}, + { 0, 0} }; + +static const struct value_mapping ATTR_TYPES[] = { + { SWITCH_STUN_ATTR_MAPPED_ADDRESS, "MAPPED_ADDRESS" }, + { SWITCH_STUN_ATTR_RESPONSE_ADDRESS, "RESPONSE_ADDRESS" }, + { SWITCH_STUN_ATTR_CHANGE_REQUEST, "CHANGE_REQUEST" }, + { SWITCH_STUN_ATTR_SOURCE_ADDRESS, "SOURCE_ADDRESS" }, + { SWITCH_STUN_ATTR_CHANGED_ADDRESS, "CHANGED_ADDRESS" }, + { SWITCH_STUN_ATTR_USERNAME, "USERNAME" }, + { SWITCH_STUN_ATTR_PASSWORD, "PASSWORD" }, + { SWITCH_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE_INTEGRITY" }, + { SWITCH_STUN_ATTR_ERROR_CODE, "ERROR_CODE" }, + { SWITCH_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN_ATTRIBUTES" }, + { SWITCH_STUN_ATTR_REFLECTED_FROM, "REFLECTED_FROM" }, + { SWITCH_STUN_ATTR_TRANSPORT_PREFERENCES, "TRANSPORT_PREFERENCES" }, + { SWITCH_STUN_ATTR_LIFETIME, "LIFETIME" }, + { SWITCH_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE_SERVER" }, + { SWITCH_STUN_ATTR_MAGIC_COOKIE, "MAGIC_COOKIE" }, + { SWITCH_STUN_ATTR_BANDWIDTH, "BANDWIDTH" }, + { SWITCH_STUN_ATTR_DESTINATION_ADDRESS, "DESTINATION_ADDRESS" }, + { SWITCH_STUN_ATTR_SOURCE_ADDRESS2, "SOURCE_ADDRESS2" }, + { SWITCH_STUN_ATTR_DATA, "DATA" }, + { SWITCH_STUN_ATTR_OPTIONS, "OPTIONS" }, + { 0, 0} }; + +static const struct value_mapping ERROR_TYPES[] = { + { SWITCH_STUN_ERROR_BAD_REQUEST, "BAD_REQUEST" }, + { SWITCH_STUN_ERROR_UNAUTHORIZED, "UNAUTHORIZED" }, + { SWITCH_STUN_ERROR_UNKNOWN_ATTRIBUTE, "UNKNOWN_ATTRIBUTE" }, + { SWITCH_STUN_ERROR_STALE_CREDENTIALS, "STALE_CREDENTIALS" }, + { SWITCH_STUN_ERROR_INTEGRITY_CHECK_FAILURE, "INTEGRITY_CHECK_FAILURE" }, + { SWITCH_STUN_ERROR_MISSING_USERNAME, "MISSING_USERNAME" }, + { SWITCH_STUN_ERROR_USE_TLS, "USE_TLS" }, + { SWITCH_STUN_ERROR_SERVER_ERROR, "SERVER_ERROR" }, + { SWITCH_STUN_ERROR_GLOBAL_FAILURE, "GLOBAL_FAILURE" }, + { 0, 0 }}; + +SWITCH_DECLARE(void) switch_stun_random_string(char *buf, uint16_t len, char *set) +{ + char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + int max; + uint8_t x; + + if (!set) { + set = chars; + } + + max = (int)strlen(set) - 1; + + for(x = 0; x < len; x++) { + int j = 1+(int)(max*1.0*rand()/(RAND_MAX+1.0)); + buf[x] = set[j]; + } +} + + + +SWITCH_DECLARE(switch_stun_packet_t *)switch_stun_packet_parse(uint8_t *buf, uint32_t len) +{ + switch_stun_packet_t *packet; + switch_stun_packet_attribute_t *attr; + + if (len < SWITCH_STUN_PACKET_MIN_LEN) { + return NULL; + } + + packet = (switch_stun_packet_t *) buf; + packet->header.type = ntohs(packet->header.type); + packet->header.length = ntohs(packet->header.length); + attr = &packet->first_attribute; + switch_stun_packet_first_attribute(packet, attr); + do { + attr->length = ntohs(attr->length); + attr->type = ntohs(attr->type); + if (!attr->length) { + break; + } + switch(attr->type) { + case SWITCH_STUN_ATTR_MAPPED_ADDRESS: + if (attr->type) { + switch_stun_ip_t *ip; + ip = (switch_stun_ip_t *) attr->value; + ip->port = ntohs(ip->port); + + } + break; + } + } while (switch_stun_packet_next_attribute(attr)); + return packet; +} + +SWITCH_DECLARE(const char *)switch_stun_value_to_name(int32_t type, int32_t value) + +{ + uint32_t x = 0; + const struct value_mapping *map = NULL; + switch (type) { + case SWITCH_STUN_TYPE_PACKET_TYPE: + map = PACKET_TYPES; + break; + case SWITCH_STUN_TYPE_ATTRIBUTE: + map = ATTR_TYPES; + break; + case SWITCH_STUN_TYPE_ERROR: + map = ERROR_TYPES; + break; + default: + map = NULL; + break; + } + + if (map) { + for(x = 0; map[x].value; x++) { + if (map[x].value == value) { + return map[x].name; + } + } + } + + return "INVALID"; +} + +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_get_mapped_address(switch_stun_packet_attribute_t *attribute, char *ipstr, uint16_t *port) +{ + switch_stun_ip_t *ip; + uint8_t x, *i; + char *p = ipstr; + + ip = (switch_stun_ip_t *) attribute->value; + i = (uint8_t *) &ip->address; + *ipstr = 0; + for(x =0; x < 4; x++) { + sprintf(p, "%u%s", i[x], x == 3 ? "" : "."); + p = ipstr + strlen(ipstr); + } + *port = ip->port; + return 1; +} + +SWITCH_DECLARE(char *)switch_stun_packet_attribute_get_username(switch_stun_packet_attribute_t *attribute, char *username, uint16_t len) +{ + uint16_t cpylen; + + cpylen = attribute->length > len ? attribute->length : len; + return memcpy(username, attribute->value, cpylen); +} + +SWITCH_DECLARE(switch_stun_packet_t *)switch_stun_packet_build_header(switch_stun_message_t type, + char *id, + uint8_t *buf + ) +{ + switch_stun_packet_header_t *header; + + + header = (switch_stun_packet_header_t *) buf; + header->type = htons(type); + header->length = 0; + + if (id) { + memcpy(header->id, id, 16); + } else { + switch_stun_random_string(header->id, 16, NULL); + } + + return (switch_stun_packet_t *) buf; +} + +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port) +{ + switch_stun_packet_attribute_t *attribute; + switch_stun_ip_t *ip; + uint8_t *i, x; + char *p = ipstr; + + attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) &packet->first_attribute + ntohs(packet->header.length)); + attribute->type = htons(SWITCH_STUN_ATTR_MAPPED_ADDRESS); + attribute->length = htons(8); + ip = (switch_stun_ip_t *) attribute->value; + + ip->port = htons(port); + ip->family = 1; + i = (uint8_t *) &ip->address; + + for(x = 0; x < 4 ; x++) { + i[x] = atoi(p); + if ((p = strchr(p, '.'))) { + p++; + } else { + break; + } + } + + packet->header.length += htons(sizeof(switch_stun_packet_attribute_t)) + attribute->length; + return 1; +} + +SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_username(switch_stun_packet_t *packet, char *username, uint16_t ulen) +{ + switch_stun_packet_attribute_t *attribute; + + if (ulen % 4 != 0) { + return 0; + } + attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) &packet->first_attribute + ntohs(packet->header.length)); + attribute->type = htons(SWITCH_STUN_ATTR_USERNAME); + attribute->length = htons(ulen); + if (username) { + memcpy(attribute->value, username, ulen); + } else { + switch_stun_random_string(attribute->value, ulen, NULL); + } + + packet->header.length += htons(sizeof(switch_stun_packet_attribute_t)) + attribute->length; + return 1; +}