#ifndef JUICE_STUN_H
#define JUICE_STUN_H
#include "juice.h"
#include "addr.h"
#include "hash.h"
#include "hmac.h"
#include <stdbool.h>
#include <stdint.h>
#pragma pack(push, 1)
#define STUN_TRANSACTION_ID_SIZE 12
struct stun_header {
uint16_t type;
uint16_t length;
uint32_t magic;
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
};
#define STUN_CLASS_MASK 0x0110
typedef enum stun_class {
STUN_CLASS_REQUEST = 0x0000,
STUN_CLASS_INDICATION = 0x0010,
STUN_CLASS_RESP_SUCCESS = 0x0100,
STUN_CLASS_RESP_ERROR = 0x0110
} stun_class_t;
typedef enum stun_method {
STUN_METHOD_BINDING = 0x0001,
STUN_METHOD_ALLOCATE = 0x003,
STUN_METHOD_REFRESH = 0x004,
STUN_METHOD_SEND = 0x006,
STUN_METHOD_DATA = 0x007,
STUN_METHOD_CREATE_PERMISSION = 0x008,
STUN_METHOD_CHANNEL_BIND = 0x009
} stun_method_t;
#define STUN_IS_RESPONSE(msg_class) (msg_class & 0x0100)
struct stun_attr {
uint16_t type;
uint16_t length;
uint8_t value[];
};
typedef enum stun_attr_type {
STUN_ATTR_MAPPED_ADDRESS = 0x0001,
STUN_ATTR_USERNAME = 0x0006,
STUN_ATTR_MESSAGE_INTEGRITY = 0x0008,
STUN_ATTR_ERROR_CODE = 0x0009,
STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000A,
STUN_ATTR_REALM = 0x0014,
STUN_ATTR_NONCE = 0x0015,
STUN_ATTR_MESSAGE_INTEGRITY_SHA256 = 0x001C,
STUN_ATTR_PASSWORD_ALGORITHM = 0x001D,
STUN_ATTR_USERHASH = 0x001E,
STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,
STUN_ATTR_PRIORITY = 0x0024,
STUN_ATTR_USE_CANDIDATE = 0x0025,
STUN_ATTR_PASSWORD_ALGORITHMS = 0x8002,
STUN_ATTR_ALTERNATE_DOMAIN = 0x8003,
STUN_ATTR_SOFTWARE = 0x8022,
STUN_ATTR_ALTERNATE_SERVER = 0x8023,
STUN_ATTR_FINGERPRINT = 0x8028,
STUN_ATTR_ICE_CONTROLLED = 0x8029,
STUN_ATTR_ICE_CONTROLLING = 0x802A,
STUN_ATTR_CHANNEL_NUMBER = 0x000C,
STUN_ATTR_LIFETIME = 0x000D,
STUN_ATTR_XOR_PEER_ADDRESS = 0x0012,
STUN_ATTR_DATA = 0x0013,
STUN_ATTR_XOR_RELAYED_ADDRESS = 0x0016,
STUN_ATTR_EVEN_PORT = 0x0018,
STUN_ATTR_REQUESTED_TRANSPORT = 0x0019,
STUN_ATTR_DONT_FRAGMENT = 0x001A,
STUN_ATTR_RESERVATION_TOKEN = 0x0022
} stun_attr_type_t;
#define STUN_IS_OPTIONAL_ATTR(attr_type) (attr_type & 0x8000)
struct stun_value_mapped_address {
uint8_t padding;
uint8_t family;
uint16_t port;
uint8_t address[];
};
typedef enum stun_address_family {
STUN_ADDRESS_FAMILY_IPV4 = 0x01,
STUN_ADDRESS_FAMILY_IPV6 = 0x02,
} stun_address_family_t;
struct stun_value_error_code {
uint16_t reserved;
uint8_t code_class; uint8_t code_number;
uint8_t reason[];
};
#define STUN_ERROR_INTERNAL_VALIDATION_FAILED 599
struct stun_value_channel_number {
uint16_t channel_number;
uint16_t reserved;
};
struct stun_value_even_port {
uint8_t r;
};
struct stun_value_requested_transport {
uint8_t protocol;
uint8_t reserved1;
uint16_t reserved2;
};
struct stun_value_password_algorithm {
uint16_t algorithm;
uint16_t parameters_length;
uint8_t parameters[];
};
typedef enum stun_password_algorithm {
STUN_PASSWORD_ALGORITHM_UNSET = 0x0000,
STUN_PASSWORD_ALGORITHM_MD5 = 0x0001,
STUN_PASSWORD_ALGORITHM_SHA256 = 0x0002,
} stun_password_algorithm_t;
#pragma pack(pop)
#define STUN_MAX_USERNAME_LEN 513 + 1
#define STUN_MAX_REALM_LEN 763 + 1
#define STUN_MAX_NONCE_LEN 763 + 1
#define STUN_MAX_SOFTWARE_LEN 763 + 1
#define STUN_MAX_ERROR_REASON_LEN 763 + 1
#define STUN_MAX_PASSWORD_LEN STUN_MAX_USERNAME_LEN
#define STUN_NONCE_COOKIE "obMatJos2"
#define STUN_NONCE_COOKIE_LEN 9
#define USERHASH_SIZE HASH_SHA256_SIZE
#define STUN_SECURITY_PASSWORD_ALGORITHMS_BIT 0x01
#define STUN_SECURITY_USERNAME_ANONYMITY_BIT 0x02
#define STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE 256
typedef struct stun_credentials {
char username[STUN_MAX_USERNAME_LEN];
char realm[STUN_MAX_REALM_LEN];
char nonce[STUN_MAX_NONCE_LEN];
uint8_t userhash[USERHASH_SIZE];
bool enable_userhash;
stun_password_algorithm_t password_algorithm;
uint8_t password_algorithms_value[STUN_MAX_PASSWORD_ALGORITHMS_VALUE_SIZE];
size_t password_algorithms_value_size;
} stun_credentials_t;
typedef struct stun_message {
stun_class_t msg_class;
stun_method_t msg_method;
uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE];
unsigned int error_code;
uint32_t priority;
uint64_t ice_controlling;
uint64_t ice_controlled;
bool use_candidate;
addr_record_t mapped;
stun_credentials_t credentials;
bool has_integrity;
bool has_fingerprint;
addr_record_t peer;
addr_record_t relayed;
addr_record_t alternate_server;
const char *data;
size_t data_size;
uint32_t lifetime;
uint16_t channel_number;
bool lifetime_set;
bool even_port;
bool next_port;
bool dont_fragment;
bool requested_transport;
uint64_t reservation_token;
} stun_message_t;
int stun_write(void *buf, size_t size, const stun_message_t *msg,
const char *password); int stun_write_header(void *buf, size_t size, stun_class_t class, stun_method_t method,
const uint8_t *transaction_id);
size_t stun_update_header_length(void *buf, size_t length);
int stun_write_attr(void *buf, size_t size, uint16_t type, const void *value, size_t length);
int stun_write_value_mapped_address(void *buf, size_t size, const struct sockaddr *addr,
socklen_t addrlen, const uint8_t *mask);
bool is_stun_datagram(const void *data, size_t size);
int stun_read(void *data, size_t size, stun_message_t *msg);
int stun_read_attr(const void *data, size_t size, stun_message_t *msg, uint8_t *begin,
uint8_t *attr_begin, uint32_t *security_bits);
int stun_read_value_mapped_address(const void *data, size_t size, addr_record_t *mapped,
const uint8_t *mask);
bool stun_check_integrity(void *buf, size_t size, const stun_message_t *msg, const char *password);
void stun_compute_userhash(const char *username, const char *realm, uint8_t *out);
void stun_prepend_nonce_cookie(char *nonce);
void stun_process_credentials(const stun_credentials_t *credentials, stun_credentials_t *dst);
const char *stun_get_error_reason(unsigned int code);
JUICE_EXPORT int _juice_stun_read(void *data, size_t size, stun_message_t *msg);
JUICE_EXPORT bool _juice_stun_check_integrity(void *buf, size_t size, const stun_message_t *msg,
const char *password);
#endif