mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
7834 lines
363 KiB
C
7834 lines
363 KiB
C
/*
|
|
Copyright 2006 - 2022 Intel Corporation
|
|
|
|
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#if !defined(NO_WEBRTC)
|
|
#if !defined(MICROSTACK_NOTLS)
|
|
|
|
// This is a version of the WebRTC stack with Initiator, TURN and proper retry logic.
|
|
|
|
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(_MINCORE)
|
|
#define _CRTDBG_MAP_ALLOC
|
|
#include <crtdbg.h>
|
|
#endif
|
|
|
|
#if defined(WINSOCK2)
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#elif defined(WINSOCK1)
|
|
#include <winsock.h>
|
|
#include <wininet.h>
|
|
#endif
|
|
|
|
#include "ILibParsers.h"
|
|
#include "ILibAsyncSocket.h"
|
|
#include "ILibAsyncUDPSocket.h"
|
|
#include "ILibWebRTC.h"
|
|
#include "ILibCrypto.h"
|
|
|
|
#include "ILibRemoteLogging.h"
|
|
#ifdef _REMOTELOGGINGSERVER
|
|
#include "ILibWebServer.h"
|
|
#define ILibWebRTC_LoggingServerPort 0
|
|
#endif
|
|
|
|
#define STUN_NUM_ADDR 5
|
|
#define ILibStunClient_TIMEOUT 2
|
|
#define ILibRUDP_WindowSize 32000
|
|
#define ILibRUDP_StartBufferSize 2048
|
|
#define ILibRUDP_StartMTU 1400
|
|
#define ILibRUDP_MaxMTU 2048
|
|
|
|
#define RTO_MIN 1000
|
|
#define RTO_MAX 6000
|
|
#define RTO_ALPHA 0.125
|
|
#define RTO_BETA 0.25
|
|
|
|
#define ILibSCTP_MaxReceiverCredits 100000
|
|
#define ILibSCTP_MaxSenderCredits 0 // When we do real-time traffic, reduce the buffering. In theory, this should never be used, leave to zero
|
|
#define ILibSCTP_Stream_SparseArraySize 16 // Must be a power of 2
|
|
#define ILibSCTP_Stream_MaximumCount 1024 // This is what Chrome/Firefox Support
|
|
#define ILibSTUN_MaxSlots 10 // MUST be less then 128, otherwise IceSlot and dtlsSlot will collide
|
|
#define ILibSTUN_MaxOfferAgeSeconds 60 // Offers are only valid for this amount of time
|
|
#define ILibSCTP_FastRetry_GAP 3
|
|
|
|
#define ILibSCTP_UnorderedFlag 0x04
|
|
|
|
//
|
|
// NAT Keep Alive Interval. We'll use a random value between these two values
|
|
//
|
|
#define ILibSTUN_MaxNATKeepAliveIntervalSeconds 15
|
|
#define ILibSTUN_MinNATKeepAliveIntervalSeconds 1
|
|
|
|
//
|
|
// WebRTC Spec requires that we continue to send STUN packets on the DTLS candidate pair, even after DTLS is established, for a variety of reasons, according to the following
|
|
// link posted by Google: http://tools.ietf.org/html/draft-muthu-behave-consent-freshness-04
|
|
//
|
|
#define ILibStun_MaxConsentFreshnessTimeoutSeconds 15 // Must be 15 Seconds or less to be spec compliant
|
|
|
|
#define RCTPDEBUG(x)
|
|
#define RCTPRCVDEBUG(x)
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
#define FOURBYTEBOUNDARY(a) ((a) + ((4 - ((a) % 4)) % 4))
|
|
|
|
#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)))
|
|
#define INET_SOCKADDR_PORT(x) (x->sa_family==AF_INET6?(unsigned short)(((struct sockaddr_in6*)x)->sin6_port):(unsigned short)(((struct sockaddr_in*)x)->sin_port))
|
|
#define UPDC32(octet, crc) (ILibStun_CRC32_table[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8))
|
|
|
|
#define IS_REQUEST(msg_type) (((msg_type) & 0x0110) == 0x0000)
|
|
#define IS_INDICATION(msg_type) (((msg_type) & 0x0110) == 0x0010)
|
|
#define IS_SUCCESS_RESP(msg_type) (((msg_type) & 0x0110) == 0x0100)
|
|
#define IS_ERR_RESP(msg_type) (((msg_type) & 0x0110) == 0x0110)
|
|
|
|
#define NAT_MAPPING_DETECTION(TransactionID) (TransactionID[11])
|
|
#define DTLS_PAUSE_FLAG 0x01
|
|
#define DTLS_RESUME_FLAG 0x02
|
|
|
|
#define ILibTURN_FLAGS_DATA_INDICATION 0x10000
|
|
#define ILibTURN_FLAGS_CHANNEL_DATA 0x20000
|
|
#define ILibTURN_MASK_CHANNEL_DATA 0xFFFF
|
|
#define ILibTURN_CHANNEL_BINDING_REFRESH_INTERVAL 480
|
|
#define ILibTURN_PERMISSION_REFRESH_INTERVAL 210
|
|
|
|
#define ReceiveHoldBuffer_Increment(list, value) {union{int i;void*p;}u; u.p = ILibLinkedList_GetTag(list); u.i += value; ILibLinkedList_SetTag(list, u.p);}
|
|
#define ReceiveHoldBuffer_Decrement(list, value) {union{int i;void*p;}u; u.p = ILibLinkedList_GetTag(list); u.i -= value; ILibLinkedList_SetTag(list, u.p);}
|
|
#ifdef _REMOTELOGGING
|
|
void ILibSctp_DebugSctpPacket(ILibRemoteLogging *logger, char *packet, int packetLen, char *note);
|
|
#endif
|
|
|
|
int ReceiveHoldBuffer_Used(ILibLinkedList list)
|
|
{
|
|
union { int i; void *p; } u;
|
|
u.p = ILibLinkedList_GetTag(list);
|
|
return u.i;
|
|
}
|
|
|
|
static unsigned int ILibStun_CRC32_table[] = { /* CRC polynomial 0xedb88320 */
|
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
|
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
|
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
|
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
|
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
|
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
|
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
|
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
|
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
|
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
|
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
|
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
|
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
|
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
|
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
|
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
|
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
|
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
|
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
|
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
|
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
|
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
|
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
|
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
|
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
|
};
|
|
|
|
typedef enum STUN_STATUS
|
|
{
|
|
STUN_STATUS_CHECKING_UDP_CONNECTIVITY = 0,
|
|
STUN_STATUS_CHECKING_FULL_CONE_NAT = 1,
|
|
STUN_STATUS_CHECKING_SYMETRIC_NAT = 2,
|
|
STUN_STATUS_CHECKING_RESTRICTED_NAT = 3,
|
|
STUN_STATUS_COMPLETE = 5,
|
|
STUN_STATUS_NOT_TESTED = 0xFFFF,
|
|
} STUN_STATUS;
|
|
|
|
typedef enum STUN_TYPE
|
|
{
|
|
STUN_BINDING_REQUEST = 0x0001,
|
|
STUN_BINDING_RESPONSE = 0x0101,
|
|
STUN_BINDING_ERROR_RESPONSE = 0x0111,
|
|
STUN_SHAREDSECRET_REQUEST = 0x0002,
|
|
STUN_SHAREDSECRET_RESPONSE = 0x0102,
|
|
STUN_SHAREDSECRET_ERROR_RESPONSE = 0x0112,
|
|
|
|
TURN_ALLOCATE = 0x003,
|
|
TURN_ALLOCATE_RESPONSE = 0x103,
|
|
TURN_ALLOCATE_ERROR = 0x113,
|
|
TURN_REFRESH = 0x004,
|
|
TURN_REFRESH_RESPONSE = 0x104,
|
|
TURN_REFRESH_ERROR = 0x114,
|
|
TURN_SEND = 0x016,
|
|
TURN_DATA = 0x017,
|
|
TURN_CREATE_PERMISSION = 0x008,
|
|
TURN_CREATE_PERMISSION_RESPONSE = 0x108,
|
|
TURN_CREATE_PERMISSION_ERROR = 0x118,
|
|
TURN_CHANNEL_BIND = 0x009,
|
|
TURN_CHANNEL_BIND_RESPONSE = 0x109,
|
|
TURN_CHANNEL_BIND_ERROR = 0x119,
|
|
TURN_TCP_CONNECT = 0x00A,
|
|
TURN_TCP_CONNECT_RESPONSE = 0x10A,
|
|
TURN_TCP_CONNECT_ERROR = 0x11A,
|
|
TURN_TCP_CONNECTION_BIND = 0x00B,
|
|
TURN_TCP_CONNECTION_BIND_RESPONSE = 0x10B,
|
|
TURN_TCP_CONNECTION_BIND_ERROR = 0x11B,
|
|
TURN_TCP_CONNECTION_ATTEMPT = 0x01C
|
|
} STUN_TYPE;
|
|
|
|
typedef enum STUN_ATTRIBUTES
|
|
{
|
|
STUN_ATTRIB_MAPPED_ADDRESS = 0x0001,
|
|
STUN_ATTRIB_RESPONSE_ADDRESS = 0x0002,
|
|
STUN_ATTRIB_CHANGE_REQUEST = 0x0003,
|
|
STUN_ATTRIB_SOURCE_ADDRESS = 0x0004,
|
|
STUN_ATTRIB_CHANGED_ADDRESS = 0x0005,
|
|
STUN_ATTRIB_USERNAME = 0x0006,
|
|
STUN_ATTRIB_PASSWORD = 0x0007,
|
|
STUN_ATTRIB_MESSAGE_INTEGRITY = 0x0008,
|
|
STUN_ATTRIB_ERROR_CODE = 0x0009,
|
|
STUN_ATTRIB_UNKNOWN = 0x000A,
|
|
STUN_ATTRIB_REFLECTED_FROM = 0x000B,
|
|
STUN_ATTRIB_XOR_PEER_ADDRESS = 0x0012,
|
|
STUN_ATTRIB_DATA = 0x0013,
|
|
STUN_ATTRIB_REALM = 0x0014,
|
|
STUN_ATTRIB_NONCE = 0x0015,
|
|
STUN_ATTRIB_XOR_RELAY_ADDRESS = 0x0016,
|
|
STUN_ATTRIB_REQUESTED_ADDRESS_FAMILLY = 0x0017,
|
|
STUN_ATTRIB_EVENPORT = 0x0018,
|
|
STUN_ATTRIB_REQUESTED_TRANSPORT = 0x0019,
|
|
STUN_ATTRIB_XOR_MAPPED_ADDRESS = 0x0020,
|
|
STUN_ATTRIB_RESERVATION_TOKEN = 0x0022,
|
|
STUN_ATTRIB_PRIORITY = 0x0024, // 36
|
|
STUN_ATTRIB_USE_CANDIDATE = 0x0025, // 37
|
|
STUN_ATTRIB_PADDING = 0x0026, // 38
|
|
STUN_ATTRIB_RESPONSE_PORT = 0x0027,
|
|
STUN_ATTRIB_SOFTWARE = 0x8022,
|
|
STUN_ATTRIB_ALTERNATE_SERVER = 0x8023,
|
|
STUN_ATTRIB_CACHE_TIMEOUT = 0x8027,
|
|
STUN_ATTRIB_FINGERPRINT = 0x8028,
|
|
STUN_ATTRIB_ICE_CONTROLLED = 0x8029,
|
|
STUN_ATTRIB_ICE_CONTROLLING = 0x802a,
|
|
STUN_ATTRIB_RESPONSE_ORIGIN = 0x802b,
|
|
STUN_ATTRIB_OTHER_ADDRESS = 0x802c,
|
|
STUN_ATTRIB_ECN_CHECK_STUN = 0x802d,
|
|
|
|
TURN_CHANNEL_NUMBER = 0x000C,
|
|
TURN_LIFETIME = 0x000D,
|
|
TURN_RESERVED_WAS_BANDWIDTH = 0x0010,
|
|
TURN_DONT_FRAGMENT = 0x001A,
|
|
TURN_RESERVED_WAS_TIMER_VAL = 0x0021,
|
|
TURN_TCP_CONNECTION_ID = 0x002A
|
|
} STUN_ATTRIBUTES;
|
|
|
|
typedef enum SCTP_COMMON_HEADER_FLAGS
|
|
{
|
|
SCTP_COMMON_HEADER_FLAGS_DATA = 0x01,
|
|
SCTP_COMMON_HEADER_FLAGS_CONTROL = 0x02,
|
|
SCTP_COMMON_HEADER_FLAGS_REORDER = 0x04
|
|
} SCTP_COMMON_HEADER_FLAGS;
|
|
typedef enum RCTP_CHUNK_TYPES
|
|
{
|
|
RCTP_CHUNK_TYPE_DATA = 0x0000,
|
|
RCTP_CHUNK_TYPE_INIT = 0x0001,
|
|
RCTP_CHUNK_TYPE_INITACK = 0x0002,
|
|
RCTP_CHUNK_TYPE_SACK = 0x0003,
|
|
RCTP_CHUNK_TYPE_HEARTBEAT = 0x0004,
|
|
RCTP_CHUNK_TYPE_HEARTBEATACK = 0x0005,
|
|
RCTP_CHUNK_TYPE_ABORT = 0x0006,
|
|
RCTP_CHUNK_TYPE_SHUTDOWN = 0x0007,
|
|
RCTP_CHUNK_TYPE_SHUTDOWNACK = 0x0008,
|
|
RCTP_CHUNK_TYPE_ERROR = 0x0009,
|
|
RCTP_CHUNK_TYPE_COOKIEECHO = 0x000A,
|
|
RCTP_CHUNK_TYPE_COOKIEACK = 0x000B,
|
|
RCTP_CHUNK_TYPE_ECNE = 0x000C,
|
|
RCTP_CHUNK_TYPE_RECONFIG = 0x0082,
|
|
RCTP_CHUNK_TYPE_FWDTSN = 0x00C0,
|
|
RCTP_CHUNK_TYPE_ASCONF = 0x00C1,
|
|
RCTP_CHUNK_TYPE_ASCONFACK = 0x0080
|
|
} RCTP_CHUNK_TYPES;
|
|
|
|
char* SCTP_CHUNK_TYPE_NAMES[] =
|
|
{
|
|
"DATA",
|
|
"INIT",
|
|
"INIT ACK",
|
|
"SACK",
|
|
"HEARTBEAT",
|
|
"HEARTBEAT ACK",
|
|
"ABORT",
|
|
"SHUTDOWN",
|
|
"SHUTDOWN ACK",
|
|
"ERROR",
|
|
"COOKIE ECHO",
|
|
"COOKIE ACK",
|
|
"ECNE",
|
|
"RECONFIG",
|
|
"FWDTSN",
|
|
"ASCONF",
|
|
"ASCONF ACK"
|
|
};
|
|
|
|
typedef enum SCTP_ERROR_CAUSE_CODES
|
|
{
|
|
SCTP_ERROR_CAUSE_CODE_INVALID_STREAM = 0x01,
|
|
SCTP_ERROR_CAUSE_CODE_MISSING_MANDATORY_PARAMATER = 0x02,
|
|
SCTP_ERROR_CAUSE_CODE_STALE_COOKIE = 0x03,
|
|
SCTP_ERROR_CAUSE_CODE_OUT_OF_RESOURCES = 0x04,
|
|
SCTP_ERROR_CAUSE_CODE_UNRESOLVABLE_ADDRESS = 0x05,
|
|
SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_CHUNK_TYPE = 0x06,
|
|
SCTP_ERROR_CAUSE_CODE_INVALID_MANDATORY_PARAMETER = 0x07,
|
|
SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_PARAMETERS = 0x08,
|
|
SCTP_ERROR_CAUSE_CODE_NO_USER_DATA = 0x09,
|
|
SCTP_ERROR_CAUSE_CODE_COOKIE_RECEIVED_DURING_SHUTDOWN = 0x0A,
|
|
SCTP_ERROR_CAUSE_CODE_ASSOCIATION_RESTART_NEW_ADDRESS = 0x0B,
|
|
SCTP_ERROR_CAUSE_CODE_USER_ABORT = 0x0C,
|
|
SCTP_ERROR_CAUSE_CODE_PROTOCOL_VIOLATION = 0x0D,
|
|
}SCTP_ERROR_CAUSE_CODES;
|
|
|
|
typedef enum SCTP_RECONFIG_TYPES
|
|
{
|
|
SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST = 0x0D,
|
|
SCTP_RECONFIG_TYPE_INCOMING_SSN_RESET_REQUEST = 0x0E,
|
|
SCTP_RECONFIG_TYPE_SSN_TSN_RESET_REQUEST = 0x0F,
|
|
SCTP_RECONFIG_TYPE_RECONFIGURATION_RESPONSE = 0x10,
|
|
SCTP_RECONFIG_TYPE_ADD_OUTGOING_STREAMS_REQUEST = 0x11,
|
|
SCTP_RECONFIG_TYPE_ADD_INCOMING_STREAMS_REQUEST = 0x12,
|
|
}SCTP_RECONFIG_TYPES;
|
|
|
|
typedef enum SCTP_INIT_PARAMS
|
|
{
|
|
SCTP_INIT_PARAM_STATE_COOKIE = 0x0007,
|
|
SCTP_INIT_PARAM_UNRELIABLE_STREAM = 0xC000,
|
|
SCTP_INIT_PARAM_PARTIAL_CHECKSUM = 0xC004,
|
|
SCTP_INIT_PARAM_RANDOM = 0x8002,
|
|
SCTP_INIT_PARAM_CHUNK_LIST = 0x8003,
|
|
SCTP_INIT_PARAM_REQUESTED_HMAC_ALGORITHM = 0x8004,
|
|
SCTP_INIT_PARAM_SUPPORTED_EXTENSIONS = 0x8008,
|
|
}SCTP_INIT_PARAMS;
|
|
|
|
struct STUN_MAPPED_ADDRESS
|
|
{
|
|
unsigned char unused;
|
|
unsigned char family;
|
|
unsigned short port;
|
|
unsigned int address;
|
|
};
|
|
|
|
struct TLV
|
|
{
|
|
unsigned short type;
|
|
unsigned short length;
|
|
char* data;
|
|
};
|
|
|
|
struct STUNHEADER
|
|
{
|
|
unsigned short type;
|
|
unsigned short length;
|
|
char transactionID[16];
|
|
void *Attributes;
|
|
};
|
|
|
|
#pragma pack(push,1)
|
|
#ifdef WIN32
|
|
#pragma warning( push )
|
|
#pragma warning( disable : 4200 )
|
|
#endif
|
|
struct ILibStun_IceStateCandidate
|
|
{
|
|
unsigned int addr;
|
|
unsigned short port;
|
|
};
|
|
typedef struct ILibSCTP_InitAckChunk
|
|
{
|
|
unsigned int InitiateTag;
|
|
unsigned int A_RWND;
|
|
unsigned short NumberOfOutboundStreams;
|
|
unsigned short NumberOfInboundStreams;
|
|
unsigned int InitialTSN;
|
|
}ILibSCTP_InitAckChunk;
|
|
typedef struct ILibSCTP_ChunkHeader
|
|
{
|
|
unsigned char chunkType;
|
|
unsigned char chunkFlags;
|
|
unsigned short chunkLength;
|
|
char chunkData[];
|
|
}ILibSCTP_ChunkHeader;
|
|
typedef struct ILibSCTP_ErrorCause_Header
|
|
{
|
|
unsigned short CauseCode;
|
|
unsigned short CauseLength;
|
|
char CauseInformation[];
|
|
}ILibSCTP_ErrorCause_Header;
|
|
typedef struct ILibSCTP_ReconfigChunk
|
|
{
|
|
unsigned char type;
|
|
unsigned char chunkFlags;
|
|
unsigned short chunkLength;
|
|
char* reconfigurationParameter;
|
|
}ILibSCTP_ReconfigChunk;
|
|
typedef struct ILibSCTP_Reconfig_Parameter_Header
|
|
{
|
|
unsigned short parameterType;
|
|
unsigned short parameterLength;
|
|
}ILibSCTP_Reconfig_Parameter_Header;
|
|
typedef struct ILibSCTP_Reconfig_OutgoingSSNResetRequest
|
|
{
|
|
unsigned short parameterType;
|
|
unsigned short parameterLength;
|
|
unsigned int RReqSeqNum;
|
|
unsigned int RResSeqNum;
|
|
unsigned int LastTSN;
|
|
unsigned short Streams[];
|
|
}ILibSCTP_Reconfig_OutgoingSSNResetRequest;
|
|
typedef struct ILibSCTP_Reconfig_IncomingSSNResetRequest
|
|
{
|
|
unsigned short parameterType;
|
|
unsigned short parameterLength;
|
|
unsigned int RReqSeqNum;
|
|
unsigned short Streams[];
|
|
}ILibSCTP_Reconfig_IncomingSSNResetRequest;
|
|
typedef struct ILibSCTP_StreamAttributesStruct
|
|
{
|
|
unsigned short StatusFlags;
|
|
unsigned short ReliabilityFlags;
|
|
}ILibSCTP_StreamAttributesStruct;
|
|
typedef struct ILibSCTP_StreamAttributesStruct_Data
|
|
{
|
|
unsigned short NextSequenceNumber;
|
|
unsigned short ReliabilityValue;
|
|
}ILibSCTP_StreamAttributesStruct_Data;
|
|
typedef struct ILibSCTP_Reconfig_Response
|
|
{
|
|
unsigned short parameterType;
|
|
unsigned short parameterLength;
|
|
unsigned int RESSEQNum;
|
|
unsigned int Result;
|
|
}ILibSCTP_Reconfig_Response;
|
|
|
|
typedef struct ILibSCTP_RPACKET
|
|
{
|
|
struct ILibSCTP_RPACKET* NextPacket;
|
|
unsigned short PacketSize;
|
|
unsigned char PacketResendCounter;
|
|
unsigned char PacketGAPCounter;
|
|
unsigned short Reliability;
|
|
unsigned int CreationTimeStamp;
|
|
unsigned int LastSentTimeStamp;
|
|
char GAP[12];
|
|
char Data[];
|
|
}ILibSCTP_RPACKET;
|
|
|
|
typedef struct ILibSCTP_DataPayload
|
|
{
|
|
unsigned char type;
|
|
unsigned char flags;
|
|
unsigned short length;
|
|
unsigned int TSN;
|
|
unsigned short StreamID;
|
|
unsigned short StreamSequenceNumber;
|
|
unsigned int ProtocolID;
|
|
char UserData[];
|
|
}ILibSCTP_DataPayload;
|
|
typedef struct ILibSCTP_FwdTSNPayload_Stream
|
|
{
|
|
unsigned int StreamNumber;
|
|
unsigned int StreamSequence;
|
|
}ILibSCTP_FwdTSNPayload_Stream;
|
|
typedef struct ILibSCTP_FwdTSNPayload
|
|
{
|
|
unsigned char type;
|
|
unsigned char flags;
|
|
unsigned short length;
|
|
unsigned int NewTSN;
|
|
ILibSCTP_FwdTSNPayload_Stream SkippedStreams[];
|
|
}ILibSCTP_FwdTSNPayload;
|
|
typedef union ILibSCTP_PendingTSN_Data
|
|
{
|
|
struct
|
|
{
|
|
unsigned char Type;
|
|
unsigned char Flags;
|
|
unsigned short StreamIdOffset;
|
|
}Data;
|
|
void* Raw;
|
|
}ILibSCTP_PendingTSN_Data;
|
|
typedef struct ILibICE_PeriodicState
|
|
{
|
|
struct sockaddr_in6 addr;
|
|
char flags;
|
|
void *ptr;
|
|
}ILibICE_PeriodicState;
|
|
typedef struct ILibTURN_TransactionData
|
|
{
|
|
void *Handler;
|
|
void *user;
|
|
}ILibTURN_TransactionData;
|
|
#ifdef WIN32
|
|
#pragma warning( pop )
|
|
#endif
|
|
#pragma pack(pop)
|
|
|
|
typedef struct ILibSCTP_Accumulator
|
|
{
|
|
char *buffer;
|
|
int bufferPtr;
|
|
int bufferLen;
|
|
}ILibSCTP_Accumulator;
|
|
|
|
ILibSCTP_Accumulator* ILibSCTP_CreateAccumulator()
|
|
{
|
|
ILibSCTP_Accumulator* retVal = (ILibSCTP_Accumulator*)malloc(sizeof(ILibSCTP_Accumulator));
|
|
if(retVal == NULL) {ILIBCRITICALEXIT(254);}
|
|
memset(retVal, 0, sizeof(ILibSCTP_Accumulator));
|
|
return retVal;
|
|
}
|
|
|
|
typedef union ILibSCTP_StreamAttributes
|
|
{
|
|
ILibSCTP_StreamAttributesStruct Data;
|
|
void* Raw;
|
|
}ILibSCTP_StreamAttributes;
|
|
typedef union ILibSCTP_StreamAttributes_Data
|
|
{
|
|
ILibSCTP_StreamAttributesStruct_Data Data;
|
|
unsigned int TSN;
|
|
void* Raw;
|
|
}ILibSCTP_StreamAttributes_Data;
|
|
|
|
typedef enum ILibSCTP_Reconfig_Result
|
|
{
|
|
ILibSCTP_Reconfig_Result_Success_NOP = 0,
|
|
ILibSCTP_Reconfig_Result_Success_Performed = 1,
|
|
ILibSCTP_Reconfig_Result_Denied = 2,
|
|
ILibSCTP_Reconfig_Result_Error_Wrong_SSN = 3,
|
|
ILibSCTP_Reconfig_Result_Error_Already_In_Progress = 4,
|
|
ILibSCTP_Reconfig_Result_Error_Bad_Sequence_Number = 5,
|
|
ILibSCTP_Reconfig_Result_In_Progress = 6,
|
|
}ILibSCTP_Reconfig_Result;
|
|
|
|
typedef enum ILibSCTP_SackStatus
|
|
{
|
|
ILibSCTP_SackStatus_NotSent = 0,
|
|
ILibSCTP_SackStatus_Sent = 1,
|
|
ILibSCTP_SackStatus_Skip = 2
|
|
}ILibSCTP_SackStatus;
|
|
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED 0x8000
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_ACK 0x4000
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_TSN_BEFORE_CLOSE 0x2000
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_PENDING_CLOSE 0x1000
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_FRAGMENTED 0x100
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_UNORDERED 0x0080
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_TIMED 0x0002
|
|
#define ILibSCTP_StreamAttributesData_Assigned_Status_REXMIT 0x0001
|
|
|
|
#if defined(_REMOTELOGGINGSERVER)
|
|
unsigned char ILibWebRTC_LoggingServer_HTML[3368] =
|
|
{
|
|
0x1F,0x8B,0x08,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xB5,0x5A,0xFD,0x72,0xDB,0xB8,0x11,0x7F,0x15,0x84,0x99,0xC4,0x64,0x24,0x51,0xA2,0xBE,0x6C,0x7D,0x50,0x19,0x5B,
|
|
0xD2,0x25,0x9E,0x3A,0x4E,0x1A,0xDB,0x49,0x6E,0x32,0x69,0x06,0x24,0x21,0x89,0x0D,0x45,0xAA,0x24,0x64,0xD9,0xA7,0xFA,0xC9,0xFA,0x47,0x1F,0xA9,0xAF,0xD0,0x5D,0x80,
|
|
0xA4,0x40,0x4A,0xF6,0x5D,0xA6,0xD7,0x51,0xC6,0x22,0x81,0xC5,0x7E,0x61,0xF7,0xB7,0x0B,0x28,0xFF,0xF9,0xD7,0xBF,0x87,0xCF,0x26,0xEF,0xC7,0xD7,0xBF,0x7E,0x98,0x92,
|
|
0x05,0x5F,0x06,0xE4,0xC3,0xCD,0xD9,0xC5,0xF9,0x98,0x68,0xB5,0x7A,0xFD,0x73,0x6B,0x5C,0xAF,0x4F,0xAE,0x27,0xE4,0xCB,0xDB,0xEB,0x77,0x17,0xC4,0x32,0x1B,0xE4,0x3A,
|
|
0xA6,0x61,0xE2,0x73,0x3F,0x0A,0x69,0x50,0xAF,0x4F,0x2F,0x35,0xA2,0x2D,0x38,0x5F,0xF5,0xEB,0xF5,0xCD,0x66,0x63,0x6E,0x5A,0x66,0x14,0xCF,0xEB,0xD7,0x1F,0xEB,0x77,
|
|
0xC8,0xCB,0xC2,0xC5,0xE9,0x63,0x8D,0x2B,0x2B,0x4D,0x8F,0x7B,0xDA,0x88,0x0C,0x71,0x06,0xBF,0x18,0xF5,0xE0,0x6B,0xC9,0x38,0x25,0x6E,0x14,0x72,0x16,0x72,0x5B,0xE3,
|
|
0xEC,0x8E,0xD7,0x91,0x60,0x40,0xDC,0x05,0x8D,0x13,0xC6,0xED,0x35,0x9F,0xD5,0x4E,0x34,0x82,0x02,0x6B,0xEC,0x1F,0x6B,0xFF,0xD6,0xD6,0xC6,0x92,0xBC,0x76,0x7D,0xBF,
|
|
0x62,0x5A,0xC6,0x43,0x25,0xF8,0x52,0xBB,0x39,0xAD,0x8D,0xA3,0xE5,0x8A,0x72,0xDF,0x09,0x98,0xB6,0x13,0x70,0x3E,0xB5,0xA7,0x93,0x37,0xD3,0x7C,0x55,0x48,0x97,0xCC,
|
|
0xD6,0x66,0x51,0xBC,0xA4,0xBC,0xE6,0x31,0xCE,0x5C,0x54,0x56,0x53,0x35,0x0A,0xD8,0x6A,0x11,0x85,0xCC,0x0E,0x23,0x5C,0xC5,0x7D,0x1E,0xB0,0xD1,0x67,0xE6,0x7C,0xBC,
|
|
0x1E,0x93,0x09,0x73,0xD6,0xF3,0x61,0x5D,0x8E,0x91,0x61,0xC2,0xEF,0xF1,0xDB,0x89,0xBC,0xFB,0xED,0x92,0xC6,0x73,0x3F,0xEC,0x37,0x06,0x2B,0xEA,0x79,0x7E,0x38,0x87,
|
|
0x27,0x27,0x8A,0x3D,0x16,0xC3,0x83,0x1B,0x05,0x51,0xDC,0x77,0x02,0xEA,0xFE,0x18,0xCC,0x40,0x50,0x2D,0xF1,0x7F,0x63,0x7D,0xAB,0xB5,0xBA,0x93,0xAF,0x33,0xBA,0xF4,
|
|
0x83,0xFB,0xBE,0x76,0x1D,0x03,0x7F,0x77,0xC1,0x38,0x79,0x77,0xA5,0x55,0xC9,0x69,0xEC,0xD3,0xA0,0x4A,0xDE,0xB2,0xE0,0x96,0x71,0xDF,0xA5,0x55,0x92,0x80,0x77,0x6B,
|
|
0x09,0x8B,0xFD,0xD9,0xC0,0x01,0x66,0xF3,0x38,0x5A,0x87,0x5E,0x4D,0xB2,0x7F,0xEE,0xB5,0xBC,0x9E,0xD7,0x1D,0x3C,0x3C,0x47,0x5B,0xA8,0x1F,0xB2,0x78,0xBB,0x4F,0x34,
|
|
0x9B,0xCD,0x06,0x1B,0xDF,0xE3,0x8B,0x7E,0xAF,0xDB,0x00,0xF9,0x99,0xDA,0x84,0xAE,0x79,0x94,0x6A,0x5C,0xE3,0xD1,0x2A,0x57,0xBF,0x16,0xFB,0xF3,0x05,0xEF,0x5B,0xAB,
|
|
0x3B,0x92,0x44,0x81,0xEF,0x91,0xE7,0xCE,0x31,0x7E,0xB2,0x69,0x27,0xE2,0x3C,0x5A,0xEE,0xC8,0x03,0x36,0x3B,0x44,0xBD,0xF3,0xCA,0xC3,0xF3,0x25,0x4D,0x38,0x46,0xC3,
|
|
0x56,0x2A,0x22,0x24,0x1F,0x70,0x5F,0x74,0xCB,0xE2,0x59,0x10,0x6D,0x24,0x01,0x46,0x4A,0x8D,0x06,0xFE,0x3C,0xEC,0x0B,0x8D,0x0E,0x38,0xA0,0xD1,0xEA,0x16,0x6C,0xDB,
|
|
0x51,0xF4,0xE3,0xB9,0xA3,0xB7,0x3B,0xD5,0x93,0x6E,0xD5,0x6A,0x1D,0x1B,0x03,0xA2,0x4C,0xD5,0x96,0xD1,0x6F,0xB5,0x00,0xDC,0x45,0xE3,0xDA,0x3C,0xA6,0x9E,0x0F,0x61,
|
|
0xA0,0xA3,0x15,0x55,0x02,0xAB,0xE8,0x6E,0x59,0xD5,0x32,0x48,0xE3,0x45,0x3A,0xDA,0xA8,0x76,0xAC,0xAA,0xD5,0x68,0xE2,0x60,0xB3,0xF7,0xA2,0xC4,0x72,0xC3,0x9C,0x1F,
|
|
0x3E,0x57,0xD8,0x09,0xF6,0x55,0x82,0x6C,0x09,0x78,0x17,0x98,0xA0,0x0D,0xF2,0x51,0x68,0x5F,0x4B,0xE0,0x59,0x07,0xF6,0x7B,0x32,0x8D,0x02,0x05,0xC8,0xAA,0x96,0x15,
|
|
0x30,0x0E,0x4B,0xFF,0x09,0x9B,0xFE,0x80,0x49,0xD1,0x9F,0xCC,0x6F,0x99,0xFC,0xB9,0x0C,0xCB,0xCC,0x78,0x24,0x7D,0xFC,0x73,0x0C,0x67,0x7E,0xC0,0x21,0x65,0x57,0x71,
|
|
0x34,0xF7,0xBD,0xFE,0xE4,0xCB,0xF9,0x92,0xCE,0x99,0x80,0x43,0x84,0x0C,0xF3,0x9D,0xEF,0xC6,0x51,0x12,0xCD,0xB8,0x99,0xCB,0x21,0x09,0xA7,0x31,0x1F,0xE3,0x0E,0x25,
|
|
0x3C,0xB6,0x8F,0x9E,0x37,0xBD,0x4E,0xF7,0xA4,0x77,0x54,0x25,0x2C,0xF4,0x94,0xE1,0x46,0xA3,0xD5,0xEA,0x76,0x8F,0xAA,0x6F,0xD2,0x85,0x88,0x65,0xB6,0x45,0x40,0x26,
|
|
0x26,0x6C,0xB0,0x5E,0x86,0xDF,0x83,0xED,0x2A,0x92,0xE0,0xD9,0x8F,0x59,0x00,0x50,0x76,0xCB,0x06,0x90,0x00,0x94,0xF7,0xD1,0x33,0x59,0x68,0xB7,0xD4,0xB4,0xDD,0xA5,
|
|
0x0B,0xB1,0x3A,0x85,0x90,0x57,0x13,0xFE,0xE1,0xF9,0x2C,0x8A,0xC0,0xAE,0xAD,0x1B,0x80,0x8F,0xFA,0x90,0xB2,0x8B,0x52,0x76,0xA9,0x69,0xA3,0x64,0x9A,0x0B,0x8A,0xB2,
|
|
0xF8,0x00,0x57,0xCB,0x6A,0xF5,0xBA,0xCD,0x4C,0xBA,0x00,0x0C,0x14,0x9F,0xBD,0xA7,0xA0,0x80,0x43,0x99,0x6C,0x42,0xB7,0x8A,0x46,0x42,0x86,0xC7,0xDC,0x28,0xA6,0xC2,
|
|
0x5E,0x60,0xCD,0x62,0xDC,0x42,0x85,0xBE,0xBF,0x40,0x1D,0x9F,0x5A,0x15,0x46,0xB8,0x20,0x67,0x8C,0xF9,0xFF,0x04,0x63,0x53,0xC0,0x75,0x6B,0xBB,0x6F,0x9F,0x5C,0xBF,
|
|
0x59,0xF8,0x9C,0x1D,0xB0,0xF5,0xA4,0x81,0x1F,0x89,0xD4,0x1B,0x26,0xD0,0xD0,0x89,0x02,0x2F,0x63,0xD8,0xDD,0xAA,0x5E,0x68,0xEE,0x7B,0xA1,0x79,0x70,0x5F,0xC6,0x0D,
|
|
0xFC,0x00,0x93,0xBB,0x44,0xD6,0xA0,0x03,0x60,0x3D,0x6D,0xE0,0x27,0xDF,0x63,0x70,0x27,0x69,0x36,0xF0,0x4F,0xF6,0x94,0x63,0x34,0xC4,0xD4,0x3A,0xE9,0x5B,0x38,0xF4,
|
|
0x60,0xCE,0xFC,0x3B,0xE6,0xA1,0xBA,0x5B,0xB5,0xBA,0xB8,0xD1,0x3A,0xF6,0x19,0x80,0x90,0x96,0x3E,0x91,0x90,0x6D,0xA0,0xCA,0x2C,0xA3,0x30,0x4A,0x56,0xD4,0x45,0x0F,
|
|
0xF9,0xE1,0x2C,0x02,0x97,0xC4,0xF7,0x05,0x9B,0x2C,0xC1,0xF5,0xCD,0xF4,0x72,0xFA,0xF1,0x7C,0xFC,0xFD,0xDD,0xFB,0xC9,0xCD,0xC5,0x74,0x4B,0xD4,0xA2,0xF6,0x60,0x5E,
|
|
0x5D,0xDF,0x5C,0x7E,0x3F,0x1F,0x4F,0xF7,0xA6,0xD7,0xC8,0x77,0x72,0x7D,0x71,0x55,0x9A,0xF1,0x68,0x0C,0xD6,0x32,0x16,0xE2,0xE2,0xF1,0xF5,0x87,0x03,0xD3,0xB7,0x7E,
|
|
0x14,0x40,0x2D,0x7C,0x30,0x6F,0x2E,0xFF,0x72,0xF9,0xFE,0xF3,0x65,0x89,0x24,0x66,0xB8,0x07,0x17,0xEF,0xDF,0x7C,0xBF,0x98,0x7E,0x9A,0x5E,0x7C,0xB7,0xB6,0x44,0x7D,
|
|
0x6D,0x16,0x5F,0x5B,0xC5,0xD7,0x76,0xF1,0xB5,0x53,0x7C,0xED,0x66,0xAF,0x9F,0xCE,0xAF,0xCE,0xCF,0x50,0xA4,0xE7,0x27,0xAB,0x80,0xDE,0xF7,0xFD,0x30,0x0D,0x26,0x9C,
|
|
0x7D,0x7B,0x3E,0x99,0x4C,0x2F,0x77,0x93,0x32,0x1E,0x4D,0xD1,0x4B,0xDC,0xF1,0x77,0x2C,0x5C,0x2B,0x9B,0xDA,0x7F,0xFE,0x4B,0x0F,0x3F,0xB0,0x67,0x77,0xB5,0x64,0x41,
|
|
0x3D,0xC8,0xBD,0x06,0x81,0xBC,0x85,0xF8,0x90,0x18,0x45,0x1A,0xD5,0xF4,0x9F,0xD9,0x02,0x60,0x48,0xDB,0x07,0xA5,0x96,0xBA,0xAE,0x3B,0x28,0x0A,0xCB,0x01,0x83,0x3A,
|
|
0x40,0xB3,0x86,0xF8,0x95,0xB5,0x5B,0x54,0x61,0xF8,0xF2,0x13,0x68,0x36,0x30,0x4A,0x25,0xF9,0x1E,0x6C,0x60,0x86,0x2E,0xFD,0xB0,0x26,0xF3,0xBF,0x23,0xA1,0xE5,0x2E,
|
|
0x7D,0xB5,0x1A,0xF8,0xFE,0x5B,0xCD,0x87,0x1C,0xBA,0x83,0x49,0x8C,0x56,0x77,0x89,0xA6,0x65,0x19,0xD7,0x6E,0xB7,0x07,0x45,0xCF,0xD4,0x9C,0x20,0x82,0x80,0xC8,0xC2,
|
|
0x47,0xE8,0x71,0xA2,0xE4,0x84,0xEC,0x26,0xD4,0x91,0x27,0xB0,0xE3,0x60,0xBE,0x4B,0xDD,0x4E,0x3A,0x2F,0x06,0xEE,0x3A,0x4E,0x30,0x54,0xD8,0x8C,0xAE,0x03,0xBE,0x43,
|
|
0xB4,0x85,0xEF,0x79,0x10,0x57,0xFB,0x60,0x9A,0xA9,0x5F,0x42,0x96,0x1E,0x7E,0xD4,0x6E,0x41,0xD8,0xF5,0x30,0xAC,0xA7,0xFD,0xDD,0xB0,0x9E,0xF6,0xAE,0xD8,0xE8,0x91,
|
|
0x28,0x04,0x48,0xF6,0x6C,0xCD,0x9F,0x11,0x9D,0x03,0x88,0x47,0x33,0x5D,0x94,0x80,0xF5,0xCA,0x20,0xCF,0x6C,0x9B,0x1C,0x21,0xE4,0xCC,0xC0,0x15,0xDE,0x91,0x41,0xD2,
|
|
0x19,0xDD,0x18,0x68,0xB0,0x30,0x0D,0x8C,0x25,0x04,0x86,0xAD,0x2D,0x68,0xE8,0x05,0x6C,0xBC,0x8B,0x15,0x9D,0xDD,0x42,0xEE,0x19,0xD8,0x74,0x7A,0xFE,0x2D,0xF1,0x41,
|
|
0x46,0xDE,0xC9,0xA9,0x83,0x59,0xFB,0xA4,0x11,0xA1,0x1F,0x70,0x92,0xA0,0xD4,0xED,0x82,0xCB,0xF2,0x9D,0x7B,0x51,0xF6,0x47,0xC6,0x22,0x5D,0xA4,0x14,0x16,0x75,0x7D,
|
|
0xEA,0x13,0xF7,0x04,0x3F,0xC5,0x6D,0x14,0x80,0xA3,0xEE,0x1A,0xEC,0xA2,0x36,0x82,0x26,0x38,0x8E,0xC2,0xF9,0x68,0x88,0x58,0x93,0x33,0xCF,0x9B,0xDC,0x76,0xB7,0xD4,
|
|
0xE4,0x3E,0xD9,0xD4,0x6A,0xD8,0x68,0xD7,0xB0,0xD3,0xFE,0xC8,0x68,0xC0,0xFD,0x25,0x23,0x17,0xD1,0x1C,0x42,0x16,0x9A,0x6E,0x64,0x32,0xC2,0x3D,0x91,0xE2,0xEA,0x60,
|
|
0xCB,0xFF,0x6A,0x51,0xA7,0x64,0x90,0xD5,0xFE,0x03,0x16,0x21,0xD1,0xCF,0x58,0x34,0x04,0x70,0x0D,0xC5,0xC6,0x89,0x63,0xC3,0x22,0x4A,0xB8,0x86,0x76,0xC0,0xE8,0xE8,
|
|
0x31,0xAB,0x14,0xE3,0xC4,0xC2,0x68,0xE5,0x50,0x11,0x03,0x9C,0xC2,0xF1,0x26,0xD3,0x49,0xD9,0xEB,0xD4,0xE0,0x26,0xD6,0x1A,0x38,0xCC,0xB0,0x20,0x48,0xED,0xB2,0xB5,
|
|
0x86,0x7C,0x47,0x88,0xCF,0xDF,0x03,0x9A,0x24,0xB6,0x26,0xD8,0x58,0x82,0x6D,0x8C,0x7F,0x3C,0x21,0xEC,0x8A,0xAF,0xC3,0x73,0x97,0x9D,0xAD,0x21,0x0D,0x43,0x6D,0x4F,
|
|
0x16,0x08,0xC8,0x84,0xA1,0x23,0xD2,0x1C,0x5C,0x45,0xBE,0xAC,0xA4,0xF2,0x15,0x63,0x5B,0x04,0x7C,0xE0,0xBB,0x3F,0x50,0xFF,0xF9,0x3C,0x60,0xC8,0x58,0x37,0x8A,0xD2,
|
|
0x5B,0xDA,0x08,0x2B,0x47,0x1D,0x2A,0x07,0x1C,0xAB,0xBC,0x9D,0x16,0x13,0x1E,0x24,0x7F,0xBE,0x0A,0xC8,0xF5,0x80,0x0A,0x58,0x9E,0x8A,0xE2,0xAF,0x5C,0xBE,0xFA,0x3F,
|
|
0x78,0x00,0xB8,0x1E,0xF2,0x00,0x94,0xBF,0x54,0x7C,0x5D,0xEC,0x44,0x5D,0x6C,0xF3,0x7E,0x1C,0xAC,0xA0,0x19,0xFD,0x9E,0x9E,0x54,0x8B,0x30,0x21,0xFB,0x47,0x65,0xEC,
|
|
0x28,0x66,0xB3,0x98,0x25,0x0B,0x69,0xC4,0xC4,0xBF,0x3D,0x2A,0xA6,0x89,0x3C,0x43,0xC9,0x7A,0x20,0x1B,0x17,0xB4,0x4B,0xAD,0x2D,0x10,0xA4,0x7E,0xB8,0x5A,0xC3,0x21,
|
|
0x05,0x3B,0x55,0xCD,0x49,0xBD,0x71,0x4B,0xA1,0xA4,0xDB,0xDA,0x47,0xC9,0x5E,0x31,0x31,0x15,0x88,0x50,0xA7,0x66,0xE7,0x68,0xB8,0xB0,0x76,0xD1,0x8F,0x53,0x0B,0x2B,
|
|
0x9F,0x5F,0xA5,0x80,0xE6,0x87,0xAA,0x39,0x00,0x9A,0x7C,0x9D,0xC0,0x5B,0x19,0xE1,0xDA,0x8D,0x7D,0x25,0xE5,0x32,0xD1,0xCE,0x81,0x23,0x44,0x3F,0x97,0x2F,0xCB,0xCA,
|
|
0x5D,0x39,0xCD,0x45,0x9B,0xBB,0xCB,0x68,0x34,0x1D,0x19,0xE5,0x89,0x2A,0x15,0x40,0x5C,0xCE,0x33,0x95,0x94,0x52,0xF3,0x70,0xA2,0x02,0x58,0x7D,0x86,0x6A,0x19,0x6D,
|
|
0xF6,0xA3,0x26,0xCF,0xD1,0xAE,0x30,0x22,0x03,0xE7,0xDA,0x7D,0x3F,0x81,0x33,0x45,0x20,0xF6,0xAE,0xC4,0x4D,0x36,0xC2,0xBB,0xB4,0x7F,0x32,0xAD,0xAD,0xC6,0x01,0x99,
|
|
0xA5,0xDC,0x3E,0xBA,0x93,0x2C,0xF3,0x58,0x50,0x5A,0x61,0x81,0x9B,0xA5,0xFD,0xCF,0x53,0x62,0x9F,0x5C,0xC6,0x8F,0xC4,0xD6,0x00,0x9F,0xE1,0x40,0x74,0xAF,0x8D,0xCE,
|
|
0x43,0x79,0xB5,0x02,0x65,0x97,0x50,0x4E,0x86,0x34,0x5B,0x5A,0xA6,0x24,0x0B,0x88,0x17,0x5B,0x5C,0x2B,0x25,0xFD,0x7A,0x1D,0x1B,0x4F,0x73,0x09,0xE1,0x83,0x3B,0x18,
|
|
0xD3,0x00,0x7A,0xA8,0xA5,0x36,0x3A,0x34,0x3A,0xAC,0xD3,0xD1,0x93,0xC9,0x52,0x72,0xA2,0xD2,0x8C,0xE5,0x89,0x57,0x18,0x4B,0x15,0x3C,0x14,0x55,0x19,0xB9,0x68,0x1A,
|
|
0x94,0x50,0x87,0x6D,0xBE,0x80,0x62,0x1D,0xE8,0x96,0xA8,0xD6,0xCE,0xE8,0x13,0x8B,0x1D,0xEC,0x36,0xEE,0xFB,0xC4,0x1A,0xD6,0x9D,0xA2,0x16,0xBF,0xCB,0xA5,0xB9,0xCF,
|
|
0xA5,0xF9,0xF3,0x5C,0x5A,0xFB,0x5C,0x5A,0x3F,0xCF,0xA5,0xBD,0xCF,0xA5,0xFD,0xF3,0x5C,0x3A,0xFB,0x5C,0x3A,0x05,0x2E,0x8B,0xF8,0x77,0x58,0x8D,0xF1,0xA4,0x0A,0xFC,
|
|
0x10,0xAE,0x05,0x2B,0x31,0x80,0xDD,0x40,0x72,0x98,0x91,0xB2,0xD7,0x1F,0xE8,0x3A,0x61,0x1F,0x59,0xB2,0x5E,0x32,0xED,0x51,0x01,0xD7,0x02,0x91,0x15,0xD2,0x4C,0xD0,
|
|
0x87,0xD3,0x9B,0xAB,0x69,0x41,0x46,0xFA,0x05,0x79,0xEA,0xAF,0x32,0x38,0x14,0x17,0x97,0x7F,0xA7,0xB7,0x54,0x8E,0x6A,0xA3,0x5B,0xD0,0x4E,0x2C,0x9D,0xD8,0x33,0x1A,
|
|
0x24,0x6C,0x80,0x03,0x58,0xDB,0x94,0x57,0xAC,0x33,0xEA,0x2C,0xE0,0xBE,0xF2,0xBA,0x49,0xA0,0x73,0x66,0x5C,0x3C,0x83,0x25,0x21,0x1C,0x0B,0x99,0xA7,0xCC,0x8F,0xDF,
|
|
0x4D,0xEC,0xED,0xE5,0xFB,0xCB,0x69,0xBF,0x51,0xCD,0x8E,0x5B,0xFD,0x66,0x15,0x99,0xF6,0xDB,0x55,0x64,0xD6,0x3F,0x79,0x10,0x94,0xB9,0xDB,0x6D,0x6B,0x30,0x5B,0x87,
|
|
0xE2,0x78,0x49,0xFE,0xAA,0x53,0x63,0x1B,0x33,0xBE,0x8E,0x43,0xE2,0x45,0x2E,0x58,0x1C,0x72,0x73,0xCE,0xF8,0x34,0x60,0xF8,0x78,0x76,0x7F,0xEE,0x01,0xC5,0xC3,0x8E,
|
|
0xFE,0x4A,0x59,0x80,0x8B,0xE5,0x71,0x57,0x21,0x98,0xEA,0xB4,0xEA,0x18,0x5B,0x31,0x07,0xE9,0x83,0x69,0xE8,0xD9,0xCF,0x1C,0x85,0xE2,0x53,0x4A,0x71,0x95,0x92,0x60,
|
|
0x86,0xD9,0xBA,0xF3,0x5A,0xD3,0xFA,0x9A,0x48,0x34,0x55,0xDE,0xA9,0xC2,0xCE,0x07,0xF3,0x63,0xBC,0x8F,0xAE,0xD8,0x2A,0xBF,0xB7,0x87,0x48,0x0A,0x14,0xE3,0xA7,0xAD,
|
|
0x4C,0xCE,0xEE,0xC7,0x18,0x10,0x97,0x14,0x36,0xBC,0x60,0xED,0xF8,0x93,0xEE,0x56,0x3D,0x63,0x8B,0xFE,0xA3,0x36,0xF0,0x71,0x0D,0x28,0x11,0xB1,0x8E,0xEF,0x8E,0xDD,
|
|
0x18,0x38,0x43,0x6A,0x06,0x2C,0x9C,0xF3,0xC5,0xC0,0xA9,0x54,0x8C,0x2D,0xFD,0xEA,0x7C,0x93,0x1E,0xC9,0x0D,0xF3,0x1E,0x76,0xFC,0xCE,0x43,0xDE,0x6A,0x5E,0x47,0x57,
|
|
0x3C,0x56,0x14,0x82,0x37,0xC0,0x6A,0x73,0x16,0x47,0xCB,0xF1,0x82,0xC6,0xE3,0xC8,0x63,0xBA,0x4E,0x47,0xA3,0x66,0xDB,0x78,0xD9,0xEC,0x74,0xAA,0xF8,0x6C,0x75,0x77,
|
|
0xCF,0x27,0xF2,0x91,0xE2,0x5F,0xA3,0xC0,0xDB,0xEA,0xFE,0x51,0xDE,0x8F,0xF0,0x80,0xFE,0xDA,0xBB,0x5A,0x44,0x31,0x97,0x1E,0x95,0x4C,0x74,0x6A,0xBA,0xE9,0xDA,0x53,
|
|
0xAE,0x3B,0xC6,0x70,0x78,0x62,0x54,0x8A,0x63,0x15,0xAB,0xC4,0x04,0x94,0x79,0x8A,0xC5,0x2B,0xAB,0x7B,0x7C,0x7C,0xDC,0x04,0xAB,0x2A,0xFA,0x1E,0xA7,0x57,0xDD,0x4E,
|
|
0xA7,0x75,0x60,0xA6,0x69,0xBC,0x6A,0x76,0xBA,0x7B,0xA2,0x5B,0x25,0xD1,0x17,0xD0,0x32,0x17,0x64,0xEF,0x09,0x3F,0x36,0x9B,0x8D,0xCE,0x71,0xA7,0xD7,0x6E,0xB4,0x8E,
|
|
0x7B,0xCD,0xE3,0x5E,0x9B,0x55,0x1E,0xD1,0xA4,0x79,0x62,0xB5,0x8F,0xDB,0xBD,0xE3,0xEE,0xB1,0xD5,0xE8,0x76,0x0E,0xEB,0x64,0x35,0x7A,0xBD,0x8E,0x65,0x75,0x9B,0x60,
|
|
0xD1,0x01,0x8A,0x96,0xF1,0xAA,0xDD,0xEC,0xB5,0x7B,0xDD,0xE3,0x66,0x6F,0x5F,0xF9,0xF6,0x53,0xAE,0xE8,0x3C,0xEA,0x8A,0xEE,0x61,0x57,0x1C,0x2B,0xAE,0xB8,0x12,0x17,
|
|
0x8D,0xCB,0x25,0x74,0x9C,0xD2,0x1B,0x29,0x90,0x98,0x09,0x4C,0xE8,0x85,0x60,0xA9,0x38,0xCA,0x3A,0x05,0x62,0xB7,0x90,0x59,0x4A,0xF3,0x52,0xD5,0xD4,0xC4,0x7C,0xEB,
|
|
0x7B,0xEC,0x26,0x94,0x1D,0xAC,0x07,0xB4,0xFE,0x4C,0x7F,0x86,0xF8,0x03,0xAB,0x20,0x69,0xB4,0xD2,0xCD,0x0F,0x2C,0x4E,0x33,0x1B,0xE9,0x10,0x9C,0x52,0x3A,0xE5,0x0E,
|
|
0xA8,0x48,0x83,0xC8,0x95,0xF1,0xDA,0x5D,0x04,0xED,0x68,0x76,0x9A,0x1C,0xC0,0x6C,0xA1,0x8D,0xC4,0x5D,0x63,0x7B,0xEA,0x41,0x4C,0xCC,0x75,0xED,0xEB,0xCD,0x39,0x11,
|
|
0x54,0xDE,0x37,0x58,0x9F,0xA2,0x72,0x4A,0x35,0x40,0x53,0x0F,0x14,0x8A,0xAA,0x06,0xD8,0xAF,0x55,0x74,0x49,0xF5,0x5A,0xFB,0x38,0xBD,0xBA,0x79,0x37,0x05,0x9C,0x12,
|
|
0x03,0x9A,0x51,0xD1,0xB0,0x26,0x68,0xC6,0xE0,0x31,0x81,0x92,0x8F,0x90,0xB8,0xD3,0x38,0xAF,0x88,0xB0,0x2D,0x3B,0x54,0x76,0x06,0xD9,0xC2,0x7C,0x8C,0x08,0x32,0xFC,
|
|
0x11,0x2C,0x9C,0x33,0x8F,0xF0,0xA8,0x4F,0x34,0xD8,0xAD,0x81,0x84,0xA3,0x86,0x80,0x22,0x0A,0x70,0x4E,0x87,0x76,0x77,0x50,0xA9,0xD0,0xD4,0x61,0xBB,0x2B,0x2B,0xAD,
|
|
0x42,0xAB,0x30,0x09,0xD8,0x2A,0x2F,0x62,0x76,0x08,0x5B,0xDA,0xBF,0x81,0x1A,0x2F,0x50,0x53,0x4C,0x2C,0x29,0x55,0x25,0x4C,0xAC,0xE1,0x30,0x57,0xCB,0x50,0xE2,0x20,
|
|
0xD5,0x99,0x56,0x67,0x55,0x57,0xE2,0xA4,0x63,0x6B,0x1A,0xFA,0x63,0x66,0xDB,0xF9,0xA5,0x07,0x28,0x76,0x5A,0x8C,0x25,0xA5,0xCE,0x1F,0x15,0x2F,0x11,0x8F,0xC8,0x08,
|
|
0xAC,0xA4,0xC2,0xB5,0x71,0x7A,0x28,0x00,0x0F,0xCB,0x6C,0xC6,0xD8,0xC8,0xFC,0x9C,0x8E,0xA0,0x4C,0xA8,0xBD,0x44,0xB9,0xF3,0xD3,0x84,0x87,0x3C,0x5B,0x77,0x87,0x76,
|
|
0x51,0xF3,0xD7,0x92,0x4E,0xDC,0xE0,0x9D,0x5F,0xE0,0x4E,0x92,0xDD,0x9D,0x1D,0x48,0x49,0x36,0x3E,0x77,0x17,0x00,0xF3,0x5B,0x97,0x26,0x8C,0x34,0xFB,0x7B,0x9C,0x9D,
|
|
0x98,0xD1,0x1F,0x03,0x31,0xDB,0x2E,0xCD,0x36,0x0B,0xB3,0x27,0xA5,0xD9,0x56,0x61,0xD6,0xEA,0x96,0xA6,0xDB,0x85,0xE9,0x56,0x59,0x72,0x27,0x9B,0x4E,0x2F,0xB6,0x1E,
|
|
0x51,0xEC,0x21,0x35,0x80,0xFE,0xAD,0xD5,0x3C,0xEE,0x9E,0xA4,0x66,0xE0,0x86,0xE6,0xCD,0x01,0xEC,0x4F,0x39,0x3B,0x55,0xC9,0x48,0x2B,0xBA,0x07,0xA0,0x53,0xB3,0xB3,
|
|
0x4C,0x23,0x5A,0x0B,0xE4,0xA5,0x64,0x67,0x49,0x45,0x98,0x2D,0x5E,0xD2,0x66,0x4A,0x3E,0x15,0x0C,0x10,0xE0,0x15,0x56,0xF1,0x2A,0x9A,0x8C,0x03,0x3D,0x64,0x1B,0x32,
|
|
0xA1,0x1C,0xB2,0xDA,0xE4,0xD1,0x45,0xE4,0xD2,0x80,0x5D,0xFB,0x4B,0x26,0x2B,0x9B,0x6E,0x40,0x0A,0x62,0x52,0xCC,0x4A,0xE1,0xB2,0x0B,0xD0,0xFC,0xB2,0x6D,0x9B,0x57,
|
|
0xFE,0xAC,0xDD,0xCB,0x68,0xF4,0x2C,0x71,0xD4,0x23,0x40,0x8E,0x34,0x79,0x5A,0x4E,0x13,0x3C,0x54,0xF8,0xC9,0x02,0x04,0x67,0xBD,0x18,0xAC,0x36,0x4D,0x13,0x02,0x27,
|
|
0x85,0x57,0x1B,0xD5,0xFD,0xCC,0x9C,0x2B,0xF1,0xA6,0x6B,0x1B,0x3C,0xC7,0x68,0x95,0x8D,0xB0,0xD4,0x0C,0x40,0x7D,0xB1,0x04,0xEF,0x7B,0x2A,0x5A,0x7D,0xB7,0xCE,0x74,
|
|
0xFC,0x90,0xC6,0xF7,0xE2,0xF7,0x1F,0x8D,0xC6,0x70,0x18,0x72,0xD6,0xB3,0x19,0x9C,0xF3,0x72,0x82,0x28,0x8C,0x56,0x2C,0x2C,0xE8,0xBC,0xEB,0x07,0x79,0xBC,0x66,0xB9,
|
|
0x9E,0xF9,0xB0,0xD0,0xEC,0x41,0xE1,0xE0,0x06,0x51,0xC2,0x1E,0x61,0x21,0x5B,0xCA,0x8C,0xC7,0x38,0x37,0x8F,0xC0,0x1A,0xBE,0xC7,0x09,0xCE,0x5F,0x09,0x9D,0x17,0x79,
|
|
0xC9,0xE4,0x47,0x07,0xFC,0xE2,0x07,0x0C,0x6B,0x31,0x8B,0x01,0x59,0x1C,0x33,0xBD,0x1C,0xCD,0x69,0xD3,0x86,0x6A,0x6E,0x7B,0x26,0x6C,0x0E,0xF4,0x61,0x66,0x0C,0x30,
|
|
0x19,0xA4,0x4D,0xAE,0xBD,0xEB,0x42,0xE6,0xD5,0x86,0x04,0xBB,0x59,0x61,0xB0,0x29,0xF0,0x56,0x77,0x5F,0xCA,0x18,0xB7,0xED,0x34,0xD6,0x53,0xE5,0xDD,0xEA,0xDC,0x4C,
|
|
0xD6,0x4E,0x22,0x23,0xA4,0x6D,0x54,0x67,0x00,0xBE,0xA0,0x06,0x84,0x9E,0x77,0x9A,0x9C,0x09,0x47,0xA7,0xE1,0x83,0xCA,0x9E,0x05,0x91,0xA3,0x7F,0xA5,0xA6,0x47,0x39,
|
|
0xFD,0x66,0xA8,0x38,0xBD,0x7F,0x19,0xEB,0x3E,0x11,0x28,0x03,0x69,0x7F,0x1E,0x65,0x4C,0x36,0x97,0xBF,0x40,0xEF,0xF5,0x01,0xAF,0x81,0x74,0xD7,0xC4,0xAB,0x9A,0x2F,
|
|
0xB5,0x9C,0x02,0x6F,0x8E,0x4D,0x79,0xD0,0xBF,0xC0,0x5F,0x39,0x25,0xC1,0xAF,0x07,0x09,0xAE,0xA3,0x95,0xB0,0xDA,0x79,0xF9,0xD2,0x79,0x66,0x87,0xEB,0x20,0x78,0xF9,
|
|
0x52,0x77,0x4C,0x38,0xE0,0x94,0x2F,0xE7,0xFE,0xF9,0xCF,0x74,0x58,0xB9,0x2D,0xCB,0xC7,0x94,0x2B,0x2C,0x23,0x6F,0x6B,0x8B,0xF6,0x18,0x03,0x9A,0xB6,0xB0,0x78,0xFA,
|
|
0xB7,0x53,0xAD,0x2B,0xDA,0xEA,0x4E,0xCB,0x67,0x78,0xB4,0x4A,0x27,0x7E,0x2D,0x4E,0x64,0x5D,0xAF,0x26,0x6E,0xFC,0xB5,0x07,0x20,0x8A,0xC5,0x0D,0xF6,0x44,0x62,0x82,
|
|
0x9E,0x01,0x39,0x11,0xF1,0xB6,0xF3,0xB5,0xAC,0x44,0xE2,0xA7,0x51,0xD9,0xAF,0xD0,0xAF,0x5A,0xF9,0xB7,0x2F,0xED,0x1B,0x56,0x32,0xFC,0xF5,0x08,0x70,0x7B,0x1E,0xB3,
|
|
0x7B,0xAD,0xBC,0x5E,0xDE,0x26,0x6E,0xC5,0x09,0x4B,0xB4,0x23,0x03,0x95,0x2F,0x9C,0x38,0x4A,0xBE,0x32,0xC4,0x99,0x69,0xBF,0xFA,0x65,0x00,0xA9,0x56,0x40,0x1C,0x7B,
|
|
0xDD,0xEC,0x5B,0x86,0x31,0x38,0xDC,0xE2,0x08,0x82,0xBD,0x42,0x5B,0xD2,0x50,0x5E,0x36,0x6E,0xC5,0xA1,0x4F,0x34,0x42,0x7B,0x1A,0x2A,0xDB,0x66,0x88,0x73,0xDC,0xBE,
|
|
0x7A,0x38,0xAA,0xAA,0x86,0xEF,0xAA,0x6A,0x85,0xAE,0x4A,0x4C,0xFE,0x9E,0x5A,0xF2,0x12,0x72,0x2B,0x0E,0x9F,0xA2,0xF7,0xDA,0x77,0x9C,0x12,0x39,0xE2,0x5C,0x79,0xC0,
|
|
0x6B,0x30,0x5A,0xF0,0x18,0xBC,0x17,0x3C,0xA6,0x36,0x72,0x62,0x72,0x4F,0xAD,0xC1,0xB0,0x2E,0xCF,0xCE,0x78,0xC2,0xC6,0xF0,0x17,0x3F,0xB4,0xE0,0xFF,0x15,0xFA,0x2F,
|
|
0x6E,0x8F,0x04,0x18,0xAF,0x24,0x00,0x00
|
|
};
|
|
#endif
|
|
|
|
struct ILibStun_IceState
|
|
{
|
|
// These 2 fields must be the first 2 fields of this structure
|
|
char* rusername;
|
|
char* rkey;
|
|
|
|
//
|
|
int rusernamelen;
|
|
int rkeylen;
|
|
int isDoingConnectivityChecks;
|
|
char* offerblock;
|
|
unsigned short blockversion;
|
|
unsigned int blockflags;
|
|
char userAndKey[43];
|
|
char tieBreaker[8];
|
|
char* dtlscerthash;
|
|
int dtlscerthashlen;
|
|
struct ILibStun_IceStateCandidate* hostcandidates;
|
|
char* hostcandidateResponseFlag;
|
|
int hostcandidatecount;
|
|
int requerycount;
|
|
int peerHasActiveOffer;
|
|
int dtlsInitiator;
|
|
int dtlsSession;
|
|
struct ILibStun_Module* parentStunModule;
|
|
long long creationTime;
|
|
int useTurn;
|
|
void *userObject;
|
|
struct sockaddr_in6 ReceivedAddr[STUN_NUM_ADDR];
|
|
};
|
|
|
|
typedef struct ILibStun_dTlsSession
|
|
{
|
|
ILibTransport Transport;
|
|
// DO NOT MODIFY ABOVE FIELDS (ILibTransport)
|
|
|
|
struct ILibStun_Module* parent;
|
|
SSL* ssl;
|
|
void* User;
|
|
int sessionId;
|
|
int iceStateSlot;
|
|
struct sockaddr_in6* remoteInterface;
|
|
long channelBindingRefresh;
|
|
|
|
unsigned short inport;
|
|
unsigned short outport;
|
|
unsigned int tag;
|
|
int receiverCredits;
|
|
int senderCredits;
|
|
int congestionWindowSize;
|
|
unsigned int outtsn;
|
|
unsigned int intsn;
|
|
unsigned int userTSN;
|
|
long long lastResent;
|
|
int state; // 0 = Free, 1 = Setup, 2 = Connecting, 3 = Disconnecting, 4 = Handshake
|
|
ILibSpinLock Lock;
|
|
|
|
ILibSparseArray DataChannelMetaDeta;
|
|
ILibSparseArray DataChannelMetaDetaValues;
|
|
ILibSparseArray PeerFeatureSet;
|
|
ILibSparseArray DataAccumulator;
|
|
|
|
unsigned short maxInStreams;
|
|
unsigned short maxOutStreams;
|
|
|
|
char* pendingReconfigPacket;
|
|
int reconfigFailures;
|
|
int flags;
|
|
|
|
unsigned int RREQSEQ,RRESSEQ;
|
|
|
|
unsigned int FastRetransmitExitPoint;
|
|
unsigned int zeroWindowProbeTime;
|
|
unsigned int lastSackTime;
|
|
unsigned int lastRetransmitTime;
|
|
unsigned int fwdTsnDelayTime;
|
|
|
|
int timervalue;
|
|
unsigned short pendingCount;
|
|
unsigned int pendingByteCount;
|
|
char* pendingQueueHead;
|
|
char* pendingQueueTail;
|
|
unsigned short holdingCount;
|
|
unsigned int holdingByteCount;
|
|
char* holdingQueueHead;
|
|
char* holdingQueueTail;
|
|
|
|
ILibLinkedList receiveHoldBuffer;
|
|
BIO *writeBIO;
|
|
BUF_MEM *writeBIOBuffer;
|
|
|
|
char* rpacket;
|
|
int rpacketptr;
|
|
int rpacketsize;
|
|
|
|
long freshnessTimestampStart;
|
|
void* User2;
|
|
int User3;
|
|
int User4;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
ILibSCTP_OnTSNChanged onTSNChanged; // TODO: This is not used
|
|
|
|
ILibSCTP_OnSCTPDebug onHold;
|
|
ILibSCTP_OnSCTPDebug onReceiverCredits;
|
|
ILibSCTP_OnSCTPDebug onSenderCredits;
|
|
ILibSCTP_OnSCTPDebug onSendFastRetry;
|
|
ILibSCTP_OnSCTPDebug onSendRetry;
|
|
ILibSCTP_OnSCTPDebug onCongestionWindowSizeChanged;
|
|
ILibSCTP_OnSCTPDebug onSACKreceived;
|
|
|
|
ILibSCTP_OnSCTPDebug onFastRecovery;
|
|
ILibSCTP_OnSCTPDebug onT3RTX;
|
|
ILibSCTP_OnSCTPDebug onRTTCalculated;
|
|
|
|
ILibSCTP_OnSCTPDebug onTSNFloorNotRaised;
|
|
ILibSCTP_OnSCTPDebug onRetryPacket;
|
|
#endif
|
|
|
|
unsigned int PARTIAL_BYTES_ACKED;
|
|
int SSTHRESH;
|
|
int SRTT;
|
|
int RTTVAR;
|
|
int RTO;
|
|
unsigned int T3RTXTIME;
|
|
}ILibStun_dTlsSession;
|
|
|
|
typedef struct ILibStun_Module
|
|
{
|
|
ILibChain_Link ChainLink;
|
|
|
|
void *UDP;
|
|
void *UDP6;
|
|
void *Timer;
|
|
ILibLinkedList StunUsers;
|
|
ILibStun_Results StunResult;
|
|
ILibStunClient_OnResult OnResult;
|
|
|
|
// STUN State
|
|
struct sockaddr_in LocalIf;
|
|
struct sockaddr_in6 LocalIf6;
|
|
struct sockaddr_in6 Public;
|
|
struct sockaddr_in6 Public2;
|
|
struct sockaddr_in6 Public3;
|
|
struct sockaddr_in6 StunServer;
|
|
|
|
struct sockaddr_in6 StunServer2_PrimaryPort;
|
|
struct sockaddr_in6 StunServer2_AlternatePort;
|
|
|
|
|
|
STUN_STATUS State;
|
|
char Secret[32];
|
|
char TransactionId[12];
|
|
|
|
// ICE State
|
|
ILibSCTP_OnConnect OnConnect;
|
|
ILibSCTP_OnData OnData;
|
|
ILibSCTP_OnSendOK OnSendOK;
|
|
|
|
SSL_CTX* SecurityContext;
|
|
int IceStatesNextSlot;
|
|
struct ILibStun_IceState* IceStates[ILibSTUN_MaxSlots];
|
|
struct ILibStun_dTlsSession* dTlsSessions[ILibSTUN_MaxSlots];
|
|
int dtlsSessionNextSlot;
|
|
char* CertThumbprint;
|
|
int CertThumbprintLength;
|
|
|
|
ILibWebRTC_OnOfferUpdated OnOfferUpdated;
|
|
|
|
// WebRTC Data Channel
|
|
ILibWebRTC_OnDataChannel OnWebRTCDataChannel;
|
|
ILibWebRTC_OnDataChannelClosed OnWebRTCDataChannelClosed;
|
|
ILibWebRTC_OnDataChannelAck OnWebRTCDataChannelAck;
|
|
|
|
// TURN Client
|
|
ILibTURN_ClientModule mTurnClientModule;
|
|
struct sockaddr_in6 mTurnServerAddress;
|
|
struct sockaddr_in6 mRelayedTransportAddress;
|
|
char* turnUsername;
|
|
char* turnPassword;
|
|
int turnUsernameLength;
|
|
int turnPasswordLength;
|
|
int alwaysUseTurn;
|
|
int alwaysConnectTurn;
|
|
int allocationLifetime;
|
|
|
|
int consentFreshnessDisabled;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
int lossPercentage;
|
|
int inboundDropPackets;
|
|
int outboundDropPackets;
|
|
#endif
|
|
}ILibStun_Module;
|
|
|
|
unsigned int ILibStun_CRC32(char *buf, int len)
|
|
{
|
|
unsigned int c = 0xFFFFFFFF;
|
|
for (; len; --len, ++buf) { c = UPDC32(*buf, c); }
|
|
return ~c;
|
|
}
|
|
|
|
#define ILibWebRTC_DTLS_TO_TIMER_OBJECT(d) ((char*)d+16)
|
|
#define ILibWebRTC_DTLS_FROM_TIMER_OBJECT(d) ((struct ILibStun_dTlsSession*)((char*)d-16))
|
|
#define ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(d) ((char*)d+1)
|
|
#define ILibWebRTC_DTLS_FROM_CONSENT_FRESHNESS_TIMER_OBJECT(d) ((ILibStun_dTlsSession*)((char*)d-1))
|
|
#define ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(d) d
|
|
#define ILibWebRTC_DTLS_FROM_SCTP_HEARTBEAT_TIMER_OBJECT(d) ((ILibStun_dTlsSession*)d)
|
|
#define ILibWebRTC_STUN_TO_CONNECTIVITY_CHECK_TIMER(s) ((char*)s+1)
|
|
#define ILibWebRTC_STUN_TO_PERIODIC_CHECK_TIMER(s) ((char*)s+3)
|
|
#define ILibWebRTC_STUN_FROM_PERIODIC_CHECK_TIMER(s) ((ILibStun_Module*)((char*)s-3))
|
|
#define ILibWebRTC_STUN_FROM_CONNECTIVITY_CHECK_TIMER(s) ((ILibStun_Module*)((char*)s-1))
|
|
int ILibStunClientIndex = -1;
|
|
|
|
// Function prototypes
|
|
void ILibStun_DTLS_Success_OnCreateTURNChannelBinding(ILibTURN_ClientModule turnModule, unsigned short channelNumber, int success, void* user);
|
|
void ILibStun_DTLS_Success_OnCreateTURNChannelBinding2(ILibTURN_ClientModule turnModule, unsigned short channelNumber, int success, void* user);
|
|
int ILibTURN_GetErrorCode(char* buffer, int length);
|
|
char* ILibTURN_GetErrorReason(char* buffer, int length);
|
|
|
|
ILibTransport_DoneState ILibStun_SendSctpPacket(struct ILibStun_Module *obj, int session, char* buffer, int bufferLength);
|
|
void ILibStun_OnTimeout(void *object);
|
|
void ILibStun_ProcessSctpPacket(struct ILibStun_Module *obj, int session, char* buffer, int bufferLength);
|
|
void ILibStun_SctpDisconnect(struct ILibStun_Module *obj, int session);
|
|
void ILibStun_SendIceRequest(struct ILibStun_IceState *IceState, int SlotNumber, int useCandidate, struct sockaddr_in6* remoteInterface);
|
|
void ILibStun_SendIceRequestEx(struct ILibStun_IceState *IceState, char* TransactionID, int useCandidate, struct sockaddr_in6* remoteInterface);
|
|
void ILibStun_ICE_Start(struct ILibStun_IceState *state, int SelectedSlot);
|
|
uint32_t crc32c(uint32_t crci, const unsigned char *buf, uint32_t len);
|
|
int ILibStun_GetDtlsSessionSlotForIceState(struct ILibStun_Module *obj, struct ILibStun_IceState* ice);
|
|
void ILibStun_InitiateDTLS(struct ILibStun_IceState *IceState, int IceSlot, struct sockaddr_in6* remoteInterface);
|
|
void ILibStun_PeriodicStunCheck(struct ILibStun_Module* obj);
|
|
int ILibTURN_GenerateStunFormattedPacketHeader(char* rbuffer, STUN_TYPE packetType, char* transactionID);
|
|
int ILibTURN_AddAttributeToStunFormattedPacketHeader(char* rbuffer, int rptr, STUN_ATTRIBUTES attrType, char* data, int dataLen);
|
|
void ILibTURN_DisconnectFromServer(ILibTURN_ClientModule turnModule);
|
|
ILibWebRTC_DataChannel_CloseStatus ILibWebRTC_CloseDataChannelEx2(void *WebRTCModule, unsigned short *streamIds, int streamIdLength);
|
|
void ILibWebRTC_PropagateChannelCloseEx(ILibSparseArray sender, struct ILibStun_dTlsSession* obj);
|
|
ILibSparseArray ILibWebRTC_PropagateChannelClose(struct ILibStun_dTlsSession* obj, char* packet);
|
|
|
|
typedef enum ILibStunClient_PreviousResults
|
|
{
|
|
ILibStunClient_PreviousResults_NeverTested = 0,
|
|
ILibStunClient_PreviousResults_Expired = 1,
|
|
ILibStunClient_PreviousResults_ValidResults = 2,
|
|
}ILibStunClient_PreviousResults;
|
|
|
|
typedef enum ILibWebRTC_DTLS_ContentTypes_Def
|
|
{
|
|
ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec = 20,
|
|
ILibAsyncSocket_TLSPlainText_ContentType_Alert = 21,
|
|
ILibAsyncSocket_TLSPlainText_ContentType_Handshake = 22,
|
|
ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData = 23
|
|
}ILibWebRTC_DTLS_ContentTypes;
|
|
typedef enum ILibWebRTC_DTLS_HandshakeTypes_Def
|
|
{
|
|
ILibWebRTC_DTLSHandshakeType_hello = 0,
|
|
ILibWebRTC_DTLSHandshakeType_clienthello = 1,
|
|
ILibWebRTC_DTLSHandshakeType_serverhello = 2,
|
|
ILibWebRTC_DTLSHandshakeType_certificate = 11,
|
|
ILibWebRTC_DTLSHandshakeType_serverkeyexchange = 12,
|
|
ILibWebRTC_DTLSHandshakeType_certificaterequest = 13,
|
|
ILibWebRTC_DTLSHandshakeType_serverhellodone = 14,
|
|
ILibWebRTC_DTLSHandshakeType_certificateverify = 15,
|
|
ILibWebRTC_DTLSHandshakeType_clientkeyexchange = 16,
|
|
ILibWebRTC_DTLSHandshakeType_finished = 20
|
|
}ILibWebRTC_DTLS_HandshakeTypes;
|
|
|
|
typedef struct ILibSCTP_HoldingQueueFlags
|
|
{
|
|
char DataPropagated : 1;
|
|
}ILibSCTP_HoldingQueueFlags;
|
|
|
|
#define ILibSCTP_GetHoldingQueueFlags(node) ((ILibSCTP_HoldingQueueFlags*)ILibLinkedList_GetExtendedMemory(node))
|
|
|
|
void* ILibWebRTC_Dtls2SSL(void *dtls)
|
|
{
|
|
return((void*)((ILibStun_dTlsSession*)dtls)->ssl);
|
|
}
|
|
void ILibWebRTC_DTLS_HandshakeDetect(struct ILibStun_Module* obj, char* directionPrefix, char* buffer, int offset, int length)
|
|
{
|
|
ILibWebRTC_DTLS_ContentTypes contentType = (ILibWebRTC_DTLS_ContentTypes)buffer[offset+0];
|
|
#ifdef _REMOTELOGGING
|
|
unsigned char versionMajor = 255 - buffer[offset+1];
|
|
unsigned char versionMinor = 255 - buffer[offset+2];
|
|
#endif
|
|
ILibWebRTC_DTLS_HandshakeTypes tlsHandshakeType = (ILibWebRTC_DTLS_HandshakeTypes)(buffer+offset+13)[0];
|
|
|
|
UNREFERENCED_PARAMETER(obj);
|
|
UNREFERENCED_PARAMETER(directionPrefix);
|
|
UNREFERENCED_PARAMETER(length);
|
|
|
|
switch(contentType)
|
|
{
|
|
case ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CHANGE-CIPHER-SPEC]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibAsyncSocket_TLSPlainText_ContentType_Alert:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [ALERT]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [APP/DATA]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibAsyncSocket_TLSPlainText_ContentType_Handshake:
|
|
switch(tlsHandshakeType)
|
|
{
|
|
case ILibWebRTC_DTLSHandshakeType_hello:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [HELLO]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_clienthello:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CLIENT-HELLO]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_serverhello:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [SERVER-HELLO]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_certificate:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CERTIFICATE]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_serverkeyexchange:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [SERVER-KEY-EXCHANGE]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_certificaterequest:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CERTIFICATE-REQUEST]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_serverhellodone:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [SERVER-HELLO-DONE]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_certificateverify:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CERTIFICATE-VERIFY]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_clientkeyexchange:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [CLIENT-KEY-EXCHANGE]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
case ILibWebRTC_DTLSHandshakeType_finished:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [FINISHED]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
default:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s DTLS v%d.%d [UNKNOWN Handshake Type]", directionPrefix, versionMajor, versionMinor);
|
|
break;
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int ILibAlignOnFourByteBoundary(char *data, int dataLen)
|
|
{
|
|
int retVal = FOURBYTEBOUNDARY(dataLen);
|
|
if (retVal - dataLen > 0) {memset(data + dataLen, 0, retVal - dataLen);}
|
|
return retVal;
|
|
}
|
|
|
|
int ILibWebRTC_DataChannelBucketizer(int index)
|
|
{
|
|
return (index & (ILibSCTP_Stream_SparseArraySize - 1));
|
|
}
|
|
|
|
void ILibWebRTC_DestroySparseArrayTables_Accumulator(ILibSparseArray sender, int index, void *value, void *user)
|
|
{
|
|
ILibSCTP_Accumulator *acc = (ILibSCTP_Accumulator*)value;
|
|
|
|
UNREFERENCED_PARAMETER(sender);
|
|
UNREFERENCED_PARAMETER(index);
|
|
UNREFERENCED_PARAMETER(user);
|
|
|
|
if(acc != NULL)
|
|
{
|
|
free(acc->buffer);
|
|
free(acc);
|
|
}
|
|
}
|
|
void ILibWebRTC_DestroySparseArrayTables(struct ILibStun_dTlsSession *obj)
|
|
{
|
|
ILibSparseArray_Destroy(obj->DataChannelMetaDeta);
|
|
ILibSparseArray_Destroy(obj->PeerFeatureSet);
|
|
ILibSparseArray_Destroy(obj->DataChannelMetaDetaValues);
|
|
ILibSparseArray_DestroyEx(obj->DataAccumulator, &ILibWebRTC_DestroySparseArrayTables_Accumulator, NULL);
|
|
}
|
|
void ILibWebRTC_CreateSparseArrayTables(struct ILibStun_dTlsSession *obj)
|
|
{
|
|
obj->DataChannelMetaDeta = ILibSparseArray_Create(ILibSCTP_Stream_SparseArraySize,&ILibWebRTC_DataChannelBucketizer);
|
|
obj->PeerFeatureSet = ILibSparseArray_Create(ILibSCTP_Stream_SparseArraySize, &ILibWebRTC_DataChannelBucketizer);
|
|
obj->DataChannelMetaDetaValues = ILibSparseArray_Create(ILibSCTP_Stream_SparseArraySize, &ILibWebRTC_DataChannelBucketizer);
|
|
obj->DataAccumulator = ILibSparseArray_Create(ILibSCTP_Stream_SparseArraySize, &ILibWebRTC_DataChannelBucketizer);
|
|
}
|
|
|
|
char* SCTP_ERROR_CAUSE_TO_STRING(ILibSCTP_ErrorCause_Header *cause)
|
|
{
|
|
|
|
switch((SCTP_ERROR_CAUSE_CODES)ntohs(cause->CauseCode))
|
|
{
|
|
case SCTP_ERROR_CAUSE_CODE_ASSOCIATION_RESTART_NEW_ADDRESS:
|
|
return("SCTP_ERROR_CAUSE_CODE_ASSOCIATION_RESTART_NEW_ADDRESS");
|
|
case SCTP_ERROR_CAUSE_CODE_COOKIE_RECEIVED_DURING_SHUTDOWN:
|
|
return("SCTP_ERROR_CAUSE_CODE_COOKIE_RECEIVED_DURING_SHUTDOWN");
|
|
case SCTP_ERROR_CAUSE_CODE_INVALID_MANDATORY_PARAMETER:
|
|
return("SCTP_ERROR_CAUSE_CODE_INVALID_MANDATORY_PARAMETER");
|
|
case SCTP_ERROR_CAUSE_CODE_INVALID_STREAM:
|
|
return("SCTP_ERROR_CAUSE_CODE_INVALID_STREAM");
|
|
case SCTP_ERROR_CAUSE_CODE_MISSING_MANDATORY_PARAMATER:
|
|
return("SCTP_ERROR_CAUSE_CODE_MISSING_MANDATORY_PARAMATER");
|
|
case SCTP_ERROR_CAUSE_CODE_NO_USER_DATA:
|
|
return("SCTP_ERROR_CAUSE_CODE_NO_USER_DATA");
|
|
case SCTP_ERROR_CAUSE_CODE_OUT_OF_RESOURCES:
|
|
return("SCTP_ERROR_CAUSE_CODE_OUT_OF_RESOURCES");
|
|
case SCTP_ERROR_CAUSE_CODE_PROTOCOL_VIOLATION:
|
|
return("SCTP_ERROR_CAUSE_CODE_PROTOCOL_VIOLATION");
|
|
case SCTP_ERROR_CAUSE_CODE_STALE_COOKIE:
|
|
return("SCTP_ERROR_CAUSE_CODE_STALE_COOKIE");
|
|
case SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_CHUNK_TYPE:
|
|
return("SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_CHUNK_TYPE");
|
|
case SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_PARAMETERS:
|
|
return("SCTP_ERROR_CAUSE_CODE_UNRECOGNIZED_PARAMETERS");
|
|
case SCTP_ERROR_CAUSE_CODE_UNRESOLVABLE_ADDRESS:
|
|
return("SCTP_ERROR_CAUSE_CODE_UNRESOLVABLE_ADDRESS");
|
|
case SCTP_ERROR_CAUSE_CODE_USER_ABORT:
|
|
return("SCTP_ERROR_CAUSE_CODE_USER_ABORT");
|
|
default:
|
|
return("---");
|
|
}
|
|
}
|
|
|
|
//! Associate custom user data with a local ICE session
|
|
/*!
|
|
\param stunModule Stun/ICE Client
|
|
\param localUsername Username for local ICE Session
|
|
\param userObject Custom user data to set
|
|
*/
|
|
void ILibWebRTC_SetUserObject(void *stunModule, char* localUsername, void *userObject)
|
|
{
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*) stunModule;
|
|
int SlotNumber = ILibStun_CharToSlot(localUsername[0]);
|
|
if (SlotNumber >= 0 && SlotNumber < ILibSTUN_MaxSlots && obj->IceStates[SlotNumber] != NULL) { obj->IceStates[SlotNumber]->userObject = userObject; }
|
|
}
|
|
|
|
//! Get custom user data associated with a local ICE Session
|
|
/*!
|
|
\param stunModule Stun/ICE client
|
|
\param localUsername Username for local ICE Session
|
|
\return Associated custom user data
|
|
*/
|
|
void* ILibWebRTC_GetUserObject(void *stunModule, char* localUsername)
|
|
{
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*) stunModule;
|
|
int SlotNumber = ILibStun_CharToSlot(localUsername[0]);
|
|
if (SlotNumber >= 0 && SlotNumber < ILibSTUN_MaxSlots && obj->IceStates[SlotNumber] != NULL) { return(obj->IceStates[SlotNumber]->userObject); }
|
|
return NULL;
|
|
}
|
|
//! Get associated custom user data from an active peer connection session
|
|
/*!
|
|
\param webRTCSession Peer Connection Session
|
|
\return Associated custom user data
|
|
*/
|
|
void* ILibWebRTC_GetUserObjectFromDtlsSession(void *webRTCSession)
|
|
{
|
|
struct ILibStun_dTlsSession* session = (struct ILibStun_dTlsSession*)webRTCSession;
|
|
return (session->parent->IceStates[session->iceStateSlot] != NULL ? session->parent->IceStates[session->iceStateSlot]->userObject : NULL);
|
|
}
|
|
|
|
//! Was this DTLS Session initiated locally
|
|
/*!
|
|
\param dtlsSession Session to query
|
|
\return Non-Zero = Yes
|
|
*/
|
|
int ILibWebRTC_IsDtlsInitiator(void* dtlsSession)
|
|
{
|
|
struct ILibStun_dTlsSession* session = (struct ILibStun_dTlsSession*)dtlsSession;
|
|
return (session->parent->IceStates[session->iceStateSlot]->dtlsInitiator);
|
|
}
|
|
|
|
void ILibStun_OnCompleted(void *object)
|
|
{
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*) object;
|
|
obj->State = STUN_STATUS_COMPLETE;
|
|
}
|
|
|
|
void ILibStun_OnDestroy(void *object)
|
|
{
|
|
int i, extraClean = 0;
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)object;
|
|
|
|
obj->UDP = obj->UDP6 = NULL;
|
|
|
|
// Clean up all reliable UDP state && all ICE offers (Use the same loop since both tables are ILibSTUN_MaxSlots long)
|
|
for (i = 0; i < ILibSTUN_MaxSlots; i++)
|
|
{
|
|
// Clean up OpenSSL dTLS session
|
|
if (obj->dTlsSessions[i] != NULL)
|
|
{
|
|
if (obj->dTlsSessions[i]->state != 0) extraClean = 1;
|
|
ILibStun_SctpDisconnect(obj, i);
|
|
}
|
|
|
|
if (obj->IceStates[i] != NULL)
|
|
{
|
|
// Clean up ICE state
|
|
free(obj->IceStates[i]->offerblock);
|
|
free(obj->IceStates[i]);
|
|
obj->IceStates[i] = NULL;
|
|
}
|
|
}
|
|
|
|
ILibLinkedList_Destroy(obj->StunUsers);
|
|
if (obj->turnUsername != NULL) { free(obj->turnUsername); obj->turnUsername = NULL; }
|
|
if (obj->turnPassword != NULL) { ILibMemory_SecureZero(obj->turnPassword, obj->turnPasswordLength); free(obj->turnPassword); obj->turnPassword = NULL; }
|
|
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_STUN_TO_PERIODIC_CHECK_TIMER(obj));
|
|
if (extraClean == 0) return;
|
|
|
|
#ifdef WIN32
|
|
Sleep(500);
|
|
#else
|
|
sleep(1);
|
|
#endif
|
|
|
|
for (i = 0; i < ILibSTUN_MaxSlots; i++)
|
|
{
|
|
// Clean up OpenSSL dTLS session
|
|
if (obj->dTlsSessions[i] != NULL)
|
|
{
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(obj->dTlsSessions[i]));
|
|
free(obj->dTlsSessions[i]);
|
|
obj->dTlsSessions[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ILibStun_GetFreeSessionSlot(void *StunModule)
|
|
{
|
|
int i, s, j = -1;
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)StunModule;
|
|
|
|
// Find a free dTLS session slot
|
|
for (i = 0; i < ILibSTUN_MaxSlots; i++)
|
|
{
|
|
s = (obj->dtlsSessionNextSlot + i) % ILibSTUN_MaxSlots;
|
|
if (obj->dTlsSessions[s] == NULL || obj->dTlsSessions[s]->state == 0)
|
|
{
|
|
j = s; break;
|
|
}
|
|
}
|
|
return j;
|
|
}
|
|
|
|
int ILibStun_GetExistingIceOfferIndex(void* stunModule, char *iceOffer, int iceOfferLen)
|
|
{
|
|
ILibStun_Module* obj = (ILibStun_Module*)stunModule;
|
|
int rusernamelen = (int)iceOffer[6];
|
|
char* rusername = iceOffer + 7;
|
|
int rkeylen = iceOffer[7 + rusernamelen];
|
|
char *rkey = iceOffer + 7 + rusernamelen + 1;
|
|
int i;
|
|
|
|
UNREFERENCED_PARAMETER(iceOfferLen);
|
|
|
|
// Check to see if this iceState is an update to an existing one... Keying by remote username/password
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL)
|
|
{
|
|
if (obj->IceStates[i]->rusernamelen == rusernamelen && obj->IceStates[i]->rkeylen == rkeylen && memcmp(obj->IceStates[i]->rusername, rusername, rusernamelen) == 0 && memcmp(obj->IceStates[i]->rkey, rkey, rkeylen) == 0)
|
|
{
|
|
return(i); // Index of an existing ice offer with the same remote username and password
|
|
}
|
|
}
|
|
}
|
|
return(-1); // Not Found
|
|
}
|
|
|
|
// Returns slot number that was used... -1 on Error
|
|
int ILibStun_GetFreeIceStateSlot(struct ILibStun_Module *stunModule, struct ILibStun_IceState* newIceState, struct ILibStun_IceState** oldIceState, int checkUpdateFirst)
|
|
{
|
|
int i, slot;
|
|
|
|
if (newIceState->userAndKey[0] != 0)
|
|
{
|
|
// If this is nonzero, it's because we generated the original offer. Thus, the slotnumber is encoded in the username
|
|
slot = ILibStun_CharToSlot(newIceState->userAndKey[1]);
|
|
if (slot < ILibSTUN_MaxSlots)
|
|
{
|
|
if (oldIceState != NULL) { *oldIceState = stunModule->IceStates[slot]; }
|
|
stunModule->IceStates[slot] = newIceState;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibStun_GetFreeIceStateSlot: Encoded Slot = %d", slot);
|
|
return slot;
|
|
}
|
|
}
|
|
|
|
if (checkUpdateFirst != 0)
|
|
{
|
|
// Check to see if this iceState is an update to an existing one... Keying by remote username/password
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (stunModule->IceStates[i] != NULL)
|
|
{
|
|
if (stunModule->IceStates[i]->rusernamelen == newIceState->rusernamelen && stunModule->IceStates[i]->rkeylen == newIceState->rkeylen && memcmp(stunModule->IceStates[i]->rusername, newIceState->rusername, newIceState->rusernamelen) == 0 && memcmp(stunModule->IceStates[i]->rkey, newIceState->rkey, newIceState->rkeylen) == 0)
|
|
{
|
|
// These offers are for the same session
|
|
// Check if there is a DTLS session already
|
|
if (stunModule->IceStates[i]->dtlsSession < 0)
|
|
{
|
|
if (oldIceState != NULL) { *oldIceState = stunModule->IceStates[i]; }
|
|
stunModule->IceStates[i] = newIceState;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibStun_GetFreeIceStateSlot: Update Slot = %d", i);
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
// Return Error, becuase we won't update if DTLS is already active
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
slot = (stunModule->IceStatesNextSlot + i) % ILibSTUN_MaxSlots;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibStun_GetFreeIceStateSlot: NextSlot: %d, i: %d, Slot: %d ", stunModule->IceStatesNextSlot, i, slot);
|
|
if (stunModule->IceStates[slot] == NULL || (stunModule->IceStates[slot] != NULL && stunModule->IceStates[slot]->dtlsSession < 0 && ((ILibGetUptime() - stunModule->IceStates[slot]->creationTime) > (ILibSTUN_MaxOfferAgeSeconds * 1000))))
|
|
{
|
|
// This slot is either empty, or contains an offer with no DTLS session, and is older than what is allowed
|
|
if (oldIceState != NULL) { *oldIceState = stunModule->IceStates[slot]; }
|
|
stunModule->IceStates[slot] = newIceState;
|
|
stunModule->IceStatesNextSlot = slot + 1;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibStun_GetFreeIceStateSlot: Free Slot = %d", slot);
|
|
return slot;
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Slot Busy[%d]: Dtls: %d, Age: %d", slot, stunModule->IceStates[slot]->dtlsSession, ILibGetUptime() - stunModule->IceStates[slot]->creationTime);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
//! Clear internal ICE State
|
|
/*!
|
|
\param stunModule Stun Client Module
|
|
\param iceSlot Slot number to clear
|
|
*/
|
|
void ILibStun_ClearIceState(void *stunModule, int iceSlot)
|
|
{
|
|
struct ILibStun_IceState* ice;
|
|
struct ILibStun_Module* module = (struct ILibStun_Module*)stunModule;
|
|
|
|
// Start by removing the IceState Object
|
|
ice = module->IceStates[iceSlot];
|
|
module->IceStates[iceSlot] = NULL;
|
|
|
|
if (ice != NULL)
|
|
{
|
|
if (ice->offerblock != NULL) { free(ice->offerblock); }
|
|
free(ice);
|
|
}
|
|
}
|
|
|
|
int ILibStun_GetNextPeriodicInterval(int minVal, int maxVal)
|
|
{
|
|
char i;
|
|
float v;
|
|
|
|
util_random(1, (char*)&i);
|
|
v = (float)i / (float)255;
|
|
v = (v*(float)maxVal) + (minVal);
|
|
return (int)v;
|
|
}
|
|
|
|
|
|
// Get and set the user for a RUDP connection
|
|
void ILibSCTP_SetUser(void* module, void* user) { ((struct ILibStun_dTlsSession*)module)->User = user; }
|
|
void* ILibSCTP_GetUser(void* module) { return ((struct ILibStun_dTlsSession*)module)->User; }
|
|
void ILibSCTP_SetUser2(void* module, void* user) { ((struct ILibStun_dTlsSession*)module)->User2 = user; }
|
|
void* ILibSCTP_GetUser2(void* module) { return ((struct ILibStun_dTlsSession*)module)->User2; }
|
|
void ILibSCTP_SetUser3(void* module, int user) { ((struct ILibStun_dTlsSession*)module)->User3 = user; }
|
|
int ILibSCTP_GetUser3(void* module) { return ((struct ILibStun_dTlsSession*)module)->User3; }
|
|
void ILibSCTP_SetUser4(void* module, int user) { ((struct ILibStun_dTlsSession*)module)->User4 = user; }
|
|
int ILibSCTP_GetUser4(void* module) { return ((struct ILibStun_dTlsSession*)module)->User4; }
|
|
//! Get the number of bytes that are pending in the send buffer
|
|
/*!
|
|
\param sctpSession The SCTP Session to query
|
|
\return Number of bytes pending in the send buffer
|
|
*/
|
|
int ILibSCTP_GetPendingBytesToSend(void* sctpSession)
|
|
{
|
|
int r;
|
|
ILibSpinLock_Lock(&(((struct ILibStun_dTlsSession*)sctpSession)->Lock));
|
|
r = ((struct ILibStun_dTlsSession*)sctpSession)->holdingByteCount;
|
|
ILibSpinLock_UnLock(&(((struct ILibStun_dTlsSession*)sctpSession)->Lock));
|
|
if (r < 0) return 0 - r;
|
|
return 0;
|
|
}
|
|
//! Set SCTP Callback handlers
|
|
/*!
|
|
\param StunModule Local Stun/ICE Client
|
|
\param onconnect Event triggered when SCTP connection state changes
|
|
\param ondata Event triggered when data is received on the SCTP session
|
|
\param onsendok Event triggered when buffered send data is flushed
|
|
*/
|
|
void ILibSCTP_SetCallbacks(void* StunModule, ILibSCTP_OnConnect onconnect, ILibSCTP_OnData ondata, ILibSCTP_OnSendOK onsendok)
|
|
{
|
|
struct ILibStun_Module* stunModule = (struct ILibStun_Module*)StunModule;
|
|
if (onconnect != NULL) { stunModule->OnConnect = onconnect; }
|
|
if (ondata != NULL) { stunModule->OnData = ondata; }
|
|
if (onsendok != NULL) { stunModule->OnSendOK = onsendok; }
|
|
if (onconnect == NULL && ondata == NULL && onsendok == NULL)
|
|
{
|
|
stunModule->OnConnect = NULL;
|
|
stunModule->OnData = NULL;
|
|
stunModule->OnSendOK = NULL;
|
|
}
|
|
}
|
|
//! Set WebRTC Event Handlers
|
|
/*!
|
|
\param StunModule Local STUN/ICE Client
|
|
\param OnDataChannel New Data Channel Event handler
|
|
\param OnDataChannelClosed Closed Data Channel Event handler
|
|
\param OnDataChannelAck ACK Event handler for when Remote Peer ACK's Data Channel Creation request
|
|
\param OnOfferUpdated Reserved for Future Use
|
|
*/
|
|
void ILibWebRTC_SetCallbacks(void *StunModule, ILibWebRTC_OnDataChannel OnDataChannel, ILibWebRTC_OnDataChannelClosed OnDataChannelClosed, ILibWebRTC_OnDataChannelAck OnDataChannelAck, ILibWebRTC_OnOfferUpdated OnOfferUpdated)
|
|
{
|
|
struct ILibStun_Module* stunModule = (struct ILibStun_Module*)StunModule;
|
|
stunModule->OnWebRTCDataChannel = OnDataChannel;
|
|
stunModule->OnWebRTCDataChannelClosed = OnDataChannelClosed;
|
|
stunModule->OnWebRTCDataChannelAck = OnDataChannelAck;
|
|
stunModule->OnOfferUpdated = OnOfferUpdated;
|
|
}
|
|
|
|
// user must be 8 byte buffer, secret must be 32 byte buffer, key must be 32 byte buffer
|
|
void ILibStun_ComputeIntegrityKey(char* user, char* secret, char* key)
|
|
{
|
|
char buf[40];
|
|
char result[32];
|
|
memcpy_s(buf, sizeof(buf), user, 8);
|
|
memcpy_s(buf + 8, sizeof(buf) - 8, secret, 32);
|
|
util_sha256(buf, 40, result);
|
|
util_tohex(result, 16, key);
|
|
}
|
|
|
|
// result must be 42 byte buffer. Result will contrain user length (8), 8 byte username, password length (32), 32 byte password
|
|
void ILibStun_GenerateUserAndKey(int iceSlot, char* secret, char* result)
|
|
{
|
|
char rand[4];
|
|
util_random(4, rand);
|
|
result[0] = 8;
|
|
util_tohex(rand, 4, result + 1);
|
|
// 1st byte of username will be encoded with IceState slot number
|
|
// So when we receive an ICE request, we'll know which offer the request is for, so if the peer
|
|
// elects a candidate we can mark it in the IceState object.
|
|
result[1] = (char)ILibStun_SlotToChar(iceSlot);
|
|
result[9] = 32;
|
|
ILibStun_ComputeIntegrityKey(result + 1, secret, result + 10);
|
|
}
|
|
|
|
int ILibStun_AddMessageIntegrityAttr(char* rbuffer, int ptr, char* integritykey, int integritykeylen)
|
|
{
|
|
HMAC_CTX* hmac;
|
|
unsigned int hmaclen = 20;
|
|
|
|
((unsigned short*)(rbuffer))[1] = htons((unsigned short)(ptr + 24 - 20)); // Set the length
|
|
((unsigned short*)(rbuffer + ptr))[0] = htons((unsigned short)STUN_ATTRIB_MESSAGE_INTEGRITY); // Attribute header
|
|
((unsigned short*)(rbuffer + ptr))[1] = htons(20); // Attribute length
|
|
|
|
// Setup and perform HMAC-SHA1
|
|
hmac = HMAC_CTX_new();
|
|
HMAC_Init_ex(hmac, integritykey, integritykeylen, EVP_sha1(), NULL);
|
|
HMAC_Update(hmac, (unsigned char*)rbuffer, ptr);
|
|
HMAC_Final(hmac, (unsigned char*)(rbuffer + ptr + 4), &hmaclen); // Put the HMAC in the outgoing result location
|
|
HMAC_CTX_free(hmac);
|
|
return 24;
|
|
}
|
|
|
|
int ILibStun_AddFingerprint(char* rbuffer, int ptr)
|
|
{
|
|
((unsigned short*)rbuffer)[1] = htons((unsigned short)(ptr + 8 - 20)); // Set the length
|
|
((unsigned short*)(rbuffer + ptr))[0] = htons((unsigned short)STUN_ATTRIB_FINGERPRINT); // Attribute header
|
|
((unsigned short*)(rbuffer + ptr))[1] = htons(4); // Attribute length
|
|
((unsigned int*)(rbuffer + ptr))[1] = htonl(ILibStun_CRC32(rbuffer, ptr) ^ 0x5354554e); // Compute and set the CRC32
|
|
return 8;
|
|
}
|
|
|
|
int ILib_Stun_GetAttributeChangeRequestPacket(int flags, char* TransactionId, char* rbuffer)
|
|
{
|
|
int rptr = 20;
|
|
|
|
// Should generate a 36 byte packet
|
|
((unsigned short*)rbuffer)[0] = htons(STUN_BINDING_REQUEST); // Response type, skip setting length for now
|
|
((unsigned int*)rbuffer)[1] = htonl(0x2112A442); // Set the magic string
|
|
util_random(12, TransactionId); // Random used for transaction id
|
|
|
|
TransactionId[0] = (char)255; // Set the first byte to 255, so it doesn't collide with IceStateSlot
|
|
memcpy_s(rbuffer + 8, 12, TransactionId, 12);
|
|
|
|
((unsigned short*)(rbuffer + rptr))[0] = htons(STUN_ATTRIB_CHANGE_REQUEST); // Attribute header
|
|
((unsigned short*)(rbuffer + rptr))[1] = htons(4); // Attribute length
|
|
((unsigned int*)(rbuffer + rptr))[1] = htonl(flags); // Attribute data
|
|
rptr += 8;
|
|
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr); // Set the length in this function
|
|
return rptr;
|
|
}
|
|
|
|
void ILib_Stun_SendAttributeChangeRequest(void* module, struct sockaddr* StunServer, int flags)
|
|
{
|
|
struct ILibStun_Module *StunModule = (struct ILibStun_Module*)module;
|
|
int rptr = 20;
|
|
char rbuffer[64]; // Should generate a 36 byte packet
|
|
|
|
((unsigned short*)rbuffer)[0] = htons(STUN_BINDING_REQUEST); // Response type, skip setting length for now
|
|
((unsigned int*)rbuffer)[1] = htonl(0x2112A442); // Set the magic string
|
|
util_random(12, StunModule->TransactionId); // Random used for transaction id
|
|
|
|
StunModule->TransactionId[0] = (char)255; // Set the first byte to 255, so it doesn't collide with IceStateSlot
|
|
NAT_MAPPING_DETECTION(StunModule->TransactionId) = (char)((flags & 0x8000)==0x8000)?(char)255:0; // Mapping Detection vs Public Interface Only Detection
|
|
memcpy_s(rbuffer + 8, sizeof(rbuffer) - 8, StunModule->TransactionId, 12);
|
|
|
|
((unsigned short*)(rbuffer + rptr))[0] = htons(STUN_ATTRIB_CHANGE_REQUEST); // Attribute header
|
|
((unsigned short*)(rbuffer + rptr))[1] = htons(4); // Attribute length
|
|
((unsigned int*)(rbuffer + rptr))[1] = htonl(flags & 0xFFFF7FFF); // Attribute data
|
|
rptr += 8;
|
|
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr); // Set the length in this function
|
|
ILibAsyncUDPSocket_SendTo(StunModule->UDP, (struct sockaddr*)StunServer, rbuffer, rptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
|
|
int ILibStun_WebRTC_UpdateOfferResponse(struct ILibStun_IceState *iceState, char **answer)
|
|
{
|
|
int rlen, i;
|
|
int LocalInterfaceListV4Len;
|
|
struct sockaddr_in *LocalInterfaceListV4;
|
|
int BlockFlags = 0;
|
|
|
|
int turnRecordSize = 0;
|
|
|
|
if (iceState->useTurn != 0) { turnRecordSize = 1 + sizeof(struct sockaddr_in6); }
|
|
if (iceState->dtlsInitiator == 0) { BlockFlags |= ILibWebRTC_SDP_Flags_DTLS_SERVER; }
|
|
|
|
// Generate an return answer
|
|
LocalInterfaceListV4Len = ILibGetLocalIPv4AddressList(&LocalInterfaceListV4, 0);
|
|
if (LocalInterfaceListV4Len > 8) LocalInterfaceListV4Len = 8; // Don't send more than 8 candidates
|
|
|
|
if (iceState->parentStunModule->alwaysUseTurn != 0) { LocalInterfaceListV4Len = 0; }
|
|
|
|
rlen = (6 + 42 + 1 + 32 + 1 + (LocalInterfaceListV4Len * 6));
|
|
|
|
if ((*answer = (char*)malloc(rlen + turnRecordSize)) == NULL) ILIBCRITICALEXIT(254);
|
|
((unsigned short*)(*answer))[0] = 1; // Block version number
|
|
((unsigned int*)(*answer + 2))[0] = htonl(BlockFlags); // Block flags
|
|
memcpy_s(*answer + 6, rlen + turnRecordSize - 6, iceState->userAndKey, 42);
|
|
|
|
(*answer)[48] = (char)(iceState->parentStunModule->CertThumbprintLength);
|
|
memcpy_s(*answer + 49, rlen + turnRecordSize - 49, iceState->parentStunModule->CertThumbprint, iceState->parentStunModule->CertThumbprintLength); // Set DTLS fingerprint here
|
|
(*answer)[49 + 32] = (char)LocalInterfaceListV4Len;
|
|
for (i = 0; i < LocalInterfaceListV4Len; i++)
|
|
{
|
|
((int*)(*answer + 49 + 33 + (i * 6)))[0] = LocalInterfaceListV4[i].sin_addr.s_addr;
|
|
((short*)(*answer + 49 + 33 + (i * 6) + 4))[0] = ((struct sockaddr_in*)(&(iceState->parentStunModule->LocalIf)))->sin_port;
|
|
}
|
|
if (LocalInterfaceListV4 != NULL) free(LocalInterfaceListV4);
|
|
if (turnRecordSize > 0)
|
|
{
|
|
(*answer + rlen)[0] = INET_SOCKADDR_LENGTH(iceState->parentStunModule->mRelayedTransportAddress.sin6_family);
|
|
memcpy_s(*answer + rlen + 1, turnRecordSize - 1, &(iceState->parentStunModule->mRelayedTransportAddress), INET_SOCKADDR_LENGTH(iceState->parentStunModule->mRelayedTransportAddress.sin6_family));
|
|
}
|
|
return (rlen + turnRecordSize);
|
|
}
|
|
|
|
void ILibStun_WebRTC_ConsentFreshness_Continue(void *object)
|
|
{
|
|
struct ILibStun_dTlsSession *session = ILibWebRTC_DTLS_FROM_CONSENT_FRESHNESS_TIMER_OBJECT(object);
|
|
struct timeval tv;
|
|
char TransactionID[12];
|
|
int SessionSlot;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
// If this was triggered, it means we haven't received a STUN/Response yet
|
|
// Based on http://tools.ietf.org/html/draft-muthu-behave-consent-freshness-04
|
|
if (tv.tv_sec - session->freshnessTimestampStart >= ILibStun_MaxConsentFreshnessTimeoutSeconds)
|
|
{
|
|
// We need to disconnect DTLS
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(session->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "Consent Freshness Timeout, Closing Session: %d", session->sessionId);
|
|
ILibStun_SctpDisconnect(session->parent, session->sessionId);
|
|
}
|
|
else
|
|
{
|
|
// Keep sending a Probe and wait 500ms for a response, using the same TransactionID
|
|
SessionSlot = session->sessionId;
|
|
memset(TransactionID, 0, 12);
|
|
TransactionID[0] = (char)(SessionSlot ^ 0x80);
|
|
memcpy_s(TransactionID + 1, sizeof(TransactionID) - 1, &(session->freshnessTimestampStart), sizeof(long) < 11 ? sizeof(long) : 11);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(session->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "Probing Consent Freshness for Session: %d with %s:%u", session->sessionId, ILibRemoteLogging_ConvertAddress((struct sockaddr*)(session->remoteInterface)), htons(session->remoteInterface->sin6_port));
|
|
|
|
ILibLifeTime_AddEx(session->parent->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(session), 500, ILibStun_WebRTC_ConsentFreshness_Continue, NULL);
|
|
ILibStun_SendIceRequestEx(session->parent->IceStates[session->iceStateSlot], TransactionID, 0, session->remoteInterface);
|
|
}
|
|
}
|
|
void ILibStun_WebRTC_ConsentFreshness_Start(void *object)
|
|
{
|
|
struct ILibStun_dTlsSession *session = ILibWebRTC_DTLS_FROM_CONSENT_FRESHNESS_TIMER_OBJECT(object);
|
|
struct timeval tv;
|
|
char TransactionID[12];
|
|
int SessionSlot = session->sessionId;
|
|
|
|
if (session->parent->IceStates[session->iceStateSlot] == NULL) { return; }
|
|
|
|
memset(TransactionID, 0, 12);
|
|
gettimeofday(&tv, NULL);
|
|
session->freshnessTimestampStart = tv.tv_sec;
|
|
|
|
if ((((int*)ILibMemory_GetExtraMemory(session->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_CHANNEL_DATA) == ILibTURN_FLAGS_CHANNEL_DATA)
|
|
{
|
|
// This is using a TURN Channel Binding, so check to see if we need to refresh it
|
|
if (tv.tv_sec - session->channelBindingRefresh > ILibTURN_CHANNEL_BINDING_REFRESH_INTERVAL)
|
|
{
|
|
ILibTURN_CreateChannelBinding(session->parent->mTurnClientModule, (unsigned short)session->sessionId, session->remoteInterface, ILibStun_DTLS_Success_OnCreateTURNChannelBinding, session->parent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((((int*)ILibMemory_GetExtraMemory(session->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
// This is using a TURN SendIndication, so check to see if we need to refresh it
|
|
if (tv.tv_sec - session->channelBindingRefresh > ILibTURN_PERMISSION_REFRESH_INTERVAL)
|
|
{
|
|
ILibTURN_CreatePermission(session->parent->mTurnClientModule, session->remoteInterface, 1, NULL, NULL);
|
|
ILibTURN_CreateChannelBinding(session->parent->mTurnClientModule, (unsigned short)session->sessionId, session->remoteInterface, ILibStun_DTLS_Success_OnCreateTURNChannelBinding2, session->parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
TransactionID[0] = ((unsigned char)SessionSlot ^ 0x80);
|
|
memcpy_s(TransactionID + 1, sizeof(TransactionID) - 1, &(session->freshnessTimestampStart), sizeof(long) < 11 ? sizeof(long) : 11);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(session->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "Probing Consent Freshness for Session: %d with %s:%u", session->sessionId, ILibRemoteLogging_ConvertAddress((struct sockaddr*)(session->remoteInterface)), htons(session->remoteInterface->sin6_port));
|
|
|
|
ILibLifeTime_AddEx(session->parent->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(session), 500, ILibStun_WebRTC_ConsentFreshness_Continue, NULL); // Wait 500ms for a response
|
|
ILibStun_SendIceRequestEx(session->parent->IceStates[session->iceStateSlot], TransactionID, 0, session->remoteInterface);
|
|
}
|
|
|
|
enum ILibAsyncSocket_SendStatus ILibStun_SendPacket(struct ILibStun_Module *stunModule, char* buffer, int offset, int length, struct sockaddr_in6* remoteInterface, enum ILibAsyncSocket_MemoryOwnership memoryOwnership)
|
|
{
|
|
int flags = ((int*)ILibMemory_GetExtraMemory(remoteInterface, sizeof(struct sockaddr_in6)))[0];
|
|
ILibAsyncSocket_SendStatus retVal;
|
|
|
|
if (flags == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_5, "[Sending UDP to: %s:%u]", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
return (ILibAsyncSocket_SendTo(stunModule->UDP, buffer + offset, length, (struct sockaddr*)remoteInterface, memoryOwnership));
|
|
}
|
|
else
|
|
{
|
|
if ((flags & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_5, "[Sending TURN Indication to: %s:%u]", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
retVal = ILibTURN_SendIndication(stunModule->mTurnClientModule, remoteInterface, buffer, offset, length);
|
|
if (memoryOwnership == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
|
|
return retVal;
|
|
}
|
|
else if ((flags & ILibTURN_FLAGS_CHANNEL_DATA) == ILibTURN_FLAGS_CHANNEL_DATA)
|
|
{
|
|
unsigned short port = flags & ILibTURN_MASK_CHANNEL_DATA;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_5, "[Sending on TURN Channel Binding: %u]", port);
|
|
retVal = ILibTURN_SendChannelData(stunModule->mTurnClientModule, port, buffer, offset, length);
|
|
if (memoryOwnership == ILibAsyncSocket_MemoryOwnership_CHAIN) { free(buffer); }
|
|
return retVal;
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_5, "*UNKNOWN FLAG* [Sending UDP to: %s:%u]", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
return (ILibAsyncSocket_SendTo(stunModule->UDP, buffer + offset, length, (struct sockaddr*)remoteInterface, memoryOwnership));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILibWebRTC_CloseDataChannel_Timeout(void *obj)
|
|
{
|
|
struct ILibStun_dTlsSession *o = ILibWebRTC_DTLS_FROM_TIMER_OBJECT(obj);
|
|
int newTimeout;
|
|
|
|
ILibSpinLock_Lock(&o->Lock);
|
|
++o->reconfigFailures;
|
|
|
|
newTimeout = RTO_MIN * (0x01 << o->reconfigFailures); // Exponential Backoff
|
|
if(newTimeout > RTO_MAX)
|
|
{
|
|
// Too many failures
|
|
ILibSparseArray dup = ILibWebRTC_PropagateChannelClose(o, o->pendingReconfigPacket);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Exceeded maximum retries for RECONFIG CHUNK on Dtls Session: %d", o->sessionId);
|
|
|
|
o->pendingReconfigPacket = NULL;
|
|
o->reconfigFailures = 0;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
|
|
if (dup != NULL) { ILibWebRTC_PropagateChannelCloseEx(dup, o); }
|
|
}
|
|
else
|
|
{
|
|
// Retransmit the Reconfig Chunk, and set a new timeout
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "RECONFIG CHUNK retry attempt %d on Dtls Session: %d", o->reconfigFailures, o->sessionId);
|
|
ILibLifeTime_AddEx(o->parent->Timer, ILibWebRTC_DTLS_TO_TIMER_OBJECT(o), newTimeout, &ILibWebRTC_CloseDataChannel_Timeout, NULL);
|
|
ILibStun_SendSctpPacket(o->parent, o->sessionId, o->pendingReconfigPacket, (int)ntohs(((unsigned short*)(o->pendingReconfigPacket + 14))[0]));
|
|
ILibSpinLock_UnLock(&o->Lock);
|
|
}
|
|
}
|
|
|
|
void ILibStun_FireResults(ILibStun_Module *StunModule, ILibStun_Results Result, struct sockaddr_in* PublicIP)
|
|
{
|
|
void* tempStack;
|
|
void* node;
|
|
ILibCreateStack(&tempStack);
|
|
|
|
ILibLinkedList_Lock(StunModule->StunUsers);
|
|
StunModule->StunResult = Result;
|
|
StunModule->State = STUN_STATUS_COMPLETE;
|
|
while ((node = ILibLinkedList_GetNode_Tail(StunModule->StunUsers)) != NULL)
|
|
{
|
|
ILibPushStack(&tempStack, ILibLinkedList_GetDataFromNode(node));
|
|
ILibLinkedList_Remove(node);
|
|
}
|
|
ILibLinkedList_UnLock(StunModule->StunUsers);
|
|
|
|
while (tempStack != NULL)
|
|
{
|
|
node = ILibPopStack(&tempStack);
|
|
if (StunModule->OnResult != NULL) { StunModule->OnResult(StunModule, Result, PublicIP, node); }
|
|
}
|
|
}
|
|
#ifdef _WEBRTCDEBUG
|
|
void _DebugSTUN(char *buffer, int bufferLen)
|
|
{
|
|
char tid[25];
|
|
printf(" --> DEBUG Outbound STUN Packet\n");
|
|
unsigned short messageType = ntohs(((unsigned short*)buffer)[0]); // Get the message type
|
|
//unsigned short messageLength = ntohs(((unsigned short*)buffer)[1]) + 20; // Get the message length
|
|
|
|
switch (messageType)
|
|
{
|
|
case STUN_BINDING_REQUEST:
|
|
printf("STUN_BINDING_REQUEST");
|
|
break;
|
|
case STUN_BINDING_RESPONSE:
|
|
printf("STUN_BINDING_RESPONSE");
|
|
break;
|
|
case STUN_BINDING_ERROR_RESPONSE:
|
|
printf("STUN_BINDING_ERROR_RESPONSE");
|
|
break;
|
|
case STUN_SHAREDSECRET_REQUEST:
|
|
printf("STUN_SHARED_SECRET_REQUEST");
|
|
break;
|
|
case STUN_SHAREDSECRET_RESPONSE:
|
|
printf("STUN_SHARED_SECRET_RESPONSE");
|
|
break;
|
|
case STUN_SHAREDSECRET_ERROR_RESPONSE:
|
|
printf("STUN_SHARED_SECRET_ERROR_RESPONSE");
|
|
break;
|
|
case TURN_ALLOCATE:
|
|
printf("TURN_ALLOCATE");
|
|
break;
|
|
case TURN_ALLOCATE_RESPONSE:
|
|
printf("TURN_ALLOCATE_RESPONSE");
|
|
break;
|
|
case TURN_ALLOCATE_ERROR:
|
|
printf("TURN_ALLOCATE_ERROR");
|
|
break;
|
|
case TURN_REFRESH:
|
|
printf("TURN_REFRESH");
|
|
break;
|
|
case TURN_REFRESH_RESPONSE:
|
|
printf("TURN_REFRESH_RESPONSE");
|
|
break;
|
|
case TURN_REFRESH_ERROR:
|
|
printf("TURN_REFRESH_ERROR");
|
|
break;
|
|
case TURN_SEND:
|
|
printf("TURN_SEND");
|
|
break;
|
|
case TURN_DATA:
|
|
printf("TURN_DATA");
|
|
break;
|
|
case TURN_CREATE_PERMISSION:
|
|
printf("TURN_CREATE_PERMISSION");
|
|
break;
|
|
case TURN_CREATE_PERMISSION_RESPONSE:
|
|
printf("TURN_CREATE_PERMISSION_RESPONSE");
|
|
break;
|
|
case TURN_CREATE_PERMISSION_ERROR:
|
|
printf("TURN_CREATE_PERMISSION_ERROR");
|
|
break;
|
|
case TURN_CHANNEL_BIND:
|
|
printf("TURN_CHANNEL_BIND");
|
|
break;
|
|
case TURN_CHANNEL_BIND_RESPONSE:
|
|
printf("TURN_CHANNEL_BIND_RESPONSE");
|
|
break;
|
|
case TURN_CHANNEL_BIND_ERROR:
|
|
printf("TURN_CHANNEL_BIND_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECT:
|
|
printf("TURN_TCP_CONNECT");
|
|
break;
|
|
case TURN_TCP_CONNECT_RESPONSE:
|
|
printf("TURN_TCP_CONNECT_RESPONSE");
|
|
break;
|
|
case TURN_TCP_CONNECT_ERROR:
|
|
printf("TURN_TCP_CONNECT_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND:
|
|
printf("TURN_TCP_CONNECTION_BIND");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND_RESPONSE:
|
|
printf("TURN_TCP_CONNECTION_BIND_RESPONSE");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND_ERROR:
|
|
printf("TURN_TCP_CONNECTION_BIND_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECTION_ATTEMPT:
|
|
printf("TURN_TCP_CONNECTION_ATTEMPT");
|
|
break;
|
|
default:
|
|
printf("UNKNOWN");
|
|
break;
|
|
}
|
|
printf("\n");
|
|
|
|
|
|
util_tohex(buffer + 8, 12, tid);
|
|
printf("...TransactionID: %s\n", tid);
|
|
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
int ILibStun_ProcessStunPacket(void *j, char* buffer, int bufferLength, struct sockaddr_in6 *remoteInterface)
|
|
{
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)j;
|
|
int ptr = 20;
|
|
char* username = NULL;
|
|
unsigned short messageType;
|
|
unsigned short messageLength;
|
|
unsigned short attrType;
|
|
unsigned short attrLength;
|
|
struct sockaddr_in mappedAddress;
|
|
struct sockaddr_in changedAddress;
|
|
char integritykey[33]; // Key is 32, but need 33 to put the terminating null char.
|
|
int integritykeySet = 0;
|
|
int processed = 0;
|
|
int isControlled = 0;
|
|
int isControlling = 0;
|
|
unsigned long long tiebreakValue = 0;
|
|
int ignore = 0;
|
|
|
|
// Check the length of the packet & IPv4
|
|
if (buffer == NULL || bufferLength < 20 || bufferLength > 10000 || remoteInterface->sin6_family != AF_INET) { ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "Skipping: Not a STUN Packet (Packet Length/Address Family)"); return 0; }
|
|
|
|
messageType = ntohs(((unsigned short*)buffer)[0]); // Get the message type
|
|
messageLength = ntohs(((unsigned short*)buffer)[1]) + 20; // Get the message length
|
|
|
|
// Check the length and magic string
|
|
if (messageLength != bufferLength || ntohl(((int*)buffer)[1]) != 0x2112A442) { ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_4, "Skipping: Not a STUN Packet (Magic String)"); return 0; }
|
|
|
|
memset(&mappedAddress, 0, sizeof(mappedAddress));
|
|
memset(&changedAddress, 0, sizeof(changedAddress));
|
|
|
|
switch(messageType)
|
|
{
|
|
case STUN_BINDING_REQUEST:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_BINDING_REQUEST");
|
|
break;
|
|
case STUN_BINDING_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_BINDING_RESPONSE");
|
|
break;
|
|
case STUN_BINDING_ERROR_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_BINDING_ERROR_RESPONSE");
|
|
break;
|
|
case STUN_SHAREDSECRET_REQUEST:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_SHARED_SECRET_REQUEST");
|
|
break;
|
|
case STUN_SHAREDSECRET_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_SHARED_SECRET_RESPONSE");
|
|
break;
|
|
case STUN_SHAREDSECRET_ERROR_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: STUN_SHARED_SECRET_ERROR_RESPONSE");
|
|
break;
|
|
case TURN_ALLOCATE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_ALLOCATE");
|
|
break;
|
|
case TURN_ALLOCATE_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_ALLOCATE_RESPONSE");
|
|
break;
|
|
case TURN_ALLOCATE_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_ALLOCATE_ERROR");
|
|
break;
|
|
case TURN_REFRESH:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_REFRESH");
|
|
break;
|
|
case TURN_REFRESH_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_REFRESH_RESPONSE");
|
|
break;
|
|
case TURN_REFRESH_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_REFRESH_ERROR");
|
|
break;
|
|
case TURN_SEND:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_SEND");
|
|
break;
|
|
case TURN_DATA:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_DATA");
|
|
break;
|
|
case TURN_CREATE_PERMISSION:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CREATE_PERMISSION");
|
|
break;
|
|
case TURN_CREATE_PERMISSION_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CREATE_PERMISSION_RESPONSE");
|
|
break;
|
|
case TURN_CREATE_PERMISSION_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CREATE_PERMISSION_ERROR");
|
|
break;
|
|
case TURN_CHANNEL_BIND:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CHANNEL_BIND");
|
|
break;
|
|
case TURN_CHANNEL_BIND_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CHANNEL_BIND_RESPONSE");
|
|
break;
|
|
case TURN_CHANNEL_BIND_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "R: TURN_CHANNEL_BIND_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECT:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECT");
|
|
break;
|
|
case TURN_TCP_CONNECT_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECT_RESPONSE");
|
|
break;
|
|
case TURN_TCP_CONNECT_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECT_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECTION_BIND");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND_RESPONSE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECTION_BIND_RESPONSE");
|
|
break;
|
|
case TURN_TCP_CONNECTION_BIND_ERROR:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECTION_BIND_ERROR");
|
|
break;
|
|
case TURN_TCP_CONNECTION_ATTEMPT:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "R: TURN_TCP_CONNECTION_ATTEMPT");
|
|
break;
|
|
}
|
|
|
|
{
|
|
char tid[25];
|
|
util_tohex(buffer+8, 12, tid);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...TransactionID: %s", tid);
|
|
}
|
|
|
|
while (ptr + 4 < messageLength) // Decode each attribute one at a time
|
|
{
|
|
attrType = ntohs(((unsigned short*)(buffer + ptr))[0]);
|
|
attrLength = ntohs(((unsigned short*)(buffer + ptr))[1]);
|
|
if (ptr + 4 + attrLength > messageLength)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ABORT: Message Length Error");return 0;
|
|
}
|
|
|
|
if (ignore != 0 && attrType != STUN_ATTRIB_FINGERPRINT) { ptr = messageLength; break; }
|
|
|
|
switch (attrType)
|
|
{
|
|
case STUN_ATTRIB_ERROR_CODE:
|
|
{
|
|
#ifdef _REMOTELOGGING
|
|
int ErrorCode = (buffer[ptr+6] * 100) + (buffer[ptr+7] % 100);
|
|
#endif
|
|
char* ErrorReason = buffer+ptr+8;
|
|
char tid[25];
|
|
|
|
util_tohex(buffer+8, 12, tid);
|
|
ErrorReason[attrLength] = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ERROR Received: [%s] Code(%d) Reason(%s) from %s:%u", tid, ErrorCode, ErrorReason, ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
break;
|
|
}
|
|
|
|
case STUN_ATTRIB_USE_CANDIDATE:
|
|
{
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_ICE_CONTROLLED:
|
|
{
|
|
isControlled = 1;
|
|
tiebreakValue = ILibNTOHLL(((unsigned long long*)(buffer + ptr + 4))[0]);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...ICE-CONTROLLED");
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_ICE_CONTROLLING:
|
|
{
|
|
isControlling = 1;
|
|
tiebreakValue = ILibNTOHLL(((unsigned long long*)(buffer + ptr + 4))[0]);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...ICE-CONTROLLING");
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_MAPPED_ADDRESS:
|
|
case STUN_ATTRIB_XOR_MAPPED_ADDRESS:
|
|
{
|
|
if (buffer[ptr + 5] != 1) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_XOR_MAPPED_ADDRESS/ERROR"); break; }
|
|
mappedAddress.sin_family = AF_INET;
|
|
mappedAddress.sin_port = ((unsigned short*)(buffer + ptr + 6))[0];
|
|
mappedAddress.sin_addr.s_addr = ((unsigned int*)(buffer + ptr + 8))[0];
|
|
if (attrType == STUN_ATTRIB_XOR_MAPPED_ADDRESS) { mappedAddress.sin_port ^= 0x1221; mappedAddress.sin_addr.s_addr ^= 0x42A41221; }
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_OTHER_ADDRESS:
|
|
case STUN_ATTRIB_CHANGED_ADDRESS:
|
|
{
|
|
if (buffer[ptr + 5] != 1) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_CHANGED_ADDRESS/ERROR"); break; }
|
|
changedAddress.sin_family = AF_INET;
|
|
changedAddress.sin_port = ((unsigned short*)(buffer + ptr + 6))[0];
|
|
changedAddress.sin_addr.s_addr = ((unsigned int*)(buffer + ptr + 8))[0];
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_USERNAME:
|
|
{
|
|
// The username must be at least "xxxxxxxx:x", and our username is always 8 bytes long
|
|
if (attrLength < 9 || buffer[ptr + 4 + 8] != ':') {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_USERNAME/ERROR"); break; }
|
|
username = buffer + ptr + 4;
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_FINGERPRINT:
|
|
{
|
|
// Fingerprint must be 4 bytes, must be last attribute.
|
|
if (attrLength != 4 || (ptr + 8) != messageLength)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_FINGERPRINT/MISSING_OR_CORRUPT");
|
|
if (attrLength == 0) { return 1; } else { return 0; }
|
|
}
|
|
|
|
// Check the fingerprint
|
|
if ((ILibStun_CRC32(buffer, ptr) ^ 0x5354554e) != ntohl(((int*)(buffer + ptr + 4))[0])) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_FINGERPRINT/MISMATCH"); return 1; }
|
|
|
|
break;
|
|
}
|
|
case STUN_ATTRIB_MESSAGE_INTEGRITY:
|
|
{
|
|
HMAC_CTX* hmac;
|
|
unsigned int hmaclen = 20;
|
|
unsigned char hmacresult[20];
|
|
char* key = NULL;
|
|
int keylen = 0;
|
|
ignore = 1;
|
|
|
|
if (attrLength != 20) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_MESSAGE_INTEGRITY/LENGTH-ERROR"); break; }
|
|
|
|
if (username != NULL)
|
|
{
|
|
// Compute the secret key
|
|
ILibStun_ComputeIntegrityKey(username, obj->Secret, integritykey);
|
|
key = integritykey;
|
|
keylen = 32;
|
|
integritykeySet = 1;
|
|
}
|
|
else
|
|
{
|
|
// Grab the key from stored state
|
|
unsigned char slot = buffer[8];
|
|
|
|
// Check to see if this is an IceSlot
|
|
if (slot < ILibSTUN_MaxSlots && obj->IceStates[slot] != NULL)
|
|
{
|
|
key = obj->IceStates[slot]->rkey;
|
|
keylen = obj->IceStates[slot]->rkeylen;
|
|
}
|
|
// Check to see if it's a DTLS Session Slot
|
|
else if ((slot ^ 0x80) < ILibSTUN_MaxSlots && obj->dTlsSessions[slot ^ 0x80] != NULL)
|
|
{
|
|
key = obj->dTlsSessions[slot ^ 0x80]->parent->IceStates[obj->dTlsSessions[slot ^ 0x80]->iceStateSlot]->rkey;
|
|
keylen = obj->dTlsSessions[slot ^ 0x80]->parent->IceStates[obj->dTlsSessions[slot ^ 0x80]->iceStateSlot]->rkeylen;
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_MESSAGE_INTEGRITY/FAILED");
|
|
return 0; // FAILED (or don't care)
|
|
// TODO: Bryan: If we really don't care, we may need to return 1, otherwise this packet will get fed into OpenSSL.
|
|
}
|
|
}
|
|
|
|
// Fix the length: -20 + 24 = 4, but we only need to do this if fingerprint is present also
|
|
if (ntohs(((unsigned short*)(buffer + ptr + (4 + FOURBYTEBOUNDARY(attrLength))))[0]) == STUN_ATTRIB_FINGERPRINT)
|
|
{
|
|
((unsigned short*)buffer)[1] = htons((unsigned short)(ptr + 4));
|
|
}
|
|
|
|
// Setup and perform HMAC-SHA1
|
|
hmac = HMAC_CTX_new();
|
|
HMAC_Init_ex(hmac, key, keylen, EVP_sha1(), NULL);
|
|
HMAC_Update(hmac, (unsigned char*)buffer, ptr);
|
|
HMAC_Final(hmac, hmacresult, &hmaclen); // Put the HMAC in the outgoing result location
|
|
HMAC_CTX_free(hmac);
|
|
|
|
// Put the length back, if fingerprint was present
|
|
if (ntohs(((unsigned short*)(buffer + ptr + (4 + FOURBYTEBOUNDARY(attrLength))))[0]) == STUN_ATTRIB_FINGERPRINT)
|
|
{
|
|
((unsigned short*)buffer)[1] = htons(messageLength - 20);
|
|
}
|
|
|
|
// Compare the HMAC result
|
|
if (hmaclen != 20 || memcmp(hmacresult, buffer + ptr + 4, 20) != 0) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...STUN_ATTRIB_MESSAGE_INTEGRITY/FAILED(2)"); return 0; }
|
|
break;
|
|
}
|
|
}
|
|
|
|
ptr += (4 + FOURBYTEBOUNDARY(attrLength)); // Move the ptr forward by the attribute length plus padding.
|
|
}
|
|
|
|
// Message length must be exact
|
|
if (ptr != messageLength) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...PTR != messageLength"); return 0; }
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Successfully Passed Fingerprint/Message-Integrity");
|
|
|
|
if (IS_REQUEST(messageType) && integritykeySet == 1)
|
|
{
|
|
// This is a STUN/ICE request, reply to it here.
|
|
int rptr = 20 + 12;
|
|
char rbuffer[110]; // Length of this response should be exactly 64 bytes
|
|
int EncodedSlot;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "[STUN/ICE REQUEST]");
|
|
processed = 1;
|
|
|
|
if (username != NULL)
|
|
{
|
|
int i = 0;
|
|
EncodedSlot = ILibStun_CharToSlot(username[0]);
|
|
if (EncodedSlot < ILibSTUN_MaxSlots && obj->IceStates[EncodedSlot] != NULL)
|
|
{
|
|
//
|
|
// This will be true, if we are CONTROLLED
|
|
//
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...EncodedSlot: %d", EncodedSlot);
|
|
if (remoteInterface->sin6_family == AF_INET) // We currently only support IPv4 candidates
|
|
{
|
|
if (obj->IceStates[EncodedSlot]->hostcandidatecount == 0 && obj->IceStates[EncodedSlot]->hostcandidates == NULL)
|
|
{
|
|
//
|
|
// We generated the offer, which is sitting in this SLOT... We haven't received the response offer yet
|
|
// So we need to just save the interfaces we are receiving authenticated ICE requests on
|
|
//
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
if (memcmp(&(((struct sockaddr_in6*)obj->IceStates[EncodedSlot]->offerblock)[i]), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family)) == 0)
|
|
{
|
|
// Don't need to do anything here
|
|
break;
|
|
}
|
|
else if (((struct sockaddr_in6*)obj->IceStates[EncodedSlot]->offerblock)[i].sin6_family == 0)
|
|
{
|
|
// Save the interface we are receiving authentiated ICE requests on
|
|
memcpy_s(&(((struct sockaddr_in6*)obj->IceStates[EncodedSlot]->offerblock)[i]), sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Received ICE Request from: %s:%u , caching until we receive offer", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < obj->IceStates[EncodedSlot]->hostcandidatecount; ++i)
|
|
{
|
|
if (obj->IceStates[EncodedSlot]->hostcandidates[i].port == remoteInterface->sin6_port)
|
|
{
|
|
if (obj->IceStates[EncodedSlot]->hostcandidates[i].addr == ((struct sockaddr_in*)remoteInterface)->sin_addr.s_addr)
|
|
{
|
|
// Matching Candidate
|
|
if(obj->IceStates[EncodedSlot]->hostcandidateResponseFlag[i] != 1)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Ice Slot: %d Candidate Match [%s:%u]", EncodedSlot, ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
}
|
|
obj->IceStates[EncodedSlot]->hostcandidateResponseFlag[i] = 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the origin
|
|
//
|
|
for (i = 0; i < STUN_NUM_ADDR; ++i)
|
|
{
|
|
if (obj->IceStates[EncodedSlot]->ReceivedAddr[i].sin6_family == 0)
|
|
{
|
|
memcpy_s(&(obj->IceStates[EncodedSlot]->ReceivedAddr[i]), sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Adding to ReceiveAddr[%d] %s:%u", i, ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
break;
|
|
}
|
|
else if (obj->IceStates[EncodedSlot]->ReceivedAddr[i].sin6_family == remoteInterface->sin6_family &&
|
|
memcmp(&(obj->IceStates[EncodedSlot]->ReceivedAddr[i]), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family)) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// The response is going here, becuase we'll always know what the associated iceState is, because we encoded the ice state in our user name, and we
|
|
// always check MessageIntegrity of inbound messages
|
|
|
|
if (obj->IceStates[EncodedSlot]->rkey == NULL)
|
|
{
|
|
// Remote Key is unknown... This means the remote offer was not set yet.
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Remote Offer wasn't set yet, so we won't respond to ICE-REQUESTs");
|
|
return 1;
|
|
}
|
|
|
|
if ((obj->IceStates[EncodedSlot]->peerHasActiveOffer == 1 && isControlling != 0) || (obj->IceStates[EncodedSlot]->peerHasActiveOffer == 0 && isControlled != 0))
|
|
{
|
|
// Build the reply packet
|
|
((unsigned short*)rbuffer)[0] = htons(0x0101); // Response type, skip setting length for now
|
|
((unsigned int*)rbuffer)[1] = htonl(0x2112A442); // Set the magic string
|
|
memcpy_s(rbuffer + 8, sizeof(rbuffer) - 8, buffer + 8, 12); // Copy the transaction id
|
|
((unsigned short*)(rbuffer + 20))[0] = htons(STUN_ATTRIB_XOR_MAPPED_ADDRESS); // Attribute header
|
|
((unsigned short*)(rbuffer + 20))[1] = htons(8); // Attribute length
|
|
((unsigned short*)(rbuffer + 20))[2] = htons(1); // reserved + familly (IPv4)
|
|
((unsigned short*)(rbuffer + 20))[3] = ((struct sockaddr_in*)remoteInterface)->sin_port ^ 0x1221; // IPv4 port
|
|
((unsigned int*)(rbuffer + 20))[2] = ((struct sockaddr_in*)remoteInterface)->sin_addr.s_addr ^ 0x42A41221; // IPv4 address
|
|
|
|
rptr += ILibStun_AddMessageIntegrityAttr(rbuffer, rptr, integritykey, 32);
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr); // Set the length in this function
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Sending Response to %s:%u", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
#ifdef _WEBRTCDEBUG
|
|
_DebugSTUN(rbuffer, rptr);
|
|
#endif
|
|
}
|
|
else if ((obj->IceStates[EncodedSlot]->peerHasActiveOffer == 1 && isControlled != 0 &&
|
|
ILibNTOHLL(((unsigned long long*)obj->IceStates[EncodedSlot]->tieBreaker)[0]) < tiebreakValue) ||
|
|
(obj->IceStates[EncodedSlot]->peerHasActiveOffer == 0 && isControlling != 0 &&
|
|
ILibNTOHLL(((unsigned long long*)obj->IceStates[EncodedSlot]->tieBreaker)[0]) >= tiebreakValue))
|
|
{
|
|
// Respond with Role Conflict
|
|
char errorCode[] = { 0, 0, 4, 87, 'R', 'o', 'l', 'e', ' ', 'C', 'o', 'n', 'f', 'l', 'i', 'c', 't' };
|
|
char address[20];
|
|
int addressLen = ILibTURN_CreateXORMappedAddress(remoteInterface, address, buffer + 8);
|
|
rptr = ILibTURN_GenerateStunFormattedPacketHeader(rbuffer, STUN_BINDING_ERROR_RESPONSE, buffer + 8);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_XOR_MAPPED_ADDRESS, address, addressLen);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_ERROR_CODE, errorCode, 17);
|
|
rptr += ILibStun_AddMessageIntegrityAttr(rbuffer, rptr, integritykey, 32);
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr); // Set the length in this function
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Responding with Role Conflict");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...WE had a %s tiebreak", (obj->IceStates[EncodedSlot]->peerHasActiveOffer == 1)?"SMALLER":"LARGER");
|
|
}
|
|
else
|
|
{
|
|
// We need to switch roles
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Role Conflict, we're switching roles");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...WE had a %s tiebreak", (obj->IceStates[EncodedSlot]->peerHasActiveOffer == 1)?"LARGER":"SMALLER");
|
|
|
|
obj->IceStates[EncodedSlot]->peerHasActiveOffer = obj->IceStates[EncodedSlot]->peerHasActiveOffer == 0 ? 1 : 0;
|
|
ILibStun_ICE_Start(obj->IceStates[EncodedSlot], EncodedSlot);
|
|
return 1;
|
|
}
|
|
|
|
|
|
// Send the data in plain text
|
|
ILibStun_SendPacket(obj, rbuffer, 0, rptr, remoteInterface, ILibAsyncSocket_MemoryOwnership_USER);
|
|
|
|
//ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)remoteInterface, rbuffer, rptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
|
|
// Send an ICE request back. This is needed to unlock Chrome/Opera inbound port for TLS. Don't do more than ILibSTUN_MaxSlots of these.
|
|
if (obj->IceStates[EncodedSlot]->hostcandidates != NULL && obj->IceStates[EncodedSlot]->hostcandidatecount > 0)
|
|
{
|
|
if (obj->IceStates[EncodedSlot] != NULL && obj->IceStates[EncodedSlot]->requerycount < ILibSTUN_MaxSlots && obj->IceStates[EncodedSlot]->dtlsSession < 0)
|
|
{
|
|
char TransactionID[12];
|
|
util_random(12, TransactionID);
|
|
TransactionID[0] = (char)EncodedSlot;
|
|
obj->IceStates[EncodedSlot]->requerycount++;
|
|
ILibStun_SendIceRequest(obj->IceStates[EncodedSlot], EncodedSlot, 0, (struct sockaddr_in6*)remoteInterface);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (IS_SUCCESS_RESP(messageType) && (unsigned char)buffer[8] <= ILibSTUN_MaxSlots)
|
|
{
|
|
int hx;
|
|
// This is a STUN response for a STUN packet we sent as a result of an Offer
|
|
|
|
if(obj->IceStates[(int)buffer[8]]->dtlsSession < 0 || (obj->IceStates[(int)buffer[8]]->dtlsSession >= 0 && obj->dTlsSessions[obj->IceStates[(int)buffer[8]]->dtlsSession]->state != 1))
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Received Response to ICE-REQUEST, IceSlot: %d from %s:%u", (int)buffer[8], ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
}
|
|
|
|
processed = 1;
|
|
if (obj->IceStates[(int)buffer[8]]->peerHasActiveOffer == 0 && obj->IceStates[(int)buffer[8]]->dtlsSession < 0) // SlotNumer is encoded as first byte of TransactionID
|
|
{
|
|
// We'll only enter this section if we are CONTROLLING, and there is no DTLS session established yet
|
|
for (hx = 0; hx < obj->IceStates[(int)buffer[8]]->hostcandidatecount; hx++)
|
|
{
|
|
struct sockaddr_in candidateInterface;
|
|
memset(&candidateInterface, 0, sizeof(struct sockaddr_in));
|
|
candidateInterface.sin_family = AF_INET;
|
|
candidateInterface.sin_addr.s_addr = obj->IceStates[(int)buffer[8]]->hostcandidates[hx].addr;
|
|
candidateInterface.sin_port = obj->IceStates[(int)buffer[8]]->hostcandidates[hx].port;
|
|
|
|
// Enumerate the Candidates and make sure this STUN Response came from one of them
|
|
if (candidateInterface.sin_family == remoteInterface->sin6_family && memcmp(remoteInterface, &candidateInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family)) == 0)
|
|
{
|
|
// We have a matching ICE Candidate and STUN Response
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Candidate Match [%s:%u]", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
obj->IceStates[(int)buffer[8]]->hostcandidateResponseFlag[hx] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//// TODO: Bryan: I commented this out, because this logic will now take place during Candidate Nomination.
|
|
////
|
|
//if(obj->IceStates[buffer[8]]->peerHasActiveOffer==0 && obj->IceStates[buffer[8]]->dtlsSession<0) // SlotNumer is encoded as first byte of TransactionID
|
|
//{
|
|
// // We'll only enter this section if we are CONTROLLING, and no DTLS session is established yet
|
|
// for (hx = 0; hx < obj->IceStates[buffer[8]]->hostcandidatecount; hx++)
|
|
// {
|
|
// struct sockaddr_in candidateInterface;
|
|
// memset(&candidateInterface, 0, sizeof(struct sockaddr_in));
|
|
// candidateInterface.sin_family = AF_INET;
|
|
// candidateInterface.sin_addr.s_addr = obj->IceStates[buffer[8]]->hostcandidates[hx].addr;
|
|
// candidateInterface.sin_port = obj->IceStates[buffer[8]]->hostcandidates[hx].port;
|
|
|
|
// //
|
|
// // Enumerate the Candidates and make sure this STUN Response came from one of them
|
|
// //
|
|
// if(INET_SOCKADDR_LENGTH(remoteInterface->sin6_family) == INET_SOCKADDR_LENGTH(candidateInterface.sin_family))
|
|
// {
|
|
// if(remoteInterface->sin6_family == AF_INET)
|
|
// {
|
|
// // For now, only IPv4 is supported for DTLS
|
|
// if(((struct sockaddr_in*)(remoteInterface))->sin_addr.s_addr == candidateInterface.sin_addr.s_addr)
|
|
// {
|
|
// if(remoteInterface->sin6_port == candidateInterface.sin_port)
|
|
// {
|
|
// //
|
|
// // We have a matching ICE Candidate and STUN Response
|
|
// //
|
|
// ILibStun_InitiateDTLS(obj->IceStates[buffer[8]], buffer[8], remoteInterface);
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
}
|
|
if (IS_SUCCESS_RESP(messageType) && (((unsigned char)buffer[8] ^ 0x80) < ILibSTUN_MaxSlots))
|
|
{
|
|
// This is an encoded DTLS Session Slot #
|
|
int SessionSlot = (unsigned char)buffer[8] ^ 0x80;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "Consent Freshness Updated on IceSlot: %d", SessionSlot);
|
|
|
|
processed = 1;
|
|
// We got a response, so we can reset the timer for Freshness
|
|
obj->dTlsSessions[SessionSlot]->freshnessTimestampStart = 0;
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(obj->dTlsSessions[SessionSlot]));
|
|
ILibLifeTime_Add(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(obj->dTlsSessions[SessionSlot]), ILibStun_MaxConsentFreshnessTimeoutSeconds, ILibStun_WebRTC_ConsentFreshness_Start, NULL);
|
|
}
|
|
|
|
if (IS_SUCCESS_RESP(messageType) && memcmp(buffer + 8, obj->TransactionId, 12) == 0) // NAT Detection Response
|
|
{
|
|
int i, result;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "[STUN/RESPONSE]");
|
|
processed = 1;
|
|
|
|
// This is a STUN server response
|
|
ILibLifeTime_Remove(obj->Timer, obj);
|
|
|
|
// Process STUN Results, if we are currently evaluating the NAT
|
|
switch (obj->State)
|
|
{
|
|
case STUN_STATUS_CHECKING_UDP_CONNECTIVITY:
|
|
if (mappedAddress.sin_family == AF_INET)
|
|
{
|
|
if(changedAddress.sin_family == AF_INET)
|
|
{
|
|
memcpy_s(&(obj->StunServer2_AlternatePort), sizeof(struct sockaddr_in6), &(changedAddress), sizeof(struct sockaddr_in));
|
|
memcpy_s(&(obj->StunServer2_PrimaryPort), sizeof(struct sockaddr_in6), &(changedAddress), sizeof(struct sockaddr_in));
|
|
((struct sockaddr_in*)(&obj->StunServer2_PrimaryPort))->sin_port = ((struct sockaddr_in*)(&obj->StunServer))->sin_port;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "RFC-5780 Support Detected: %s:%u", ILibRemoteLogging_ConvertAddress((struct sockaddr*)&changedAddress), ntohs(changedAddress.sin_port));
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "RFC-5780 Support was NOT detected");
|
|
}
|
|
memcpy_s(&(obj->Public), sizeof(struct sockaddr_in6), &(mappedAddress), sizeof(struct sockaddr_in));
|
|
}
|
|
else
|
|
{
|
|
// Problem decoding the packet.
|
|
ILibStun_FireResults(obj, ILibStun_Results_Unknown, NULL);
|
|
return 1;
|
|
}
|
|
|
|
result = 0;
|
|
if (((struct sockaddr_in*)&(obj->LocalIf))->sin_addr.s_addr == 0)
|
|
{
|
|
// Check if the public IP address is one of our own.
|
|
struct sockaddr_in* LocalInterfaceListV4 = NULL;
|
|
int LocalInterfaceListV4Len = ILibGetLocalIPv4AddressList(&LocalInterfaceListV4, 0);
|
|
if (LocalInterfaceListV4 != NULL)
|
|
{
|
|
for (i = 0; i < LocalInterfaceListV4Len; ++i)
|
|
{
|
|
if (((struct sockaddr_in*)&(obj->Public))->sin_addr.s_addr == ((struct sockaddr_in*)&(LocalInterfaceListV4[i]))->sin_addr.s_addr) result = 1;
|
|
}
|
|
free(LocalInterfaceListV4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (((struct sockaddr_in*)&(obj->Public))->sin_addr.s_addr == ((struct sockaddr_in*)&(obj->LocalIf))->sin_addr.s_addr) result = 1;
|
|
}
|
|
|
|
if (result == 1)
|
|
{
|
|
// No Nat
|
|
ILibStun_FireResults(obj, ILibStun_Results_No_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
break;
|
|
}
|
|
|
|
if(NAT_MAPPING_DETECTION(obj->TransactionId)==0)
|
|
{
|
|
ILibStun_FireResults(obj, ILibStun_Results_Public_Interface, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
else if(changedAddress.sin_family == AF_INET)
|
|
{
|
|
obj->State = STUN_STATUS_CHECKING_FULL_CONE_NAT;
|
|
ILibLifeTime_Add(obj->Timer, obj, ILibStunClient_TIMEOUT, &ILibStun_OnTimeout, NULL); // This timeout should *NEVER* happen unless RFC5780 is not implemented
|
|
ILib_Stun_SendAttributeChangeRequest(obj, (struct sockaddr*)&(obj->StunServer2_PrimaryPort), 0x00);
|
|
}
|
|
else
|
|
{
|
|
ILibStun_FireResults(obj, ILibStun_Results_RFC5780_NOT_IMPLEMENTED, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
break;
|
|
case STUN_STATUS_CHECKING_FULL_CONE_NAT:
|
|
memcpy_s(&(obj->Public2), sizeof(struct sockaddr_in6), &(mappedAddress), sizeof(struct sockaddr_in));
|
|
if (((struct sockaddr_in*)(&obj->Public))->sin_port == ((struct sockaddr_in*)(&obj->Public2))->sin_port)
|
|
{
|
|
// Endpoint-Independent Mapping (Full-Cone)
|
|
ILibStun_FireResults(obj, ILibStun_Results_Full_Cone_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
else
|
|
{
|
|
// Run Phase-III test
|
|
obj->State = STUN_STATUS_CHECKING_SYMETRIC_NAT;
|
|
ILib_Stun_SendAttributeChangeRequest(obj, (struct sockaddr*)&(obj->StunServer2_AlternatePort), 0x00);
|
|
ILibLifeTime_Add(obj->Timer, obj, ILibStunClient_TIMEOUT, &ILibStun_OnTimeout, NULL); // This timeout should *NEVER* happen for this case
|
|
}
|
|
|
|
break;
|
|
case STUN_STATUS_CHECKING_SYMETRIC_NAT:
|
|
memcpy_s(&(obj->Public3), sizeof(struct sockaddr_in6), &(mappedAddress), sizeof(struct sockaddr_in));
|
|
if (((struct sockaddr_in*)(&obj->Public2))->sin_port == ((struct sockaddr_in*)(&obj->Public3))->sin_port)
|
|
{
|
|
// Address Dependent Mapping
|
|
ILibStun_FireResults(obj, ILibStun_Results_Restricted_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
else
|
|
{
|
|
// Address and Port Dependent Mapping (Symmetric in this case)
|
|
ILibStun_FireResults(obj, ILibStun_Results_Symetric_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
break;
|
|
case STUN_STATUS_CHECKING_RESTRICTED_NAT:
|
|
// Restricted NAT
|
|
ILibStun_FireResults(obj, ILibStun_Results_Restricted_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (processed == 0) { ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "DID NOT Process Packet of type: %u", messageType);}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void ILibStun_DelaySendIceRequest_OnLifeTimeDestroy(void *object)
|
|
{
|
|
free(object);
|
|
}
|
|
|
|
void ILibStun_DelaySendIceRequest_OnLifeTime(void *object)
|
|
{
|
|
char dest[ILibMemory_Legacy_RawSize(sizeof(struct sockaddr_in6), 4)] = { 0 };
|
|
int Ptr;
|
|
char *Packet = (char*)object;
|
|
struct ILibStun_Module *stun;
|
|
ILibICE_PeriodicState *Data;
|
|
|
|
Ptr = ntohs(((unsigned short*)Packet)[1]) + 20;
|
|
Data = (ILibICE_PeriodicState*)(Packet + Ptr);
|
|
stun = (struct ILibStun_Module*)Data->ptr;
|
|
|
|
ILibMemory_Legacy_GetExtraSizePtrEx(&dest, sizeof(struct sockaddr_in6))[0] = 4;
|
|
((int*)ILibMemory_GetExtraMemory(&dest, sizeof(struct sockaddr_in6)))[0] = Data->flags << 16;
|
|
memcpy_s(dest, sizeof(dest), &(Data->addr), sizeof(struct sockaddr_in6));
|
|
|
|
ILibStun_SendPacket(stun, Packet, 0, Ptr, (struct sockaddr_in6*)&dest, ILibAsyncSocket_MemoryOwnership_CHAIN);
|
|
//ILibAsyncSocket_SendTo(stun->UDP, Packet, Ptr, (struct sockaddr*)&dest, ILibAsyncSocket_MemoryOwnership_CHAIN);
|
|
}
|
|
|
|
int ILibStun_GenerateIceRequestPacket(struct ILibStun_IceState* module, char* Packet, char* TransactionID, int useCandidate, struct sockaddr_in6* remote)
|
|
{
|
|
char remKey[64];
|
|
char *stunUsername;
|
|
char Address[20];
|
|
int AddressLen;
|
|
int Ptr = 0;
|
|
|
|
AddressLen = ILibTURN_CreateXORMappedAddress(remote, Address, TransactionID);
|
|
|
|
if ((stunUsername = (char*)malloc(10 + module->rusernamelen)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(stunUsername, 10 + module->rusernamelen, module->rusername, module->rusernamelen);
|
|
memcpy_s(stunUsername + module->rusernamelen, 10, ":", 1);
|
|
memcpy_s(stunUsername + module->rusernamelen + 1, 9, module->userAndKey + 1, 8);
|
|
stunUsername[9 + module->rusernamelen] = 0;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_2, "Generating ICE Request [%s] Credentials[%s]", ILibRemoteLogging_ConvertToHex(TransactionID, 12), stunUsername);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Address = %s", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remote));
|
|
|
|
Ptr = ILibTURN_GenerateStunFormattedPacketHeader(Packet, STUN_BINDING_REQUEST, TransactionID);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_USERNAME, stunUsername, 9 + module->rusernamelen);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...USERNAME >> PTR = %d", Ptr);
|
|
|
|
if (useCandidate != 0)
|
|
{
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_USE_CANDIDATE, NULL, 0);
|
|
}
|
|
|
|
if (module->peerHasActiveOffer == 0)
|
|
{
|
|
// We are CONTROLLING
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_ICE_CONTROLLING, module->tieBreaker, 8);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_2, "...CONTROLLING");
|
|
}
|
|
else
|
|
{
|
|
// We are CONTROLLED
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_ICE_CONTROLLED, module->tieBreaker, 8);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_2, "...CONTROLLED");
|
|
}
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "... >> PTR = %d", Ptr);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_XOR_MAPPED_ADDRESS, Address, AddressLen);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "... XORADDR >> PTR = %d", Ptr);
|
|
Ptr += ILibStun_AddMessageIntegrityAttr(Packet, Ptr, module->rkey, module->rkeylen);
|
|
Ptr += ILibStun_AddFingerprint(Packet, Ptr);
|
|
free(stunUsername);
|
|
|
|
memcpy_s(remKey, sizeof(remKey), module->rkey, module->rkeylen);
|
|
remKey[module->rkeylen] = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Using Integrity Key: %s", remKey);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_3, "...PTR = %d", Ptr);
|
|
return Ptr;
|
|
}
|
|
|
|
void ILibStun_SendIceRequestEx(struct ILibStun_IceState *IceState, char* TransactionID, int useCandidate, struct sockaddr_in6* remoteInterface)
|
|
{
|
|
int Ptr;
|
|
char Packet[512];
|
|
|
|
Ptr = ILibStun_GenerateIceRequestPacket(IceState, Packet, TransactionID, useCandidate, remoteInterface);
|
|
ILibStun_SendPacket(IceState->parentStunModule, Packet, 0, Ptr, remoteInterface, ILibAsyncSocket_MemoryOwnership_USER);
|
|
//ILibAsyncSocket_SendTo(IceState->parentStunModule->UDP, Packet, Ptr, (struct sockaddr*)remoteInterface, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
|
|
void ILibStun_SendIceRequest(struct ILibStun_IceState *IceState, int SlotNumber, int useCandidate, struct sockaddr_in6* remoteInterface)
|
|
{
|
|
char TransactionID[12];
|
|
TransactionID[0] = (char)SlotNumber;
|
|
util_random(11, TransactionID + 1);
|
|
ILibStun_SendIceRequestEx(IceState, TransactionID, useCandidate, remoteInterface);
|
|
}
|
|
|
|
void ILibStun_DelaySendIceRequest(struct ILibStun_IceState* module, int selectedSlot, int useCandidate)
|
|
{
|
|
char TransactionID[12];
|
|
char *Packet;
|
|
int Ptr;
|
|
|
|
int i, ii, x;
|
|
struct sockaddr_in remote;
|
|
int delay;
|
|
|
|
for (i = 0; i < module->hostcandidatecount; ++i) // Queue an ICE Request for each candidate
|
|
{
|
|
for (ii = 0; ii < 2; ++ii) // Queue TWO requests for each candidate
|
|
{
|
|
for (x = 0; x < 2; ++x)
|
|
{
|
|
if (module->parentStunModule->alwaysUseTurn || (module->dtlsSession >= 0 && module->parentStunModule->dTlsSessions[module->dtlsSession]->remoteInterface->sin6_family == 0)) { x = 1; }
|
|
memset(&remote, 0, sizeof(struct sockaddr_in));
|
|
|
|
remote.sin_family = AF_INET;
|
|
remote.sin_port = module->hostcandidates[i].port;
|
|
remote.sin_addr.s_addr = module->hostcandidates[i].addr;
|
|
TransactionID[0] = (char)selectedSlot; // We're going to encode the IceState slot in the Transaction ID, so we can refer to it in the response
|
|
util_random(11, TransactionID + 1);
|
|
|
|
if ((Packet = (char*)malloc(512)) == NULL) { ILIBCRITICALEXIT(254); }
|
|
Ptr = ILibStun_GenerateIceRequestPacket(module, Packet, TransactionID, useCandidate, (struct sockaddr_in6*)&remote);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(module->parentStunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "IceSlot: %d Sending ICE Request to: %s:%u [Controlling: %s] (PTR: %d)", selectedSlot, ILibRemoteLogging_ConvertAddress((struct sockaddr*)&remote), ntohs(remote.sin_port), (module->peerHasActiveOffer == 0 ? "YES" : "NO"), Ptr);
|
|
|
|
memset(Packet + Ptr, 0, sizeof(ILibICE_PeriodicState));
|
|
if (module->dtlsSession >= 0)
|
|
{
|
|
memcpy_s(&(((ILibICE_PeriodicState*)(Packet + Ptr))->addr), sizeof(struct sockaddr_in6), module->parentStunModule->dTlsSessions[module->dtlsSession]->remoteInterface, sizeof(struct sockaddr_in6));
|
|
|
|
x = 1;
|
|
}
|
|
else
|
|
{
|
|
((ILibICE_PeriodicState*)(Packet + Ptr))->addr.sin6_family = AF_INET;
|
|
((struct sockaddr_in*)&(((ILibICE_PeriodicState*)(Packet + Ptr))->addr))->sin_addr.s_addr = module->hostcandidates[i].addr;
|
|
((ILibICE_PeriodicState*)(Packet + Ptr))->addr.sin6_port = module->hostcandidates[i].port;
|
|
((ILibICE_PeriodicState*)(Packet + Ptr))->flags = (char)x;
|
|
}
|
|
|
|
((ILibICE_PeriodicState*)(Packet + Ptr))->ptr = module->parentStunModule;
|
|
|
|
delay = ILibStun_GetNextPeriodicInterval(100, 500);
|
|
ILibLifeTime_AddEx(module->parentStunModule->Timer, Packet, delay, ILibStun_DelaySendIceRequest_OnLifeTime, ILibStun_DelaySendIceRequest_OnLifeTimeDestroy);
|
|
if (module->useTurn == 0) { break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//! Generate a local WebRTC offer (Encoded Offer Block)
|
|
/*!
|
|
\param StunModule Stun Client Module
|
|
\param[out] offer Encoded Offer Block
|
|
\param[in,out] userName Local Username (Minimum 9 bytes / 8 bytes + terminating NULL)
|
|
\param[in,out] password Local Password (Minimum 33 bytes / 32 bytes + terminating NULL)
|
|
\return length of encoded offer block
|
|
*/
|
|
int ILibStun_GenerateIceOffer(void* StunModule, char** offer, char* userName, char* password)
|
|
{
|
|
int slot;
|
|
char userAndKey[43];
|
|
struct ILibStun_IceState *ice;
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)StunModule;
|
|
|
|
if ((ice = (struct ILibStun_IceState*)malloc(sizeof(struct ILibStun_IceState))) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memset(ice, 0, sizeof(struct ILibStun_IceState));
|
|
util_random(8, ice->tieBreaker);
|
|
ice->useTurn = obj->alwaysConnectTurn;
|
|
ice->parentStunModule = obj;
|
|
ice->creationTime = ILibGetUptime();
|
|
if ((ice->offerblock = (char*)malloc(8 * sizeof(struct sockaddr_in6))) == NULL){ ILIBCRITICALEXIT(254); }
|
|
ice->peerHasActiveOffer = 0;
|
|
ice->dtlsInitiator = 1;
|
|
memset(ice->offerblock, 0, 8 * sizeof(struct sockaddr_in6));
|
|
slot = ILibStun_GetFreeIceStateSlot(obj, ice, NULL, 0);
|
|
|
|
if (slot < 0)
|
|
{
|
|
// No free slots
|
|
free(ice->offerblock);
|
|
free(ice);
|
|
return 0;
|
|
}
|
|
|
|
// Encoding the slot number into the user name, so we can do some processing when we receive ICE requests
|
|
ILibStun_GenerateUserAndKey(slot, obj->Secret, userAndKey);
|
|
memcpy_s(ice->userAndKey, sizeof(ice->userAndKey), userAndKey, 43);
|
|
memcpy_s(userName, 9, userAndKey + 1, userAndKey[0]);
|
|
memcpy_s(password, 33, userAndKey + userAndKey[0] + 2, userAndKey[userAndKey[0] + 1]);
|
|
userName[(int)userAndKey[0]] = 0;
|
|
password[(int)userAndKey[(int)userAndKey[0] + 1]] = 0;
|
|
|
|
return ILibStun_WebRTC_UpdateOfferResponse(ice, offer);
|
|
}
|
|
|
|
void ILibStun_ICE_FinalizeConnectivityChecks(void *object)
|
|
{
|
|
struct ILibStun_Module* obj = ILibWebRTC_STUN_FROM_CONNECTIVITY_CHECK_TIMER(object);
|
|
int i;
|
|
int x;
|
|
|
|
// Go through each ICE offer that is saved, and find the ones that were doing ICE Connectivity Checks, and finalize all of them
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->isDoingConnectivityChecks != 0)
|
|
{
|
|
obj->IceStates[i]->isDoingConnectivityChecks = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "FINALIZING Connectivity checks on IceSlot: %d", i);
|
|
|
|
for (x = 0; x < obj->IceStates[i]->hostcandidatecount; ++x)
|
|
{
|
|
if (obj->IceStates[i]->hostcandidateResponseFlag[x] != 0)
|
|
{
|
|
if (obj->IceStates[i]->peerHasActiveOffer == 0)
|
|
{
|
|
// Since this list is in priority order, we'll nominate the highest priority candidate that received a response
|
|
struct sockaddr_in *dest = (struct sockaddr_in *)ILibMemory_AllocateA(8 + sizeof(struct sockaddr_in6));
|
|
|
|
dest->sin_family = AF_INET;
|
|
dest->sin_port = obj->IceStates[i]->hostcandidates[x].port;
|
|
dest->sin_addr.s_addr = obj->IceStates[i]->hostcandidates[x].addr;
|
|
ILibStun_SendIceRequest(obj->IceStates[i], i, 1, (struct sockaddr_in6*)dest);
|
|
|
|
if (obj->IceStates[i]->dtlsInitiator != 0)
|
|
{
|
|
// Simultaneously initiate DTLS
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Initiating DTLS to: %s:%u", ILibRemoteLogging_ConvertAddress((struct sockaddr*)&dest), ntohs(dest->sin_port));
|
|
ILibStun_InitiateDTLS(obj->IceStates[i], i, (struct sockaddr_in6*)dest);
|
|
}
|
|
else
|
|
{
|
|
// We are DTLS Server, not Client
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Waiting for DTLS from: %s:%u", ILibRemoteLogging_ConvertAddress((struct sockaddr*)&dest), ntohs(dest->sin_port));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILibStun_PeriodicStunCheckEx(void *obj)
|
|
{
|
|
ILibStun_PeriodicStunCheck(ILibWebRTC_STUN_FROM_PERIODIC_CHECK_TIMER(obj));
|
|
}
|
|
|
|
void ILibStun_PeriodicStunCheck(struct ILibStun_Module* obj)
|
|
{
|
|
int i;
|
|
int needRecheck = 0;
|
|
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_STUN_TO_PERIODIC_CHECK_TIMER(obj));
|
|
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->hostcandidates != NULL && obj->IceStates[i]->dtlsSession < 0 && ((ILibGetUptime() - obj->IceStates[i]->creationTime) < ILibSTUN_MaxOfferAgeSeconds * 1000))
|
|
{
|
|
// We will only do periodic stuns for IceOffers that don't have DTLS sessions associated, and are not expired
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_4, "PeriodicStun for IceStateSlot[%d]", i);
|
|
ILibStun_DelaySendIceRequest(obj->IceStates[i], i, 0);
|
|
needRecheck = 1;
|
|
}
|
|
}
|
|
|
|
if (needRecheck != 0)
|
|
{
|
|
|
|
ILibLifeTime_Add(obj->Timer,
|
|
ILibWebRTC_STUN_TO_PERIODIC_CHECK_TIMER(obj),
|
|
ILibStun_GetNextPeriodicInterval(ILibSTUN_MinNATKeepAliveIntervalSeconds, ILibSTUN_MaxNATKeepAliveIntervalSeconds),
|
|
ILibStun_PeriodicStunCheckEx,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
void ILibStun_ICE_Start(struct ILibStun_IceState *state, int SelectedSlot)
|
|
{
|
|
struct ILibStun_Module *obj = state->parentStunModule;
|
|
if (state->peerHasActiveOffer == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Starting Connectivity Checks...");
|
|
// Since we're not a STUN-LITE implementation, we need to perform connectivity checks
|
|
// We'll only do this, if we're the initiator and the peer is passive
|
|
state->isDoingConnectivityChecks = 1;
|
|
ILibStun_DelaySendIceRequest(state, SelectedSlot, 0);
|
|
|
|
// All of these packets are distributed within a 500ms window.
|
|
// We'll set a timer for 3 seconds, to finalize the connectivity check
|
|
// The timer is set on the stunModule, so we don't have a race condition if the ice offer is updated/deleted during
|
|
// these 3 seconds.
|
|
ILibLifeTime_Add(obj->Timer, ILibWebRTC_STUN_TO_CONNECTIVITY_CHECK_TIMER(obj), 3, ILibStun_ICE_FinalizeConnectivityChecks, NULL);
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Skipping Connectivity Checks...");
|
|
// We are CONTROLLED, so we just need to start the Periodic ICE Request Packets
|
|
ILibStun_PeriodicStunCheck(obj);
|
|
}
|
|
}
|
|
|
|
void ILibStun_SetIceOffer2_TURN_CreatePermissionResponse(ILibTURN_ClientModule turnModule, int success, void *user)
|
|
{
|
|
int SelectedSlot = (int)(uintptr_t)user; // We're casting it this way, becuase SelectedSlot can never be > 255
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)ILibTURN_GetTag(turnModule);
|
|
struct ILibStun_IceState* state = obj->IceStates[SelectedSlot];
|
|
|
|
if (success != 0)
|
|
{
|
|
if (state->peerHasActiveOffer == 0)
|
|
{
|
|
// Since we're not a STUN-LITE implementation, we need to perform connectivity checks
|
|
// We'll only do this, if we're the initiator and the peer is passive
|
|
state->isDoingConnectivityChecks = 1;
|
|
ILibStun_DelaySendIceRequest(state, SelectedSlot, 0);
|
|
|
|
// All of these packets are distributed within a 500ms window.
|
|
// We'll set a timer for 3 seconds, to finalize the connectivity check
|
|
// The timer is set on the stunModule, so we don't have a race condition if the ice offer is updated/deleted during
|
|
// these 3 seconds.
|
|
ILibLifeTime_Add(obj->Timer, ILibWebRTC_STUN_TO_CONNECTIVITY_CHECK_TIMER(obj), 3, ILibStun_ICE_FinalizeConnectivityChecks, NULL);
|
|
}
|
|
else
|
|
{
|
|
// We are CONTROLLED, so we just need to start the Periodic ICE Request Packets
|
|
ILibStun_PeriodicStunCheck(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILibStun_ProcessCandidates(void *StunModule, int iceStateSlot)
|
|
{
|
|
struct ILibStun_IceState *state = ((ILibStun_Module*)StunModule)->IceStates[iceStateSlot];
|
|
int i;
|
|
|
|
if (state->hostcandidatecount > 0)
|
|
{
|
|
// If we are using a TURN server, we need to Create Permissions
|
|
if (state->useTurn != 0)
|
|
{
|
|
struct sockaddr_in6* permission = (struct sockaddr_in6*)malloc(state->hostcandidatecount * sizeof(struct sockaddr_in6));
|
|
if (permission == NULL) { ILIBCRITICALEXIT(254); }
|
|
memset(permission, 0, state->hostcandidatecount * sizeof(struct sockaddr_in6));
|
|
for (i = 0; i < state->hostcandidatecount; ++i)
|
|
{
|
|
((struct sockaddr_in*)&(permission[i]))->sin_family = AF_INET;
|
|
((struct sockaddr_in*)&(permission[i]))->sin_port = state->hostcandidates[i].port;
|
|
((struct sockaddr_in*)&(permission[i]))->sin_addr.s_addr = state->hostcandidates[i].addr;
|
|
}
|
|
|
|
ILibTURN_CreatePermission(state->parentStunModule->mTurnClientModule, permission, state->hostcandidatecount, ILibStun_SetIceOffer2_TURN_CreatePermissionResponse, (void*)(uintptr_t)iceStateSlot); // We are casting SelectedSlot this way, becuase it will always be < 255
|
|
|
|
free(permission);
|
|
return;
|
|
// We are returning here, because if we need to create permissions on the TURN server, we need to wait for it to complete,
|
|
// otherwise if we try to send the packets below, they'll get dropped... So we'll send those packets in the callback
|
|
}
|
|
|
|
|
|
ILibStun_ICE_Start(state, iceStateSlot);
|
|
}
|
|
}
|
|
|
|
//! Set an Encoded Offer Block
|
|
/*!
|
|
\param StunModule Stun client module
|
|
\param iceOffer Encoded Offer Block
|
|
\param iceOfferLen Length of encoded offer block
|
|
\param username local username from original offer
|
|
\param usernameLength length of username buffer
|
|
\param password local password from original offer
|
|
\param passwordLength length of password buffer
|
|
\param answer Encoded Counter Offer Block
|
|
\return length of encoded counter offer block
|
|
*/
|
|
int ILibStun_SetIceOffer2(void *StunModule, char* iceOffer, int iceOfferLen, char *username, int usernameLength, char* password, int passwordLength, char** answer)
|
|
{
|
|
int generateUserAndKey = 1;
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)StunModule;
|
|
struct ILibStun_IceState *state, *oldState = NULL;
|
|
int i, j, rlen;
|
|
int SelectedSlot = -1;
|
|
int candidateCount;
|
|
|
|
if (iceOffer == NULL || iceOfferLen == 0) return 0;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Setting Ice Offer:");
|
|
#ifdef _REMOTELOGGING
|
|
if(username!=NULL && usernameLength>0)
|
|
{
|
|
char tmp = username[usernameLength];
|
|
username[usernameLength] = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...username: %s", username);
|
|
username[usernameLength] = tmp;
|
|
}
|
|
#endif
|
|
|
|
// Decode the offer
|
|
if ((state = (struct ILibStun_IceState*)malloc(sizeof(struct ILibStun_IceState))) == NULL) ILIBCRITICALEXIT(254);
|
|
memset(state, 0, sizeof(struct ILibStun_IceState));
|
|
|
|
util_random(8, state->tieBreaker);
|
|
state->useTurn = obj->alwaysConnectTurn;
|
|
candidateCount = iceOffer[7 + iceOffer[6] + 1 + iceOffer[7 + iceOffer[6]] + 1 + iceOffer[7 + iceOffer[6] + 1 + iceOffer[7+iceOffer[6]]]];
|
|
|
|
// We're mallocing this so we can encapsulate the ConnectivityCheck + Turn Candidate
|
|
if ((state->offerblock = (char*)malloc(iceOfferLen + candidateCount + 1)) == NULL) ILIBCRITICALEXIT(254);
|
|
memcpy_s(state->offerblock, iceOfferLen + candidateCount + 1, iceOffer, iceOfferLen);
|
|
memset(state->offerblock + iceOfferLen, 0, candidateCount + 1);
|
|
state->blockversion = ntohs(((unsigned short*)(state->offerblock))[0]);
|
|
state->blockflags = ntohl(((unsigned int*)(state->offerblock + 2))[0]);
|
|
state->rusernamelen = state->offerblock[6];
|
|
state->rusername = state->offerblock + 7;
|
|
state->rkeylen = state->offerblock[7 + state->rusernamelen];
|
|
state->rkey = state->offerblock + 7 + state->rusernamelen + 1;
|
|
state->dtlscerthashlen = state->offerblock[7 + state->rusernamelen + 1 + state->rkeylen];
|
|
state->dtlscerthash = state->offerblock + 7 + state->rusernamelen + 1 + state->rkeylen + 1;
|
|
state->hostcandidatecount = state->offerblock[7 + state->rusernamelen + 1 + state->rkeylen + 1 + state->dtlscerthashlen];
|
|
state->hostcandidates = (struct ILibStun_IceStateCandidate*)(state->offerblock + 7 + state->rusernamelen + 1 + state->rkeylen + 1 + state->dtlscerthashlen + 1);
|
|
state->hostcandidateResponseFlag = state->offerblock + iceOfferLen;
|
|
state->requerycount = 0;
|
|
state->peerHasActiveOffer = ((state->blockflags & ILibWebRTC_SDP_Flags_DTLS_SERVER) == ILibWebRTC_SDP_Flags_DTLS_SERVER ? 0 : 1);
|
|
state->dtlsInitiator = ((state->blockflags & ILibWebRTC_SDP_Flags_DTLS_SERVER) == ILibWebRTC_SDP_Flags_DTLS_SERVER ? 1 : 0);
|
|
state->parentStunModule = obj;
|
|
state->dtlsSession = -1;
|
|
state->creationTime = ILibGetUptime();
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Remote is: [%d] %s", state->blockflags, state->peerHasActiveOffer != 0 ? "Initiator" : "Receiver");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Certificate: %s", ILibRemoteLogging_ConvertToHex(state->dtlscerthash, state->dtlscerthashlen));
|
|
|
|
|
|
//
|
|
// If username/pasword was passed in, use that because we generated the original offer,
|
|
// so we have to keep the credentials the same, otherwise the peer will not be able to connect.
|
|
//
|
|
if (username != NULL && password != NULL && usernameLength + passwordLength == 40)
|
|
{
|
|
state->userAndKey[0] = (char)usernameLength;
|
|
memcpy_s(state->userAndKey + 1, sizeof(state->userAndKey) - 1, username, usernameLength);
|
|
state->userAndKey[1 + usernameLength] = (char)passwordLength;
|
|
memcpy_s(state->userAndKey + 2 + usernameLength, sizeof(state->userAndKey) - 2 - usernameLength, password, passwordLength);
|
|
}
|
|
|
|
SelectedSlot = ILibStun_GetFreeIceStateSlot(obj, state, &oldState, 1);
|
|
if (SelectedSlot < 0)
|
|
{
|
|
// We couldn't update/add the new offer
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Unable to update/add offer");
|
|
free(state->offerblock);
|
|
free(state);
|
|
*answer = NULL;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Offer Added/Updated into Ice Slot: %d", SelectedSlot);
|
|
if (oldState != NULL)
|
|
{
|
|
// The old object that was in this slot, was just a placeholder that was storing DTLS Allowed Candidates
|
|
// for the actual offer that we are now putting in it.
|
|
if (oldState->hostcandidatecount == 0 && oldState->hostcandidates == NULL)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "......No previous offer was set, this must be the peer's answer to our original offer");
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
if (((struct sockaddr_in6*)oldState->offerblock)[i].sin6_family != 0)
|
|
{
|
|
for (j = 0; j < state->hostcandidatecount; ++j)
|
|
{
|
|
struct sockaddr_in candidate;
|
|
memset(&candidate, 0, sizeof(struct sockaddr_in));
|
|
|
|
candidate.sin_family = AF_INET;
|
|
candidate.sin_port = state->hostcandidates[j].port;
|
|
candidate.sin_addr.s_addr = state->hostcandidates[j].addr;
|
|
|
|
if (memcmp(&candidate, &(((struct sockaddr_in6*)oldState->offerblock)[i]), INET_SOCKADDR_LENGTH(candidate.sin_family)) == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Candidate: %s:%u was already unlocked", ILibRemoteLogging_ConvertAddress((struct sockaddr*)&candidate), ntohs(candidate.sin_port));
|
|
state->hostcandidateResponseFlag[j] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// We need to copy UserAndKey, because we will be using the same username and password
|
|
memcpy_s(state->userAndKey, sizeof(state->userAndKey), oldState->userAndKey, sizeof(state->userAndKey));
|
|
generateUserAndKey = 0;
|
|
|
|
state->userObject = oldState->userObject; // Copy the user object
|
|
|
|
free(oldState->offerblock);
|
|
free(oldState);
|
|
oldState = NULL;
|
|
}
|
|
}
|
|
|
|
if (generateUserAndKey != 0)
|
|
{
|
|
ILibStun_GenerateUserAndKey(SelectedSlot, obj->Secret, state->userAndKey); // We are going to encode the slot number in the username
|
|
}
|
|
|
|
// Generate an return answer
|
|
rlen = ILibStun_WebRTC_UpdateOfferResponse(state, answer);
|
|
|
|
ILibStun_ProcessCandidates(StunModule, SelectedSlot);
|
|
|
|
return rlen;
|
|
}
|
|
|
|
//! Decode and save an ICE offer block, then return an answer block
|
|
/*!
|
|
\param StunModule Stun Client Module
|
|
\param iceOffer Encoded ICE Offer Block
|
|
\param iceOfferLen Length of encoded ICE offer block
|
|
\param answer The resultant encoded ICE offer block
|
|
\return Length of the resultant ICE Offer Block
|
|
*/
|
|
int ILibStun_SetIceOffer(void* StunModule, char* iceOffer, int iceOfferLen, char** answer)
|
|
{
|
|
// Initial offers are required to be Active or ActPass, so peerHasActiveOffer is set to 1
|
|
// We need to save the username/password from our generated offer, when we pass in an offer. If we didn't generate the initial offer, pass in NULL
|
|
return ILibStun_SetIceOffer2(StunModule, iceOffer, iceOfferLen, NULL, 0, NULL, 0, answer);
|
|
}
|
|
|
|
void ILibORTC_GetLocalParameters(void *stunModule, char* username, char** password, int *passwordLength, char** certHash, int* certHashLength)
|
|
{
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)stunModule;
|
|
int slot = ILibStun_CharToSlot(username[0]);
|
|
int localUserNameLen;
|
|
|
|
if (slot < 0 || slot >= ILibSTUN_MaxSlots) { ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_GetLocalParameters called with invalid local username"); return; }
|
|
localUserNameLen = obj->IceStates[slot]->userAndKey[0];
|
|
*passwordLength = obj->IceStates[slot]->userAndKey[localUserNameLen + 1];
|
|
*password = obj->IceStates[slot]->userAndKey + 1 + localUserNameLen + 1;
|
|
|
|
*certHash = obj->CertThumbprint;
|
|
*certHashLength = obj->CertThumbprintLength;
|
|
}
|
|
|
|
void ILibORTC_SetRemoteParameters(void* stunModule, char *username, int usernameLen, char *password, int passwordLen, char *certHash, int certHashLen, char *localUserName)
|
|
{
|
|
// Generate a pseudo WebRTC Offer
|
|
char *answer;
|
|
char offer[255];
|
|
int offerLen = 7 + usernameLen + 1 + passwordLen + 1 + certHashLen + 1;
|
|
struct ILibStun_Module* obj = (struct ILibStun_Module*)stunModule;
|
|
int localUserNameLen;
|
|
char* localPassword;
|
|
int localPasswordLen;
|
|
int slot;
|
|
|
|
if(offerLen > sizeof(offer)) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_SetRemoteParameters called, but passed data is > %d bytes", (int)sizeof(offer)); return;}
|
|
slot = ILibStun_CharToSlot(localUserName[0]);
|
|
if(slot < 0 || slot >= ILibSTUN_MaxSlots) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_SetRemoteParameters called with invalid local username"); return;}
|
|
|
|
localUserNameLen = obj->IceStates[slot]->userAndKey[0];
|
|
localPasswordLen = obj->IceStates[slot]->userAndKey[localUserNameLen+1];
|
|
localPassword = obj->IceStates[slot]->userAndKey + 1 + localUserNameLen + 1;
|
|
|
|
((unsigned short*)offer)[0] = htons((short)1); // Block Version
|
|
((unsigned int*)offer+2)[0] = htons(0); // Block Version
|
|
offer[6] = (char)usernameLen; // Username Length
|
|
memcpy_s(offer+7, sizeof(offer) - 7, username, usernameLen); // Username
|
|
offer[7+usernameLen] = (char)passwordLen; // Password Length
|
|
memcpy_s(offer+7+usernameLen+1, sizeof(offer) - 7 - usernameLen - 1, password, passwordLen); // Password
|
|
offer[7 + usernameLen + 1 + passwordLen] = (char)certHashLen; // Cert Fingerprint Length
|
|
memcpy_s(offer + 7 + usernameLen + 1 + passwordLen + 1, sizeof(offer) - 9 - usernameLen - passwordLen, certHash, certHashLen); // Cert Fingerprint
|
|
offer[7 + usernameLen + 1 + passwordLen + 1 + certHashLen] = 0; // No Candidates yet
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_SetRemoteParameters -> ILibStun_SetIceOffer2");
|
|
ILibStun_SetIceOffer2(stunModule, offer, offerLen, localUserName, localUserNameLen, localPassword, localPasswordLen, &answer);
|
|
free(answer);
|
|
localPassword = NULL;
|
|
}
|
|
|
|
void ILibORTC_AddRemoteCandidate(void *stunModule, char* localUsername, struct sockaddr_in6 *candidate)
|
|
{
|
|
int blockSize;
|
|
char *newBlock;
|
|
|
|
struct ILibStun_IceState* state;
|
|
int slot = ILibStun_CharToSlot(localUsername[0]);
|
|
|
|
if (slot < 0 || slot >= ILibSTUN_MaxSlots) { ILibRemoteLogging_printf(ILibChainGetLogger(((struct ILibStun_Module*)stunModule)->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_AddCandidate called with invalid local username"); return; }
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(((struct ILibStun_Module*)stunModule)->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibORTC_AddCandidate: %s", ILibRemoteLogging_ConvertAddress((struct sockaddr*)candidate));
|
|
|
|
|
|
state = ((ILibStun_Module*)stunModule)->IceStates[slot];
|
|
blockSize = (int)(state->hostcandidateResponseFlag - state->offerblock);
|
|
|
|
newBlock = ILibMemory_Allocate(blockSize + state->hostcandidatecount + 8, 0, NULL, NULL); // Same as old block, plus 7 more bytes for another candidate
|
|
|
|
memcpy_s(newBlock, blockSize + state->hostcandidatecount + 8, state->offerblock, blockSize); // Copy Everything, up to the existing candidates
|
|
newBlock[7 + state->rusernamelen + 1 + state->rkeylen + 1 + state->dtlscerthashlen] = (char)(state->hostcandidatecount + 1);
|
|
((struct ILibStun_IceStateCandidate*)(newBlock + blockSize))->addr = ((struct sockaddr_in*)candidate)->sin_addr.s_addr;
|
|
((struct ILibStun_IceStateCandidate*)(newBlock + blockSize))->port = ((struct sockaddr_in*)candidate)->sin_port;
|
|
|
|
memcpy_s(newBlock + blockSize + sizeof(struct ILibStun_IceStateCandidate), state->hostcandidatecount + 8 - sizeof(struct ILibStun_IceStateCandidate), state->offerblock + blockSize, state->hostcandidatecount);
|
|
newBlock[blockSize + sizeof(struct ILibStun_IceStateCandidate) + state->hostcandidatecount] = 0x00;
|
|
|
|
state->rusername = newBlock + 7;
|
|
state->rkey = newBlock + 7 + state->rusernamelen + 1;
|
|
state->dtlscerthash = newBlock + 7 + state->rusernamelen + 1 + state->rkeylen + 1;
|
|
state->hostcandidatecount += 1;
|
|
state->hostcandidates = (struct ILibStun_IceStateCandidate*)(newBlock + 7 + state->rusernamelen + 1 + state->rkeylen + 1 + state->dtlscerthashlen + 1);
|
|
state->hostcandidateResponseFlag = newBlock + blockSize + (state->hostcandidatecount*sizeof(struct ILibStun_IceStateCandidate));
|
|
|
|
state->creationTime = ILibGetUptime();
|
|
|
|
free(state->offerblock);
|
|
state->offerblock = newBlock;
|
|
ILibStun_ProcessCandidates(stunModule, slot);
|
|
}
|
|
|
|
ILibTransport_DoneState ILibStun_SendDtls(struct ILibStun_Module *obj, int session, char* buffer, int bufferLength)
|
|
{
|
|
ILibTransport_DoneState r = ILibTransport_DoneState_ERROR;
|
|
if (obj == NULL || session < 0 || session > (ILibSTUN_MaxSlots-1) || obj->dTlsSessions[session] == NULL || SSL_get_state(obj->dTlsSessions[session]->ssl) != TLS_ST_OK) return ILibTransport_DoneState_ERROR;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Simulated Inbound Packet Loss
|
|
if ((obj->lossPercentage) > 0 && ((rand() % 100) >= (100 - obj->lossPercentage))) { return(ILibTransport_DoneState_COMPLETE); }
|
|
#endif
|
|
|
|
SSL_write(obj->dTlsSessions[session]->ssl, buffer, bufferLength);
|
|
|
|
if(obj->dTlsSessions[session]->writeBIOBuffer->length > 0)
|
|
{
|
|
BIO_clear_retry_flags(obj->dTlsSessions[session]->writeBIO); // Klocwork reports this could block, but this is a memory bio, so it will never block.
|
|
|
|
// Data is pending in the write buffer, send it out
|
|
if (obj->IceStates[obj->dTlsSessions[session]->iceStateSlot]->useTurn != 0)
|
|
{
|
|
if ((((int*)ILibMemory_GetExtraMemory(obj->dTlsSessions[session]->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_CHANNEL_DATA) == ILibTURN_FLAGS_CHANNEL_DATA)
|
|
{
|
|
if (ILibTURN_GetPendingBytesToSend(obj->mTurnClientModule) == 0)
|
|
{
|
|
r = (ILibTransport_DoneState)ILibTURN_SendChannelData(obj->mTurnClientModule, (unsigned short)session, obj->dTlsSessions[session]->writeBIOBuffer->data, 0, (int)obj->dTlsSessions[session]->writeBIOBuffer->length);
|
|
}
|
|
else
|
|
{
|
|
// If the TCP socket is overflowing, just drop the packet... SCTP will take care of retrying this later
|
|
r = ILibTransport_DoneState_INCOMPLETE; // ToDo: Make Sure SendOK is getting called
|
|
}
|
|
}
|
|
else if ((((int*)ILibMemory_GetExtraMemory(obj->dTlsSessions[session]->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
if (ILibTURN_GetPendingBytesToSend(obj->mTurnClientModule) == 0)
|
|
{
|
|
r = (ILibTransport_DoneState)ILibTURN_SendIndication(obj->mTurnClientModule, obj->dTlsSessions[session]->remoteInterface, obj->dTlsSessions[session]->writeBIOBuffer->data, 0, (int)obj->dTlsSessions[session]->writeBIOBuffer->length);
|
|
}
|
|
else
|
|
{
|
|
// If the TCP socket is overflowing, just drop the packet... SCTP will take care of retrying this later
|
|
r = ILibTransport_DoneState_INCOMPLETE; // ToDo: Make Sure SendOK is getting called
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r = (ILibTransport_DoneState)ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)obj->dTlsSessions[session]->remoteInterface, obj->dTlsSessions[session]->writeBIOBuffer->data, obj->dTlsSessions[session]->writeBIOBuffer->length, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r = (ILibTransport_DoneState)ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)obj->dTlsSessions[session]->remoteInterface, obj->dTlsSessions[session]->writeBIOBuffer->data, obj->dTlsSessions[session]->writeBIOBuffer->length, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
ignore_result(BIO_reset(obj->dTlsSessions[session]->writeBIO));
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// This method assumes the buffer has 12 byte available for the header.
|
|
ILibTransport_DoneState ILibStun_SendSctpPacket(struct ILibStun_Module *obj, int session, char* buffer, int bufferLength)
|
|
{
|
|
if (bufferLength < 12) return ILibTransport_DoneState_ERROR;
|
|
|
|
// Setup the header
|
|
((unsigned short*)buffer)[0] = htons(obj->dTlsSessions[session]->inport); // Source port
|
|
((unsigned short*)buffer)[1] = htons(obj->dTlsSessions[session]->outport); // Destination port
|
|
((unsigned int*)buffer)[1] = obj->dTlsSessions[session]->tag; // Tag
|
|
|
|
// Compute and put the CRC at the right place
|
|
((unsigned int*)buffer)[2] = 0;
|
|
((unsigned int*)buffer)[2] = crc32c(0, (unsigned char*)buffer, (uint32_t)bufferLength);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "SCTP[%d] (%d): Send", session, bufferLength);
|
|
|
|
int i = 12;
|
|
unsigned short utmp;
|
|
|
|
while (FOURBYTEBOUNDARY(i) < bufferLength)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "... FOURBYTEBOUNDARY (%d, %d)", i, FOURBYTEBOUNDARY(i));
|
|
|
|
if ((buffer + i)[0] < ((sizeof(SCTP_CHUNK_TYPE_NAMES) / sizeof(void*))))
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "... ChunkType: %s", SCTP_CHUNK_TYPE_NAMES[(int)((buffer+i)[0])]);
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "... ChunkType: %d", (buffer + i)[0]);
|
|
}
|
|
if ((buffer + i)[0] == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "... TSN: %u", ntohl(((ILibSCTP_DataPayload*)(buffer + i))->TSN));
|
|
}
|
|
utmp = ntohs(((uint16_t*)(buffer + i))[1]);
|
|
i += utmp;
|
|
|
|
if (utmp == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_4, "... force BREAK");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_5, "... Source: %u , Destination: %u", obj->dTlsSessions[session]->inport, obj->dTlsSessions[session]->outport);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_5, "... TAG: %u", obj->dTlsSessions[session]->tag);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_5, "... Checksum: %u", ((unsigned int*)buffer)[2]);
|
|
|
|
#ifdef _REMOTELOGGING
|
|
ILibSctp_DebugSctpPacket(ILibChainGetLogger(obj->ChainLink.ParentChain), buffer, bufferLength, "ILibStun_SendSctpPacket");
|
|
#endif
|
|
|
|
return(ILibStun_SendDtls(obj, session, buffer, bufferLength));
|
|
}
|
|
|
|
int ILibStun_AddSctpChunkHeader(char* buffer, int ptr, unsigned char chunktype, unsigned char chunkflags, unsigned short chunklength)
|
|
{
|
|
if (buffer == NULL) { ILIBCRITICALEXITMSG(255, "ILibStun_AddSctpChunkHeader() Invalid Parameter"); }
|
|
if (ptr >= 12 && ((int*)buffer)[0] == 0 && ((int*)buffer)[1] == 0)
|
|
{
|
|
// This buffer contains a common buffer area, that has control flags
|
|
if (chunktype == RCTP_CHUNK_TYPE_DATA)
|
|
{
|
|
// Data Chunk
|
|
((int*)buffer)[2] |= SCTP_COMMON_HEADER_FLAGS_DATA;
|
|
}
|
|
else
|
|
{
|
|
// Control Chunk
|
|
((int*)buffer)[2] |= SCTP_COMMON_HEADER_FLAGS_CONTROL;
|
|
if ((((int*)buffer)[2] & SCTP_COMMON_HEADER_FLAGS_DATA) == SCTP_COMMON_HEADER_FLAGS_DATA)
|
|
{
|
|
// Writing Control Chunk after Data Chunk was already written
|
|
((int*)buffer)[2] |= SCTP_COMMON_HEADER_FLAGS_REORDER;
|
|
}
|
|
}
|
|
}
|
|
buffer[ptr] = chunktype;
|
|
buffer[ptr + 1] = chunkflags;
|
|
((unsigned short*)(buffer + ptr))[1] = htons(chunklength);
|
|
return ptr + 4;
|
|
}
|
|
|
|
int ILibStun_AddSctpChunkArg(char* buffer, int ptr, unsigned short otype, char* data, int datalen)
|
|
{
|
|
ptr = ILibAlignOnFourByteBoundary(buffer, ptr);
|
|
((unsigned short*)(buffer + ptr))[0] = ntohs(otype);
|
|
((unsigned short*)(buffer + ptr))[1] = ntohs((unsigned short)(4 + datalen));
|
|
if (datalen != 0) memcpy_s(buffer + ptr + 4, datalen, data, datalen);
|
|
return ptr + 4 + datalen;
|
|
}
|
|
|
|
int ILibStun_SctpAddSackChunk(struct ILibStun_Module *obj, int session, char* packet, int ptr)
|
|
{
|
|
int clen = 16;
|
|
unsigned int tsn = obj->dTlsSessions[session]->intsn;
|
|
unsigned int bytecount = 0;
|
|
|
|
// Skip all packets with a low TSN. We do ths bacause we have not processed them yet, but will after adding this SACK
|
|
void *node = ILibLinkedList_GetNode_Head(obj->dTlsSessions[session]->receiveHoldBuffer);
|
|
while(node!=NULL && ILibLinkedList_GetDataFromNode(node) != NULL && ntohl(((ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(node))->TSN) <= obj->dTlsSessions[session]->userTSN)
|
|
{
|
|
node = ILibLinkedList_GetNextNode(node);
|
|
}
|
|
|
|
// Compute all selective ACK's
|
|
while (node != NULL)
|
|
{
|
|
ILibSCTP_DataPayload *p = (ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(node);
|
|
unsigned int gstart = (p == NULL ? 0 : ntohl(p->TSN));
|
|
unsigned int gend = gstart;
|
|
bytecount += (p == NULL ? 0 : ntohs(p->length));
|
|
|
|
node = ILibLinkedList_GetNextNode(node);
|
|
while (node != NULL && (p = (ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(node))!=NULL)
|
|
{
|
|
unsigned int x = ntohl(p->TSN);
|
|
bytecount += ntohs(p->length);
|
|
if (x == gend + 1) { gend = x; }
|
|
else break;
|
|
node = ILibLinkedList_GetNextNode(node);
|
|
}
|
|
|
|
if (clen < 500) // Cap the number of encoded gaps.
|
|
{
|
|
((unsigned short*)(packet + ptr + clen))[0] = htons((unsigned short)(gstart - tsn)); // Start
|
|
((unsigned short*)(packet + ptr + clen))[1] = htons((unsigned short)(gend - tsn)); // End
|
|
RCTPRCVDEBUG(printf("SACK %u + %u to %u\r\n", tsn, gstart, gend);)
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d SENT [SACK] %u + %u to %u", session, tsn, gstart, gend);
|
|
clen += 4;
|
|
}
|
|
}
|
|
|
|
// Create response
|
|
if (bytecount > ILibSCTP_MaxReceiverCredits) bytecount = ILibSCTP_MaxReceiverCredits;
|
|
ILibStun_AddSctpChunkHeader(packet, ptr, RCTP_CHUNK_TYPE_SACK, 0, (unsigned short)clen);
|
|
((unsigned int*)(packet + ptr + 4))[0] = htonl(obj->dTlsSessions[session]->intsn); // Cumulative TSN Ack
|
|
((unsigned int*)(packet + ptr + 8))[0] = htonl(ILibSCTP_MaxReceiverCredits - bytecount);// Advertised Receiver Window Credit (a_rwnd)
|
|
((unsigned short*)(packet + ptr + 12))[0] = htons(((unsigned short)clen - 16) / 4); // Number of Gap Ack Blocks
|
|
((unsigned short*)(packet + ptr + 14))[0] = htons(0); // Number of Duplicate TSNs
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d SENT [SACK] a_rwnd: %u Cumalative TSN: %u", session, ILibSCTP_MaxReceiverCredits - bytecount, obj->dTlsSessions[session]->intsn);
|
|
|
|
return (ptr + clen);
|
|
}
|
|
|
|
ILibTransport_DoneState ILibStun_SctpSendDataEx(struct ILibStun_Module *obj, int session, unsigned char flags, unsigned short streamid, unsigned short streamnum, int pid, char* data, int datalen)
|
|
{
|
|
ILibSCTP_StreamAttributes sattr;
|
|
ILibSCTP_StreamAttributes_Data sattrData;
|
|
int rptr = sizeof(ILibSCTP_RPACKET);
|
|
ILibSCTP_RPACKET *rpacket;
|
|
unsigned int tsn = obj->dTlsSessions[session]->outtsn;
|
|
obj->dTlsSessions[session]->outtsn++;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d] ILibStun_SctpSendDataEx -> outtsn = %u", session, tsn);
|
|
|
|
// Create data packet, allow for a header in front.
|
|
size_t newlen = sizeof(ILibSCTP_RPACKET) + datalen + 16;
|
|
newlen = FOURBYTEBOUNDARY(newlen);
|
|
|
|
if ((rpacket = (ILibSCTP_RPACKET*)malloc(newlen)) == NULL) ILIBCRITICALERREXIT(254);
|
|
memset(rpacket, 0, newlen);
|
|
rpacket->Reliability = 0; // Full Reliable Mode (Default)
|
|
rpacket->NextPacket = NULL; // Pointer to the next packet (Used for queuing)
|
|
rpacket->PacketSize = (unsigned short)(12 + 16 + FOURBYTEBOUNDARY(datalen)); // Size of the packet (Used for queuing)
|
|
rpacket->PacketGAPCounter = rpacket->PacketResendCounter = 0; // Number of times the packet was resent (Used for retry)
|
|
rpacket->LastSentTimeStamp = 0; // Last time the packet was sent (Used for retry)
|
|
rpacket->CreationTimeStamp = (unsigned int)ILibGetUptime();
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Size: %u", rpacket->PacketSize);
|
|
|
|
sattr.Raw = ILibSparseArray_Get(obj->dTlsSessions[session]->DataChannelMetaDeta, streamid);
|
|
sattrData.Raw = ILibSparseArray_Get(obj->dTlsSessions[session]->DataChannelMetaDetaValues, streamid);
|
|
rpacket->Reliability |= ((sattr.Data.ReliabilityFlags & ILibWebRTC_DataChannel_ReliabilityMode_RELIABLE_UNORDERED) << 8); // Top BIT is Ordered BIT
|
|
rpacket->Reliability |= (sattr.Data.ReliabilityFlags << 13); // Next TWO BITS is Reliability Mode
|
|
rpacket->Reliability |= (sattrData.Data.ReliabilityValue & 0x1FFF); // Remaining BITS will be Reliability Value
|
|
|
|
// There is a 12 byte GAP here to accomodate SCTP Common Header, if necessary
|
|
|
|
ILibStun_AddSctpChunkHeader(rpacket->Data, 0, RCTP_CHUNK_TYPE_DATA, flags, (unsigned short)(16 + datalen)); // Setup the data chunk header
|
|
|
|
((ILibSCTP_DataPayload*)rpacket->Data)->TSN = htonl(tsn); // Cumulative TSN Ack
|
|
((ILibSCTP_DataPayload*)rpacket->Data)->StreamID = htons(streamid); // Stream Identifier
|
|
((ILibSCTP_DataPayload*)rpacket->Data)->StreamSequenceNumber = htons(streamnum); // Stream Sequence Number
|
|
((ILibSCTP_DataPayload*)rpacket->Data)->ProtocolID = htonl(pid); // Payload Protocol Identifier
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...TSN [%u]", tsn);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...SEQ [%u]", ((ILibSCTP_DataPayload*)rpacket->Data)->StreamSequenceNumber);
|
|
|
|
memcpy_s(((ILibSCTP_DataPayload*)rpacket->Data)->UserData, datalen + 16, data, datalen); // Copy the user data
|
|
rptr += (16 + datalen);
|
|
RCTPDEBUG(printf("OUT DATA_CHUNK FLAGS: %d, TSN: %u, ID: %d, SEQ: %d, PID: %u, SIZE: %d\r\n", flags, tsn, streamid, streamnum, pid, datalen);)
|
|
|
|
// Check the credits
|
|
if ((obj->dTlsSessions[session]->receiverCredits < datalen) || datalen > obj->dTlsSessions[session]->senderCredits || (obj->dTlsSessions[session]->holdingCount != 0))
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...To Holding Queue (%u bytes)", rpacket->PacketSize);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "......LEN: %u", ntohs(((ILibSCTP_DataPayload*)rpacket->Data)->length));
|
|
|
|
// Add this packet to the holding queue
|
|
if (obj->dTlsSessions[session]->holdingQueueTail == NULL) { obj->dTlsSessions[session]->holdingQueueHead = (char*)rpacket; }
|
|
else { ((char**)(obj->dTlsSessions[session]->holdingQueueTail))[0] = (char*)rpacket; }
|
|
obj->dTlsSessions[session]->holdingQueueTail = (char*)rpacket;
|
|
obj->dTlsSessions[session]->holdingCount++;
|
|
obj->dTlsSessions[session]->holdingByteCount += datalen;
|
|
// if (obj->dTlsSessions[session]->holdingCount == 1) printf("HOLD\r\n");
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->dTlsSessions[session]->onHold != NULL) { obj->dTlsSessions[session]->onHold(obj->dTlsSessions[session], "OnHold", obj->dTlsSessions[session]->holdingCount); }
|
|
#endif
|
|
return ILibTransport_DoneState_INCOMPLETE; // Hold, we don't have anymore credits
|
|
}
|
|
|
|
// Update the packet retry data
|
|
rpacket->LastSentTimeStamp = rpacket->CreationTimeStamp; // Last time the packet was sent (Used for retry)
|
|
if (obj->dTlsSessions[session]->T3RTXTIME == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Start T3RTX Timer");
|
|
// Only set the T3RTX timer if it is not already running
|
|
obj->dTlsSessions[session]->T3RTXTIME = rpacket->LastSentTimeStamp;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->dTlsSessions[session]->onT3RTX != NULL){ obj->dTlsSessions[session]->onT3RTX(obj->dTlsSessions[session], "OnT3RTX", obj->dTlsSessions[session]->RTO); }
|
|
#endif
|
|
}
|
|
|
|
// Add this packet to the pending ack queue
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Added to Pending ACK Queue");
|
|
|
|
obj->dTlsSessions[session]->receiverCredits -= datalen; // Receiver Window
|
|
obj->dTlsSessions[session]->senderCredits -= datalen; // Congestion Window
|
|
|
|
if (obj->dTlsSessions[session]->pendingQueueTail == NULL)
|
|
{
|
|
obj->dTlsSessions[session]->pendingQueueHead = (char*)rpacket;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "......HEAD");
|
|
}
|
|
else
|
|
{
|
|
((char**)(obj->dTlsSessions[session]->pendingQueueTail))[0] = (char*)rpacket;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "......TAIL");
|
|
}
|
|
obj->dTlsSessions[session]->pendingQueueTail = (char*)rpacket;
|
|
obj->dTlsSessions[session]->pendingCount++;
|
|
|
|
obj->dTlsSessions[session]->pendingByteCount += (rpacket->PacketSize - (12 + 16));
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Debug Event
|
|
if (obj->dTlsSessions[session]->onReceiverCredits != NULL) { obj->dTlsSessions[session]->onReceiverCredits(obj->dTlsSessions[session], "OnReceiverCredits", obj->dTlsSessions[session]->receiverCredits); }
|
|
#endif
|
|
|
|
// Send the packet now
|
|
if ((flags & 0x03) == 0x03 && obj->dTlsSessions[session]->rpacketptr > 0 && obj->dTlsSessions[session]->rpacketsize > (obj->dTlsSessions[session]->rpacketptr + 16 + datalen + 4) && (obj->dTlsSessions[session]->rpacketptr + 16 + datalen + 4) < 1400)
|
|
{
|
|
int st;
|
|
// Merge this data chunk in packet that is going to be sent
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Merging");
|
|
|
|
// Align/Pad on 32bit aligned pointer
|
|
st = obj->dTlsSessions[session]->rpacketptr;
|
|
obj->dTlsSessions[session]->rpacketptr = ILibAlignOnFourByteBoundary(obj->dTlsSessions[session]->rpacket, obj->dTlsSessions[session]->rpacketptr);
|
|
|
|
if (st != obj->dTlsSessions[session]->rpacketptr)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Aligning on 4 byte boundary [%d => %d]", st, obj->dTlsSessions[session]->rpacketptr);
|
|
}
|
|
memcpy_s(obj->dTlsSessions[session]->rpacket + obj->dTlsSessions[session]->rpacketptr, 16 + datalen, rpacket->Data, 16 + datalen);
|
|
obj->dTlsSessions[session]->rpacketptr += FOURBYTEBOUNDARY(16 + datalen);
|
|
((int*)obj->dTlsSessions[session]->rpacket)[2] |= SCTP_COMMON_HEADER_FLAGS_DATA;
|
|
}
|
|
else
|
|
{
|
|
// Send in a seperate packet
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Sending as separate packet");
|
|
return ILibStun_SendSctpPacket(obj, session, rpacket->Data - 12, rpacket->PacketSize); // Problem sending
|
|
}
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Complete");
|
|
return ILibTransport_DoneState_COMPLETE; // Everything is ok
|
|
}
|
|
|
|
|
|
ILibTransport_DoneState ILibStun_SctpSendData(struct ILibStun_Module *obj, int session, unsigned short streamid, int pid, char* data, int datalen)
|
|
{
|
|
ILibTransport_DoneState r = ILibTransport_DoneState_ERROR;
|
|
int len, ptr = 0;
|
|
unsigned char flags = 0; // 2 = Start, 0 = Middle, 1 = End, 3 = Start & End
|
|
ILibSCTP_StreamAttributes attr;
|
|
ILibSCTP_StreamAttributes_Data attrData;
|
|
unsigned short seq;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibStun_SctpSendData[%d]: %d bytes (SID: %u, PID: %d)", session, datalen, streamid, pid);
|
|
|
|
attr.Raw = ILibSparseArray_Get(obj->dTlsSessions[session]->DataChannelMetaDeta, streamid);
|
|
attrData.Raw = ILibSparseArray_Get(obj->dTlsSessions[session]->DataChannelMetaDetaValues, streamid);
|
|
|
|
if(pid != 50 && ((attr.Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED) != ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED ||
|
|
data == NULL || datalen == 0))
|
|
{
|
|
return ILibTransport_DoneState_ERROR; // Error
|
|
}
|
|
|
|
seq = attrData.Data.NextSequenceNumber++;
|
|
ILibSparseArray_Add(obj->dTlsSessions[session]->DataChannelMetaDetaValues, streamid, attrData.Raw);
|
|
|
|
|
|
// Send the data in one block
|
|
if (datalen <= 1232) return ILibStun_SctpSendDataEx(obj, session, 3, streamid, seq, pid, data, datalen);
|
|
|
|
// Break the data into parts
|
|
while (ptr < datalen)
|
|
{
|
|
// Compute the length of this block
|
|
len = datalen - ptr;
|
|
if (len > 1232) len = 1232;
|
|
|
|
// Compute the flags
|
|
flags = 0;
|
|
if (ptr == 0) flags |= 0x02;
|
|
if (ptr + len == datalen) flags |= 0x01;
|
|
|
|
// Send the block
|
|
r = ILibStun_SctpSendDataEx(obj, session, flags, streamid, seq, pid, data + ptr, len);
|
|
ptr += len;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
//! Send binary data on an SCTP stream
|
|
/*!
|
|
\param sctpSession SCTP Session object
|
|
\param streamId Stream Identifier of the stream to utilize for the send operation
|
|
\param data Binary data to send
|
|
\param datalen Length of data to send
|
|
\return Send status of the operation
|
|
*/
|
|
ILibTransport_DoneState ILibSCTP_Send(void* sctpSession, unsigned short streamId, char* data, int datalen)
|
|
{
|
|
return ILibSCTP_SendEx(sctpSession, streamId, data, datalen, 53);
|
|
}
|
|
|
|
//! Send raw data on an SCTP stream
|
|
/*!
|
|
\param sctpSession SCTP Session object
|
|
\param streamId Stream Identifier of the stream to utilize for the send operation
|
|
\param data Binary data to send
|
|
\param datalen Length of data to send
|
|
\param dataType Type of the data to send (Eg: 51 = string, 53 = binary, etc)
|
|
\return Send status of the operation
|
|
*/
|
|
ILibTransport_DoneState ILibSCTP_SendEx(void* sctpSession, unsigned short streamId, char* data, int datalen, int dataType)
|
|
{
|
|
ILibTransport_DoneState r;
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)sctpSession;
|
|
if (obj->state != 2) return ILibTransport_DoneState_ERROR; // Error
|
|
ILibSpinLock_Lock(&(obj->Lock));
|
|
r = ILibStun_SctpSendData(obj->parent, obj->sessionId, streamId, dataType, data, datalen);
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
RCTPDEBUG(printf("ILibSCTP_Send() len=%d, r=%d\r\n", datalen, r);)
|
|
return r;
|
|
}
|
|
|
|
void ILibStun_SctpProcessStreamData(struct ILibStun_Module *obj, int session, unsigned short streamId, unsigned short steamSeq, unsigned char chunkflags, int pid, char* data, int datalen)
|
|
{
|
|
struct ILibStun_dTlsSession *o = (struct ILibStun_dTlsSession*)obj->dTlsSessions[session];
|
|
UNREFERENCED_PARAMETER(steamSeq); // We expect all packets to be in order now.
|
|
|
|
// TODO: Add error case for when invalid streamId is specified. Bryan
|
|
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[session]->Lock));
|
|
if(pid == 50)
|
|
{
|
|
// WebRTC Control
|
|
ILibSCTP_StreamAttributes attributes;
|
|
attributes.Raw = ILibSparseArray_Get(obj->dTlsSessions[session]->DataChannelMetaDeta, streamId);
|
|
|
|
switch(data[0])
|
|
{
|
|
case 0x02: // WebRTC Data Channel Protocol: DATA_CHANNEL_ACK
|
|
if((attributes.Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_ACK) == ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_ACK)
|
|
{
|
|
attributes.Data.StatusFlags ^= ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_ACK;
|
|
attributes.Data.StatusFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED;
|
|
ILibSparseArray_Add(obj->dTlsSessions[session]->DataChannelMetaDeta, streamId, attributes.Raw);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Data Channel ACK on session: %u for StreamId: %u", session, streamId);
|
|
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[session]->Lock));
|
|
if (obj->OnWebRTCDataChannelAck != NULL) { obj->OnWebRTCDataChannelAck(obj, obj->dTlsSessions[session], streamId); }
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[session]->Lock));
|
|
}
|
|
break;
|
|
case 0x03: // WebRTC Data Channel Protocol: DATA_CHANNEL_OPEN
|
|
{
|
|
// Respond with DATA_CHANNEL_ACK
|
|
char DATA_CHANNEL_ACK = 0x02;
|
|
char *channelName = data + 12;
|
|
int channelNameLength = (int)ntohs(((unsigned short*)data)[4]);
|
|
int sendAck = 1;
|
|
ILibSCTP_StreamAttributes_Data attributesData;
|
|
|
|
channelName[channelNameLength] = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Data Channel Open request on session: %u for StreamId: %u [%s]", session, streamId, channelName);
|
|
|
|
if((attributes.Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED) == ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED)
|
|
{
|
|
// This data channel already exists
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...This stream ID already exists!");
|
|
break;
|
|
}
|
|
attributes.Raw = 0x00;
|
|
attributesData.Raw = 0x00;
|
|
|
|
attributes.Data.StatusFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED;
|
|
|
|
switch((ILibWebRTC_DataChannel_ReliabilityModes)((unsigned char)data[1]))
|
|
{
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_RELIABLE:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Reliable");
|
|
break;
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_RELIABLE_UNORDERED:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Reliable / UNORDERED");
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_UNORDERED;
|
|
break;
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_PARTIAL_RELIABLE_REXMIT:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Partial-Reliable / REXMIT");
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_REXMIT;
|
|
attributesData.Data.ReliabilityValue = (unsigned short)(ntohl(((unsigned int*)(data+4))[0]));
|
|
break;
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_PARTIAL_RELIABLE_REXMIT_UNORDERED:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Partial-Reliable / UNORDERED + REXMIT");
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_UNORDERED;
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_REXMIT;
|
|
attributesData.Data.ReliabilityValue = (unsigned short)(ntohl(((unsigned int*)(data+4))[0]));
|
|
break;
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_PARTIAL_RELIABLE_TIMED:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Partial-Reliable / TIMED");
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_TIMED;
|
|
attributesData.Data.ReliabilityValue = (unsigned short)(ntohl(((unsigned int*)(data+4))[0]));
|
|
break;
|
|
case ILibWebRTC_DataChannel_ReliabilityMode_PARTIAL_RELIABLE_TIMED_UNORDERED:
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Partial-Reliable / TIMED + UNORDERED");
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_TIMED;
|
|
attributes.Data.ReliabilityFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_UNORDERED;
|
|
attributesData.Data.ReliabilityValue = (unsigned short)(ntohl(((unsigned int*)(data+4))[0]));
|
|
break;
|
|
}
|
|
ILibSparseArray_Add(obj->dTlsSessions[session]->DataChannelMetaDeta, streamId, attributes.Raw);
|
|
ILibSparseArray_Add(obj->dTlsSessions[session]->DataChannelMetaDetaValues, streamId, attributesData.Raw);
|
|
|
|
if (sendAck == 0)
|
|
{
|
|
// Respond with DATA_CHANNEL_ACK
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Responding with DataChannel ACK");
|
|
ILibStun_SctpSendData(obj, session, streamId, 50, &DATA_CHANNEL_ACK, 1);
|
|
}
|
|
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[session]->Lock));
|
|
if (obj->OnWebRTCDataChannel != NULL) { sendAck = obj->OnWebRTCDataChannel(obj, obj->dTlsSessions[session], streamId, data + 12, channelNameLength); }
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[session]->Lock));
|
|
|
|
//if (sendAck == 0)
|
|
//{
|
|
// // Respond with DATA_CHANNEL_ACK
|
|
// ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Responding with DataChannel ACK");
|
|
// ILibStun_SctpSendData(obj, session, streamId, 50, &DATA_CHANNEL_ACK, 1);
|
|
//}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Other
|
|
if ((chunkflags & 0x03) == 0x03)
|
|
{
|
|
// This is a full data block
|
|
if (obj->OnData != NULL && obj->dTlsSessions[session]->state == 2)
|
|
{
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[session]->Lock));
|
|
obj->OnData(obj, obj->dTlsSessions[session], streamId, pid, data, datalen, &(obj->dTlsSessions[session]->User));
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state != 2) return;
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[session]->Lock));
|
|
}
|
|
// ILibStun_SctpSendData(obj, session, 0, pid, data, datalen); // ECHO
|
|
}
|
|
else
|
|
{
|
|
// Start of a data accumulation
|
|
ILibSCTP_Accumulator *acc = (ILibSCTP_Accumulator*)ILibSparseArray_Get(obj->dTlsSessions[session]->DataAccumulator, streamId);
|
|
if(acc == NULL)
|
|
{
|
|
if (chunkflags & 0x02) { acc = ILibSCTP_CreateAccumulator(); } // Only create, if we received a 'Begin' fragment
|
|
}
|
|
|
|
if (acc != NULL)
|
|
{
|
|
if (chunkflags & 0x02) { acc->bufferPtr = 0; } // Set to Zero when a 'BeginFragment' is received
|
|
if (acc->bufferPtr >= 0) // If this is < 0, it means we never received a 'BeginFragment'
|
|
{
|
|
// Accumulate data
|
|
if (acc->bufferPtr + datalen > acc->bufferLen)
|
|
{
|
|
// Realloc the accumulation buffer if needed
|
|
if ((acc->buffer = (char*)realloc(acc->buffer, acc->bufferPtr + datalen)) == NULL) ILIBCRITICALEXIT(254); // MS Static Analyser erronesouly reports a leak here
|
|
acc->bufferLen = acc->bufferPtr + datalen;
|
|
}
|
|
memcpy_s(acc->buffer + acc->bufferPtr, acc->bufferLen - acc->bufferPtr, data, datalen);
|
|
acc->bufferPtr += datalen;
|
|
|
|
ILibSparseArray_Add(obj->dTlsSessions[session]->DataAccumulator, streamId, acc);
|
|
|
|
// End of data accumulation
|
|
if (chunkflags & 0x01)
|
|
{
|
|
if (obj->OnData != NULL && obj->dTlsSessions[session]->state == 2)
|
|
{
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[session]->Lock));
|
|
obj->OnData(obj, obj->dTlsSessions[session], streamId, pid, acc->buffer, acc->bufferPtr, &(obj->dTlsSessions[session]->User));
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state != 2) return;
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[session]->Lock));
|
|
}
|
|
acc->bufferPtr = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if there are any Channel Close operations that are pending, waiting for a specific TSN
|
|
if(o->pendingReconfigPacket != NULL && ((ILibSCTP_PendingTSN_Data*)((char*)&o->pendingReconfigPacket))->Data.Type == 0xFF)
|
|
{
|
|
unsigned short offset = ((ILibSCTP_PendingTSN_Data*)((char*)&o->pendingReconfigPacket))->Data.StreamIdOffset;
|
|
int streamIdCount = (offset / 2) - 2;
|
|
|
|
if(((unsigned int*)(o->rpacket + o->rpacketsize - offset))[0] <= o->userTSN)
|
|
{
|
|
// The LastTSN specified by the peer has passed, so we can continue with the ChannelClose operation
|
|
// Let's locally initiate a ChannelClose... We'll propagate the close in the response handler
|
|
char tmp[4096];
|
|
if(streamIdCount * 2 * sizeof(unsigned short) <= 4096) // Only do this if we have enough memory to copy the streamId values
|
|
{
|
|
if(streamIdCount > 0) {memcpy_s(tmp, sizeof(tmp), o->rpacket + o->rpacketsize - offset + 4, streamIdCount * sizeof(unsigned short));}
|
|
o->pendingReconfigPacket = NULL;
|
|
ILibWebRTC_CloseDataChannelEx2(o, (unsigned short*)tmp, streamIdCount);
|
|
}
|
|
else
|
|
{
|
|
// Not enough memory... Just dump all the streams, as the Peer endpoint is being retarded by specifying so many streams individually
|
|
o->pendingReconfigPacket = NULL;
|
|
ILibWebRTC_CloseDataChannelEx2(o, (unsigned short*)tmp, 0); // Ignore Klocwork Error, it's not a problem in this case
|
|
}
|
|
}
|
|
}
|
|
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[session]->Lock));
|
|
}
|
|
|
|
void ILibStun_SctpDisconnect_Final(void *obj)
|
|
{
|
|
struct ILibStun_dTlsSession* o = (struct ILibStun_dTlsSession*)obj;
|
|
char* packet;
|
|
void *node;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SctpDisconnect_Final dispatched on: Session %d [0x%p]", o->sessionId, obj);
|
|
|
|
// Free the SSL state
|
|
if (o->ssl != NULL) { SSL_free(o->ssl); o->ssl = NULL; }
|
|
|
|
if (o->holdingByteCount > 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP(%d) Was closed while %d bytes are pending in HoldingQueue", o->sessionId, o->holdingByteCount);
|
|
}
|
|
|
|
// Free the rpacket buffer
|
|
if (o->rpacket != NULL) { free(o->rpacket); o->rpacket = NULL; }
|
|
|
|
// Free all packets pending ACK queue
|
|
while (o->pendingQueueHead != NULL)
|
|
{
|
|
packet = ((char**)(o->pendingQueueHead))[0]; // MS Static Analyser reports that this may not be initialized, but it will be NULL if it is not
|
|
free(o->pendingQueueHead);
|
|
o->pendingQueueHead = packet;
|
|
}
|
|
|
|
// Free all packets in holding queue
|
|
while (o->holdingQueueHead != NULL)
|
|
{
|
|
packet = ((char**)(o->holdingQueueHead))[0]; // MS Static Analyser reports that this may not be initialized, but it will be NULL if it is not
|
|
free(o->holdingQueueHead);
|
|
o->holdingQueueHead = packet;
|
|
}
|
|
|
|
// Free all packets in receive holding queue
|
|
node = ILibLinkedList_GetNode_Head(o->receiveHoldBuffer);
|
|
while(node != NULL)
|
|
{
|
|
if (ILibLinkedList_GetDataFromNode(node) != NULL) { free(ILibLinkedList_GetDataFromNode(node)); }
|
|
node = ILibLinkedList_GetNextNode(node);
|
|
}
|
|
ILibLinkedList_Destroy(o->receiveHoldBuffer);
|
|
|
|
ILibWebRTC_DestroySparseArrayTables(o);
|
|
|
|
// Free the session
|
|
o->state = 0;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Cleaning up Dtls Session Object for session: %d", o->sessionId);
|
|
ILibLifeTime_Remove(o->parent->Timer, ILibWebRTC_DTLS_TO_TIMER_OBJECT(o));
|
|
}
|
|
|
|
void ILibStun_SctpDisconnect_Continue(void *chain, void *j)
|
|
{
|
|
char exBuffer[4096];
|
|
int i;
|
|
struct ILibStun_dTlsSession* o = (struct ILibStun_dTlsSession*)j;
|
|
struct ILibStun_Module* obj = o != NULL ? o->parent : NULL;
|
|
|
|
UNREFERENCED_PARAMETER(chain);
|
|
|
|
// Now that we are on the microstack thread, we can continue the disconnet
|
|
RCTPDEBUG(if (o != NULL) printf("ILibStun_SctpDisconnect state=%d\r\n", o->state);)
|
|
|
|
if (o == NULL || o->state == 0 || o->state == 3) return;
|
|
|
|
ILibWebRTC_CloseDataChannel_ALL(o);
|
|
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
if (o->state < 1 || o->state > 2) { ILibSpinLock_UnLock(&(o->Lock)); return; }
|
|
|
|
// Send the event
|
|
if (o->state == 2 && obj->OnConnect != NULL)
|
|
{
|
|
o->state = 3;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
obj->OnConnect(obj, o, 0);
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
else
|
|
{
|
|
o->state = 3;
|
|
}
|
|
|
|
// Rather then sending an Abort/Shutdown, I changed this to just send an SSL/Close, which is what Chrome/Firefox do
|
|
|
|
// Send an abort & shutdown
|
|
//ILibStun_AddSctpChunkHeader(rpacket, 12, RCTP_CHUNK_TYPE_ABORT, 0, 4);
|
|
//ILibStun_AddSctpChunkHeader(rpacket, 16, RCTP_CHUNK_TYPE_SHUTDOWN, 0, 8);
|
|
//((unsigned int*)(rpacket + 20))[0] = htonl(obj->dTlsSessions[session]->intsn);
|
|
//ILibStun_SendSctpPacket(obj, session, rpacket, 24);
|
|
//ILibStun_SendSctpPacket(obj, session, rpacket, 24);
|
|
//ILibStun_SendSctpPacket(obj, session, rpacket, 24);
|
|
|
|
if (o->rpacketptr > 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP(%d) trying to close, flushing %d bytes from RPACKET", o->sessionId, o->rpacketptr);
|
|
ILibStun_SendSctpPacket(obj, o->sessionId, o->rpacket, o->rpacketptr);
|
|
}
|
|
|
|
if (obj->IceStates[o->iceStateSlot] != NULL)
|
|
{
|
|
SSL_shutdown(o->ssl);
|
|
|
|
while (BIO_ctrl_pending(SSL_get_wbio(o->ssl)) > 0)
|
|
{
|
|
// Data is pending in the write buffer, send it out
|
|
i = BIO_read(SSL_get_wbio(o->ssl), exBuffer, 4096);
|
|
if (obj->IceStates[o->iceStateSlot]->useTurn != 0)
|
|
{
|
|
if ((((int*)ILibMemory_GetExtraMemory(o->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_CHANNEL_DATA) == ILibTURN_FLAGS_CHANNEL_DATA)
|
|
{
|
|
ILibTURN_SendChannelData(obj->mTurnClientModule, (unsigned short)(o->sessionId), exBuffer, 0, i);
|
|
}
|
|
else if ((((int*)ILibMemory_GetExtraMemory(o->remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
ILibTURN_SendIndication(obj->mTurnClientModule, o->remoteInterface, exBuffer, 0, i);
|
|
}
|
|
else
|
|
{
|
|
ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)o->remoteInterface, exBuffer, i, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)o->remoteInterface, exBuffer, i, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lets abort Consent-Freshness Checks
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(o));
|
|
|
|
// Remove the SCTP Heartbeat timer
|
|
ILibLifeTime_Remove(o->parent->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(o));
|
|
|
|
// Start by clearing the IceState Object
|
|
ILibStun_ClearIceState(obj, o->iceStateSlot);
|
|
|
|
// Follow by clearing the DTLS object
|
|
obj->dTlsSessions[o->sessionId] = NULL;
|
|
|
|
ILibStun_SctpDisconnect_Final(o); // sem_post(&(o->Lock)) done inside this method.
|
|
|
|
free(o);
|
|
|
|
//
|
|
// Note:
|
|
//
|
|
// Klocwork and MS Static Analyser will report a spurious error here that o->Lock has been locked in line xxx was not unlocked
|
|
// This is not an issue, because it is actually unlocked and destroyed within ILibStun_SctpDisconnect_Final(), which
|
|
// will get called above...
|
|
//
|
|
// If we try to unlock here to satisfy Klocwork, you'll just end up corrupting the heap, because
|
|
// the lock will already have been destroyed before the end of this method.
|
|
}
|
|
|
|
void ILibStun_SctpDisconnect(struct ILibStun_Module *obj, int session)
|
|
{
|
|
struct ILibStun_dTlsSession* o = obj->dTlsSessions[session];
|
|
|
|
ILibLifeTime_Remove(o->parent->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(o)); // Stop SCTP Heartbeats
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(o->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Disconnect Requested on Session: %d", session);
|
|
|
|
// Need to context switch to MicrostackChain thread
|
|
ILibChain_RunOnMicrostackThread(obj->ChainLink.ParentChain, ILibStun_SctpDisconnect_Continue, o);
|
|
}
|
|
//! Close an SCTP association and any associated data channels
|
|
/*!
|
|
\param sctpSession Sctp Session to Close
|
|
*/
|
|
void ILibSCTP_Close(void* sctpSession)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)sctpSession;
|
|
if (obj != NULL) { ILibStun_SctpDisconnect(obj->parent, obj->sessionId); }
|
|
}
|
|
|
|
int ILibSCTP_FWDTSN_Check(struct ILibStun_dTlsSession *obj, char* buffer)
|
|
{
|
|
ILibSCTP_RPACKET *packet = (ILibSCTP_RPACKET*)obj->pendingQueueHead;
|
|
ILibSCTP_RPACKET *tmp, *tmp2;
|
|
unsigned int FWDTSN = 0; // Network Order
|
|
int ptr = 0;
|
|
ILibSCTP_FwdTSNPayload *chunk = (ILibSCTP_FwdTSNPayload*)buffer;
|
|
|
|
while (packet != NULL)
|
|
{
|
|
if (obj->intsn < ntohl(((ILibSCTP_DataPayload*)packet->Data)->TSN))
|
|
{
|
|
// These packets are not covered with the Cumulative TSN
|
|
if (packet->PacketGAPCounter == 0xFD )
|
|
{
|
|
chunk->SkippedStreams[ptr].StreamNumber = ((ILibSCTP_DataPayload*)packet->Data)->StreamID;
|
|
chunk->SkippedStreams[ptr].StreamSequence = (((ILibSCTP_DataPayload*)packet->Data)->flags & ILibSCTP_UnorderedFlag) != ILibSCTP_UnorderedFlag ? ((ILibSCTP_DataPayload*)packet->Data)->StreamSequenceNumber : 0x00;
|
|
while (packet != NULL && ((((ILibSCTP_DataPayload*)packet->Data)->flags & 0x01) != 0x01))
|
|
{
|
|
if (packet->NextPacket != NULL && (((ILibSCTP_DataPayload*)packet->Data)->flags & ILibSCTP_UnorderedFlag) != ILibSCTP_UnorderedFlag)
|
|
{
|
|
chunk->SkippedStreams[ptr].StreamSequence = ((ILibSCTP_DataPayload*)packet->NextPacket->Data)->StreamSequenceNumber;
|
|
}
|
|
packet = packet->NextPacket;
|
|
}
|
|
if (packet != NULL)
|
|
{
|
|
FWDTSN = ((ILibSCTP_DataPayload*)packet->Data)->TSN; // Advance the FWD-TSN as far as we can, encompassing an entire fragment if fragmented
|
|
}
|
|
else
|
|
{
|
|
FWDTSN = ((ILibSCTP_DataPayload*)((ILibSCTP_RPACKET*)obj->pendingQueueTail)->Data)->TSN; // Don't have the whole fragment, so everything needs to be abandoned
|
|
}
|
|
++ptr;
|
|
}
|
|
else
|
|
{
|
|
break; // Can't advance FWD-TSN any further, becuase this packet is not abandoned
|
|
}
|
|
}
|
|
packet = packet != NULL ? packet->NextPacket : NULL;
|
|
}
|
|
|
|
if (FWDTSN != 0)
|
|
{
|
|
tmp = (ILibSCTP_RPACKET*)obj->pendingQueueHead;
|
|
while (tmp != NULL && tmp != packet)
|
|
{
|
|
tmp2 = tmp->NextPacket;
|
|
free(tmp);
|
|
tmp = tmp2;
|
|
}
|
|
|
|
// Remove the abandoned packets from the pendingQueue
|
|
obj->pendingQueueHead = (char*)packet;
|
|
if (packet == NULL) { obj->pendingQueueTail = NULL; }
|
|
|
|
// Send a FWD-TSN Chunk
|
|
chunk->type = RCTP_CHUNK_TYPE_FWDTSN;
|
|
chunk->flags = 0x00;
|
|
chunk->NewTSN = FWDTSN;
|
|
|
|
ptr = 8 + (ptr * 4);
|
|
chunk->length = htons((unsigned short)ptr);
|
|
obj->fwdTsnDelayTime = 0;
|
|
}
|
|
else
|
|
{
|
|
ptr = 0;
|
|
}
|
|
return(ptr);
|
|
}
|
|
|
|
void ILibStun_SctpResent(struct ILibStun_dTlsSession *obj)
|
|
{
|
|
unsigned int time = (unsigned int)ILibGetUptime();
|
|
ILibSCTP_RPACKET *rpacket = (ILibSCTP_RPACKET*)obj->pendingQueueHead;
|
|
|
|
// If T3RTXTIME == 0, it is disabled, otherwise it is the timestamp of when it was enabled
|
|
if (obj->T3RTXTIME > 0 && time >= (obj->T3RTXTIME + obj->RTO) && rpacket != NULL)
|
|
{
|
|
obj->T3RTXTIME = 0;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->onT3RTX != NULL) { obj->onT3RTX(obj, "OnT3RTX", -1); } // Debug event informing of the T3RTX timer expiration
|
|
#endif
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: T3TX Timer Expired", obj->sessionId);
|
|
obj->senderCredits = ILibRUDP_StartMTU; // Set CWND to 1 MTU
|
|
obj->SSTHRESH = MAX(obj->congestionWindowSize / 2, 4 * ILibRUDP_StartMTU); // Update Slow Start Threshold
|
|
obj->congestionWindowSize = ILibRUDP_StartMTU; // Reset the size of the Congestion Window, so that we'll initialy only have one SCTP packet in flight
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->onCongestionWindowSizeChanged != NULL) { obj->onCongestionWindowSizeChanged(obj, "OnCongestionWindowSizeChanged", obj->congestionWindowSize); }
|
|
#endif
|
|
|
|
while (rpacket != NULL)
|
|
{
|
|
// 0xFE means this packet was already ACK'ed with a GAP Ack Block, so we don't need to retransmit it
|
|
// 0xFD means this packet is marked as ABANDONED
|
|
if(rpacket->PacketGAPCounter != 0xFE && rpacket->PacketGAPCounter != 0xFD)
|
|
{
|
|
if ((rpacket->LastSentTimeStamp + obj->RTO) > time)
|
|
{
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->onT3RTX != NULL) { obj->onT3RTX(obj, "OnT3RTX", -2); } // Debug event informing of the T3RTX timer non-expiration
|
|
#endif
|
|
break; // T3RTX did not expire for this packet
|
|
}
|
|
if ((rpacket->Reliability & 0x2000) == 0x2000 && rpacket->PacketResendCounter >= (rpacket->Reliability & 0x1FFF))
|
|
{
|
|
// Partial-Reliability REXMIT : Exceeded Max Retransmit
|
|
rpacket->PacketGAPCounter = 0xFD; // ABANDON PACKET
|
|
rpacket = rpacket->NextPacket;
|
|
obj->fwdTsnDelayTime = time;
|
|
obj->timervalue = 0; // Reset this value, so we can gaurantee that a FWD-TSN chunk is sent within 500ms
|
|
continue;
|
|
}
|
|
else if ((rpacket->Reliability & 0x4000) == 0x4000 && (unsigned short)(time - rpacket->CreationTimeStamp) >= (rpacket->Reliability & 0x1FFF))
|
|
{
|
|
// Partial-Reliability TIMED : Exceeded Max Timeout
|
|
rpacket->PacketGAPCounter = 0xFD; // ABANDON PACKET
|
|
rpacket = rpacket->NextPacket;
|
|
obj->fwdTsnDelayTime = time;
|
|
obj->timervalue = 0; // Reset this value, so we can gaurantee that a FWD-TSN chunk is sent within 500ms
|
|
continue;
|
|
}
|
|
|
|
if (obj->senderCredits >= (rpacket->PacketSize)) // If we have available CWND, then retransmit packets
|
|
{
|
|
rpacket->PacketResendCounter++; // Update retry counter
|
|
rpacket->PacketGAPCounter = 0; // Update gap counter, so that it will not FastRetry
|
|
rpacket->LastSentTimeStamp = time; // Update last send time
|
|
obj->lastRetransmitTime = time; // Anytime we retransmit a packet, we set a timestamp, for RTT calculation purposes
|
|
|
|
obj->senderCredits -= rpacket->PacketSize; // Deduct CWND
|
|
if (obj->T3RTXTIME == 0)
|
|
{
|
|
// Only set the T3-RTX timer if it's not already running
|
|
obj->T3RTXTIME = time;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (obj->onT3RTX != NULL) { obj->onT3RTX(obj, "OnT3RTX", obj->RTO); } // Debug event informing of the T3RTX timer expiration
|
|
#endif
|
|
}
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: RESEND", obj->sessionId);
|
|
ILibStun_SendSctpPacket(obj->parent, obj->sessionId, rpacket->Data - 12, rpacket->PacketSize);
|
|
#ifdef _WEBRTCDEBUG
|
|
//if (obj->onSendRetry != NULL) { obj->onSendRetry(obj, "OnSendRetry", ((unsigned short*)(rpacket->Data + sizeof(char*)))[0]); }
|
|
if (obj->onSendRetry != NULL) { obj->onSendRetry(obj, "OnSendRetry", ntohl(((unsigned int*)rpacket->Data)[1])); }
|
|
if (obj->onRetryPacket != NULL)
|
|
{
|
|
char *tmp = ILibMemory_AllocateA(rpacket->PacketSize + 14);
|
|
sprintf_s(tmp, 14, "OnRetryPacket");
|
|
memcpy_s(tmp + 14, rpacket->PacketSize, rpacket->Data - 12, rpacket->PacketSize);
|
|
obj->onRetryPacket(obj, tmp, rpacket->PacketSize);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
rpacket->PacketGAPCounter = 0xFF; // Mark for retransmit later (when CWND allows)
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->parent->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: MARK FOR RESEND", obj->sessionId);
|
|
}
|
|
}
|
|
rpacket = rpacket->NextPacket; // Move to the next packet
|
|
}
|
|
|
|
obj->RTO = obj->RTO * 2; // Double the RT Timer
|
|
if (obj->RTO > RTO_MAX) { obj->RTO = RTO_MAX; } // Enforce a cap on the max timeout
|
|
}
|
|
}
|
|
|
|
void ILibStun_SctpOnTimeout(void *object)
|
|
{
|
|
struct ILibStun_dTlsSession *obj = ILibWebRTC_DTLS_FROM_SCTP_HEARTBEAT_TIMER_OBJECT(object);
|
|
ILibSpinLock_Lock(&(obj->Lock));
|
|
// printf("Timer state=%d, value=%d\r\n", obj->state, obj->timervalue);
|
|
if (obj->state < 1 || obj->state > 2) { ILibSpinLock_UnLock(&(obj->Lock)); return; } // Check if we are still needed, connecting or connected state only.
|
|
obj->timervalue++;
|
|
|
|
if (obj->timervalue < 80)
|
|
{
|
|
// Resent packets
|
|
if (obj->state == 2) ILibStun_SctpResent(obj);
|
|
}
|
|
else
|
|
{
|
|
// Close the connection
|
|
printf("************************************\n");
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP Timeout on Session: %d", obj->sessionId);
|
|
ILibStun_SctpDisconnect(obj->parent, obj->sessionId);
|
|
return;
|
|
}
|
|
|
|
if (obj->timervalue >= 40 && obj->state == 2)
|
|
{
|
|
// Send Heartbeat
|
|
char hb[16];
|
|
ILibStun_AddSctpChunkHeader(hb, 12, RCTP_CHUNK_TYPE_HEARTBEAT, 0, 4);
|
|
ILibStun_SendSctpPacket(obj->parent, obj->sessionId, hb, 16);
|
|
}
|
|
|
|
ILibLifeTime_AddEx(obj->parent->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(obj), 100 + (200 * obj->timervalue), &ILibStun_SctpOnTimeout, NULL);
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
}
|
|
|
|
int ILibSCTP_AddOptionalVariableParameter(char* insertionPoint, unsigned short parameterType, void *parameterData, int parameterDataLen)
|
|
{
|
|
int retValue;
|
|
((unsigned short*)insertionPoint)[0] = parameterType;
|
|
((unsigned short*)insertionPoint)[1] = htons((unsigned short)(4 + parameterDataLen));
|
|
if(parameterDataLen>0)
|
|
{
|
|
memcpy_s(insertionPoint + 4, parameterDataLen, parameterData, parameterDataLen);
|
|
}
|
|
parameterDataLen += 4;
|
|
retValue = ILibAlignOnFourByteBoundary(insertionPoint, parameterDataLen);
|
|
return(retValue);
|
|
}
|
|
|
|
void ILibWebRTC_PropagateChannelCloseEx2(ILibSparseArray sender, int index, void *value, void *user)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)user;
|
|
|
|
UNREFERENCED_PARAMETER(sender);
|
|
|
|
if ((((ILibSCTP_StreamAttributes*)(char*)&value)->Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED) == ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED)
|
|
{
|
|
if (obj->parent->OnWebRTCDataChannelClosed != NULL) { obj->parent->OnWebRTCDataChannelClosed(obj->parent, obj, (unsigned short)index); }
|
|
}
|
|
}
|
|
void ILibWebRTC_PropagateChannelCloseEx(ILibSparseArray sender, struct ILibStun_dTlsSession* obj)
|
|
{
|
|
ILibSparseArray_DestroyEx(sender, &ILibWebRTC_PropagateChannelCloseEx2, obj);
|
|
}
|
|
ILibSparseArray ILibWebRTC_PropagateChannelClose(struct ILibStun_dTlsSession* obj, char* packet)
|
|
{
|
|
ILibSCTP_ReconfigChunk* outChunk = (ILibSCTP_ReconfigChunk*)(packet + 12);
|
|
ILibSCTP_Reconfig_Parameter_Header *hdr;
|
|
ILibSparseArray retVal = NULL;
|
|
unsigned short len = ntohs(outChunk->chunkLength);
|
|
int bytesProcessed = 0;
|
|
|
|
if (packet == NULL || ((ILibSCTP_PendingTSN_Data*)((char*)&packet))->Data.Flags == 0xFF) { return NULL; }
|
|
|
|
|
|
while((bytesProcessed + 4) < len)
|
|
{
|
|
hdr = (ILibSCTP_Reconfig_Parameter_Header*)((char*)&outChunk->reconfigurationParameter + bytesProcessed);
|
|
if(hdr->parameterType == ntohs(SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST))
|
|
{
|
|
ILibSCTP_Reconfig_OutgoingSSNResetRequest *req = (ILibSCTP_Reconfig_OutgoingSSNResetRequest*)hdr;
|
|
unsigned short count = (ntohs(req->parameterLength) - 16) / 2;
|
|
unsigned short sid;
|
|
|
|
if(count==0)
|
|
{
|
|
ILibSparseArray_ClearEx(obj->DataChannelMetaDetaValues, NULL, NULL);
|
|
retVal = ILibSparseArray_Move(obj->DataChannelMetaDeta);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
retVal = ILibSparseArray_CreateEx(obj->DataChannelMetaDeta);
|
|
while(count > 0)
|
|
{
|
|
sid = ntohs(req->Streams[count-1]);
|
|
ILibSparseArray_Add(retVal, sid, ILibSparseArray_Remove(obj->DataChannelMetaDeta, sid));
|
|
ILibSparseArray_Remove(obj->DataChannelMetaDetaValues, sid);
|
|
--count;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
bytesProcessed += FOURBYTEBOUNDARY(ntohs(hdr->parameterLength));
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
int ILibSCTP_AddPacketToHoldingQueue_Comparer(void *obj1, void *obj2)
|
|
{
|
|
unsigned int v2 = ntohl(((ILibSCTP_DataPayload*)obj2)->TSN);
|
|
unsigned int v1 = ntohl(((ILibSCTP_DataPayload*)obj1)->TSN);
|
|
|
|
return(v2 == v1 ? 0 : ((v2 < v1)?-1:1));
|
|
}
|
|
void* ILibSCTP_AddPacketToHoldingQueue_Chooser(void *oldValue, void *newValue, void *user)
|
|
{
|
|
if (oldValue != NULL)
|
|
{
|
|
return(oldValue);
|
|
}
|
|
else
|
|
{
|
|
unsigned short len = ntohs(((ILibSCTP_DataPayload*)newValue)->length);
|
|
char* retVal = (char*)malloc(len+1);
|
|
if (retVal == NULL) ILIBCRITICALEXIT(254);
|
|
|
|
memcpy_s(retVal, len + 1, (char*)newValue, len);
|
|
ReceiveHoldBuffer_Increment((ILibLinkedList)user, ((ILibSCTP_DataPayload*)newValue)->length);
|
|
return retVal;
|
|
}
|
|
}
|
|
void* ILibSCTP_AddPacketToHoldingQueue(struct ILibStun_dTlsSession* o, ILibSCTP_DataPayload *payload, ILibSCTP_SackStatus *sentsack)
|
|
{
|
|
void* retVal = NULL;
|
|
// Out of sequence packet, find a spot in the receive queue.
|
|
|
|
RCTPRCVDEBUG(printf("STORING %u, size = %d\r\n", ntohl(payload->TSN), ntohs(payload->length));)
|
|
if (ReceiveHoldBuffer_Used(o->receiveHoldBuffer) + payload->length > ILibSCTP_MaxReceiverCredits) { *sentsack = ILibSCTP_SackStatus_Skip; return(NULL); }
|
|
|
|
retVal = ILibLinkedList_SortedInsertEx(o->receiveHoldBuffer, &ILibSCTP_AddPacketToHoldingQueue_Comparer, &ILibSCTP_AddPacketToHoldingQueue_Chooser, payload, o->receiveHoldBuffer);
|
|
|
|
// Send ACK now
|
|
if (*sentsack == ILibSCTP_SackStatus_NotSent)
|
|
{
|
|
*sentsack = ILibSCTP_SackStatus_Sent;
|
|
o->rpacketptr = ILibStun_SctpAddSackChunk(o->parent, o->sessionId, o->rpacket, o->rpacketptr); // Send the ACK with the TSN as far forward as we can
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
int ILibSCTP_HoldingQueue_TSNComparer(void *obj1, void *obj2)
|
|
{
|
|
return((((ILibSCTP_DataPayload*)obj1)->TSN == ((ILibSCTP_DataPayload*)obj2)->TSN) ? 0 : 1);
|
|
}
|
|
|
|
#ifdef _REMOTELOGGING
|
|
void ILibSctp_DebugSctpPacket(ILibRemoteLogging *logger, char *packet, int packetLen, char *note)
|
|
{
|
|
int ptr = 12, stop = 0;
|
|
ILibSCTP_ChunkHeader *chunkHdr;
|
|
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "Debugging SCTP (%s) -->", note);
|
|
|
|
while ((chunkHdr = (ILibSCTP_ChunkHeader*)(packet + ptr)) != NULL && (ptr + 4 <= packetLen) && stop == 0)
|
|
{
|
|
unsigned char chunktype = chunkHdr->chunkType;
|
|
unsigned short chunksize = ntohs(chunkHdr->chunkLength);
|
|
if (chunksize < 4 || ptr + chunksize > packetLen) break;
|
|
|
|
switch (chunktype)
|
|
{
|
|
case RCTP_CHUNK_TYPE_DATA:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[DATA] TSN: %u, U: %u, B: %u, E: %u", ntohl(((ILibSCTP_DataPayload*)(packet + ptr))->TSN), (((ILibSCTP_ChunkHeader*)(packet + ptr))->chunkFlags >> 2) & 0x01, (((ILibSCTP_ChunkHeader*)(packet + ptr))->chunkFlags >> 1) & 0x01, ((ILibSCTP_ChunkHeader*)(packet + ptr))->chunkFlags & 0x01);
|
|
break;
|
|
case RCTP_CHUNK_TYPE_INIT:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[INIT]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_INITACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[INIT-ACK]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_SACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[SACK]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_HEARTBEAT:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[HEARTBEAT]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_HEARTBEATACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[HEARTBEAT-ACK]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ABORT:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[ABORT]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_SHUTDOWN:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[SHUTDOWN]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_SHUTDOWNACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[SHUTDOWN-ACK]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ERROR:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[ERROR]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_COOKIEECHO:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[COOKIE]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_COOKIEACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[COOKIE-ACK]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ECNE:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[ECNE]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_RECONFIG:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[RECONFIG]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_FWDTSN:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[FWDTSN]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ASCONF:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[ASCONF]");
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ASCONFACK:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...[ASCONF-ACK]");
|
|
break;
|
|
default:
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...ChunkFlags: %u", ((ILibSCTP_ChunkHeader*)(packet + ptr))->chunkFlags);
|
|
break;
|
|
}
|
|
|
|
ptr += FOURBYTEBOUNDARY(chunksize); // Add chunk size and padding
|
|
}
|
|
ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "<-- End Debugging SCTP (%s)", note);
|
|
}
|
|
#endif
|
|
|
|
void ILibSCTP_ReorderChunks(struct ILibStun_Module *obj, int sessionID, char *buffer, int bufferLen)
|
|
{
|
|
int ptr = 12, stop = 0;
|
|
ILibSCTP_ChunkHeader *chunkHdr;
|
|
|
|
char *dataChunks = ILibScratchPad2;
|
|
char controlChunks[65535];
|
|
int dataChunksWritten = 0;
|
|
int controlChunksWritten = 0;
|
|
|
|
// Save Data Chunks to Scratchpad
|
|
while ((chunkHdr = (ILibSCTP_ChunkHeader*)(buffer + ptr)) != NULL && (ptr + 4 <= bufferLen) && stop == 0)
|
|
{
|
|
unsigned char chunktype = chunkHdr->chunkType;
|
|
//unsigned char chunkflags = chunkHdr->chunkFlags;
|
|
unsigned short chunksize = ntohs(chunkHdr->chunkLength);
|
|
unsigned short actualSize = FOURBYTEBOUNDARY(chunksize);
|
|
if (chunksize < 4 || ptr + chunksize > bufferLen) break;
|
|
|
|
if(chunktype == RCTP_CHUNK_TYPE_DATA)
|
|
{
|
|
// TempWrite Data Chunks
|
|
memcpy_s(dataChunks + dataChunksWritten, sizeof(ILibScratchPad2) - dataChunksWritten, (char*)chunkHdr, chunksize);
|
|
if (actualSize > chunksize) { memset(dataChunks + chunksize, 0, actualSize - chunksize); }
|
|
dataChunksWritten += actualSize;
|
|
}
|
|
else
|
|
{
|
|
// TempWrite Control Chunks
|
|
memcpy_s(controlChunks + controlChunksWritten, sizeof(controlChunks) - controlChunksWritten, (char*)chunkHdr, chunksize);
|
|
if (actualSize > chunksize) { memset(controlChunks + chunksize, 0, actualSize - chunksize); }
|
|
controlChunksWritten += actualSize;
|
|
}
|
|
ptr += FOURBYTEBOUNDARY(chunksize); // Add chunk size and padding
|
|
}
|
|
|
|
// Merge Control and Data Chunks
|
|
if (controlChunksWritten > 0 && dataChunksWritten > 0 && (controlChunksWritten + dataChunksWritten + 12) == bufferLen)
|
|
{
|
|
memcpy_s(buffer + 12, bufferLen - 12, controlChunks, controlChunksWritten);
|
|
memcpy_s(buffer + 12 + controlChunksWritten, bufferLen - 12 - controlChunksWritten, dataChunks, dataChunksWritten);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "ILibSCTP_ReorderChunks[ID = %d]: SCTP Chunks were reordered", sessionID);
|
|
#ifdef _REMOTELOGGING
|
|
ILibSctp_DebugSctpPacket(ILibChainGetLogger(obj->ChainLink.ParentChain), buffer, bufferLen, "REORDERED");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "ILibSCTP_ReorderChunks[ID = %d]: Error Reordering SCTP Chunks", sessionID);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...SCTP Size = %d, but reordered size is: %d", bufferLen, controlChunksWritten + dataChunksWritten + 12);
|
|
}
|
|
}
|
|
// Main RFC specification: http://tools.ietf.org/html/rfc4960
|
|
void ILibStun_ProcessSctpPacket(struct ILibStun_Module *obj, int session, char* buffer, int bufferLength)
|
|
{
|
|
ILibSCTP_ChunkHeader *chunkHdr;
|
|
char* rpacket = NULL;
|
|
unsigned int crc;
|
|
int ptr = 12, stop = 0;
|
|
int* rptr = NULL;
|
|
struct ILibStun_dTlsSession* o = obj->dTlsSessions[session];
|
|
int rttCalculated = 0;
|
|
ILibSCTP_SackStatus sentsack = ILibSCTP_SackStatus_NotSent;
|
|
|
|
// Setup the session
|
|
if (bufferLength < 12 || o == NULL) return;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Simulated Inbound Packet Loss
|
|
if ((obj->lossPercentage) > 0 && ((rand() % 100) >= (100 - obj->lossPercentage))) { return; }
|
|
#endif
|
|
|
|
// Check size and the RCTP (RFC4960) checksum using CRC32c
|
|
crc = ((unsigned int*)buffer)[2];
|
|
((unsigned int*)buffer)[2] = 0;
|
|
if (crc != crc32c(0, (unsigned char*)buffer, (uint32_t)bufferLength)) return;
|
|
|
|
if (o->freshnessTimestampStart != 0)
|
|
{
|
|
// Technically speaking, consent freshness is supposed to be independent of SCTP/DTLS, but Chromium and Firefox no longer
|
|
// respond to a freshness probe, so I have to work-around this issue
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_2, "Consent Freshness Updated (via dTLS) on IceSlot: %d", session);
|
|
|
|
o->freshnessTimestampStart = 0;
|
|
ILibLifeTime_Remove(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(o));
|
|
ILibLifeTime_Add(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(o), ILibStun_MaxConsentFreshnessTimeoutSeconds, ILibStun_WebRTC_ConsentFreshness_Start, NULL);
|
|
}
|
|
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
|
|
// Decode the rest of the header
|
|
if (o->tag != ((unsigned int*)buffer)[1] && ((unsigned int*)buffer)[1] != 0) { ILibSpinLock_UnLock(&(o->Lock)); return; } // Check the verification tag
|
|
|
|
// Setup the response packet
|
|
rptr = &(o->rpacketptr);
|
|
rpacket = o->rpacket;
|
|
((int*)rpacket)[0] = ((int*)rpacket)[1] = ((int*)rpacket)[2] = 0; // Set common header area to zeros, so we can temporarily stick some flags in there to keep track of control/data packet order
|
|
*rptr = 12;
|
|
|
|
// printf("SCTP size: %d\r\n", bufferLength);
|
|
|
|
// Decode each chunk
|
|
#ifdef _REMOTELOGGING
|
|
ILibSctp_DebugSctpPacket(ILibChainGetLogger(obj->ChainLink.ParentChain), buffer, bufferLength, "INBOUND");
|
|
#endif
|
|
|
|
while ((chunkHdr = (ILibSCTP_ChunkHeader*)(buffer + ptr)) != NULL && (ptr + 4 <= bufferLength) && stop == 0)
|
|
{
|
|
unsigned char chunktype = chunkHdr->chunkType;
|
|
unsigned char chunkflags = chunkHdr->chunkFlags;
|
|
unsigned short chunksize = ntohs(chunkHdr->chunkLength);
|
|
if (chunksize < 4 || ptr + chunksize > bufferLength) break;
|
|
|
|
switch (chunktype) {
|
|
case RCTP_CHUNK_TYPE_RECONFIG:
|
|
{
|
|
ILibSCTP_StreamAttributes attr;
|
|
ILibSCTP_ReconfigChunk *recon = (ILibSCTP_ReconfigChunk*)(buffer + ptr);
|
|
ILibSCTP_Reconfig_Parameter_Header *hdr;
|
|
int bytesProcessed = 0;
|
|
unsigned short chunkLen = ntohs(recon->chunkLength);
|
|
unsigned short streamId;
|
|
ILibSCTP_ReconfigChunk *outChunk = (ILibSCTP_ReconfigChunk*)(rpacket + *rptr);
|
|
unsigned short outOffset = 0;
|
|
unsigned short hdrLen;
|
|
ILibSCTP_Reconfig_OutgoingSSNResetRequest *outRequest = NULL;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [RECONFIG]", session);
|
|
|
|
while((bytesProcessed + 4 )< chunkLen)
|
|
{
|
|
hdr = (ILibSCTP_Reconfig_Parameter_Header*)((char*)&recon->reconfigurationParameter + bytesProcessed);
|
|
hdrLen = ntohs(hdr->parameterLength);
|
|
|
|
switch(ntohs(hdr->parameterType))
|
|
{
|
|
case SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST: // Outgoing SSN Reset
|
|
{
|
|
ILibSCTP_Reconfig_Response *OutboundResponse = (ILibSCTP_Reconfig_Response*)((char*)(&outChunk->reconfigurationParameter) + outOffset); // Ignore Klocwork Error, it's not a problem in this case
|
|
ILibSCTP_Reconfig_OutgoingSSNResetRequest *req = (ILibSCTP_Reconfig_OutgoingSSNResetRequest*)hdr;
|
|
unsigned short streamCount = (ntohs(req->parameterLength) - 16) / 2;
|
|
unsigned int lastTSN = ntohl(req->LastTSN);
|
|
|
|
OutboundResponse->parameterType = htons(SCTP_RECONFIG_TYPE_RECONFIGURATION_RESPONSE);
|
|
OutboundResponse->parameterLength = htons(sizeof(ILibSCTP_Reconfig_Response));
|
|
OutboundResponse->RESSEQNum = req->RReqSeqNum;
|
|
outOffset += sizeof(ILibSCTP_Reconfig_Response);
|
|
|
|
if(outRequest!=NULL)
|
|
{
|
|
// ERROR: This should not happen unless the RECONFIG chunk was malformed
|
|
OutboundResponse->Result = htonl((unsigned int)ILibSCTP_Reconfig_Result_Denied);
|
|
break;
|
|
}
|
|
|
|
outRequest = (ILibSCTP_Reconfig_OutgoingSSNResetRequest*)((char*)(&outChunk->reconfigurationParameter) + outOffset);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Outgoing Reset Request: Seq/%u", (unsigned int)ntohl(req->RReqSeqNum));
|
|
|
|
if(lastTSN <= o->userTSN)
|
|
{
|
|
// Response to the OUTGOING_RESET_REQUEST with a SUCCESS/RESPONSE and an OUTBOUND_RESET_REQUEST
|
|
OutboundResponse->Result = htonl((unsigned int)ILibSCTP_Reconfig_Result_Success_Performed);
|
|
|
|
outRequest->parameterType = htons(SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST);
|
|
outRequest->parameterLength = htons(16 + 2*streamCount);
|
|
outRequest->LastTSN = htonl(o->outtsn - 1);
|
|
outRequest->RReqSeqNum = htonl(o->RREQSEQ++);
|
|
outRequest->RResSeqNum = htonl(o->RRESSEQ++);
|
|
|
|
if(streamCount == 0)
|
|
{
|
|
// Reset All Streams
|
|
// We're going to move the contents to a new SparseArray, so we can post the events without a lock
|
|
|
|
ILibSparseArray dup = ILibSparseArray_Move(o->DataChannelMetaDeta);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Resetting all streams");
|
|
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
ILibWebRTC_PropagateChannelCloseEx(dup, o);
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Number of streams to be reset: %u", streamCount);
|
|
while(streamCount > 0)
|
|
{
|
|
streamId = ntohs(req->Streams[streamCount-1]);
|
|
attr.Raw = ILibSparseArray_Remove(o->DataChannelMetaDeta, streamId);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, ".........Resetting stream: %u", streamId);
|
|
|
|
if((attr.Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED) == ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED)
|
|
{
|
|
OutboundResponse->Result = htonl(ILibSCTP_Reconfig_Result_Success_Performed);
|
|
ILibSparseArray_Remove(o->DataChannelMetaDetaValues, streamId); // Clear associated data with this stream ID
|
|
if(obj->OnWebRTCDataChannelClosed != NULL)
|
|
{
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
obj->OnWebRTCDataChannelClosed(obj, o, streamId);
|
|
if (obj->dTlsSessions[session] == NULL) { return; }
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutboundResponse->Result = htonl(ILibSCTP_Reconfig_Result_Success_NOP);
|
|
}
|
|
|
|
outRequest->Streams[streamCount-1] = htons(streamId);
|
|
--streamCount;
|
|
}
|
|
}
|
|
|
|
if (OutboundResponse->Result != htonl(ILibSCTP_Reconfig_Result_Success_NOP) && o->pendingReconfigPacket == NULL)
|
|
{
|
|
//unsigned short orl = ntohs(outRequest->parameterLength);
|
|
outOffset += (16 + 2 * streamCount); // We can send the outbound reset request, becuase we actually closed
|
|
o->pendingReconfigPacket = o->rpacket + (o->rpacketsize - FOURBYTEBOUNDARY((ntohs(outRequest->parameterLength) + 16)));
|
|
o->reconfigFailures = 0;
|
|
|
|
((ILibSCTP_ReconfigChunk*)(o->pendingReconfigPacket + 12))->type = RCTP_CHUNK_TYPE_RECONFIG;
|
|
((ILibSCTP_ReconfigChunk*)(o->pendingReconfigPacket + 12))->chunkFlags = 0;
|
|
((ILibSCTP_ReconfigChunk*)(o->pendingReconfigPacket + 12))->chunkLength = htons((4 + (16 + 2 * streamCount)));
|
|
|
|
memcpy_s(o->pendingReconfigPacket + 16, 4096, outRequest, ntohs(outRequest->parameterLength));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have not received all data for the stream yet, so we need to defer the reset
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......LastTSN/%u has not elapsed, DEFERRING", lastTSN);
|
|
OutboundResponse->Result = htonl((unsigned int)ILibSCTP_Reconfig_Result_In_Progress);
|
|
|
|
if(o->pendingReconfigPacket !=NULL && o->pendingReconfigPacket >= o->rpacket && o->pendingReconfigPacket <= (o->rpacket + o->rpacketsize))
|
|
{
|
|
// Locally initiated Channel Close is pending. Deny request, becuase we don't have resources to process this yet
|
|
OutboundResponse->Result = htonl(ILibSCTP_Reconfig_Result_Denied);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Insufficient resources to process Close, due to locally initiated close in progress");
|
|
}
|
|
else
|
|
{
|
|
ILibSCTP_PendingTSN_Data pending;
|
|
int i;
|
|
|
|
// We're going to use this field, which already contains malloc'ed memory, to store some data at the tail end
|
|
if(o->pendingReconfigPacket != NULL)
|
|
{
|
|
// ALREADY IN PROGRESS
|
|
OutboundResponse->Result = htonl((unsigned int)ILibSCTP_Reconfig_Result_In_Progress);
|
|
}
|
|
else
|
|
{
|
|
// Continue with deferring the request
|
|
pending.Raw = o->pendingReconfigPacket;
|
|
pending.Data.Type = 0xFF;
|
|
pending.Data.Flags = 0x00;
|
|
pending.Data.StreamIdOffset = (4 + (2*streamCount));
|
|
|
|
// We are going to store the TSN followed by the StreamIds
|
|
((int*)(o->rpacket + o->rpacketsize - pending.Data.StreamIdOffset))[0] = lastTSN;
|
|
for(i = 0; i < streamCount; ++i)
|
|
{
|
|
((unsigned short*)(o->rpacket + o->rpacketsize - pending.Data.StreamIdOffset))[i+2] = ntohs(req->Streams[i]);
|
|
}
|
|
o->pendingReconfigPacket = (char*)pending.Raw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SCTP_RECONFIG_TYPE_INCOMING_SSN_RESET_REQUEST: // Incoming Reset Request
|
|
{
|
|
ILibSCTP_Reconfig_IncomingSSNResetRequest *req = (ILibSCTP_Reconfig_IncomingSSNResetRequest*)hdr;
|
|
unsigned short streamCount = (ntohs(req->parameterLength) - 8) / 2;
|
|
ILibSCTP_PendingTSN_Data pending;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Incoming Reset Request: Seq/%u", (unsigned int)ntohl(req->RReqSeqNum));
|
|
pending.Raw = o->pendingReconfigPacket;
|
|
|
|
if(outRequest != NULL)
|
|
{
|
|
// We already received an Outbound Reset Request, which would have triggered us to generate our own Outbound Request.
|
|
// That means we can just reply with an Response Parameter, and call it a day
|
|
ILibSCTP_Reconfig_Response *OutboundResponse = (ILibSCTP_Reconfig_Response*)((char*)(&outChunk->reconfigurationParameter) + outOffset); // Ignore Klocwork Error, it's not a problem in this case
|
|
OutboundResponse->parameterType = htons(SCTP_RECONFIG_TYPE_RECONFIGURATION_RESPONSE);
|
|
OutboundResponse->parameterLength = htons(sizeof(ILibSCTP_Reconfig_Response));
|
|
OutboundResponse->RESSEQNum = req->RReqSeqNum;
|
|
OutboundResponse->Result = htonl((unsigned int)(pending.Data.Type == 0xFF && pending.Data.Flags == 0x00)?ILibSCTP_Reconfig_Result_In_Progress:ILibSCTP_Reconfig_Result_Success_NOP);
|
|
outOffset += sizeof(ILibSCTP_Reconfig_Response);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Responded with Response Parameter");
|
|
break;
|
|
}
|
|
|
|
// Lone INBOUND RESET REQUEST
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Responding with Outbound Reset Request");
|
|
outRequest = (ILibSCTP_Reconfig_OutgoingSSNResetRequest*)((char*)(&outChunk->reconfigurationParameter) + outOffset); // Ignore Klocwork Error, it's not a problem in this case
|
|
outRequest->parameterType = htons(SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST);
|
|
//outRequest->parameterLength = htons(16 + 2 * streamCount);
|
|
outRequest->parameterLength = 16 + 2 * streamCount;
|
|
outRequest->LastTSN = htonl(o->outtsn - 1);
|
|
outRequest->RReqSeqNum = htonl(o->RREQSEQ++);
|
|
outRequest->RResSeqNum = htonl(o->RRESSEQ++);
|
|
|
|
outOffset += FOURBYTEBOUNDARY(outRequest->parameterLength); // Add parameter size and padding
|
|
outRequest->parameterLength = htons(outRequest->parameterLength);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Number of streams to be reset: %u", streamCount);
|
|
while(streamCount > 0)
|
|
{
|
|
streamId = ntohs(req->Streams[streamCount-1]);
|
|
attr.Raw = ILibSparseArray_Remove(o->DataChannelMetaDeta, streamId);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, ".........Resetting stream: %u", streamId);
|
|
|
|
if ((attr.Data.StatusFlags & ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED) == ILibSCTP_StreamAttributesData_Assigned_Status_ASSIGNED)
|
|
{
|
|
ILibSparseArray_Remove(o->DataChannelMetaDetaValues, streamId); // Clear associated data with this stream ID
|
|
if (obj->OnWebRTCDataChannelClosed != NULL)
|
|
{
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
obj->OnWebRTCDataChannelClosed(obj, o, streamId);
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
}
|
|
outRequest->Streams[streamCount-1] = htons(streamId);
|
|
--streamCount;
|
|
}
|
|
}
|
|
break;
|
|
case SCTP_RECONFIG_TYPE_RECONFIGURATION_RESPONSE: // Response
|
|
{
|
|
ILibSCTP_Reconfig_Response *res = (ILibSCTP_Reconfig_Response*)hdr;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Received Response [%u/%s]: Seq/%u",(unsigned int)ntohl(res->Result), ((unsigned int)ntohl(res->Result)==0 || (unsigned int)ntohl(res->Result)==1 || (unsigned int)ntohl(res->Result)==4 || (unsigned int)ntohl(res->Result)==6)?"SUCCESS":"ERROR", (unsigned int)ntohl(res->RESSEQNum));
|
|
|
|
if(o->pendingReconfigPacket!=NULL && ((ILibSCTP_PendingTSN_Data*)((char*)&o->pendingReconfigPacket))->Data.Type != 0xFF)
|
|
{
|
|
if (res->RESSEQNum == ((unsigned int*)(o->pendingReconfigPacket + 20))[0])
|
|
{
|
|
// Both sides of the connection are aware of the ChannelClose... We can propagate this up.
|
|
ILibSparseArray arr = ILibWebRTC_PropagateChannelClose(o, o->pendingReconfigPacket);
|
|
|
|
ILibLifeTime_Remove(o->parent->Timer, ILibWebRTC_DTLS_TO_TIMER_OBJECT(o));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......[Response for a pending Close]");
|
|
o->pendingReconfigPacket = NULL;
|
|
|
|
// We need to shed the Lock before we call up the stack
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
ILibWebRTC_PropagateChannelCloseEx(arr, o);
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
bytesProcessed += FOURBYTEBOUNDARY(hdrLen); // Add parameter size and padding
|
|
}
|
|
|
|
if(outOffset != 0)
|
|
{
|
|
outChunk->type = RCTP_CHUNK_TYPE_RECONFIG;
|
|
outChunk->chunkFlags = 0x00;
|
|
outChunk->chunkLength = htons(4 + outOffset);
|
|
*rptr += (4 + outOffset);
|
|
}
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_COOKIEACK:
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [COOKIEACK]", session);
|
|
if (obj->OnConnect != NULL && o->state == 1)
|
|
{
|
|
o->state = 2;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
obj->OnConnect(obj, o, 1);
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state == 0) return;
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
}
|
|
case RCTP_CHUNK_TYPE_INITACK:
|
|
{
|
|
void *cookie = NULL;
|
|
int cookieLen = 0;
|
|
if (chunksize < 20 || session == -1 || o->state != 1) break;
|
|
|
|
// Set TSN
|
|
o->RRESSEQ = o->userTSN = o->intsn = ntohl(((unsigned int*)(buffer + ptr + 16))[0]) - 1;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [INIT-ACK]", session);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TSN/IN = %u", o->intsn);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TSN/OUT = %u", o->outtsn);
|
|
|
|
// Optional/Variable Fields
|
|
{
|
|
int varLen = 0;
|
|
unsigned short tLen = 0;
|
|
int chunkIndex;
|
|
int tmpval1, tmpval2, tmpval3;
|
|
|
|
while (chunksize - varLen > 20)
|
|
{
|
|
SCTP_INIT_PARAMS optionalParam = (SCTP_INIT_PARAMS)ntohs(((unsigned short*)(buffer + ptr + 20 + varLen))[0]);
|
|
tLen = ntohs(((unsigned short*)(buffer + ptr + 20 + varLen))[1]);
|
|
switch (optionalParam)
|
|
{
|
|
case SCTP_INIT_PARAM_UNRELIABLE_STREAM:
|
|
ILibSparseArray_Add(o->PeerFeatureSet, SCTP_INIT_PARAM_UNRELIABLE_STREAM, (void*)0x01);
|
|
break;
|
|
case SCTP_INIT_PARAM_SUPPORTED_EXTENSIONS:
|
|
for (chunkIndex = 0; chunkIndex < tLen - 4; ++chunkIndex)
|
|
{
|
|
ILibSparseArray_Add(o->PeerFeatureSet, (int)((unsigned char*)(buffer + ptr + 20 + varLen + 4))[chunkIndex], (void*)0x01);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Supported Extension: %d", (int)((unsigned char*)(buffer + ptr + 20 + varLen + 4))[chunkIndex]);
|
|
}
|
|
break;
|
|
case SCTP_INIT_PARAM_STATE_COOKIE:
|
|
cookie = (char*)(buffer + ptr + 20 + varLen + 4);
|
|
cookieLen = tLen - 4;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Received Cookie: %s", ILibRemoteLogging_ConvertToHex(cookie, cookieLen));
|
|
|
|
tmpval1 = *rptr;
|
|
|
|
// We need to send a RCTP_CHUNK_TYPE_COOKIEECHO response
|
|
tmpval3 = *rptr = ILibStun_AddSctpChunkHeader(rpacket, *rptr, RCTP_CHUNK_TYPE_COOKIEECHO, 0, tLen);
|
|
memcpy_s(rpacket + *rptr, 4096 - *rptr, cookie, cookieLen);
|
|
*rptr += FOURBYTEBOUNDARY(tLen);
|
|
|
|
tmpval2 = *rptr;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "......RPTR Pre: %d Mid %d Post: %d tLen: %u", tmpval1, tmpval3, tmpval2, tLen);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
varLen += FOURBYTEBOUNDARY(tLen); // Add parameter size and padding
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_INIT:
|
|
if (chunksize < 20 || session == -1 || o->state != 1) break;
|
|
o->sessionId = session;
|
|
o->tag = ((unsigned int*)(buffer + ptr + 4))[0];
|
|
if (o->tag == 0) { ILibSpinLock_UnLock(&(o->Lock)); return; } // This tag can't be zeroes
|
|
o->receiverCredits = ntohl(((unsigned int*)(buffer + ptr + 8))[0]);
|
|
#if ILibSCTP_MaxSenderCredits > 0
|
|
if (o->receiverCredits > ILibSCTP_MaxSenderCredits) o->receiverCredits = ILibSCTP_MaxSenderCrtedits; // Since we do real-time KVM, reduce the buffering.
|
|
#endif
|
|
o->userTSN = o->intsn = ntohl(((unsigned int*)(buffer + ptr + 16))[0]) - 1;
|
|
util_random(4, (char*)&(o->outtsn));
|
|
o->RREQSEQ = o->outtsn;
|
|
o->RRESSEQ = o->intsn;
|
|
o->inport = ntohs(((unsigned short*)buffer)[1]);
|
|
o->outport = ntohs(((unsigned short*)buffer)[0]);
|
|
o->maxOutStreams = MIN(ntohs(((unsigned short*)(buffer + ptr + 12))[0]), ILibSCTP_Stream_MaximumCount);
|
|
o->maxInStreams = MIN(ntohs(((unsigned short*)(buffer + ptr + 12))[1]), ILibSCTP_Stream_MaximumCount);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [INIT]", session);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TSN/IN = %u", o->intsn);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TSN/OUT = %u", o->outtsn);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...SenderCredits = %u", o->senderCredits);
|
|
|
|
// Optional/Variable Fields
|
|
{
|
|
int varLen = 0;
|
|
unsigned short tLen = 0;
|
|
int chunkIndex;
|
|
|
|
while(chunksize - varLen > 20)
|
|
{
|
|
SCTP_INIT_PARAMS optionalParam = (SCTP_INIT_PARAMS)ntohs(((unsigned short*)(buffer+ptr+20 + varLen))[0]);
|
|
tLen = ntohs(((unsigned short*)(buffer+ptr+20+varLen))[1]);
|
|
switch(optionalParam)
|
|
{
|
|
case SCTP_INIT_PARAM_UNRELIABLE_STREAM:
|
|
ILibSparseArray_Add(o->PeerFeatureSet, SCTP_INIT_PARAM_UNRELIABLE_STREAM, (void*)0x01);
|
|
break;
|
|
case SCTP_INIT_PARAM_SUPPORTED_EXTENSIONS:
|
|
for(chunkIndex = 0 ; chunkIndex < tLen - 4; ++chunkIndex)
|
|
{
|
|
ILibSparseArray_Add(o->PeerFeatureSet, (int)((unsigned char*)(buffer + ptr + 20 + varLen + 4))[chunkIndex], (void*)0x01);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Supported Extension: %d", (int)((unsigned char*)(buffer + ptr + 20 + varLen + 4))[chunkIndex]);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
varLen += FOURBYTEBOUNDARY(tLen); // Add parameter size and padding
|
|
}
|
|
}
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Debug Events
|
|
if (o->onReceiverCredits != NULL) { o->onReceiverCredits(o, "OnReceiverCredits", o->receiverCredits); }
|
|
#endif
|
|
|
|
RCTPDEBUG(printf("RCTP_CHUNK_TYPE_INIT, Flags=%d, outTSN=%u, inTSN=%u\r\n", chunkflags, obj->dTlsSessions[session]->outtsn, obj->dTlsSessions[session]->intsn);)
|
|
|
|
// Create response
|
|
{
|
|
long long uptime = ILibGetUptime();
|
|
char chunks[1] = {130};
|
|
ILibStun_AddSctpChunkHeader(rpacket, *rptr, RCTP_CHUNK_TYPE_INITACK, 0, 37);
|
|
*rptr += 4;
|
|
((ILibSCTP_InitAckChunk*)(rpacket + *rptr))->InitiateTag = o->tag; // Initiate Tag
|
|
((ILibSCTP_InitAckChunk*)(rpacket + *rptr))->A_RWND = htonl(ILibSCTP_MaxReceiverCredits); // Advertised Receiver Window Credit (a_rwnd)
|
|
((ILibSCTP_InitAckChunk*)(rpacket + *rptr))->NumberOfOutboundStreams = htons(o->maxOutStreams); // Number of Outbound Streams
|
|
((ILibSCTP_InitAckChunk*)(rpacket + *rptr))->NumberOfInboundStreams = htons(o->maxInStreams); // Number of Inbound Streams
|
|
((ILibSCTP_InitAckChunk*)(rpacket + *rptr))->InitialTSN = htonl(o->outtsn); // Initial TSN
|
|
*rptr += sizeof(ILibSCTP_InitAckChunk);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Setting Cookie: %s", ILibRemoteLogging_ConvertToHex((char*)&uptime, sizeof(uptime)));
|
|
*rptr += ILibSCTP_AddOptionalVariableParameter(rpacket + *rptr, htons(SCTP_INIT_PARAM_STATE_COOKIE), (void*)&uptime, sizeof(uptime)); // Stick uptime as cookie, so we can calculate initial RTT
|
|
*rptr += ILibSCTP_AddOptionalVariableParameter(rpacket + *rptr, htons(SCTP_INIT_PARAM_SUPPORTED_EXTENSIONS), chunks, 1); // Supports RE-CONFIG
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_SACK:
|
|
{
|
|
int FastRetryDisabled = 0;
|
|
int GapAckPtr = 0, DuplicatePtr = 0, oldHoldCount;
|
|
unsigned int gstart, gend = 0;
|
|
//char* packet = NULL;
|
|
ILibSCTP_RPACKET* rpacket = NULL;
|
|
unsigned int tsn = ntohl(((unsigned int*)(buffer + ptr + 4))[0]);
|
|
#ifdef _REMOTELOGGING
|
|
unsigned int arwnd = ntohl(((unsigned int*)(buffer + ptr + 8))[0]);
|
|
#endif
|
|
unsigned short GapAckCount = ntohs(((unsigned short*)(buffer + ptr + 12))[0]);
|
|
unsigned short DuplicateCount = ntohs(((unsigned short*)(buffer + ptr + 12))[1]);
|
|
unsigned int tsnx = 0;
|
|
unsigned int lastTSNX = 0;
|
|
int windowReset = 0;
|
|
int cumulativeTSNAdvanced = 0;
|
|
int pbc = o->pendingByteCount;
|
|
|
|
o->zeroWindowProbeTime = 0;
|
|
o->lastSackTime = (unsigned int)ILibGetUptime();
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP [SACK] Received");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "... TSN = %u", tsn);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "... GAP Count = %u", GapAckCount);
|
|
if (GapAckCount > 0)
|
|
{
|
|
int y;
|
|
for (y = 0; y < GapAckCount; ++y)
|
|
{
|
|
gstart = tsn + ntohs(((unsigned short*)(buffer + ptr + 16 + (y * 4)))[0]); // Start of GAP Block
|
|
gend = tsn + ntohs(((unsigned short*)(buffer + ptr + 16 + (y * 4)))[1]); // End of GAP Block
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... GAP [%u] - [%u]", gstart, gend);
|
|
}
|
|
}
|
|
|
|
|
|
if (o->FastRetransmitExitPoint != 0 && tsn >= o->FastRetransmitExitPoint)
|
|
{
|
|
// We have exited Fast Recovery Mode
|
|
o->FastRetransmitExitPoint = 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d] has exited Fast-Recovery Mode (Sender Credits: %d / Receiver Credits: %d)", o->sessionId, o->senderCredits, o->receiverCredits);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onFastRecovery != NULL){ o->onFastRecovery(o, "OnFastRecovery", 0); }
|
|
#endif
|
|
}
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onSACKreceived != NULL) { o->onSACKreceived(o, "OnSACKReceived", tsn); }
|
|
#endif
|
|
|
|
while (DuplicatePtr < DuplicateCount) { tsnx = ntohl(((unsigned int*)(buffer + ptr + 16 + (GapAckCount * 4) + (DuplicatePtr * 4)))[0]); ++DuplicatePtr; }
|
|
|
|
// Clear all packets that are fully acknowledged
|
|
if (o->pendingQueueHead != NULL) { tsnx = ntohl(((ILibSCTP_DataPayload*)((ILibSCTP_RPACKET*)o->pendingQueueHead)->Data)->TSN); }
|
|
while (o->pendingQueueHead != NULL && tsnx <= tsn)
|
|
{
|
|
// If we haven't made an RTT calculation, let's try to
|
|
if (((ILibSCTP_RPACKET*)o->pendingQueueHead)->PacketResendCounter == 0 && rttCalculated == 0)
|
|
{
|
|
// But only if no packets were re-transmitted since the last time we sent a packet
|
|
if (o->lastRetransmitTime < ((ILibSCTP_RPACKET*)o->pendingQueueHead)->LastSentTimeStamp)
|
|
{
|
|
// This packet that was ACK'ed and was NOT retransmitted, and NO OTHER packets
|
|
// were retransmitted since this packet was originally sent
|
|
int r = (int)(o->lastSackTime - ((ILibSCTP_RPACKET*)o->pendingQueueHead)->LastSentTimeStamp);
|
|
if (r >= 0)
|
|
{
|
|
o->RTTVAR = (int)((double)(1 - RTO_BETA) * (double)o->RTTVAR + RTO_BETA * (double)abs(o->SRTT - r));
|
|
o->SRTT = (int)((double)(1 - RTO_ALPHA) * (double)o->SRTT + RTO_ALPHA * (double)r);
|
|
o->RTO = o->SRTT + 4 * o->RTTVAR;
|
|
if (o->RTO < RTO_MIN) { o->RTO = RTO_MIN; }
|
|
if (o->RTO > RTO_MAX) { o->RTO = RTO_MAX; }
|
|
rttCalculated = 1; // We only need to calculate this once for each packet received
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onRTTCalculated != NULL) { o->onRTTCalculated(o, "OnRTTCalculated", o->SRTT); }
|
|
if (o->onRTTCalculated != NULL) { o->onRTTCalculated(o, "OnLastSackTime", o->lastSackTime); }
|
|
if (o->onRTTCalculated != NULL) { o->onRTTCalculated(o, "OnLastSentTime", ((ILibSCTP_RPACKET*)o->pendingQueueHead)->LastSentTimeStamp); }
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
cumulativeTSNAdvanced += (((ILibSCTP_RPACKET*)o->pendingQueueHead)->PacketSize - (12 + 16));
|
|
o->pendingByteCount -= (((ILibSCTP_RPACKET*)o->pendingQueueHead)->PacketSize - (12 + 16));
|
|
o->pendingCount--;
|
|
rpacket = (ILibSCTP_RPACKET*)o->pendingQueueHead;
|
|
o->pendingQueueHead = (char*)((ILibSCTP_RPACKET*)o->pendingQueueHead)->NextPacket;
|
|
if (o->pendingQueueHead == NULL) { o->pendingQueueTail = NULL; }
|
|
else { tsnx = ntohl(((ILibSCTP_DataPayload*)((ILibSCTP_RPACKET*)o->pendingQueueHead)->Data)->TSN); }
|
|
|
|
free(rpacket);
|
|
o->timervalue = 0; // This is a valid SACK, reset the timeout for packet resent
|
|
}
|
|
|
|
if (cumulativeTSNAdvanced == 0)
|
|
{
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onTSNFloorNotRaised != NULL) { o->onTSNFloorNotRaised(o, "OnTSNFloorNotRaised", o->pendingQueueHead != NULL ? (((unsigned char*)(o->pendingQueueHead + sizeof(char*) + 2))[0]) : -1); }
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
o->senderCredits += cumulativeTSNAdvanced; // These bytes are no longer in-flight
|
|
o->receiverCredits += cumulativeTSNAdvanced;
|
|
|
|
if (o->senderCredits > o->congestionWindowSize) { o->senderCredits = o->congestionWindowSize; }
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onReceiverCredits != NULL) { o->onReceiverCredits(o, "OnReceiverCredits", o->receiverCredits); }
|
|
#endif
|
|
}
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...A_RWND: %u", arwnd);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Sender Credits: %u", o->senderCredits);
|
|
|
|
if (o->pendingQueueHead == NULL)
|
|
{
|
|
o->senderCredits = o->congestionWindowSize;
|
|
o->PARTIAL_BYTES_ACKED = 0;
|
|
if(o->T3RTXTIME!=0) {ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: T3TX Timer OFF", o->sessionId);}
|
|
o->T3RTXTIME = 0;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL){ o->onT3RTX(o, "OnT3RTX", 0); } // All data has been ack'ed, so we can turn off the T3RTX timer
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
o->PARTIAL_BYTES_ACKED += cumulativeTSNAdvanced;
|
|
if (cumulativeTSNAdvanced > 0)
|
|
{
|
|
o->T3RTXTIME = o->lastSackTime;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: T3TX Timer Restarted", o->sessionId);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL){ o->onT3RTX(o, "OnT3RTX", o->RTO); } // The lowest TSN has been ACK'ed, and there is still data pending, so restart the timer
|
|
#endif
|
|
}
|
|
}
|
|
if (o->congestionWindowSize <= o->SSTHRESH && o->FastRetransmitExitPoint == 0 && cumulativeTSNAdvanced != 0 && o->congestionWindowSize < ILibSCTP_MaxReceiverCredits)
|
|
{
|
|
// When our window is smaller than the Slow Start Threshold, we can only grow our Window by the MIN of the total bytes ACK'ed, or one MTU
|
|
o->congestionWindowSize += MIN(cumulativeTSNAdvanced, ILibRUDP_StartMTU);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onCongestionWindowSizeChanged != NULL) { o->onCongestionWindowSizeChanged(o, "OnCongestionWindowSizeChanged", o->congestionWindowSize); }
|
|
#endif
|
|
}
|
|
else if (o->congestionWindowSize > o->SSTHRESH && (int)(o->PARTIAL_BYTES_ACKED) >= o->congestionWindowSize && pbc >= o->congestionWindowSize)
|
|
{
|
|
// When our Congestion Window is greater than the Slow Start Threshold, we only grow our Window if we are fully utilizing our congestion window
|
|
o->congestionWindowSize += ILibRUDP_StartMTU;
|
|
o->PARTIAL_BYTES_ACKED = o->PARTIAL_BYTES_ACKED - o->congestionWindowSize;
|
|
}
|
|
|
|
//printf("ARK-STR %d GAPS, %d SENDS\r\n", GapAckCount, SendCount);
|
|
|
|
// If there are any GAPS in the ACK, add to the FAST ACK counter.
|
|
rpacket = (ILibSCTP_RPACKET*)o->pendingQueueHead;
|
|
while (rpacket != NULL && GapAckPtr < GapAckCount)
|
|
{
|
|
gstart = tsn + ntohs(((unsigned short*)(buffer + ptr + 16 + (GapAckPtr * 4)))[0]); // Start of GAP Block
|
|
gend = tsn + ntohs(((unsigned short*)(buffer + ptr + 18 + (GapAckPtr * 4)))[0]); // End of GAP Block
|
|
//ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... GAP [%u] - [%u]", gstart, gend);
|
|
tsnx = ntohl(((ILibSCTP_DataPayload*)rpacket->Data)->TSN); // TSN of Pending Packet
|
|
while (rpacket != NULL && tsnx <= gend)
|
|
{
|
|
// Packet TSN could potentially be within the GAP Block
|
|
unsigned char frt = rpacket->PacketGAPCounter; // Number of times the packet was indicated to be in a gap
|
|
if (tsnx < gstart)
|
|
{
|
|
// This packet was not received by the peer
|
|
if (frt < 0xFD)
|
|
{
|
|
++frt; // Increment the GAP Counter if this packet isn't already marked for re-transmit
|
|
if (frt >= 0xFD) { frt = 0xFC; } // Cap the counter, because 0xFD, 0xFE, and 0xFF have special meaning, and won't retransmit
|
|
}
|
|
else if (frt == 0xFE)
|
|
{
|
|
// This packet was forward ACKed before, but now is not... Peer must have dropped it out of it's buffer...
|
|
frt = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This packet was received by the peer
|
|
if (frt < 0xFD)
|
|
{
|
|
// This is the first time this packet was GAP ACK'ed
|
|
frt = 0xFE; // This packet was forward ACKed by the peer. Ban this packet from being retransmitted for now
|
|
o->senderCredits += (rpacket->PacketSize - (12 + 16));
|
|
o->PARTIAL_BYTES_ACKED += (rpacket->PacketSize - (12 + 16));
|
|
}
|
|
}
|
|
|
|
if (frt >= ILibSCTP_FastRetry_GAP && frt < 0xFD)
|
|
{
|
|
// Perform fast retry. This only happens if this packet was in the ACK gap 'ILibSCTP_FastRetry_GAP' times.
|
|
if (windowReset == 0 && o->FastRetransmitExitPoint == 0)
|
|
{
|
|
// This is the first time entering Fast Recovery Mode, so we need to shrink our congestion window
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onFastRecovery != NULL){ o->onFastRecovery(o, "OnFastRecovery", 1); }
|
|
#endif
|
|
o->SSTHRESH = MAX((o->congestionWindowSize / 2), (4 * ILibRUDP_StartMTU));;
|
|
o->congestionWindowSize = o->SSTHRESH;
|
|
o->senderCredits = MIN(o->senderCredits, o->congestionWindowSize);
|
|
o->PARTIAL_BYTES_ACKED = 0;
|
|
windowReset = 1;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: Entering Fast-Retry Mode (Sender Credits: %d)", o->sessionId, o->senderCredits);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onCongestionWindowSizeChanged != NULL) { o->onCongestionWindowSizeChanged(o, "OnCongestionWindowSizeChanged", o->congestionWindowSize); }
|
|
#endif
|
|
}
|
|
|
|
|
|
frt = 0xFF; // Mark these packets for retransmit
|
|
lastTSNX = tsnx;
|
|
|
|
if (FastRetryDisabled == 0)
|
|
{
|
|
if ((char*)rpacket == o->pendingQueueHead)
|
|
{
|
|
// We are re-transmitting the lowest outstanding TSN, restart the T3-RTX timer
|
|
o->T3RTXTIME = o->lastSackTime;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: Restarting T3RTX Timer (Retransmitting lowest TSN)", o->sessionId);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL){ o->onT3RTX(o, "OnT3RTX", o->RTO); }
|
|
#endif
|
|
}
|
|
else if (o->T3RTXTIME == 0)
|
|
{
|
|
// Since we are not re-transmitting the lowest outstanding TSN, only start the T3-RTX timer, if it's not running
|
|
o->T3RTXTIME = o->lastSackTime;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: Restarting T3RTX Timer (Not retransmitting lowest TSN)", o->sessionId);
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL){ o->onT3RTX(o, "OnT3RTX", o->RTO); }
|
|
#endif
|
|
}
|
|
|
|
// Send the first FastRetransmit candidate right now, ignoring sender credits
|
|
o->lastRetransmitTime = o->lastSackTime; // Every time we retransmit, we need to take note of it, for RTT purposes
|
|
rpacket->LastSentTimeStamp = o->lastSackTime; // Update Send Time, used for retry
|
|
ILibStun_SendSctpPacket(obj, session, rpacket->Data - 12, rpacket->PacketSize);
|
|
rpacket->PacketResendCounter++; // Add to the packet resent counter
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...TSN=%u", ntohl(((ILibSCTP_DataPayload*)(rpacket->Data))->TSN));
|
|
//printf("RESEND COUNT %d, TSN=%u\r\n", ((unsigned char*)(packet + sizeof(char*) + 2))[0], tsnx);
|
|
|
|
FastRetryDisabled = 1;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onSendFastRetry != NULL) { o->onSendFastRetry(o, "OnSendFastRetry", ((unsigned short*)(rpacket->Data + sizeof(char*)))[0]); }
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// We already sent the first retransmit,
|
|
// But we can at least try to retransmit the rest when sender credits permit
|
|
if (o->senderCredits >= rpacket->PacketSize)
|
|
{
|
|
// We have enough sender credits to retransmit these
|
|
rpacket->LastSentTimeStamp = o->lastSackTime; // Update Send Time, used for retry
|
|
if (o->T3RTXTIME == 0)
|
|
{
|
|
o->T3RTXTIME = o->lastSackTime;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL) { o->onT3RTX(o, "OnT3RTX", o->RTO); }
|
|
#endif
|
|
}
|
|
rpacket->PacketResendCounter++; // Add to the packet resent counter
|
|
o->senderCredits -= (rpacket->PacketSize - (12 + 16)); // Update sender credits
|
|
o->lastRetransmitTime = o->lastSackTime; // Every time we retransmit, we need to take note of it, for RTT purposes
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: Retransmitting/T3RTX Expired", o->sessionId);
|
|
ILibStun_SendSctpPacket(obj, session, rpacket->Data - 12, rpacket->PacketSize);
|
|
#ifdef _WEBRTCDEBUG
|
|
//if (o->onSendRetry != NULL) { o->onSendRetry(obj, "OnSendRetry", ((unsigned short*)(rpacket->Data + sizeof(char*)))[0]); }
|
|
if (o->onSendRetry != NULL) { o->onSendRetry(o, "OnSendRetry", ntohl(((unsigned int*)rpacket->Data)[1])); }
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// No Sender Credits available, so we're just going to mark these packets for retransmit later (frt = 0xFF)
|
|
// (This block left blank on purpose, so we can include these comments)
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP[%d]: No sender credits available for retransmit", o->sessionId);
|
|
}
|
|
}
|
|
}
|
|
else if (frt == 0xFF && o->senderCredits >= rpacket->PacketSize)
|
|
{
|
|
// We have sufficient sender credits to send packets marked for retransmit
|
|
rpacket->LastSentTimeStamp = o->lastSackTime; // Update Send Time, used for retry
|
|
rpacket->PacketResendCounter++; // Add to the packet resent counter
|
|
o->senderCredits -= rpacket->PacketSize; // Update sender credits
|
|
o->lastRetransmitTime = o->lastSackTime; // Every time we retransmit, we need to take note of it, for RTT purposes
|
|
|
|
ILibStun_SendSctpPacket(obj, session, rpacket->Data - 12, rpacket->PacketSize);
|
|
#ifdef _WEBRTCDEBUG
|
|
//if (o->onSendRetry != NULL) { o->onSendRetry(obj, "OnSendRetry", ((unsigned short*)(rpacket->Data + sizeof(char*)))[0]); }
|
|
if (o->onSendRetry != NULL) { o->onSendRetry(o, "OnSendRetry", ntohl(((unsigned int*)rpacket->Data)[1])); }
|
|
#endif
|
|
}
|
|
rpacket->PacketGAPCounter = frt; // Possibly add to the packet gap counter
|
|
rpacket = rpacket->NextPacket;
|
|
if (rpacket != NULL) tsnx = ntohl(((ILibSCTP_DataPayload*)rpacket->Data)->TSN);
|
|
}
|
|
GapAckPtr++;
|
|
}
|
|
|
|
if (lastTSNX != 0 && o->FastRetransmitExitPoint == 0) { o->FastRetransmitExitPoint = lastTSNX; } // Set the Fast Recovery Exit Point
|
|
|
|
// printf("ARK-END %d GAPS, %d SENDS\r\n", GapAckCount, SendCount);
|
|
|
|
// RCTPDEBUG(printf("RCTP_CHUNK_TYPE_SACK, Size=%d, TSN=%u, RC1=%u, PQ=%d, HQ=%d\r\n", chunksize, tsn, obj->dTlsSessions[session]->receiverCredits, obj->dTlsSessions[session]->pendingCount, obj->dTlsSessions[session]->holdingCount);)
|
|
|
|
// Send any packets in the holding queue that we can, we do this because we may now have more credits
|
|
oldHoldCount = o->holdingCount;
|
|
while (o->receiverCredits > 0 && o->holdingQueueHead != NULL)
|
|
{
|
|
rpacket = (ILibSCTP_RPACKET*)o->holdingQueueHead;
|
|
|
|
// Check if we have sufficient credits to send the next packet
|
|
if (o->receiverCredits < (rpacket->PacketSize - (12 + 16))) break;
|
|
if (o->senderCredits < (rpacket->PacketSize - (12 + 16))) break;
|
|
|
|
// Remove the packet from the holding queue
|
|
o->holdingQueueHead = (char*)rpacket->NextPacket; // Move to the next packet
|
|
if (o->holdingQueueHead == NULL) o->holdingQueueTail = NULL;
|
|
o->holdingCount--;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Debug Events
|
|
if (o->onHold != NULL) { o->onHold(o, "OnHold", o->holdingCount); }
|
|
#endif
|
|
|
|
// Add the packet to the end of the pending queue
|
|
if (o->pendingQueueTail == NULL) { o->pendingQueueHead = (char*)rpacket; }
|
|
else { ((char**)(o->pendingQueueTail))[0] = (char*)rpacket; }
|
|
rpacket->NextPacket = NULL;
|
|
o->pendingQueueTail = (char*)rpacket;
|
|
o->pendingCount++;
|
|
o->pendingByteCount += (rpacket->PacketSize - (12 + 16));
|
|
|
|
// Remove the credits
|
|
o->senderCredits -= (rpacket->PacketSize - (12 + 16));
|
|
o->receiverCredits -= (rpacket->PacketSize - (12 + 16));
|
|
o->holdingByteCount -= (rpacket->PacketSize - (12 + 16));
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Debug Events
|
|
if (o->onReceiverCredits != NULL) { o->onReceiverCredits(o, "OnReceiverCredits", o->receiverCredits); }
|
|
#endif
|
|
|
|
if (o->T3RTXTIME == 0)
|
|
{
|
|
o->T3RTXTIME = o->lastSackTime;
|
|
#ifdef _WEBRTCDEBUG
|
|
if (o->onT3RTX != NULL){ o->onT3RTX(o, "OnT3RTX", o->RTO); } // Restart the timer, because the timer isn't currently set
|
|
#endif
|
|
}
|
|
|
|
// Update the packet retry data
|
|
rpacket->LastSentTimeStamp = o->lastSackTime; // Last time the packet was sent (Used for retry)
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "Sending %u/%u bytes from Holding Queue", (rpacket->PacketSize - (12 + 16)), o->holdingByteCount);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...TSN=%u", ntohl(((ILibSCTP_DataPayload*)rpacket->Data)->TSN));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...LEN=%u", ntohs(((ILibSCTP_DataPayload*)rpacket->Data)->length));
|
|
|
|
// Send the packet
|
|
ILibStun_SendSctpPacket(obj, session, rpacket->Data - 12, rpacket->PacketSize); // Send the packet
|
|
}
|
|
|
|
// If we can now send more packets, notify the application
|
|
if (obj->OnSendOK != NULL && o->holdingCount == 0 && oldHoldCount > 0)
|
|
{
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
// printf("SendOK\r\n");
|
|
obj->OnSendOK(obj, o, o->User);
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state != 2) return;
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_HEARTBEAT:
|
|
// Echo back the heartbeat
|
|
RCTPDEBUG(printf("RCTP_CHUNK_TYPE_HEARTBEAT, Size=%d\r\n", chunksize);)
|
|
o->timervalue = 0;
|
|
ILibStun_AddSctpChunkHeader(rpacket, *rptr, RCTP_CHUNK_TYPE_HEARTBEATACK, 0, chunksize);
|
|
memcpy_s(rpacket + *rptr + 4, 4092 - *rptr, buffer + ptr + 4, chunksize - 4);
|
|
*rptr += FOURBYTEBOUNDARY(chunksize);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d Received [HEARTBEAT/%u]", session, ntohs(chunkHdr->chunkLength));
|
|
break;
|
|
case RCTP_CHUNK_TYPE_HEARTBEATACK:
|
|
RCTPDEBUG(printf("RCTP_CHUNK_TYPE_HEARTBEATACK, Size=%d\r\n", chunksize);)
|
|
o->timervalue = 0;
|
|
break;
|
|
case RCTP_CHUNK_TYPE_ABORT:
|
|
{
|
|
unsigned short len = ntohs(chunkHdr->chunkLength) - 4;
|
|
unsigned short start = 0;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
|
|
while(start < len)
|
|
{
|
|
ILibSCTP_ErrorCause_Header *cause = (ILibSCTP_ErrorCause_Header*)(chunkHdr->chunkData + start);
|
|
unsigned short causeLen = FOURBYTEBOUNDARY(ntohs(cause->CauseLength));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d Received [ABORT:%u/%s]", session, ntohs(cause->CauseCode), SCTP_ERROR_CAUSE_TO_STRING(cause));
|
|
if(ntohs(cause->CauseCode)==SCTP_ERROR_CAUSE_CODE_PROTOCOL_VIOLATION)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...%s", cause->CauseInformation);
|
|
}
|
|
start += causeLen;
|
|
}
|
|
ILibStun_SctpDisconnect(obj, session);
|
|
return;
|
|
}
|
|
case RCTP_CHUNK_TYPE_SHUTDOWN:
|
|
case RCTP_CHUNK_TYPE_SHUTDOWNACK:
|
|
case RCTP_CHUNK_TYPE_ERROR:
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [SHUTDOWN/ERROR] (%u)", session, chunktype);
|
|
ILibStun_SctpDisconnect(obj, session);
|
|
return;
|
|
case RCTP_CHUNK_TYPE_COOKIEECHO:
|
|
o->SRTT = (int)(ILibGetUptime() - *((long long*)(buffer + ptr + 4)));
|
|
o->RTTVAR = o->SRTT / 2;
|
|
o->RTO = o->SRTT + 4 * o->RTTVAR;
|
|
o->SSTHRESH = 4 * ILibRUDP_StartMTU;
|
|
if (o->RTO < RTO_MIN) { o->RTO = RTO_MIN; }
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [COOKIE-ECHO]", session);
|
|
|
|
*rptr = ILibStun_AddSctpChunkHeader(rpacket, *rptr, RCTP_CHUNK_TYPE_COOKIEACK, 0, 4);
|
|
if (obj->OnConnect != NULL && o->state == 1)
|
|
{
|
|
o->state = 2;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
obj->OnConnect(obj, o, 1);
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state == 0) return;
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_FWDTSN:
|
|
{
|
|
ILibSCTP_FwdTSNPayload *fdata = (ILibSCTP_FwdTSNPayload*)(buffer + ptr);
|
|
int i;
|
|
int len = (htons(fdata->length) - 8) / 4;
|
|
void *holding;
|
|
unsigned int NewTSN = ntohl(fdata->NewTSN);
|
|
ILibSCTP_DataPayload *data;
|
|
ILibSCTP_Accumulator *acc;
|
|
|
|
if (NewTSN <= o->intsn) { break; } // Skip this FWD-TSN because it's stale
|
|
while ((holding = ILibLinkedList_GetNode_Head(o->receiveHoldBuffer)) != NULL && (data = (ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(holding)) != NULL && data->TSN <= NewTSN)
|
|
{
|
|
free(data);
|
|
ILibLinkedList_Remove(holding);
|
|
}
|
|
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
acc = (ILibSCTP_Accumulator*)ILibSparseArray_Get(o->DataAccumulator, ntohl(fdata->SkippedStreams[i].StreamNumber));
|
|
if (acc != NULL) { acc->bufferPtr = -1; } // If an accumulator was created, reset the bufferPtr to abort the Re-Assembly
|
|
}
|
|
|
|
o->intsn = NewTSN;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d received [FWDTSN]", session);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Updated TSN to: %u", NewTSN);
|
|
}
|
|
break;
|
|
case RCTP_CHUNK_TYPE_DATA:
|
|
{
|
|
void* holdingNode;
|
|
unsigned int tsn;
|
|
unsigned short streamId;
|
|
unsigned short streamSeq;
|
|
unsigned int pid;
|
|
ILibSCTP_DataPayload *data;
|
|
int pulled = 0;
|
|
|
|
RCTPDEBUG(printf("RCTP_CHUNK_TYPE_DATA, Flags=%d, Size=%d\r\n", chunkflags, chunksize);)
|
|
data = (ILibSCTP_DataPayload*)(buffer + ptr);
|
|
tsn = ntohl(data->TSN);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP [DATA] Received");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "... TSN = %u", tsn);
|
|
|
|
if ((chunkflags & ILibSCTP_UnorderedFlag) == ILibSCTP_UnorderedFlag && ((chunkflags & 0x03) == 0x03))
|
|
{
|
|
// 'Unordered Delivery' was specified, and we have a full fragment, Propagate Data up the stack immediately
|
|
streamId = ntohs(data->StreamID);
|
|
pid = ntohl(data->ProtocolID);
|
|
|
|
if (tsn == o->intsn + 1)
|
|
{
|
|
ILibStun_SctpProcessStreamData(obj, session, streamId, 0, chunkflags, pid, data->UserData, chunksize - 16);
|
|
}
|
|
else if (tsn > o->intsn + 1 && ILibLinkedList_GetNode_Search(o->receiveHoldBuffer, &ILibSCTP_HoldingQueue_TSNComparer, data) == NULL)
|
|
{
|
|
ILibStun_SctpProcessStreamData(obj, session, streamId, 0, chunkflags, pid, data->UserData, chunksize - 16);
|
|
}
|
|
}
|
|
|
|
if (tsn == o->intsn + 1)
|
|
{
|
|
void* rqptr;
|
|
o->intsn = tsn;
|
|
streamId = ntohs(data->StreamID);
|
|
streamSeq = ntohs(data->StreamSequenceNumber);
|
|
pid = ntohl(data->ProtocolID);
|
|
|
|
RCTPRCVDEBUG(printf("GOT %u, size = %d\r\n", tsn, chunksize);)
|
|
RCTPDEBUG(printf("IN DATA_CHUNK FLAGS: %d, TSN: %u, ID: %d, SEQ: %d, PID: %u, SIZE: %d\r\n", chunkflags, tsn, streamId, streamSeq, pid, chunksize - 16);)
|
|
|
|
if((o->flags & DTLS_PAUSE_FLAG)==DTLS_PAUSE_FLAG || (o->userTSN < o->intsn - 1))
|
|
{
|
|
// We are paused, so we need to buffer this packet for later. (Or, the userTSN isn't caught up yet)
|
|
holdingNode = ILibSCTP_AddPacketToHoldingQueue(o, data, &sentsack);
|
|
if (holdingNode != NULL)
|
|
{
|
|
ILibSCTP_GetHoldingQueueFlags(holdingNode)->DataPropagated = ((chunkflags & ILibSCTP_UnorderedFlag) == ILibSCTP_UnorderedFlag);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d Packet: %u Buffered, due to PAUSE", o->sessionId, ntohl(data->TSN));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... UserTSN = %u", o->userTSN);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... InTSN = %u", o->intsn);
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d Packet: %u DROPPED, because holding Queue is FULL", o->sessionId, ntohl(data->TSN));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... UserTSN = %u", o->userTSN);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "... InTSN = %u", o->intsn);
|
|
}
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d, exiting processing loop due to PAUSE state", o->sessionId);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
o->userTSN = o->intsn;
|
|
}
|
|
|
|
// Move the TSN as forward as we can
|
|
rqptr = ILibLinkedList_GetNode_Head(o->receiveHoldBuffer);
|
|
while (rqptr != NULL)
|
|
{
|
|
unsigned int tsnx = (ILibLinkedList_GetDataFromNode(rqptr) == NULL ? 0 : ntohl(((ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(rqptr))->TSN));
|
|
if (tsnx != o->intsn + 1) break;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "TSN Moved Forward to: %u", tsnx);
|
|
o->intsn = tsnx;
|
|
RCTPRCVDEBUG(printf("MOVED TSN to %u\r\n", o->intsn);)
|
|
rqptr = ILibLinkedList_GetNextNode(rqptr);
|
|
}
|
|
|
|
|
|
// TODO: Optimize this, send SACK only after a little bit of time (or every 3 packets or so). Or only in some cases.
|
|
if (sentsack == ILibSCTP_SackStatus_NotSent)
|
|
{
|
|
sentsack = ILibSCTP_SackStatus_Sent;
|
|
*rptr = ILibStun_SctpAddSackChunk(obj, session, rpacket, *rptr); // Send the ACK with the TSN as far forward as we can
|
|
}
|
|
|
|
if ((chunkflags & ILibSCTP_UnorderedFlag) != ILibSCTP_UnorderedFlag || ((chunkflags & 0x03) != 0x03))
|
|
{
|
|
// Only continue processing here, if 'Unordered Delivery' was not specified, or we have a partial fragment
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
ILibStun_SctpProcessStreamData(obj, session, streamId, streamSeq, chunkflags, pid, data->UserData, chunksize - 16);
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state == 0) return;
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
|
|
// Pull as many packets as we can out of the receive hold queue
|
|
pulled = 0;
|
|
while (ILibLinkedList_GetCount(o->receiveHoldBuffer) > 0)
|
|
{
|
|
ILibSCTP_DataPayload *payload = (ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(ILibLinkedList_GetNode_Head(o->receiveHoldBuffer));
|
|
unsigned char chunkflagsx = payload->flags;
|
|
unsigned int tsnx = ntohl(payload->TSN);
|
|
unsigned short chunksizex = ntohs(payload->length);
|
|
if ((tsnx > o->intsn + 1) || (tsnx > o->userTSN + 1)) break; // This is not the next expected packet
|
|
|
|
pulled += payload->length;
|
|
RCTPRCVDEBUG(printf("UNSTORING %u, size = %d\r\n", tsnx, chunksizex);)
|
|
|
|
streamId = ntohs(payload->StreamID);
|
|
streamSeq = ntohs(payload->StreamSequenceNumber);
|
|
pid = ntohl(payload->ProtocolID);
|
|
o->userTSN = o->intsn = tsnx;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d Packet: %u Pulled from Holding Queue", o->sessionId, o->userTSN);
|
|
|
|
if (ILibSCTP_GetHoldingQueueFlags(ILibLinkedList_GetNode_Head(o->receiveHoldBuffer))->DataPropagated == 0)
|
|
{
|
|
// Only propagate this up, if it hasn't been propagated already
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
ILibStun_SctpProcessStreamData(obj, session, streamId, streamSeq, chunkflagsx, pid, payload->UserData, chunksizex - 16);
|
|
if (obj->dTlsSessions[session] == NULL || obj->dTlsSessions[session]->state == 0) return;
|
|
ILibSpinLock_Lock(&(o->Lock));
|
|
}
|
|
|
|
free(payload);
|
|
ILibLinkedList_Remove(ILibLinkedList_GetNode_Head(o->receiveHoldBuffer));
|
|
if((o->flags & DTLS_PAUSE_FLAG)==DTLS_PAUSE_FLAG)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP_2: %d, exiting processing loop due to PAUSE state", o->sessionId);
|
|
break;
|
|
}
|
|
}
|
|
if (pulled > 0) ReceiveHoldBuffer_Decrement(o->receiveHoldBuffer, pulled);
|
|
}
|
|
else if (tsn > o->intsn + 1)
|
|
{
|
|
// This is an out of order packet, so lets buffer it for later
|
|
holdingNode = ILibSCTP_AddPacketToHoldingQueue(o, data, &sentsack);
|
|
if (holdingNode != NULL)
|
|
{
|
|
ILibSCTP_GetHoldingQueueFlags(holdingNode)->DataPropagated = (((chunkflags & ILibSCTP_UnorderedFlag) == ILibSCTP_UnorderedFlag) && ((chunkflags & 0x03) == 0x03));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "SCTP: %d Packet: %u Buffered, out of order", o->sessionId, ntohl(data->TSN));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_3, "...Expected: %u", o->intsn + 1);
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d Packet: %u DROPPED, because holding Queue is FULL", o->sessionId, ntohl(data->TSN));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "... UserTSN = %u", o->userTSN);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "... InTSN = %u", o->intsn);
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Already received packet.
|
|
RCTPRCVDEBUG(printf("TOSSING %u, size = %d\r\n", tsn, chunksize);)
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d Packet: %u dropped, duplicate... Expected: %u", o->sessionId, ntohl(data->TSN), o->intsn + 1);
|
|
|
|
// Send ACK now
|
|
if (sentsack == 0)
|
|
{
|
|
sentsack = 1;
|
|
*rptr = ILibStun_SctpAddSackChunk(obj, session, rpacket, *rptr); // Send the ACK with the TSN as far forward as we can
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (buffer[ptr] & 0x40) { stop = 1; } // Unknown chunk, if this bit is set, stop processing now.
|
|
break;
|
|
}
|
|
|
|
ptr += FOURBYTEBOUNDARY(chunksize); // Add chunk size and padding
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Buffer Index = %d", ptr);
|
|
}
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Stopped Reading SCTP, rptr = %d", *rptr);
|
|
if (session == -1) { ILibSpinLock_UnLock(&(o->Lock)); return; }
|
|
if (*rptr > 12)
|
|
{
|
|
#ifdef _REMOTELOGGING
|
|
ILibSctp_DebugSctpPacket(ILibChainGetLogger(obj->ChainLink.ParentChain), rpacket, *rptr, "RPACKET");
|
|
#endif
|
|
if ((((int*)rpacket)[2] & SCTP_COMMON_HEADER_FLAGS_REORDER) == SCTP_COMMON_HEADER_FLAGS_REORDER)
|
|
{
|
|
// Need to reorder Control/Data Chunks to be spec-compliant
|
|
ILibSCTP_ReorderChunks(obj, session, rpacket, *rptr);
|
|
}
|
|
ILibStun_SendSctpPacket(obj, session, rpacket, *rptr);
|
|
}
|
|
*rptr = 0;
|
|
ILibSpinLock_UnLock(&(o->Lock));
|
|
}
|
|
|
|
//! Pause processing of inbound SCTP data
|
|
/*!
|
|
\param sctpSession Peer Connection Session
|
|
*/
|
|
void ILibSCTP_Pause(void* sctpSession)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)sctpSession;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d was PAUSED by the USER", obj->sessionId);
|
|
|
|
ILibSpinLock_Lock(&(obj->Lock));
|
|
obj->flags |= DTLS_PAUSE_FLAG;
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
}
|
|
//! Resume processing of inbound SCTP data
|
|
/*!
|
|
\param sctpSession Peer Connection Session
|
|
*/
|
|
void ILibSCTP_Resume(void* sctpSession)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)sctpSession;
|
|
struct ILibStun_Module *sobj = obj->parent;
|
|
void *node;
|
|
ILibSCTP_DataPayload *payload;
|
|
int sessionID = obj->sessionId;
|
|
int pulled = 0;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d RESUME operation begin", obj->sessionId);
|
|
|
|
ILibSpinLock_Lock(&(obj->Lock));
|
|
if((obj->flags & DTLS_PAUSE_FLAG) == DTLS_PAUSE_FLAG)
|
|
{
|
|
obj->flags ^= DTLS_PAUSE_FLAG;
|
|
}
|
|
|
|
// Check the receive hold buffer to see if there are any packets we need to propagate
|
|
while((node = ILibLinkedList_GetNode_Head(obj->receiveHoldBuffer)) != NULL && (payload = (ILibSCTP_DataPayload*)ILibLinkedList_GetDataFromNode(node)) != NULL)
|
|
{
|
|
if(ntohl(payload->TSN) == obj->userTSN + 1)
|
|
{
|
|
// This packet is the next expected packet for the user
|
|
obj->userTSN = ntohl(payload->TSN);
|
|
pulled += payload->length;
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
ILibStun_SctpProcessStreamData(obj->parent, obj->sessionId, ntohs(payload->StreamID), ntohs(payload->StreamSequenceNumber), payload->flags, ntohl(payload->ProtocolID), payload->UserData, ntohs(payload->length) - 16);
|
|
if (sobj->dTlsSessions[sessionID] == NULL || sobj->dTlsSessions[sessionID]->state == 0) return; // Referencing Dtls object this way, in case it was closed/freed by the user in the last call
|
|
ILibSpinLock_Lock(&(obj->Lock));
|
|
free(payload);
|
|
ILibLinkedList_Remove(node);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (pulled > 0) ReceiveHoldBuffer_Decrement(obj->receiveHoldBuffer, pulled);
|
|
ILibSpinLock_UnLock(&(obj->Lock));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP: %d RESUME operation complete", sessionID);
|
|
}
|
|
|
|
|
|
void ILibStun_InitiateSCTP(struct ILibStun_Module *obj, int session, unsigned short sourcePort, unsigned short destinationPort)
|
|
{
|
|
char buffer[32 + 12];
|
|
int ptr;
|
|
unsigned int initiateTag;
|
|
char chunks[1] = { 130 };
|
|
|
|
obj->dTlsSessions[session]->inport = sourcePort;
|
|
obj->dTlsSessions[session]->outport = destinationPort;
|
|
obj->dTlsSessions[session]->receiverCredits = ILibSCTP_MaxReceiverCredits;
|
|
ptr = 12;
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
// Debug Events
|
|
if (obj->dTlsSessions[session]->onReceiverCredits != NULL) { obj->dTlsSessions[session]->onReceiverCredits(obj->dTlsSessions[session], "OnReceiverCredits", obj->dTlsSessions[session]->receiverCredits); }
|
|
#endif
|
|
|
|
ptr = ILibStun_AddSctpChunkHeader(buffer, ptr, RCTP_CHUNK_TYPE_INIT, 0, 32);
|
|
util_random(4, buffer + ptr); // Initiate Tag
|
|
initiateTag = ((unsigned int*)(buffer + ptr))[0];
|
|
ptr += 4;
|
|
|
|
((unsigned int*)(buffer + ptr))[0] = htonl(ILibSCTP_MaxReceiverCredits); // Receiver Credits
|
|
ptr += 4;
|
|
|
|
((unsigned short*)(buffer + ptr))[0] = htons(ILibSCTP_Stream_MaximumCount); // Num Out-Streams
|
|
((unsigned short*)(buffer + ptr))[1] = htons(ILibSCTP_Stream_MaximumCount); // Num In-Streams
|
|
ptr += 4;
|
|
|
|
util_random(4, buffer + ptr); // Initial TSN
|
|
obj->dTlsSessions[session]->RREQSEQ = obj->dTlsSessions[session]->outtsn = ntohl(((unsigned int*)(buffer + ptr))[0]);
|
|
ptr += 4;
|
|
|
|
ptr += ILibSCTP_AddOptionalVariableParameter(buffer + ptr, htons(SCTP_INIT_PARAM_UNRELIABLE_STREAM), NULL, 0); // Supports UNRELIABLE
|
|
ptr += ILibSCTP_AddOptionalVariableParameter(buffer + ptr, htons(SCTP_INIT_PARAM_SUPPORTED_EXTENSIONS), chunks, 1); // Supports RE-CONFIG
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Initiating SCTP...");
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TSN/IN = %u", obj->dTlsSessions[session]->outtsn);
|
|
|
|
if (ptr > 44)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...*** MEMORY CORRUPTION *** malloc(%d) => actual(%d)", 44, ptr);
|
|
}
|
|
|
|
ILibStun_SendSctpPacket(obj, session, buffer, ptr);
|
|
|
|
obj->dTlsSessions[session]->tag = initiateTag;
|
|
}
|
|
|
|
//! Add a new Data Channel Stream to a Peer Connection
|
|
/*!
|
|
\param WebRTCModule Peer Connection Session
|
|
\param streamId Stream ID to associate with the new Data Channel
|
|
\param channelName Name to associate with the new Data Channel
|
|
\param channelNameLength size of the buffer holding channelName
|
|
*/
|
|
void ILibWebRTC_OpenDataChannel(void *WebRTCModule, unsigned short streamId, char* channelName, int channelNameLength)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)WebRTCModule;
|
|
ILibSCTP_StreamAttributes attributes;
|
|
char* buffer;
|
|
if ((buffer = (char*)malloc(12 + channelNameLength)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
|
|
buffer[0] = 0x03; // DATA_CHANNEL_OPEN
|
|
buffer[1] = 0x00; // Reliable Channel Type
|
|
((unsigned short*)buffer)[1] = 0x00; // Priority
|
|
((unsigned int*)buffer)[1] = 0x00; // Reliability (Ignored for Reliable Channels)
|
|
|
|
((unsigned short*)buffer)[4] = htons((unsigned short)channelNameLength); // Label Name Length
|
|
((unsigned short*)buffer)[5] = 0x00; // Protocol Length
|
|
|
|
memcpy_s(buffer + 12, channelNameLength, channelName, channelNameLength);
|
|
|
|
attributes.Raw = 0x00;
|
|
attributes.Data.StatusFlags |= ILibSCTP_StreamAttributesData_Assigned_Status_WAITING_FOR_ACK;
|
|
|
|
ILibSparseArray_Add(obj->DataChannelMetaDeta, streamId, attributes.Raw);
|
|
|
|
|
|
ILibSCTP_SendEx(WebRTCModule, streamId, buffer, 12 + channelNameLength, 50);
|
|
free(buffer);
|
|
}
|
|
|
|
void ILibWebRTC_CloseDataChannel_ALL_Ex(ILibSparseArray sender, int index, void *data, void* user)
|
|
{
|
|
UNREFERENCED_PARAMETER(sender);
|
|
UNREFERENCED_PARAMETER(data);
|
|
|
|
((unsigned short*)user)[++((unsigned short*)user)[0]] = (unsigned short)index;
|
|
}
|
|
//! Close all Data Channels associated with a Peer Connection
|
|
/*!
|
|
\param WebRTCModule Peer Connection Session
|
|
*/
|
|
void ILibWebRTC_CloseDataChannel_ALL(void *WebRTCModule)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)WebRTCModule;
|
|
unsigned short ids[1024];
|
|
|
|
//ILibWebRTC_CloseDataChannelEx(obj, NULL, 0); //This is temporarily commented out, as I filed a bug against Chrome and Firefox to properly support this required behavior
|
|
|
|
ILibSparseArray channels = ILibSparseArray_Move(obj->DataChannelMetaDeta);
|
|
ids[0] = 0;
|
|
|
|
ILibSparseArray_DestroyEx(channels, &ILibWebRTC_CloseDataChannel_ALL_Ex, (void*)ids);
|
|
ILibWebRTC_CloseDataChannelEx(obj, ids+1, ids[0]);
|
|
}
|
|
|
|
ILibWebRTC_DataChannel_CloseStatus ILibWebRTC_CloseDataChannelEx2(void *WebRTCModule, unsigned short *streamIds, int streamIdLength)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)WebRTCModule;
|
|
int len = FOURBYTEBOUNDARY(16 + 2*streamIdLength) + FOURBYTEBOUNDARY((8+2*streamIdLength)) + 16;
|
|
int i;
|
|
ILibWebRTC_DataChannel_CloseStatus retVal = ILibWebRTC_DataChannel_CloseStatus_OK;
|
|
|
|
ILibSCTP_ReconfigChunk *chunk;
|
|
ILibSCTP_Reconfig_OutgoingSSNResetRequest *req;
|
|
ILibSCTP_Reconfig_IncomingSSNResetRequest *req2;
|
|
|
|
if(len > (obj->rpacketsize / 2) )
|
|
{
|
|
// We don't want to consume too much of the rpacket buffer
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Cannot process close, rpacket buffer is too small. Dtls Session: %d", obj->sessionId);
|
|
return(ILibWebRTC_DataChannel_CloseStatus_ERROR);
|
|
}
|
|
|
|
if(obj->pendingReconfigPacket == NULL)
|
|
{
|
|
obj->pendingReconfigPacket = obj->rpacket + (obj->rpacketsize - len);
|
|
obj->reconfigFailures = 0;
|
|
|
|
chunk = (ILibSCTP_ReconfigChunk*)(obj->pendingReconfigPacket + 12);
|
|
req = (ILibSCTP_Reconfig_OutgoingSSNResetRequest*)&(chunk->reconfigurationParameter);
|
|
req2 = (ILibSCTP_Reconfig_IncomingSSNResetRequest*)((char*)&chunk->reconfigurationParameter + FOURBYTEBOUNDARY(16 + 2*streamIdLength));
|
|
|
|
if(ILibSCTP_DoesPeerSupportFeature(obj, RCTP_CHUNK_TYPE_RECONFIG)!=0)
|
|
{
|
|
chunk->type = RCTP_CHUNK_TYPE_RECONFIG;
|
|
chunk->chunkFlags = 0;
|
|
chunk->chunkLength = htons((unsigned short)(4 + FOURBYTEBOUNDARY(16 + 2*streamIdLength) + FOURBYTEBOUNDARY(8+2*streamIdLength)));
|
|
|
|
req->parameterType = htons(SCTP_RECONFIG_TYPE_OUTGOING_SSN_RESET_REQUEST);
|
|
req->parameterLength = htons((unsigned short)(16 + 2*streamIdLength));
|
|
req->LastTSN = htonl(obj->outtsn-1);
|
|
|
|
req->RReqSeqNum = htonl(obj->RREQSEQ++);
|
|
req->RResSeqNum = htonl(obj->RRESSEQ++);
|
|
|
|
req2->parameterType = htons(SCTP_RECONFIG_TYPE_INCOMING_SSN_RESET_REQUEST);
|
|
req2->parameterLength = htons((unsigned short)(8 + (2 * streamIdLength))); // One Stream
|
|
req2->RReqSeqNum = htonl(obj->RREQSEQ++);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP/RECONFIG on Session(%d) LastTSN: %u", obj->sessionId, obj->outtsn - 1);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Sending Outgoing Reset Request (Req/Res): %u/%u", (unsigned int)ntohl(req->RReqSeqNum), (unsigned int)ntohl(req->RResSeqNum));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "Sending Incoming Reset Request (Req): %u", (unsigned int)ntohl(req2->RReqSeqNum));
|
|
for (i = 0; i<streamIdLength; ++i)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "...StreamID: %u", streamIds[i]);
|
|
req->Streams[i] = htons(streamIds[i]);
|
|
req2->Streams[i] = htons(streamIds[i]);
|
|
}
|
|
|
|
ILibLifeTime_AddEx(obj->parent->Timer, ILibWebRTC_DTLS_TO_TIMER_OBJECT(obj), RTO_MIN * (0x01 << obj->reconfigFailures), &ILibWebRTC_CloseDataChannel_Timeout, NULL);
|
|
ILibStun_SendSctpPacket(obj->parent, obj->sessionId, obj->pendingReconfigPacket, len);
|
|
}
|
|
else
|
|
{
|
|
// Peer does not support this operation...
|
|
retVal = ILibWebRTC_DataChannel_CloseStatus_NOT_SUPPORTED;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_SCTP, ILibRemoteLogging_Flags_VerbosityLevel_1, "SCTP/RECONFIG on PEER [NOT-SUPPORTED]");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retVal = ILibWebRTC_DataChannel_CloseStatus_ALREADY_PENDING; // There is already a pending Close Operation
|
|
}
|
|
return retVal;
|
|
}
|
|
//! Close one or more Data Channels associated with a Peer Connection
|
|
/*!
|
|
\param WebRTCModule Peer Connection Session
|
|
\param streamIds Array of Stream IDs to close
|
|
\param streamIdLength Size of array
|
|
\return Close Status
|
|
*/
|
|
ILibWebRTC_DataChannel_CloseStatus ILibWebRTC_CloseDataChannelEx(void *WebRTCModule, unsigned short *streamIds, int streamIdLength)
|
|
{
|
|
struct ILibStun_dTlsSession* obj = (struct ILibStun_dTlsSession*)WebRTCModule;
|
|
ILibWebRTC_DataChannel_CloseStatus retVal;
|
|
|
|
ILibSpinLock_Lock(&obj->Lock);
|
|
retVal = ILibWebRTC_CloseDataChannelEx2(WebRTCModule, streamIds, streamIdLength);
|
|
ILibSpinLock_UnLock(&obj->Lock);
|
|
return retVal;
|
|
}
|
|
|
|
//! Close a Data Channel associated with a Peer Connection
|
|
/*!
|
|
\param WebRTCModule Peer Connection Session
|
|
\param streamId The Data Channel ID to close
|
|
\return Close Status
|
|
*/
|
|
ILibWebRTC_DataChannel_CloseStatus ILibWebRTC_CloseDataChannel(void *WebRTCModule, unsigned short streamId)
|
|
{
|
|
return(ILibWebRTC_CloseDataChannelEx(WebRTCModule, &streamId, 1)); // Ignore Klocwork Error, it's not a problem in this case
|
|
//Note: Klocwork spuriously says that array '&chunk->reconfigurationParamter' of size 4, may use index value 20... This is false, as the array is not accessed like that
|
|
}
|
|
|
|
|
|
//! Query if an SCTP session supports an optional feature
|
|
/*!
|
|
\param sctpSession Peer Connection Session
|
|
\param feature Feature ID (eg: 0xC000 = Partial Reliability)
|
|
\return 0 = NO, 1 = YES
|
|
*/
|
|
int ILibSCTP_DoesPeerSupportFeature(void* sctpSession, int feature)
|
|
{
|
|
struct ILibStun_dTlsSession *obj = (struct ILibStun_dTlsSession*)sctpSession;
|
|
return(ILibSparseArray_Get(obj->PeerFeatureSet, feature)==NULL?0:1);
|
|
}
|
|
|
|
ILibTransport_DoneState ILibWebRTC_TransportSend(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done)
|
|
{
|
|
ILibTransport_DoneState retVal = (ILibTransport_DoneState)ILibSCTP_Send(transport, 0, buffer, bufferLength);
|
|
|
|
UNREFERENCED_PARAMETER(done);
|
|
|
|
if (ownership == ILibTransport_MemoryOwnership_CHAIN) { free(buffer); }
|
|
return retVal;
|
|
}
|
|
|
|
void ILibStun_CreateDtlsSession(struct ILibStun_Module *obj, int sessionId, int iceSlot, struct sockaddr_in6* remoteInterface)
|
|
{
|
|
if (obj->dTlsSessions[sessionId] == NULL)
|
|
{
|
|
void *mem;
|
|
|
|
obj->dTlsSessions[sessionId] = ILibMemory_Allocate(sizeof(struct ILibStun_dTlsSession), ILibMemory_Legacy_RawSize(sizeof(struct sockaddr_in6), 4), NULL, &mem);
|
|
obj->dTlsSessions[sessionId]->remoteInterface = (struct sockaddr_in6*)mem;
|
|
ILibMemory_Legacy_GetExtraSizePtrEx(mem, sizeof(struct sockaddr_in6))[0] = 4;
|
|
}
|
|
else
|
|
{
|
|
ILibWebRTC_DestroySparseArrayTables(obj->dTlsSessions[sessionId]);
|
|
|
|
memset(obj->dTlsSessions[sessionId], 0, sizeof(struct ILibStun_dTlsSession));
|
|
obj->dTlsSessions[sessionId]->remoteInterface = (struct sockaddr_in6*)ILibMemory_GetExtraMemory(obj->dTlsSessions[sessionId], sizeof(struct ILibStun_dTlsSession));
|
|
memset(obj->dTlsSessions[sessionId]->remoteInterface, 0, ILibMemory_GetExtraMemorySize(obj->dTlsSessions[sessionId]->remoteInterface));
|
|
}
|
|
obj->IceStates[iceSlot]->dtlsSession = sessionId;
|
|
obj->dtlsSessionNextSlot = sessionId + 1;
|
|
obj->dTlsSessions[sessionId]->Transport.IdentifierFlags = (unsigned short)ILibTransports_Raw_WebRTC;
|
|
obj->dTlsSessions[sessionId]->Transport.ChainLink.ParentChain = obj->ChainLink.ParentChain;
|
|
obj->dTlsSessions[sessionId]->Transport.SendPtr = &ILibWebRTC_TransportSend;
|
|
obj->dTlsSessions[sessionId]->Transport.ClosePtr = &ILibSCTP_Close;
|
|
//obj->dTlsSessions[sessionId]->closePtr = &ILibWebRTC_CloseDataChannel_ALL;
|
|
obj->dTlsSessions[sessionId]->Transport.PendingBytesPtr = (ILibTransport_PendingBytesToSendPtr)&ILibSCTP_GetPendingBytesToSend;
|
|
|
|
obj->dTlsSessions[sessionId]->iceStateSlot = iceSlot;
|
|
obj->dTlsSessions[sessionId]->state = 4; // Bryan: Changed this to 4 from 1, because we need to call SSL_do_handshake to determine when DTLS was successful
|
|
obj->dTlsSessions[sessionId]->sessionId = sessionId;
|
|
ILibSpinLock_Init(&(obj->dTlsSessions[sessionId]->Lock));
|
|
obj->dTlsSessions[sessionId]->parent = obj;
|
|
memcpy_s(obj->dTlsSessions[sessionId]->remoteInterface, sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family));
|
|
obj->dTlsSessions[sessionId]->senderCredits = 4 * ILibRUDP_StartMTU;
|
|
obj->dTlsSessions[sessionId]->congestionWindowSize = 4 * ILibRUDP_StartMTU;
|
|
obj->dTlsSessions[sessionId]->ssl = SSL_new(obj->SecurityContext);
|
|
|
|
SSL_set_ex_data(obj->dTlsSessions[sessionId]->ssl, ILibStunClientIndex, obj);
|
|
|
|
if ((obj->dTlsSessions[sessionId]->rpacket = (char*)malloc(4096)) == NULL) ILIBCRITICALEXIT(254);
|
|
obj->dTlsSessions[sessionId]->rpacketsize = 4096;
|
|
|
|
ILibWebRTC_CreateSparseArrayTables(obj->dTlsSessions[sessionId]);
|
|
obj->dTlsSessions[sessionId]->receiveHoldBuffer = ILibLinkedList_CreateEx(sizeof(int));
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "New DTLS Session: %d linked to IceStateSlot: %d using %s:%u", sessionId, iceSlot, ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), htons(remoteInterface->sin6_port));
|
|
}
|
|
|
|
void ILibStun_InitiateDTLS(struct ILibStun_IceState *IceState, int IceSlot, struct sockaddr_in6* remoteInterface)
|
|
{
|
|
long l;
|
|
BIO* read;
|
|
BIO* write;
|
|
int i, j = -1;
|
|
struct ILibStun_Module *obj = IceState->parentStunModule;
|
|
char tbuffer[4096];
|
|
|
|
// Find a free dTLS session slot
|
|
for (i = 0; i < ILibSTUN_MaxSlots; i++) { if (obj->dTlsSessions[i] == NULL || obj->dTlsSessions[i]->state == 0) { j = i; break; } }
|
|
if (j == -1) return; // No free slots
|
|
|
|
IceState->dtlsSession = j; // Set the DTLS Session ID in the IceState object this is associated with
|
|
|
|
// Start a new dTLS session
|
|
ILibStun_CreateDtlsSession(obj, j, IceSlot, remoteInterface);
|
|
|
|
// Set up the memory-buffer BIOs
|
|
read = BIO_new(BIO_s_mem());
|
|
obj->dTlsSessions[j]->writeBIO = write = BIO_new(BIO_s_mem());
|
|
BIO_get_mem_ptr(obj->dTlsSessions[j]->writeBIO, &(obj->dTlsSessions[j]->writeBIOBuffer));
|
|
BIO_set_mem_eof_return(read, -1);
|
|
BIO_set_mem_eof_return(write, -1);
|
|
|
|
// Bind everything
|
|
SSL_set_bio(obj->dTlsSessions[j]->ssl, read, write); // MS Static Code Analysis erroneously reports this could be NULL, becuase it was too dumb to see it was initialized in ILibStun_CreateDtlsSession()
|
|
SSL_set_connect_state(obj->dTlsSessions[j]->ssl);
|
|
l = SSL_do_handshake(obj->dTlsSessions[j]->ssl);
|
|
if (l <= 0) { l = SSL_get_error(obj->dTlsSessions[j]->ssl, (int)l); }
|
|
|
|
if (l == SSL_ERROR_WANT_READ)
|
|
{
|
|
while (BIO_ctrl_pending(write) > 0)
|
|
{
|
|
i = BIO_read(write, tbuffer, 4096);
|
|
if ((((int*)ILibMemory_GetExtraMemory(remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
ILibTURN_SendIndication(obj->mTurnClientModule, remoteInterface, tbuffer, 0, i);
|
|
}
|
|
else
|
|
{
|
|
ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)remoteInterface, tbuffer, i, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
}
|
|
// We're going to drop out now, becuase we need to check for received UDP data
|
|
}
|
|
}
|
|
|
|
int ILibStun_GetDtlsSessionSlotForIceState(struct ILibStun_Module *obj, struct ILibStun_IceState* ice)
|
|
{
|
|
int x, i;
|
|
struct ILibStun_dTlsSession *session;
|
|
|
|
for (x = 0; x < ILibSTUN_MaxSlots; ++x)
|
|
{
|
|
session = obj->dTlsSessions[x];
|
|
if (session != NULL)
|
|
{
|
|
for (i = 0; i < ice->hostcandidatecount; ++i)
|
|
{
|
|
if (ice->hostcandidates[i].addr == ((struct sockaddr_in*)session->remoteInterface)->sin_addr.s_addr)
|
|
{
|
|
if (ice->hostcandidates[i].port == session->remoteInterface->sin6_port) { return x; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//! Get the local ICE Username for an active Peer Connection
|
|
/*!
|
|
\param WebRTCModule Peer Connection Session
|
|
\param[in,out] username Must by at least 8 bytes big
|
|
*/
|
|
void ILibStun_DTLS_GetIceUserName(void* WebRTCModule, char* username)
|
|
{
|
|
struct ILibStun_dTlsSession* session = (struct ILibStun_dTlsSession*)WebRTCModule;
|
|
memcpy_s(username, 8, session->parent->IceStates[session->iceStateSlot]->userAndKey + 1, 8);
|
|
}
|
|
|
|
void ILibStun_DTLS_Success_OnCreateTURNChannelBinding(ILibTURN_ClientModule turnModule, unsigned short channelNumber, int success, void* user)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)user;
|
|
struct timeval tv;
|
|
|
|
UNREFERENCED_PARAMETER(turnModule);
|
|
|
|
gettimeofday(&tv, NULL);
|
|
obj->dTlsSessions[(int)channelNumber]->channelBindingRefresh = tv.tv_sec;
|
|
if (success != 0)
|
|
{
|
|
// ChannelBinding Creation was successful
|
|
((int*)ILibMemory_GetExtraMemory(obj->dTlsSessions[(int)channelNumber]->remoteInterface, sizeof(struct sockaddr_in6)))[0] = (int)channelNumber | ILibTURN_FLAGS_CHANNEL_DATA;
|
|
}
|
|
else
|
|
{
|
|
((int*)ILibMemory_GetExtraMemory(obj->dTlsSessions[(int)channelNumber]->remoteInterface, sizeof(struct sockaddr_in6)))[0] = ILibTURN_FLAGS_DATA_INDICATION;
|
|
}
|
|
|
|
if (obj->IceStates[obj->dTlsSessions[(int)channelNumber]->iceStateSlot]->dtlsInitiator != 0)
|
|
{
|
|
// We must initiate SCTP
|
|
ILibStun_InitiateSCTP(obj, (int)channelNumber, 5000, 5000);
|
|
}
|
|
else
|
|
{
|
|
// Start the timer, for SCTP Heartbeats
|
|
ILibLifeTime_AddEx(obj->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(obj->dTlsSessions[(int)channelNumber]), 100, &ILibStun_SctpOnTimeout, NULL);
|
|
}
|
|
|
|
}
|
|
void ILibStun_DTLS_Success_OnCreateTURNChannelBinding2(ILibTURN_ClientModule turnModule, unsigned short channelNumber, int success, void* user)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)user;
|
|
struct timeval tv;
|
|
|
|
UNREFERENCED_PARAMETER(turnModule);
|
|
|
|
gettimeofday(&tv, NULL);
|
|
obj->dTlsSessions[(int)channelNumber]->channelBindingRefresh = tv.tv_sec;
|
|
if (success != 0)
|
|
{
|
|
// ChannelBinding Creation was successful
|
|
((int*)ILibMemory_GetExtraMemory(obj->dTlsSessions[(int)channelNumber]->remoteInterface, sizeof(struct sockaddr_in6)))[0] = (int)channelNumber | ILibTURN_FLAGS_CHANNEL_DATA;
|
|
}
|
|
}
|
|
void ILibStun_DTLS_Success(struct ILibStun_Module *obj, int session, struct sockaddr_in6 *remoteInterface)
|
|
{
|
|
int IceSlot;
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "DTLS Session: %d [Handshake SUCCESS]", session);
|
|
|
|
obj->dTlsSessions[session]->SSTHRESH = 4 * ILibRUDP_StartMTU;
|
|
|
|
IceSlot = obj->dTlsSessions[session]->iceStateSlot;
|
|
if (IceSlot >= 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...%s", obj->IceStates[IceSlot]->dtlsInitiator == 0 ? "DTLS Server" : "DTLS Client");
|
|
|
|
if ((((int*)ILibMemory_GetExtraMemory(remoteInterface, sizeof(struct sockaddr_in6)))[0] & ILibTURN_FLAGS_DATA_INDICATION) == ILibTURN_FLAGS_DATA_INDICATION)
|
|
{
|
|
// We need to create a ChannelBinding for this DTLS Session
|
|
ILibTURN_CreateChannelBinding(obj->mTurnClientModule, (unsigned short)session, remoteInterface, ILibStun_DTLS_Success_OnCreateTURNChannelBinding, obj);
|
|
}
|
|
else if (obj->IceStates[IceSlot]->dtlsInitiator != 0) // This is in an else clause, becuase if we create a channel binding, we'll check this condition later
|
|
{
|
|
// Since the remote side didn't initiate the offer, we must initiate SCTP
|
|
ILibStun_InitiateSCTP(obj, session, 5000, 5000);
|
|
}
|
|
else
|
|
{
|
|
// Start the timer, for SCTP Heartbeats
|
|
ILibLifeTime_AddEx(obj->Timer, ILibWebRTC_DTLS_TO_SCTP_HEARTBEAT_TIMER_OBJECT(obj->dTlsSessions[session]), 100, &ILibStun_SctpOnTimeout, NULL);
|
|
}
|
|
|
|
// Since DTLS is established, we can stop sending periodic STUNS on the ICE Offer Candidates
|
|
// TODO: Bryan: Fix this, Periodic stuns don't work like this anymore
|
|
ILibLifeTime_Remove(obj->Timer, obj->IceStates[IceSlot]);
|
|
|
|
if (obj->consentFreshnessDisabled == 0) // TODO: Bryan: We should really put this after SCTP has been established...
|
|
{
|
|
// Start Consent Freshness Algorithm. Wait for the Timeout, then send first packet
|
|
obj->dTlsSessions[session]->freshnessTimestampStart = 0;
|
|
ILibLifeTime_Add(obj->Timer, ILibWebRTC_DTLS_TO_CONSENT_FRESHNESS_TIMER_OBJECT(obj->dTlsSessions[session]), ILibStun_MaxConsentFreshnessTimeoutSeconds, ILibStun_WebRTC_ConsentFreshness_Start, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILibStun_OnUDP(ILibAsyncUDPSocket_SocketModule socketModule, char* buffer, int bufferLength, struct sockaddr_in6 *remoteInterface, void *user, void *user2, int *PAUSE)
|
|
{
|
|
BIO* read;
|
|
BIO* write;
|
|
int i, cx, j = -1;
|
|
int existingSession = -1;
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)user;
|
|
char tbuffer[4096];
|
|
u_long err;
|
|
int dtlsSessionId = -1;
|
|
int iceSlotId = -1;
|
|
|
|
UNREFERENCED_PARAMETER(user2);
|
|
UNREFERENCED_PARAMETER(PAUSE);
|
|
UNREFERENCED_PARAMETER(socketModule);
|
|
|
|
if (socketModule == NULL && remoteInterface->sin6_family == 0)
|
|
{
|
|
existingSession = (int)remoteInterface->sin6_port;
|
|
remoteInterface = obj->dTlsSessions[existingSession]->remoteInterface;
|
|
}
|
|
|
|
// Process STUN Packet
|
|
if (ILibStun_ProcessStunPacket(obj, buffer, bufferLength, remoteInterface) == 1) return;
|
|
|
|
if (obj->SecurityContext == NULL || obj->CertThumbprint == NULL) return; // No dTLS support
|
|
if (existingSession < 0)
|
|
{
|
|
//
|
|
// Enumerate ICE Offers, and see if any of the associated DTLS sessions is this one
|
|
//
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->dtlsSession >= 0)
|
|
{
|
|
if (obj->dTlsSessions[obj->IceStates[i]->dtlsSession] != NULL)
|
|
{
|
|
if (memcmp(obj->dTlsSessions[obj->IceStates[i]->dtlsSession]->remoteInterface, remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family)) == 0)
|
|
{
|
|
existingSession = obj->IceStates[i]->dtlsSession;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (existingSession < 0)
|
|
{
|
|
//
|
|
// Check the existing sessions one more time, to see if the remote side switched interfaces on us
|
|
//
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->dtlsSession >= 0)
|
|
{
|
|
if (obj->dTlsSessions[obj->IceStates[i]->dtlsSession] != NULL)
|
|
{
|
|
for (cx = 0; cx < obj->IceStates[i]->hostcandidatecount; ++cx)
|
|
{
|
|
if (obj->IceStates[i]->hostcandidates[cx].port == remoteInterface->sin6_port && obj->IceStates[i]->hostcandidates[cx].addr == ((struct sockaddr_in*)remoteInterface)->sin_addr.s_addr)
|
|
{
|
|
if (obj->IceStates[i]->hostcandidateResponseFlag[cx] == 1)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "DTLS Session: %d switching candidates from: %s:%u", obj->IceStates[i]->dtlsSession, ILibRemoteLogging_ConvertAddress((struct sockaddr*)(obj->dTlsSessions[obj->IceStates[i]->dtlsSession]->remoteInterface)), ntohs(obj->dTlsSessions[obj->IceStates[i]->dtlsSession]->remoteInterface->sin6_port));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...TO: %s:%u", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
|
|
// This Candidate was Allowed, let's switch to this candidate
|
|
memcpy_s(obj->dTlsSessions[obj->IceStates[i]->dtlsSession]->remoteInterface, sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family));
|
|
existingSession = obj->IceStates[i]->dtlsSession;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (existingSession >= 0) { break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Check if this is for a new dTLS session
|
|
// Modified to remove the dTLS Hello detection, because if this isn't a STUN packet, it has to be dTLS. OpenSSL will just fail the handshake if it isn't, which is fine.
|
|
if (existingSession == -1)
|
|
{
|
|
// We don't have a session established yet, so just check to see if the candidate is allowed
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->dtlsSession < 0)
|
|
{
|
|
for (cx = 0; cx < obj->IceStates[i]->hostcandidatecount; ++cx)
|
|
{
|
|
if (obj->IceStates[i]->hostcandidates[cx].port == remoteInterface->sin6_port && obj->IceStates[i]->hostcandidates[cx].addr == ((struct sockaddr_in*)remoteInterface)->sin_addr.s_addr)
|
|
{
|
|
if (obj->IceStates[i]->hostcandidateResponseFlag[cx] == 1)
|
|
{
|
|
// This Candidate was Allowed, find a free dTLS session slot
|
|
dtlsSessionId = ILibStun_GetFreeSessionSlot(obj);
|
|
iceSlotId = i;
|
|
if (dtlsSessionId == ILibSTUN_MaxSlots)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, " ABORTING: No free dTLS Session Slots");
|
|
return; // No free slots
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...DTLS Packet from: %s:%u was using a disallowed candidate", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
return; // This candidate was not allowed
|
|
}
|
|
i = ILibSTUN_MaxSlots;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dtlsSessionId < 0)
|
|
{
|
|
// No valid candidates were found
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...DTLS Packet from: %s:%u has no valid candidates", ILibRemoteLogging_ConvertAddress((struct sockaddr*)remoteInterface), ntohs(remoteInterface->sin6_port));
|
|
|
|
// But let's check to see if we received any ICE requests on this Address/Port combination (Maybe the port in the candidate list is stale)
|
|
for (i = 0; i < ILibSTUN_MaxSlots; ++i)
|
|
{
|
|
if (obj->IceStates[i] != NULL && obj->IceStates[i]->dtlsSession < 0)
|
|
{
|
|
// ICE State object with no DTLS Session Established
|
|
for (cx = 0; cx < STUN_NUM_ADDR; ++cx)
|
|
{
|
|
if (obj->IceStates[i]->ReceivedAddr[cx].sin6_family == remoteInterface->sin6_family && memcmp(&(obj->IceStates[i]->ReceivedAddr[cx]), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sin6_family)) == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "......because the port mapping differs from the specified candidates");
|
|
|
|
// Find a free DTLS Session ID
|
|
iceSlotId = i;
|
|
dtlsSessionId = ILibStun_GetFreeSessionSlot(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (dtlsSessionId < 0 || dtlsSessionId == ILibSTUN_MaxSlots)
|
|
{
|
|
// DTLS Session ABORT, becuase either the connection was not allowed, or the slots are full
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "......aborting");
|
|
return;
|
|
}
|
|
}
|
|
|
|
j = dtlsSessionId;
|
|
|
|
// Start a new dTLS session
|
|
ILibStun_CreateDtlsSession(obj, j, iceSlotId, remoteInterface);
|
|
|
|
// Set up the memory-buffer BIOs
|
|
read = BIO_new(BIO_s_mem());
|
|
obj->dTlsSessions[j]->writeBIO = write = BIO_new(BIO_s_mem());
|
|
BIO_get_mem_ptr(obj->dTlsSessions[j]->writeBIO, &(obj->dTlsSessions[j]->writeBIOBuffer));
|
|
BIO_set_mem_eof_return(read, -1);
|
|
BIO_set_mem_eof_return(write, -1);
|
|
|
|
// Bind everything
|
|
SSL_set_bio(obj->dTlsSessions[j]->ssl, read, write);
|
|
SSL_set_accept_state(obj->dTlsSessions[j]->ssl);
|
|
|
|
// Start the timer
|
|
//ILibLifeTime_AddEx(obj->Timer, obj->dTlsSessions[j], 100, &ILibStun_SctpOnTimeout, NULL); //Moved this to after DTLS is setup, becuase SCTP might not be setup yet
|
|
|
|
switch (SSL_do_handshake(obj->dTlsSessions[j]->ssl))
|
|
{
|
|
case 0:
|
|
//
|
|
// Handshake Failed!
|
|
//
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Handshake FAILED");
|
|
while ((err = ERR_get_error()) != 0)
|
|
{
|
|
ERR_error_string_n(err, ILibScratchPad, sizeof(ILibScratchPad));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Reason: %s", ILibScratchPad);
|
|
}
|
|
existingSession = j; //ToDo: Bryan. Figure out what to do in this case
|
|
break;
|
|
case 1:
|
|
obj->dTlsSessions[j]->state = 1;
|
|
ILibStun_DTLS_Success(obj, j, remoteInterface); // Successful DTLS Handshake
|
|
break;
|
|
default:
|
|
//
|
|
// SSL_WANT_READ most likely, so do nothing for now
|
|
//
|
|
break;
|
|
}
|
|
|
|
while (BIO_ctrl_pending(write) > 0)
|
|
{
|
|
i = BIO_read(write, tbuffer, 4096);
|
|
if (socketModule == NULL)
|
|
{
|
|
// Response was from TURN
|
|
if (remoteInterface->sin6_family == 0)
|
|
{
|
|
ILibTURN_SendChannelData(obj->mTurnClientModule, remoteInterface->sin6_port, tbuffer, 0, i);
|
|
}
|
|
else
|
|
{
|
|
ILibTURN_SendIndication(obj->mTurnClientModule, remoteInterface, tbuffer, 0, i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Response was from a local socket
|
|
ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)remoteInterface, tbuffer, i, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
}
|
|
|
|
existingSession = j;
|
|
}
|
|
|
|
// If we have an existing dTLS session, process the data. This can also happen right after we create the session above.
|
|
if (existingSession != -1 && (obj->dTlsSessions[existingSession]->state == 1 || obj->dTlsSessions[existingSession]->state == 2 || obj->dTlsSessions[existingSession]->state == 4))
|
|
{
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[existingSession]->Lock));
|
|
BIO_write(SSL_get_rbio(obj->dTlsSessions[existingSession]->ssl), buffer, bufferLength);
|
|
if (obj->dTlsSessions[existingSession]->state == 4)
|
|
{
|
|
ILibWebRTC_DTLS_HandshakeDetect(obj, "R ", buffer, 0, bufferLength);
|
|
|
|
// Connecting... Handshake isn't done yet
|
|
switch (SSL_do_handshake(obj->dTlsSessions[existingSession]->ssl))
|
|
{
|
|
case 0:
|
|
// Handshake Failed!
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Handshake FAILED");
|
|
while ((err = ERR_get_error()) != 0)
|
|
{
|
|
ERR_error_string_n(err, ILibScratchPad, sizeof(ILibScratchPad));
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "......Reason: %s", ILibScratchPad);
|
|
}
|
|
// TODO: We should probably do something
|
|
break;
|
|
case 1:
|
|
obj->dTlsSessions[existingSession]->state = 1;
|
|
ILibStun_DTLS_Success(obj, existingSession, remoteInterface); // Successful DTLS Handshake
|
|
|
|
#ifdef _DEBUG
|
|
//util_savekeys(obj->dTlsSessions[existingSession]->ssl); // SAVES DTLS PRIVATE KEYS !!! WARNING - THIS CODE SHOULD ALWAYS BE COMMENTED OUT !!!
|
|
#endif
|
|
break;
|
|
default:
|
|
// SSL_WANT_READ most likely, so do nothing for now
|
|
break;
|
|
}
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[existingSession]->Lock)); //ToDo: Bryan/Is this right?
|
|
}
|
|
else
|
|
{
|
|
i = SSL_read(obj->dTlsSessions[existingSession]->ssl, tbuffer, 4096);
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[existingSession]->Lock));
|
|
if (i > 0)
|
|
{
|
|
// We got new dTLS data
|
|
ILibStun_ProcessSctpPacket(obj, existingSession, tbuffer, i);
|
|
}
|
|
else if (i == 0)
|
|
{
|
|
// Session closed, perform cleanup
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "DTLS Session: %d was closed", existingSession);
|
|
ILibStun_SctpDisconnect(obj, existingSession);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
i = SSL_get_error(obj->dTlsSessions[existingSession]->ssl, i);
|
|
}
|
|
}
|
|
|
|
if (obj->dTlsSessions[existingSession] != NULL && obj->dTlsSessions[existingSession]->state != 0)
|
|
{
|
|
ILibSpinLock_Lock(&(obj->dTlsSessions[existingSession]->Lock));
|
|
if(obj->dTlsSessions[existingSession]->writeBIOBuffer->length > 0)
|
|
{
|
|
BIO_clear_retry_flags(obj->dTlsSessions[existingSession]->writeBIO); // Klocwork reports this could block, but this is a memory bio, so it will never block.
|
|
|
|
// Data is pending in the write buffer, send it out
|
|
if (socketModule == NULL)
|
|
{
|
|
// Response was from TURN
|
|
if (remoteInterface->sin6_family == 0)
|
|
{
|
|
ILibTURN_SendChannelData(obj->mTurnClientModule, remoteInterface->sin6_port, obj->dTlsSessions[existingSession]->writeBIOBuffer->data, 0, (int)obj->dTlsSessions[existingSession]->writeBIOBuffer->length);
|
|
}
|
|
else
|
|
{
|
|
ILibTURN_SendIndication(obj->mTurnClientModule, remoteInterface, obj->dTlsSessions[existingSession]->writeBIOBuffer->data, 0, (int)obj->dTlsSessions[existingSession]->writeBIOBuffer->length);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Response was from a local Socket
|
|
ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)obj)->UDP, (struct sockaddr*)remoteInterface, obj->dTlsSessions[existingSession]->writeBIOBuffer->data, obj->dTlsSessions[existingSession]->writeBIOBuffer->length, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
if(obj->dTlsSessions[existingSession]->state == 4) {ILibWebRTC_DTLS_HandshakeDetect(obj, "S ", obj->dTlsSessions[existingSession]->writeBIOBuffer->data, 0, (int)obj->dTlsSessions[existingSession]->writeBIOBuffer->length);}
|
|
ignore_result(BIO_reset(obj->dTlsSessions[existingSession]->writeBIO));
|
|
}
|
|
ILibSpinLock_UnLock(&(obj->dTlsSessions[existingSession]->Lock));
|
|
}
|
|
}
|
|
}
|
|
|
|
int ILibStunClient_dTLS_verify_callback(int ok, X509_STORE_CTX *ctx)
|
|
{
|
|
int i, l = 32;
|
|
char thumbprint[32];
|
|
SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
|
ILibStun_Module *stunModule = SSL_get_ex_data(ssl, ILibStunClientIndex);
|
|
|
|
// Validate the incoming certificate against known allowed fingerprints.
|
|
UNREFERENCED_PARAMETER(ok);
|
|
|
|
// X509_digest(ctx->current_cert, EVP_get_digestbyname("sha256"), (unsigned char*)thumbprint, (unsigned int*)&l); // OpenSSL 1.0
|
|
X509_digest(X509_STORE_CTX_get_current_cert(ctx), EVP_get_digestbyname("sha256"), (unsigned char*)thumbprint, (unsigned int*)&l); // OpenSSL 1.1
|
|
if (l != 32 || stunModule == NULL) return 0;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "Verifying Inbound Cert: %s", ILibRemoteLogging_ConvertToHex(thumbprint, 32));
|
|
|
|
for (i = 0; i < ILibSTUN_MaxSlots; i++)
|
|
{
|
|
if (stunModule->IceStates[i] != NULL && stunModule->IceStates[i]->dtlscerthashlen == 32 && memcmp(stunModule->IceStates[i]->dtlscerthash, thumbprint, 32) == 0)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...Matches Slot[%d]", i);
|
|
return 1;
|
|
}
|
|
}
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(stunModule->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_DTLS, ILibRemoteLogging_Flags_VerbosityLevel_1, "...FAILED (No Matches)");
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// TURN Client Support for WebRTC
|
|
//
|
|
typedef struct ILibTURN_TurnClientObject
|
|
{
|
|
ILibChain_Link ChainLink;
|
|
ILibTURN_OnConnectTurnHandler OnConnectTurnCallback;
|
|
ILibTURN_OnAllocateHandler OnAllocateCallback;
|
|
ILibTURN_OnDataIndicationHandler OnDataIndicationCallback;
|
|
ILibTURN_OnChannelDataHandler OnChannelDataCallback;
|
|
|
|
void* TAG;
|
|
void* tcpClient;
|
|
char* username;
|
|
int usernameLen;
|
|
char* password;
|
|
int passwordLen;
|
|
void* transactionData;
|
|
void* sendOkStack;
|
|
|
|
char* currentNonce;
|
|
int currentNonceLen;
|
|
char* currentRealm;
|
|
int currentRealmLen;
|
|
}ILibTURN_TurnClientObject;
|
|
void ILibWebRTC_TurnAllocationRefresh(void *obj);
|
|
ILibTURN_ClientModule ILibStun_GetTurnClient(void *stunModule)
|
|
{
|
|
return(((ILibStun_Module*)stunModule)->mTurnClientModule);
|
|
}
|
|
|
|
void ILibWebRTC_OnTurnConnect(ILibTURN_ClientModule turnModule, int success)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(((ILibTURN_TurnClientObject*)turnModule)->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "TURN: Connect Status = %s", success == 0 ? "FAIL" : "SUCCESS");
|
|
if (success != 0) { ILibTURN_Allocate(turnModule, ILibTURN_TransportTypes_UDP); }
|
|
}
|
|
void ILibWebRTC_OnTurnAllocateRefresh(ILibTURN_ClientModule turnModule, int Lifetime, void *user)
|
|
{
|
|
struct ILibStun_Module* stun = (struct ILibStun_Module*)ILibTURN_GetTag(turnModule);
|
|
ILibLifeTime_Remove(stun->Timer, turnModule);
|
|
ILibLifeTime_Add(stun->Timer, turnModule, Lifetime / 5 * 4, ILibWebRTC_TurnAllocationRefresh, NULL);
|
|
|
|
UNREFERENCED_PARAMETER(user);
|
|
}
|
|
void ILibWebRTC_TurnAllocationRefresh(void *obj)
|
|
{
|
|
ILibTURN_ClientModule turnModule = (ILibTURN_ClientModule*)obj;
|
|
ILibTURN_RefreshAllocation(turnModule, ILibWebRTC_OnTurnAllocateRefresh, NULL);
|
|
}
|
|
void ILibWebRTC_OnTurnAllocate(ILibTURN_ClientModule turnModule, int Lifetime, struct sockaddr_in6* RelayedTransportAddress)
|
|
{
|
|
struct ILibStun_Module* stun = (struct ILibStun_Module*)ILibTURN_GetTag(turnModule);
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(((ILibTURN_TurnClientObject*)turnModule)->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "TURN: Allocated: %s for duration: %d", ILibRemoteLogging_ConvertAddress((struct sockaddr*)RelayedTransportAddress), Lifetime);
|
|
stun->allocationLifetime = Lifetime;
|
|
if (Lifetime > 0)
|
|
{
|
|
memcpy_s(&(stun->mRelayedTransportAddress), sizeof(struct sockaddr_in6), RelayedTransportAddress, sizeof(struct sockaddr_in6));
|
|
ILibLifeTime_Remove(stun->Timer, turnModule);
|
|
ILibLifeTime_Add(stun->Timer, turnModule, Lifetime / 5 * 4, ILibWebRTC_TurnAllocationRefresh, NULL);
|
|
}
|
|
}
|
|
|
|
void ILibWebRTC_OnTurnDataIndication(ILibTURN_ClientModule turnModule, struct sockaddr_in6* remotePeer, char* buffer, int offset, int length)
|
|
{
|
|
void* stunModule = ILibTURN_GetTag(turnModule);
|
|
ILibStun_OnUDP(NULL, buffer + offset, length, remotePeer, stunModule, NULL, NULL);
|
|
}
|
|
|
|
void ILibWebRTC_OnTurnChannelData(ILibTURN_ClientModule turnModule, unsigned short channelNumber, char* buffer, int offset, int length)
|
|
{
|
|
void* stunModule = ILibTURN_GetTag(turnModule);
|
|
struct sockaddr_in6 fakeAddress;
|
|
|
|
fakeAddress.sin6_family = 0;
|
|
fakeAddress.sin6_port = channelNumber;
|
|
|
|
ILibStun_OnUDP(NULL, buffer + offset, length, &fakeAddress, stunModule, NULL, NULL);
|
|
}
|
|
|
|
//! Configure a TURN Server for use with ICE
|
|
/*!
|
|
\param stunModule Stun/ICE Client
|
|
\param turnServer The TURN Server to configure
|
|
\param username TURN Server username
|
|
\param usernameLength TURN Server username length
|
|
\param password TURN Server Password
|
|
\param passwordLength TURN Server password length
|
|
\param turnFlags TURN Server configuration flags
|
|
*/
|
|
void ILibWebRTC_SetTurnServer(void* stunModule, struct sockaddr_in6* turnServer, char* username, int usernameLength, char* password, int passwordLength, ILibWebRTC_TURN_ConnectFlags turnFlags)
|
|
{
|
|
struct ILibStun_Module* stun = (struct ILibStun_Module*)stunModule;
|
|
|
|
// We will only support setting/changing these values if we aren't already connected to a TURN server
|
|
if (turnServer != NULL)
|
|
{
|
|
switch (turnServer->sin6_family)
|
|
{
|
|
case AF_INET:
|
|
memcpy_s(&(stun->mTurnServerAddress), sizeof(struct sockaddr_in6), turnServer, sizeof(struct sockaddr_in));
|
|
break;
|
|
case AF_INET6:
|
|
memcpy_s(&(stun->mTurnServerAddress), sizeof(struct sockaddr_in6), turnServer, sizeof(struct sockaddr_in6));
|
|
break;
|
|
}
|
|
}
|
|
if (stun->turnUsername != NULL)
|
|
{
|
|
free(stun->turnUsername);
|
|
stun->turnUsername = NULL;
|
|
}
|
|
if (usernameLength > 0 && username != NULL)
|
|
{
|
|
if ((stun->turnUsername = (char*)malloc(usernameLength + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(stun->turnUsername, usernameLength + 1, username, usernameLength);
|
|
stun->turnUsername[usernameLength] = 0;
|
|
stun->turnUsernameLength = usernameLength;
|
|
}
|
|
if (stun->turnPassword != NULL)
|
|
{
|
|
memset(stun->turnPassword, 0, stun->turnPasswordLength); free(stun->turnPassword);
|
|
stun->turnPassword = NULL;
|
|
}
|
|
if (passwordLength > 0 && password != NULL)
|
|
{
|
|
if ((stun->turnPassword = (char*)malloc(passwordLength + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(stun->turnPassword, passwordLength + 1, password, passwordLength);
|
|
stun->turnPassword[passwordLength] = 0;
|
|
stun->turnPasswordLength = passwordLength;
|
|
}
|
|
|
|
switch (turnFlags)
|
|
{
|
|
case ILibWebRTC_TURN_DISABLED:
|
|
{
|
|
stun->alwaysConnectTurn = 0;
|
|
stun->alwaysUseTurn = 0;
|
|
if (ILibTURN_IsConnectedToServer(stun->mTurnClientModule) == 0) { ILibTURN_DisconnectFromServer(stun->mTurnClientModule); }
|
|
break;
|
|
}
|
|
case ILibWebRTC_TURN_ENABLED:
|
|
{
|
|
stun->alwaysConnectTurn = 1;
|
|
stun->alwaysUseTurn = 0;
|
|
break;
|
|
}
|
|
case ILibWebRTC_TURN_ALWAYS_RELAY:
|
|
{
|
|
stun->alwaysConnectTurn = 1;
|
|
stun->alwaysUseTurn = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stun->alwaysConnectTurn != 0 && stun->turnUsername != NULL && stun->turnPassword != NULL)
|
|
{
|
|
ILibTURN_ConnectTurnServer(stun->mTurnClientModule, (struct sockaddr_in*)&(stun->mTurnServerAddress), stun->turnUsername, stun->turnUsernameLength, stun->turnPassword, stun->turnPasswordLength, NULL);
|
|
}
|
|
}
|
|
|
|
void ILibWebRTC_DisableConsentFreshness(void *stunModule)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)stunModule;
|
|
obj->consentFreshnessDisabled = 1;
|
|
}
|
|
//! Set Security Attributes for STUN/ICE/WebRTC
|
|
/*!
|
|
\param StunModule The Stun client to update (obtained from ILibStunClient_Start)
|
|
\param securityContext The SSL Security Context
|
|
\param certThumbprintSha256 The thumbprint of the local DTLS certificate
|
|
*/
|
|
void ILibStunClient_SetOptions(void* StunModule, SSL_CTX* securityContext, char* certThumbprintSha256)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)StunModule;
|
|
obj->SecurityContext = securityContext;
|
|
obj->CertThumbprint = certThumbprintSha256;
|
|
obj->CertThumbprintLength = 32; // SHA256
|
|
|
|
if(ILibStunClientIndex < 0)
|
|
{
|
|
ILibStunClientIndex = SSL_get_ex_new_index(0, "ILibStun_Module index", NULL, NULL, NULL);
|
|
}
|
|
|
|
if (obj->SecurityContext != NULL)
|
|
{
|
|
//SSL_CTX_set_ecdh_auto(obj->SecurityContext, 1); // DEPRECATED in OpenSSL/1.1.x
|
|
SSL_CTX_set_session_cache_mode(obj->SecurityContext, SSL_SESS_CACHE_OFF);
|
|
SSL_CTX_set_read_ahead(obj->SecurityContext, 1);
|
|
SSL_CTX_set_verify(obj->SecurityContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ILibStunClient_dTLS_verify_callback);
|
|
}
|
|
}
|
|
SSL_CTX* ILibStunClient_GetSSLCTX(void *StunModule)
|
|
{
|
|
return (((ILibStun_Module*)StunModule)->SecurityContext);
|
|
}
|
|
//! Creates a new instance of a Stun Client
|
|
/*!
|
|
\param Chain The microstack chain to add this module to
|
|
\param LocalPort The local UDP port to bind the stun client to. (0 = Random)
|
|
\param OnResult Callback to deliver STUN Results to
|
|
\return StunModule
|
|
*/
|
|
void* ILibStunClient_Start(void *Chain, unsigned short LocalPort, ILibStunClient_OnResult OnResult)
|
|
{
|
|
struct ILibStun_Module *obj;
|
|
|
|
if ((obj = (struct ILibStun_Module*)malloc(sizeof(struct ILibStun_Module))) == NULL) ILIBCRITICALEXIT(254);
|
|
memset(obj, 0, sizeof(struct ILibStun_Module));
|
|
obj->State = STUN_STATUS_NOT_TESTED;
|
|
obj->StunUsers = ILibLinkedList_Create();
|
|
obj->ChainLink.MetaData = ILibMemory_SmartAllocate_FromString("ILibWebRTC");
|
|
obj->OnResult = OnResult;
|
|
obj->LocalIf.sin_family = AF_INET;
|
|
obj->LocalIf.sin_port = htons(LocalPort);
|
|
obj->LocalIf6.sin6_family = AF_INET6;
|
|
obj->LocalIf6.sin6_port = htons(LocalPort);
|
|
obj->ChainLink.ParentChain = Chain;
|
|
obj->ChainLink.DestroyHandler = &ILibStun_OnDestroy;
|
|
obj->UDP = ILibAsyncUDPSocket_CreateEx(Chain, ILibRUDP_MaxMTU, (struct sockaddr*)&(obj->LocalIf), ILibAsyncUDPSocket_Reuse_EXCLUSIVE, &ILibStun_OnUDP, NULL, obj);
|
|
if (obj->UDP == NULL) { free(obj); return NULL; }
|
|
ILibChain_Link_SetMetadata(obj->UDP, "ILibWebRTC_stun_listener_ipv4");
|
|
#ifdef WIN32
|
|
obj->UDP6 = ILibAsyncUDPSocket_CreateEx(Chain, ILibRUDP_MaxMTU, (struct sockaddr*)&(obj->LocalIf6), ILibAsyncUDPSocket_Reuse_EXCLUSIVE, &ILibStun_OnUDP, NULL, obj);
|
|
if (obj->UDP6 == NULL) { free(obj); return NULL; }
|
|
ILibChain_Link_SetMetadata(obj->UDP6, "ILibWebRTC_stun_listener_ipv6");
|
|
#endif
|
|
|
|
ILibAddToChain(Chain, obj);
|
|
|
|
if (LocalPort == 0)
|
|
{
|
|
obj->LocalIf.sin_port = htons(ILibAsyncUDPSocket_GetLocalPort(obj->UDP));
|
|
#ifdef WIN32
|
|
obj->LocalIf6.sin6_port = htons(ILibAsyncUDPSocket_GetLocalPort(obj->UDP6));
|
|
#endif
|
|
}
|
|
|
|
obj->Timer = ILibGetBaseTimer(Chain);
|
|
util_random(32, obj->Secret); // Random used to generate integrity keys
|
|
|
|
// Init TURN Client
|
|
obj->mTurnClientModule = ILibTURN_CreateTurnClient(Chain, ILibWebRTC_OnTurnConnect, ILibWebRTC_OnTurnAllocate, ILibWebRTC_OnTurnDataIndication, ILibWebRTC_OnTurnChannelData);
|
|
ILibTURN_SetTag(obj->mTurnClientModule, obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
void ILibStun_OnTimeout(void *object)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)object;
|
|
|
|
switch (obj->State)
|
|
{
|
|
default:
|
|
break;
|
|
case STUN_STATUS_CHECKING_UDP_CONNECTIVITY:
|
|
// No UDP connectivity
|
|
ILibStun_FireResults(obj, ILibStun_Results_Unknown, NULL);
|
|
break;
|
|
case STUN_STATUS_CHECKING_FULL_CONE_NAT:
|
|
{
|
|
// We're not supposed to trigger this case. If we did, it means the StunSever we are using does not implement RFC5389/5780
|
|
ILibStun_FireResults(obj, ILibStun_Results_RFC5780_NOT_IMPLEMENTED, (struct sockaddr_in*)&(obj->Public));
|
|
}
|
|
break;
|
|
case STUN_STATUS_CHECKING_RESTRICTED_NAT:
|
|
// Port Restricted NAT
|
|
ILibStun_FireResults(obj, ILibStun_Results_Port_Restricted_NAT, (struct sockaddr_in*)&(obj->Public));
|
|
break;
|
|
case STUN_STATUS_CHECKING_SYMETRIC_NAT:
|
|
// Symetric NAT
|
|
ILibStun_FireResults(obj, ILibStun_Results_Symetric_NAT, (struct sockaddr_in*)&(obj->Public)); // NOTE: Changed "ILibStun_Results_Unknown" that was here, is that ok??
|
|
break;
|
|
}
|
|
}
|
|
ILibStunClient_PreviousResults ILibStunClient_CheckPreviousStunResults(ILibStun_Module *obj, struct sockaddr_in* StunServer, int flags, void *user)
|
|
{
|
|
ILibStunClient_PreviousResults RetVal = ILibStunClient_PreviousResults_NeverTested;
|
|
|
|
ILibLinkedList_Lock(obj->StunUsers);
|
|
if (obj->State == STUN_STATUS_NOT_TESTED)
|
|
{
|
|
memcpy_s(&(obj->StunServer), sizeof(struct sockaddr_in6), StunServer, INET_SOCKADDR_LENGTH(StunServer->sin_family));
|
|
ILibLinkedList_AddTail(obj->StunUsers, user);
|
|
obj->State = STUN_STATUS_CHECKING_UDP_CONNECTIVITY;
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(obj->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "Performing STUN [Flags=%04X] with: %s:%u", flags, ILibRemoteLogging_ConvertAddress((struct sockaddr*)StunServer), ntohs(StunServer->sin_port));
|
|
ILib_Stun_SendAttributeChangeRequest(obj, (struct sockaddr*)&(obj->StunServer), flags);
|
|
ILibLifeTime_Add(obj->Timer, obj, ILibStunClient_TIMEOUT, &ILibStun_OnTimeout, NULL);
|
|
RetVal = ILibStunClient_PreviousResults_NeverTested;
|
|
}
|
|
else if (obj->State == STUN_STATUS_COMPLETE)
|
|
{
|
|
RetVal = ILibStunClient_PreviousResults_ValidResults;
|
|
}
|
|
else
|
|
{
|
|
// Add to the UserList, because there is already a STUN in process... We want a piece of that action!
|
|
ILibLinkedList_AddTail(obj->StunUsers, user);
|
|
}
|
|
ILibLinkedList_UnLock(obj->StunUsers);
|
|
|
|
if (RetVal == ILibStunClient_PreviousResults_ValidResults)
|
|
{
|
|
// STUN was recently completed, so we can just immediately signal the previous results
|
|
if (obj->OnResult != NULL) { obj->OnResult(obj, obj->StunResult, (struct sockaddr_in*)&(obj->Public), user); }
|
|
}
|
|
return RetVal;
|
|
}
|
|
|
|
//! Attempt to determine NAT Type
|
|
/*!
|
|
\param StunModule Stun Client module
|
|
\param StunServer The Stun server to utilize in the query
|
|
\param user Custom user state data
|
|
*/
|
|
void ILibStunClient_PerformNATBehaviorDiscovery(void* StunModule, struct sockaddr_in* StunServer, void *user)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)StunModule;
|
|
if (StunServer->sin_family != AF_INET) return;
|
|
|
|
ILibStunClient_CheckPreviousStunResults(obj, StunServer, 0x8000, user);
|
|
}
|
|
//! Attempt to perform a basic Stun operation to obtain the public IP/Endpoint of the edge NAT Router
|
|
/*!
|
|
\param StunModule Stun Client module
|
|
\paran StunServer The stun server to utilize in the operation
|
|
\param user Custom user state data
|
|
*/
|
|
void ILibStunClient_PerformStun(void* StunModule, struct sockaddr_in* StunServer, void *user)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)StunModule;
|
|
if (StunServer->sin_family != AF_INET) return;
|
|
ILibStunClient_CheckPreviousStunResults(obj, StunServer, 0x0000, user);
|
|
}
|
|
//! Send a Stun Packet
|
|
/*!
|
|
\param StunModule The Stun Client Module
|
|
\param target Stun Packet destination
|
|
\param data Packet to send
|
|
\param datalen Size of the packet to send
|
|
\param UserFree MemoryOwnership of the data (Who is going to free the memory?)
|
|
*/
|
|
void ILibStunClient_SendData(void* StunModule, struct sockaddr* target, char* data, int datalen, enum ILibAsyncSocket_MemoryOwnership UserFree)
|
|
{
|
|
if (target->sa_family == AF_INET) ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)StunModule)->UDP, (struct sockaddr*)target, data, datalen, UserFree);
|
|
else if (target->sa_family == AF_INET6) ILibAsyncUDPSocket_SendTo(((struct ILibStun_Module*)StunModule)->UDP6, (struct sockaddr*)target, data, datalen, UserFree);
|
|
}
|
|
|
|
void ILibTURN_OnDestroy(void* object)
|
|
{
|
|
struct ILibTURN_TurnClientObject* turn = (struct ILibTURN_TurnClientObject*)object;
|
|
|
|
if (turn->currentRealm != NULL) { free(turn->currentRealm); turn->currentRealm = NULL; }
|
|
if (turn->password != NULL) { free(turn->password); turn->password = NULL; }
|
|
if (turn->username != NULL) { free(turn->username); turn->username = NULL; }
|
|
if (turn->currentNonce != NULL) { free(turn->currentNonce); turn->currentNonce = NULL; }
|
|
|
|
ILibDestroyHashTree(turn->transactionData);
|
|
}
|
|
//! Set a custom user state object
|
|
/*!
|
|
\param clientModule TURN Client
|
|
\param tag User state object
|
|
*/
|
|
void ILibTURN_SetTag(ILibTURN_ClientModule clientModule, void *tag)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)clientModule;
|
|
turn->TAG = tag;
|
|
}
|
|
//! Get a custom user state object
|
|
/*!
|
|
\param clientModule TURN Client
|
|
\return User state object
|
|
*/
|
|
void* ILibTURN_GetTag(ILibTURN_ClientModule clientModule)
|
|
{
|
|
return (((struct ILibTURN_TurnClientObject*)clientModule)->TAG);
|
|
}
|
|
//! Determine if the TURN Client is connected to a server
|
|
/*!
|
|
\param clientModule TURN Client
|
|
\return ZERO = NO, NON-ZERO = YES
|
|
*/
|
|
int ILibTURN_IsConnectedToServer(ILibTURN_ClientModule clientModule)
|
|
{
|
|
return (ILibAsyncSocket_IsConnected(((struct ILibTURN_TurnClientObject*)clientModule)->tcpClient));
|
|
}
|
|
|
|
void ILibTURN_DisconnectFromServer(ILibTURN_ClientModule clientModule)
|
|
{
|
|
ILibAsyncSocket_Disconnect(((struct ILibTURN_TurnClientObject*)clientModule)->tcpClient);
|
|
}
|
|
|
|
void ILibTURN_ProcessChannelData(struct ILibTURN_TurnClientObject *turn, unsigned short ChannelNumber, char* buffer, int offset, int length)
|
|
{
|
|
if (turn->OnChannelDataCallback != NULL) { turn->OnChannelDataCallback(turn, ChannelNumber, buffer, offset, length); }
|
|
}
|
|
|
|
STUN_TYPE ILibTURN_GetMethodType(char* buffer, int offset, int length)
|
|
{
|
|
UNREFERENCED_PARAMETER(length);
|
|
return ((STUN_TYPE)ntohs(((unsigned short*)(buffer + offset))[0]));
|
|
}
|
|
|
|
char* ILibTURN_GetTransactionID(char* buffer, int offset, int length)
|
|
{
|
|
UNREFERENCED_PARAMETER(length);
|
|
return (buffer + offset + 8);
|
|
}
|
|
|
|
int ILibTURN_GetErrorCode(char* buffer, int length)
|
|
{
|
|
int cls;
|
|
int nmb;
|
|
|
|
if (length < 4) { return 0; }
|
|
|
|
cls = ((int)buffer[2] & 0x07);
|
|
nmb = (int)buffer[3] % 100;
|
|
return((100 * cls) + nmb);
|
|
}
|
|
char* ILibTURN_GetErrorReason(char* buffer, int length)
|
|
{
|
|
if (length <= 4) { return(NULL); }
|
|
return(buffer + 4);
|
|
}
|
|
|
|
int ILibTURN_GetAttributeValue(char* buffer, int offset, int length, STUN_ATTRIBUTES attribute, int index, char** value)
|
|
{
|
|
unsigned short messageLength = 20 + ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
int i = 0;
|
|
|
|
UNREFERENCED_PARAMETER(length);
|
|
|
|
offset += 20;
|
|
while (offset + 4 <= messageLength) // Decode each attribute one at a time
|
|
{
|
|
STUN_ATTRIBUTES current = (STUN_ATTRIBUTES)ntohs(((unsigned short*)(buffer + offset))[0]);
|
|
int attrLength = ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
if (offset + 4 + attrLength > messageLength) return 0;
|
|
|
|
if (attribute == current)
|
|
{
|
|
if (i == index)
|
|
{
|
|
if (value != NULL) { *value = buffer + offset + 4; return attrLength; } else { return offset; }
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
offset += (4 + FOURBYTEBOUNDARY(attrLength)); // Move the ptr forward by the attribute length plus padding.
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ILibTURN_GetAttributeStartLocation(char *buffer, int offset, int length, STUN_ATTRIBUTES attribute, int index)
|
|
{
|
|
return(ILibTURN_GetAttributeValue(buffer, offset, length, attribute, index, NULL));
|
|
}
|
|
|
|
int ILibTURN_GetAttributeCount(char* buffer, int offset, int length, STUN_ATTRIBUTES attribute)
|
|
{
|
|
unsigned short messageLength = 20 + ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
int retVal = 0;
|
|
|
|
UNREFERENCED_PARAMETER(length);
|
|
|
|
offset += 20;
|
|
while (offset + 4 <= messageLength) // Decode each attribute one at a time
|
|
{
|
|
STUN_ATTRIBUTES current = (STUN_ATTRIBUTES)ntohs(((unsigned short*)(buffer + offset))[0]);
|
|
int attrLength = ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
if (offset + 4 + attrLength > messageLength) return 0;
|
|
|
|
if (attribute == current) { ++retVal; }
|
|
|
|
offset += (4 + FOURBYTEBOUNDARY(attrLength)); // Move the ptr forward by the attribute length plus padding.
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
void ILibTURN_GenerateIntegrityKey(char* username, char* realm, char* password, char* integrityKey)
|
|
{
|
|
char key[128];
|
|
int keyLen = sprintf_s(key, 128, "%s:%s:%s", username, realm, password);
|
|
util_md5(key, keyLen, integrityKey);
|
|
}
|
|
|
|
// integrity must be at least 20 in size
|
|
void ILibTURN_CalculateMessageIntegrity(char* buffer, int offset, int length, char* key, int keyLen, char* integrity)
|
|
{
|
|
HMAC_CTX* hmac;
|
|
unsigned int hmaclen = 20;
|
|
|
|
// Setup and perform HMAC-SHA1
|
|
hmac = HMAC_CTX_new();
|
|
HMAC_Init_ex(hmac, key, keyLen, EVP_sha1(), NULL);
|
|
HMAC_Update(hmac, (unsigned char*)buffer + offset, length);
|
|
HMAC_Final(hmac, (unsigned char*)integrity, &hmaclen); // Put the HMAC in the outgoing result location
|
|
HMAC_CTX_free(hmac);
|
|
}
|
|
|
|
int ILibTURN_IsPacketAuthenticated(struct ILibTURN_TurnClientObject *turn, char* buffer, int offset, int length)
|
|
{
|
|
char integrityKey[16];
|
|
char integrity[20];
|
|
char* fingerprint;
|
|
int fingerprintLen;
|
|
int fingerprintIndex;
|
|
unsigned int calculated, actual;
|
|
|
|
char* MessageIntegrityValue;
|
|
int MessageIntegrityValueLen, MessageIntegrityPtr;
|
|
|
|
unsigned short tempVal = 0;
|
|
|
|
MessageIntegrityValueLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_MESSAGE_INTEGRITY, 0, &MessageIntegrityValue);
|
|
if (MessageIntegrityValueLen == 0) { return 0; } // Anonymous packet, response with challenge
|
|
|
|
MessageIntegrityPtr = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_MESSAGE_INTEGRITY, 0, NULL);
|
|
fingerprintLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_FINGERPRINT, 0, &fingerprint);
|
|
|
|
// Verify Authenticity, first check Fingerprint
|
|
if (fingerprintLen > 0)
|
|
{
|
|
fingerprintIndex = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_FINGERPRINT, 0, NULL);
|
|
actual = 0x5354554e ^ ntohl(((unsigned int*)fingerprint)[0]);
|
|
calculated = ILibStun_CRC32(buffer + offset, fingerprintIndex);
|
|
if (calculated != actual) { return 0; }
|
|
}
|
|
|
|
// Fix Length if there is a fingerprint
|
|
if (fingerprintLen > 0)
|
|
{
|
|
tempVal = ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
((unsigned short*)(buffer + offset))[1] = htons(tempVal - 8);
|
|
}
|
|
|
|
ILibTURN_GenerateIntegrityKey(turn->username, turn->currentRealm, turn->password, integrityKey);
|
|
ILibTURN_CalculateMessageIntegrity(buffer, offset, MessageIntegrityPtr, integrityKey, 16, integrity);
|
|
|
|
// Put Length Back if we had to adjust the value due to fingerprint
|
|
if (fingerprintLen > 0) { ((unsigned short*)(buffer + offset))[1] = htons(tempVal); }
|
|
|
|
if (MessageIntegrityValueLen == 20 && memcmp(MessageIntegrityValue, integrity, 20) == 0) { return 1; }
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ILibTURN_ProcessStunFormattedPacket(struct ILibTURN_TurnClientObject *turn, char* buffer, int offset, int length)
|
|
{
|
|
void* tmp;
|
|
STUN_TYPE method = ILibTURN_GetMethodType(buffer, offset, length);
|
|
char* TransactionID = ILibTURN_GetTransactionID(buffer, offset, length);
|
|
|
|
if (!IS_INDICATION(method) && !IS_ERR_RESP(method) && !ILibTURN_IsPacketAuthenticated(turn, buffer, offset, length))
|
|
{
|
|
// Packet failed Authentication!
|
|
return;
|
|
}
|
|
|
|
switch (method)
|
|
{
|
|
case TURN_REFRESH_RESPONSE:
|
|
{
|
|
void *ptr;
|
|
int val;
|
|
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, (void*)&ptr, &val);
|
|
ILibTURN_GetAttributeValue(buffer, offset, length, TURN_LIFETIME, 0, (char**)&tmp);
|
|
|
|
if (ptr != NULL)
|
|
{
|
|
if (val == 0 && ptr != NULL)
|
|
{
|
|
((ILibTURN_OnRefreshHandler)ptr)(turn, ntohl(((int*)tmp)[0]), NULL);
|
|
}
|
|
else if (val == 2)
|
|
{
|
|
if (((ILibTURN_TransactionData*)ptr)->Handler != NULL) { ((ILibTURN_OnRefreshHandler)((ILibTURN_TransactionData*)ptr)->Handler)(turn, ntohl(((int*)tmp)[0]), ((ILibTURN_TransactionData*)ptr)->user); }
|
|
free(ptr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TURN_REFRESH_ERROR:
|
|
{
|
|
char *ebuf;
|
|
int ebufLen;
|
|
int ecode;
|
|
void *ptr;
|
|
int ptrLen;
|
|
|
|
ebufLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_ERROR_CODE, 0, &ebuf);
|
|
ecode = ILibTURN_GetErrorCode(ebuf, ebufLen);
|
|
ebuf = ILibTURN_GetErrorReason(ebuf, ebufLen);
|
|
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(turn->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "TURN: Allocation Refresh Error (%d) %s", ecode, ebuf);
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, (void**)&ptr, &ptrLen);
|
|
|
|
switch (ecode)
|
|
{
|
|
case 438: // Stale Nonce, resend request using new Nonce Value
|
|
if (ILibTURN_GetAttributeCount(buffer, offset, length, STUN_ATTRIB_NONCE) == 1)
|
|
{
|
|
ebufLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_NONCE, 0, &ebuf);
|
|
free(turn->currentNonce);
|
|
turn->currentNonceLen = ebufLen;
|
|
turn->currentNonce = ILibString_Copy(ebuf, ebufLen);
|
|
|
|
ILibTURN_RefreshAllocation(turn, ptrLen == 0 ? (ILibTURN_OnRefreshHandler)ptr : (ILibTURN_OnRefreshHandler)((ILibTURN_TransactionData*)ptr)->Handler, ptrLen == 0 ? NULL : ((ILibTURN_TransactionData*)ptr)->user);
|
|
}
|
|
if (ptrLen == 2) { free(ptr); }
|
|
break;
|
|
case 401: // Unauthorized
|
|
break;
|
|
default:
|
|
if (ptrLen == 2) { free(ptr); }
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TURN_CHANNEL_BIND_ERROR:
|
|
{
|
|
ILibTURN_TransactionData *ptr;
|
|
int val;
|
|
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, (void**)&ptr, &val);
|
|
if (ptr != NULL)
|
|
{
|
|
if (ptr->Handler != NULL) { ((ILibTURN_OnCreateChannelBindingHandler)ptr->Handler)(turn, (unsigned short)val, 0, ptr->user); }
|
|
free(ptr);
|
|
}
|
|
ILibDeleteEntry(turn->transactionData, TransactionID, 12);
|
|
break;
|
|
}
|
|
case TURN_CHANNEL_BIND_RESPONSE:
|
|
{
|
|
ILibTURN_TransactionData *ptr;
|
|
int val;
|
|
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, (void**)&ptr, &val);
|
|
if (ptr != NULL)
|
|
{
|
|
if (ptr->Handler != NULL) { ((ILibTURN_OnCreateChannelBindingHandler)ptr->Handler)(turn, (unsigned short)val, 1, ptr->user); }
|
|
free(ptr);
|
|
ILibDeleteEntry(turn->transactionData, TransactionID, 12);
|
|
}
|
|
break;
|
|
}
|
|
case TURN_DATA: // Data-Indication
|
|
{
|
|
char *remotePeer, *data;
|
|
int remotePeerLen, dataLen;
|
|
|
|
remotePeerLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_XOR_PEER_ADDRESS, 0, &remotePeer);
|
|
dataLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_DATA, 0, &data);
|
|
|
|
if (remotePeerLen > 0 && dataLen > 0)
|
|
{
|
|
if (turn->OnDataIndicationCallback != NULL)
|
|
{
|
|
char peer[8+sizeof(struct sockaddr_in6)];
|
|
((int*)(peer + sizeof(struct sockaddr_in6)))[0] = 4;
|
|
((int*)(peer + sizeof(struct sockaddr_in6)))[1] = 0x10000;
|
|
|
|
ILibTURN_GetXORMappedAddress(remotePeer, remotePeerLen, TransactionID, (struct sockaddr_in6*)peer);
|
|
turn->OnDataIndicationCallback(turn, (struct sockaddr_in6*)peer, data, 0, dataLen);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case TURN_CREATE_PERMISSION_ERROR:
|
|
{
|
|
void *ptr;
|
|
int val;
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, &ptr, &val);
|
|
|
|
if (val == 0)
|
|
{
|
|
if (ptr != NULL) { ((ILibTURN_OnCreatePermissionHandler)ptr)(turn, 0, NULL); }
|
|
}
|
|
else
|
|
{
|
|
if (((ILibTURN_TransactionData*)ptr)->Handler != NULL)
|
|
{
|
|
((ILibTURN_OnCreatePermissionHandler)((ILibTURN_TransactionData*)ptr)->Handler)(turn, 0, ((ILibTURN_TransactionData*)ptr)->user);
|
|
}
|
|
free(ptr);
|
|
}
|
|
|
|
ILibDeleteEntry(turn->transactionData, TransactionID, 12);
|
|
break;
|
|
}
|
|
case TURN_CREATE_PERMISSION_RESPONSE:
|
|
{
|
|
void *ptr;
|
|
int val;
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, &ptr, &val);
|
|
|
|
if (val == 0)
|
|
{
|
|
if (ptr != NULL) { ((ILibTURN_OnCreatePermissionHandler)ptr)(turn, 1, NULL); }
|
|
}
|
|
else
|
|
{
|
|
if (((ILibTURN_TransactionData*)ptr)->Handler != NULL)
|
|
{
|
|
((ILibTURN_OnCreatePermissionHandler)((ILibTURN_TransactionData*)ptr)->Handler)(turn, 1, ((ILibTURN_TransactionData*)ptr)->user);
|
|
}
|
|
free(ptr);
|
|
}
|
|
ILibDeleteEntry(turn->transactionData, TransactionID, 12);
|
|
break;
|
|
}
|
|
case TURN_ALLOCATE_ERROR:
|
|
{
|
|
if (ILibTURN_GetAttributeCount(buffer, offset, length, STUN_ATTRIB_REALM) == 1 && ILibTURN_GetAttributeCount(buffer, offset, length, STUN_ATTRIB_NONCE) == 1)
|
|
{
|
|
//
|
|
// Resend the Allocate Request, but this time with Message Integrity
|
|
//
|
|
char* nonce;
|
|
char* realm;
|
|
int nonceLen, realmLen, inputKeyLen, packetPtr;
|
|
char inputKey[128];
|
|
char outputKey[16];
|
|
char packet[256];
|
|
char NewTransactionID[12];
|
|
char Transport[4];
|
|
ILibTURN_TransportTypes transport;
|
|
|
|
if (ILibHasEntry(turn->transactionData, TransactionID, 12))
|
|
{
|
|
nonceLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_NONCE, 0, &nonce);
|
|
realmLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_REALM, 0, &realm);
|
|
|
|
if (turn->currentNonce != NULL) { free(turn->currentNonce); }
|
|
if ((turn->currentNonce = (char*)malloc(nonceLen + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(turn->currentNonce, nonceLen + 1, nonce, nonceLen);
|
|
turn->currentNonce[nonceLen] = 0;
|
|
turn->currentNonceLen = nonceLen;
|
|
|
|
if (turn->currentRealm != NULL) { free(turn->currentRealm); }
|
|
if ((turn->currentRealm = (char*)malloc(realmLen + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(turn->currentRealm, realmLen + 1, realm, realmLen);
|
|
turn->currentRealm[realmLen] = 0;
|
|
turn->currentRealmLen = realmLen;
|
|
|
|
inputKeyLen = sprintf_s(inputKey, 128, "%s:%s:%s", turn->username, turn->currentRealm, turn->password);
|
|
|
|
util_md5(inputKey, inputKeyLen, outputKey);
|
|
util_random(12, NewTransactionID);
|
|
ILibGetEntryEx(turn->transactionData, TransactionID, 12, &tmp, (int*)(&transport));
|
|
ILibDeleteEntry(turn->transactionData, TransactionID, 12); // We're going to delete our TransactionID, without adding the new one, so if we fail again, we'll abort
|
|
((int*)Transport)[0] = 0;
|
|
Transport[0] = (char)transport;
|
|
|
|
packetPtr = ILibTURN_GenerateStunFormattedPacketHeader(packet, TURN_ALLOCATE, NewTransactionID);
|
|
packetPtr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, packetPtr, STUN_ATTRIB_REQUESTED_TRANSPORT, Transport, 4);
|
|
packetPtr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, packetPtr, STUN_ATTRIB_NONCE, nonce, nonceLen);
|
|
packetPtr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, packetPtr, STUN_ATTRIB_REALM, realm, realmLen);
|
|
packetPtr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, packetPtr, STUN_ATTRIB_USERNAME, turn->username, turn->usernameLen);
|
|
|
|
packetPtr += ILibStun_AddMessageIntegrityAttr(packet, packetPtr, outputKey, 16);
|
|
packetPtr += ILibStun_AddFingerprint(packet, packetPtr);
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, packet, packetPtr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
else
|
|
{
|
|
// TransactionID was not found. This means we already tried to authenticate, but it has failed.
|
|
// Nothing we can do, but error out.
|
|
if (turn->OnAllocateCallback != NULL) { turn->OnAllocateCallback(turn, 0, NULL); }
|
|
}
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case TURN_ALLOCATE_RESPONSE:
|
|
{
|
|
char *val, *transportAddress;
|
|
int valLen, transportAddressLen;
|
|
struct sockaddr_in6 address;
|
|
|
|
valLen = ILibTURN_GetAttributeValue(buffer, offset, length, TURN_LIFETIME, 0, &val);
|
|
transportAddressLen = ILibTURN_GetAttributeValue(buffer, offset, length, STUN_ATTRIB_XOR_RELAY_ADDRESS, 0, &transportAddress);
|
|
if (transportAddressLen > 0)
|
|
{
|
|
ILibTURN_GetXORMappedAddress(transportAddress, transportAddressLen, TransactionID, &address);
|
|
}
|
|
if (valLen > 0 && transportAddressLen > 0)
|
|
{
|
|
int lifetime = ntohl(((unsigned int*)val)[0]);
|
|
if (turn->OnAllocateCallback != NULL) { turn->OnAllocateCallback(turn, lifetime, &address); }
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ILibTURN_GetStunPacketLength(char* buffer, int offset, int length)
|
|
{
|
|
unsigned short messageLength = 20 + ntohs(((unsigned short*)(buffer + offset))[1]);
|
|
int magic = ntohl(((unsigned int*)(buffer + offset))[1]);
|
|
if (messageLength > length || magic != 0x2112A442) // Check the length and magic string
|
|
{
|
|
return 0;
|
|
}
|
|
return ((int)messageLength);
|
|
}
|
|
|
|
void ILibTURN_TCP_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)*user;
|
|
|
|
UNREFERENCED_PARAMETER(socketModule);
|
|
UNREFERENCED_PARAMETER(OnInterrupt);
|
|
UNREFERENCED_PARAMETER(PAUSE);
|
|
|
|
if (endPointer >= 4)
|
|
{
|
|
if (ntohs(((unsigned short*)(buffer + *p_beginPointer))[0]) >> 14 == 1)
|
|
{
|
|
// This is Channel Data
|
|
unsigned short ChannelNumber = (unsigned short)((int)ntohs(((unsigned short*)(buffer + *p_beginPointer))[0]) ^ (int)0x4000);
|
|
unsigned short ChannelDataLength = ntohs(((unsigned short*)(buffer + *p_beginPointer))[1]);
|
|
if (endPointer >= (4 + FOURBYTEBOUNDARY(ChannelDataLength)))
|
|
{
|
|
ILibTURN_ProcessChannelData(turn, ChannelNumber, buffer, *p_beginPointer + 4, ChannelDataLength);
|
|
*p_beginPointer += (4 + FOURBYTEBOUNDARY(ChannelDataLength));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (endPointer >= 8)
|
|
{
|
|
// STUN Formatted Packet
|
|
int stunLength = ILibTURN_GetStunPacketLength(buffer, *p_beginPointer, endPointer);
|
|
if (endPointer >= stunLength)
|
|
{
|
|
ILibTURN_ProcessStunFormattedPacket(turn, buffer, *p_beginPointer, stunLength);
|
|
*p_beginPointer += stunLength;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ILibTURN_TCP_OnConnect(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)user;
|
|
|
|
UNREFERENCED_PARAMETER(socketModule);
|
|
|
|
if (turn->OnConnectTurnCallback != NULL) { turn->OnConnectTurnCallback(turn, Connected); }
|
|
}
|
|
|
|
void ILibTURN_TCP_OnDisconnect(ILibAsyncSocket_SocketModule socketModule, void *user)
|
|
{
|
|
UNREFERENCED_PARAMETER(socketModule);
|
|
UNREFERENCED_PARAMETER(user);
|
|
|
|
// We aren't doing anything here, because when the user goes to send something, the return value will reflect that this disconnected
|
|
}
|
|
|
|
ILibTURN_ClientModule ILibTURN_CreateTurnClient(void* chain, ILibTURN_OnConnectTurnHandler OnConnectTurn, ILibTURN_OnAllocateHandler OnAllocate, ILibTURN_OnDataIndicationHandler OnData, ILibTURN_OnChannelDataHandler OnChannelData)
|
|
{
|
|
struct ILibTURN_TurnClientObject* retVal = (struct ILibTURN_TurnClientObject*)malloc(sizeof(struct ILibTURN_TurnClientObject));
|
|
if (retVal == NULL){ ILIBCRITICALEXIT(254); }
|
|
memset(retVal, 0, sizeof(struct ILibTURN_TurnClientObject));
|
|
retVal->ChainLink.DestroyHandler = &ILibTURN_OnDestroy;
|
|
retVal->tcpClient = ILibCreateAsyncSocketModule(chain, 4096, &ILibTURN_TCP_OnData, &ILibTURN_TCP_OnConnect, &ILibTURN_TCP_OnDisconnect, NULL);
|
|
retVal->OnConnectTurnCallback = OnConnectTurn;
|
|
retVal->OnAllocateCallback = OnAllocate;
|
|
retVal->OnDataIndicationCallback = OnData;
|
|
retVal->OnChannelDataCallback = OnChannelData;
|
|
retVal->transactionData = ILibInitHashTree();
|
|
retVal->ChainLink.ParentChain = chain;
|
|
retVal->ChainLink.MetaData = ILibMemory_SmartAllocate_FromString("ILibWebRTC_TURN_Client");
|
|
ILibAddToChain(chain, retVal);
|
|
|
|
return retVal;
|
|
}
|
|
//! Initiates a connection with a TURN Server
|
|
|
|
//! Will trigger the OnConnectTurn handler specified in ILibTURN_CreateTurnClient
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param turnServer TURN Server Address/Port
|
|
\param username TURN/Username
|
|
\param usernameLen TURN/Username Length
|
|
\param password TURN/Password
|
|
\param passwordLen TURN/Password Length
|
|
\param proxyServer [Reserved for future use]
|
|
*/
|
|
void ILibTURN_ConnectTurnServer(ILibTURN_ClientModule turnModule, struct sockaddr_in* turnServer, char* username, int usernameLen, char* password, int passwordLen, struct sockaddr_in* proxyServer)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*) turnModule;
|
|
|
|
UNREFERENCED_PARAMETER(proxyServer);
|
|
|
|
if ((turn->username = (char*)malloc(usernameLen + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
if ((turn->password = (char*)malloc(passwordLen + 1)) == NULL){ ILIBCRITICALEXIT(254); }
|
|
memcpy_s(turn->username, usernameLen + 1, username, usernameLen);
|
|
memcpy_s(turn->password, passwordLen + 1, password, passwordLen);
|
|
turn->username[usernameLen] = 0;
|
|
turn->password[passwordLen] = 0;
|
|
turn->usernameLen = usernameLen;
|
|
turn->passwordLen = passwordLen;
|
|
|
|
ILibAsyncSocket_ConnectTo(turn->tcpClient, NULL, (struct sockaddr*)turnServer, NULL, turn);
|
|
}
|
|
|
|
int ILibTURN_GenerateStunFormattedPacketHeader(char* rbuffer, STUN_TYPE packetType, char* transactionID)
|
|
{
|
|
((unsigned short*)rbuffer)[0] = htons(packetType); // Allocate, skip setting length for now
|
|
((unsigned int*)rbuffer)[1] = htonl(0x2112A442); // Set the magic string
|
|
memcpy_s(rbuffer + 8, 12, transactionID, 12);
|
|
return 20;
|
|
}
|
|
|
|
int ILibTURN_AddAttributeToStunFormattedPacketHeader(char* rbuffer, int rptr, STUN_ATTRIBUTES attrType, char* data, int dataLen)
|
|
{
|
|
((unsigned short*)(rbuffer + rptr))[0] = htons(attrType); // Attribute header
|
|
((unsigned short*)(rbuffer + rptr))[1] = htons((unsigned short)dataLen); // Attribute length
|
|
if (dataLen > 0) { memcpy_s(rbuffer + rptr + 4, dataLen, data, dataLen); }
|
|
return(ILibAlignOnFourByteBoundary(rbuffer+rptr, 4+dataLen));
|
|
}
|
|
//! Decode a STUN/XORMAPPED Address
|
|
/*!
|
|
\param buffer Buffer containing the STUN/XORMAPPED Address
|
|
\param bufferLen Length of Buffer
|
|
\param transactionID TransactionID of the packet that the address was encoded to
|
|
\param[in,out] value The decoded address
|
|
*/
|
|
void ILibTURN_GetXORMappedAddress(char* buffer, int bufferLen, char* transactionID, struct sockaddr_in6 *value)
|
|
{
|
|
int i;
|
|
|
|
UNREFERENCED_PARAMETER(bufferLen);
|
|
|
|
switch (buffer[1])
|
|
{
|
|
case 1: // IPv4
|
|
{
|
|
int addr = ntohl(((unsigned int*)buffer)[1]) ^ 0x2112A442;
|
|
memset(value, 0, INET_SOCKADDR_LENGTH(AF_INET));
|
|
value->sin6_family = AF_INET;
|
|
((struct sockaddr_in*)value)->sin_addr.s_addr = htonl(addr);
|
|
}
|
|
break;
|
|
case 2: // IPv6
|
|
{
|
|
unsigned char addr[16];
|
|
unsigned char material[16];
|
|
((unsigned int*)material)[0] = 0x2112A442;
|
|
memcpy_s(material + 4, sizeof(material) - 4, transactionID, 12);
|
|
memset(value, 0, INET_SOCKADDR_LENGTH(AF_INET6));
|
|
value->sin6_family = AF_INET6;
|
|
|
|
if (htonl(16) == 16)
|
|
{
|
|
for (i = 0; i < 16; ++i) { value->sin6_addr.s6_addr[i] = (buffer + 4)[i] ^ material[i]; }
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 16; ++i) { addr[i] = material[i] ^ (buffer + 4)[15 - i]; value->sin6_addr.s6_addr[15 - i] = addr[i]; }
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
value->sin6_port = htons(ntohs(((unsigned short*)buffer)[1]) ^ (unsigned short)((unsigned int)0x2112A442 >> 16));
|
|
}
|
|
//! Create a STUN XOR/MAPPED Address
|
|
/*!
|
|
\param endpoint IPAddress (IPv4 or IPv6) to map
|
|
\param buffer Buffer to write the result to
|
|
\param transactionID STUN Transaction ID of the packet to encode the address for [Required for IPv6]
|
|
\return Number of bytes written to buffer
|
|
*/
|
|
int ILibTURN_CreateXORMappedAddress(struct sockaddr_in6 *endpoint, char* buffer, char* transactionID)
|
|
{
|
|
int i, retVal;
|
|
unsigned short port = ntohs(endpoint->sin6_port);
|
|
port = port ^ (unsigned short)((unsigned int)0x2112A442 >> 16);
|
|
|
|
buffer[0] = 0;
|
|
buffer[1] = endpoint->sin6_family == AF_INET ? 1 : 2;
|
|
((unsigned short*)buffer)[1] = htons(port);
|
|
|
|
switch (endpoint->sin6_family)
|
|
{
|
|
case AF_INET:
|
|
((unsigned int*)buffer)[1] = htonl(ntohl(((struct sockaddr_in*)endpoint)->sin_addr.s_addr) ^ 0x2112A442);
|
|
retVal = 8;
|
|
break;
|
|
case AF_INET6:
|
|
{
|
|
unsigned char addr[16];
|
|
unsigned char material[16];
|
|
((unsigned int*)material)[0] = 0x2112A442;
|
|
memcpy_s(material + 4, sizeof(material) - 4, transactionID, 12);
|
|
|
|
if (htonl(16) == /* DISABLES CODE */ (16))
|
|
{
|
|
for (i = 0; i < 16; ++i) { endpoint->sin6_addr.s6_addr[i] = endpoint->sin6_addr.s6_addr[i] ^ material[i]; }
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 16; ++i) { addr[i] = endpoint->sin6_addr.s6_addr[15 - i] ^ material[i]; }
|
|
for (i = 0; i < 16; ++i) { endpoint->sin6_addr.s6_addr[15 - i] = addr[i]; }
|
|
}
|
|
|
|
memcpy_s(buffer + 4, 16, endpoint->sin6_addr.s6_addr, 16);
|
|
retVal = 20;
|
|
}
|
|
break;
|
|
default:
|
|
retVal = 0;
|
|
break;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
//! Request a TURN Allocation
|
|
|
|
//! Allocation Response will be received via OnAllocate handler specified in ILibTURN_CreateTurnClient
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param transportType Desired Transport Type to allocate
|
|
*/
|
|
void ILibTURN_Allocate(ILibTURN_ClientModule turnModule, ILibTURN_TransportTypes transportType)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
char rbuffer[256];
|
|
char TransactionID[12];
|
|
int rptr;
|
|
char Transport[4];
|
|
|
|
((int*)Transport)[0] = 0;
|
|
Transport[0] = (char)transportType;
|
|
|
|
util_random(12, TransactionID); // Random used for transaction id
|
|
rptr = ILibTURN_GenerateStunFormattedPacketHeader(rbuffer, TURN_ALLOCATE, TransactionID);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_REQUESTED_TRANSPORT, Transport, 4);
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr); // Set the length in this function
|
|
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, NULL, (int)transportType);
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, rbuffer, rptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
void ILibTURN_RefreshAllocation(ILibTURN_ClientModule turnModule, ILibTURN_OnRefreshHandler handler, void *user)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
char rbuffer[256];
|
|
char TransactionID[12];
|
|
int rptr;
|
|
char integrityKey[16];
|
|
|
|
util_random(12, TransactionID); // Random used for transaction id
|
|
ILibTURN_GenerateIntegrityKey(turn->username, turn->currentRealm, turn->password, integrityKey);
|
|
|
|
rptr = ILibTURN_GenerateStunFormattedPacketHeader(rbuffer, TURN_REFRESH, TransactionID);
|
|
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_USERNAME, turn->username, turn->usernameLen);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_REALM, turn->currentRealm, turn->currentRealmLen);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_NONCE, turn->currentNonce, turn->currentNonceLen);
|
|
|
|
rptr += ILibStun_AddMessageIntegrityAttr(rbuffer, rptr, integrityKey, 16);
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr);
|
|
|
|
if (user != NULL)
|
|
{
|
|
ILibTURN_TransactionData *ptr = ILibMemory_Allocate(sizeof(ILibTURN_TransactionData), 0, NULL, NULL);
|
|
ptr->Handler = (void*)handler;
|
|
ptr->user = user;
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, ptr, 2);
|
|
}
|
|
else
|
|
{
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, (void*)handler, 0);
|
|
}
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, rbuffer, rptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
|
|
//! Create/Renew a Transport Permission
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param permissions Array of addresses to allow on the RELAY
|
|
\param permissionsLength Number of array elements in permissions
|
|
\param result Event handler to trigger on completion of this request
|
|
\param user Custom user state object
|
|
*/
|
|
void ILibTURN_CreatePermission(ILibTURN_ClientModule turnModule, struct sockaddr_in6* permissions, int permissionsLength, ILibTURN_OnCreatePermissionHandler result, void *user)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
char peer[20];
|
|
char rbuffer[256];
|
|
char TransactionID[12];
|
|
int rptr, peerLen;
|
|
int i;
|
|
char integrityKey[16];
|
|
|
|
util_random(12, TransactionID); // Random used for transaction id
|
|
ILibTURN_GenerateIntegrityKey(turn->username, turn->currentRealm, turn->password, integrityKey);
|
|
|
|
rptr = ILibTURN_GenerateStunFormattedPacketHeader(rbuffer, TURN_CREATE_PERMISSION, TransactionID);
|
|
for (i = 0; i < permissionsLength; ++i)
|
|
{
|
|
ILibRemoteLogging_printf(ILibChainGetLogger(turn->ChainLink.ParentChain), ILibRemoteLogging_Modules_WebRTC_STUN_ICE, ILibRemoteLogging_Flags_VerbosityLevel_1, "TURN: Creating Permission for %s", ILibRemoteLogging_ConvertAddress((struct sockaddr*)&(permissions[i])));
|
|
peerLen = ILibTURN_CreateXORMappedAddress(&(permissions[i]), peer, TransactionID);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_XOR_PEER_ADDRESS, peer, peerLen);
|
|
}
|
|
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_USERNAME, turn->username, turn->usernameLen);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_REALM, turn->currentRealm, turn->currentRealmLen);
|
|
rptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(rbuffer, rptr, STUN_ATTRIB_NONCE, turn->currentNonce, turn->currentNonceLen);
|
|
|
|
rptr += ILibStun_AddMessageIntegrityAttr(rbuffer, rptr, integrityKey, 16);
|
|
rptr += ILibStun_AddFingerprint(rbuffer, rptr);
|
|
|
|
if (result != NULL)
|
|
{
|
|
if (user != NULL)
|
|
{
|
|
ILibTURN_TransactionData *ptr = ILibMemory_Allocate(sizeof(ILibTURN_TransactionData), 0, NULL, NULL);
|
|
ptr->Handler = (void*)result;
|
|
ptr->user = user;
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, ptr, 2);
|
|
}
|
|
else
|
|
{
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, (void*)result, 0);
|
|
}
|
|
}
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, rbuffer, rptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
//! Send Data as a TURN/INDICATION
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param remotePeer Destination
|
|
\param buffer Data to send
|
|
\param offset Data offset
|
|
\param length Data length
|
|
\return Send Status of operation
|
|
*/
|
|
enum ILibAsyncSocket_SendStatus ILibTURN_SendIndication(ILibTURN_ClientModule turnModule, struct sockaddr_in6* remotePeer, char* buffer, int offset, int length)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
char Address[20];
|
|
char TransactionID[12];
|
|
char *packet = (char*)malloc(50 + length);
|
|
int ptr, AddressLen;
|
|
if (packet == NULL){ ILIBCRITICALEXIT(254); }
|
|
util_random(12, TransactionID);
|
|
ptr = ILibTURN_GenerateStunFormattedPacketHeader(packet, TURN_SEND, TransactionID);
|
|
AddressLen = ILibTURN_CreateXORMappedAddress(remotePeer, Address, TransactionID);
|
|
|
|
ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, ptr, STUN_ATTRIB_XOR_PEER_ADDRESS, Address, AddressLen);
|
|
ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(packet, ptr, STUN_ATTRIB_DATA, buffer + offset, length);
|
|
ptr += ILibStun_AddFingerprint(packet, ptr);
|
|
|
|
return ILibAsyncSocket_Send(turn->tcpClient, packet, ptr, ILibAsyncSocket_MemoryOwnership_CHAIN);
|
|
}
|
|
//! Create/Renew a TURN Channel Binding
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param channelNumber Channel Number to create/renew
|
|
\param remotePeer Remote Endpoint to bind
|
|
\param result Event to trigger on Create/Renew completion
|
|
\param user Custom user state object
|
|
*/
|
|
void ILibTURN_CreateChannelBinding(ILibTURN_ClientModule turnModule, unsigned short channelNumber, struct sockaddr_in6* remotePeer, ILibTURN_OnCreateChannelBindingHandler result, void* user)
|
|
{
|
|
char TransactionID[12];
|
|
char Peer[20];
|
|
char IntegrityKey[16];
|
|
char Packet[256];
|
|
char Channel[4];
|
|
int Ptr, PeerLen;
|
|
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
|
|
util_random(12, TransactionID);
|
|
PeerLen = ILibTURN_CreateXORMappedAddress(remotePeer, Peer, TransactionID);
|
|
ILibTURN_GenerateIntegrityKey(turn->username, turn->currentRealm, turn->password, IntegrityKey);
|
|
((unsigned short*)Channel)[0] = htons(channelNumber ^ 0x4000);
|
|
((unsigned short*)Channel)[1] = 0;
|
|
|
|
Ptr = ILibTURN_GenerateStunFormattedPacketHeader(Packet, TURN_CHANNEL_BIND, TransactionID);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_XOR_PEER_ADDRESS, Peer, PeerLen);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, TURN_CHANNEL_NUMBER, Channel, 4);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_USERNAME, turn->username, turn->usernameLen);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_REALM, turn->currentRealm, turn->currentRealmLen);
|
|
Ptr += ILibTURN_AddAttributeToStunFormattedPacketHeader(Packet, Ptr, STUN_ATTRIB_NONCE, turn->currentNonce, turn->currentNonceLen);
|
|
|
|
Ptr += ILibStun_AddMessageIntegrityAttr(Packet, Ptr, IntegrityKey, 16);
|
|
Ptr += ILibStun_AddFingerprint(Packet, Ptr);
|
|
|
|
if (result != NULL)
|
|
{
|
|
// Only malloc memory if the user actually cares to get called back
|
|
ILibTURN_TransactionData *u = ILibMemory_Allocate(sizeof(ILibTURN_TransactionData), 0, NULL, NULL);
|
|
u->Handler = (void*)result;
|
|
u->user = user;
|
|
ILibAddEntryEx(turn->transactionData, TransactionID, 12, u, (int)channelNumber);
|
|
}
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, Packet, Ptr, ILibAsyncSocket_MemoryOwnership_USER);
|
|
}
|
|
//! Get the number of bytes that are buffered to be sent
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\return Number of bytes waiting to be sent
|
|
*/
|
|
unsigned int ILibTURN_GetPendingBytesToSend(ILibTURN_ClientModule turnModule)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
return ILibAsyncSocket_GetPendingBytesToSend(turn->tcpClient);
|
|
}
|
|
//! Send data through a TURN/CHANNEL Binding
|
|
/*!
|
|
\param turnModule TURN Client
|
|
\param channelNumber Channel number binding to use
|
|
\param buffer Data to send
|
|
\param offset Data offset
|
|
\param length Data Length
|
|
\return Send Status of operation
|
|
*/
|
|
enum ILibAsyncSocket_SendStatus ILibTURN_SendChannelData(ILibTURN_ClientModule turnModule, unsigned short channelNumber, char* buffer, int offset, int length)
|
|
{
|
|
struct ILibTURN_TurnClientObject *turn = (struct ILibTURN_TurnClientObject*)turnModule;
|
|
char header[4];
|
|
enum ILibAsyncSocket_SendStatus retVal;
|
|
|
|
((unsigned short*)header)[0] = htons(channelNumber ^ 0x4000);
|
|
((unsigned short*)header)[1] = htons((unsigned short)length);
|
|
|
|
ILibAsyncSocket_Send(turn->tcpClient, header, 4, ILibAsyncSocket_MemoryOwnership_USER);
|
|
retVal = ILibAsyncSocket_Send(turn->tcpClient, buffer + offset, length, ILibAsyncSocket_MemoryOwnership_USER);
|
|
if (length % 4 > 0) { retVal = ILibAsyncSocket_Send(turn->tcpClient, header, 4 - (length % 4), ILibAsyncSocket_MemoryOwnership_USER); }
|
|
return retVal;
|
|
}
|
|
|
|
#ifdef _WEBRTCDEBUG
|
|
void ILibSCTP_SetSimulatedInboundLossPercentage(void *stunModule, int lossPercentage)
|
|
{
|
|
struct ILibStun_Module *obj = (struct ILibStun_Module*)stunModule;
|
|
obj->lossPercentage = lossPercentage;
|
|
}
|
|
|
|
void ILibSCTP_SetTSNCallback(void *dtlsSession, ILibSCTP_OnTSNChanged tsnHandler)
|
|
{
|
|
struct ILibStun_dTlsSession *session = (struct ILibStun_dTlsSession*)dtlsSession;
|
|
session->onTSNChanged = tsnHandler;
|
|
}
|
|
|
|
int ILibSCTP_Debug_SetDebugCallback(void* dtlsSession, char* debugFieldName, ILibSCTP_OnSCTPDebug handler)
|
|
{
|
|
struct ILibStun_dTlsSession *session = (struct ILibStun_dTlsSession*)dtlsSession;
|
|
|
|
if (strcmp(debugFieldName, "OnHold") == 0)
|
|
{
|
|
session->onHold = handler;
|
|
if (handler != NULL) { handler(session, "OnHold", session->holdingCount); }
|
|
}
|
|
else if (strcmp(debugFieldName, "OnReceiverCredits") == 0)
|
|
{
|
|
session->onReceiverCredits = handler;
|
|
if (handler != NULL) { handler(session, "OnReceiverCredits", session->receiverCredits); }
|
|
}
|
|
else if (strcmp(debugFieldName, "OnSendFastRetry") == 0)
|
|
{
|
|
session->onSendFastRetry = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnSendRetry") == 0)
|
|
{
|
|
session->onSendRetry = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnCongestionWindowSizeChanged") == 0)
|
|
{
|
|
session->onCongestionWindowSizeChanged = handler;
|
|
if (handler != NULL) { handler(session, "OnCongestionWindowSizeChanged", session->congestionWindowSize); }
|
|
}
|
|
else if (strcmp(debugFieldName, "OnSACKReceived") == 0)
|
|
{
|
|
session->onSACKreceived = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnT3RTX") == 0)
|
|
{
|
|
session->onT3RTX = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnFastRecovery") == 0)
|
|
{
|
|
session->onFastRecovery = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnRTTCalculated") == 0)
|
|
{
|
|
session->onRTTCalculated = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnTSNFloorNotRaised") == 0)
|
|
{
|
|
session->onTSNFloorNotRaised = handler;
|
|
}
|
|
else if (strcmp(debugFieldName, "OnRetryPacket") == 0)
|
|
{
|
|
session->onRetryPacket = handler;
|
|
}
|
|
else { return 1; }
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#include "ILibParsers.h"
|
|
#ifdef MICROSTACK_NOTLS
|
|
#if defined(WINSOCK2)
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
#endif
|
|
|
|
/* zlib.h -- interface of the 'zlib' general purpose compression library
|
|
version 1.2.11, January 15th, 2017
|
|
|
|
Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Jean-loup Gailly Mark Adler
|
|
jloup@gzip.org madler@alumni.caltech.edu
|
|
|
|
|
|
The data format used by the zlib library is described by RFCs (Request for
|
|
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
|
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
|
|
|
|
***************************************************************************
|
|
***************************************************************************
|
|
*
|
|
* The CRC32/zlib implementation below was modified March 2018 by Intel Corp, to implement CRC32C (Castagnoli CRC32)
|
|
* Original source obtained from: https://zlib.net/zlib-1.2.11.tar.gz
|
|
* More information about zlib can be found at: https://zlib.net/
|
|
*
|
|
*/
|
|
|
|
uint32_t crc_table[2][256] =
|
|
{
|
|
{
|
|
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
|
|
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
|
|
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
|
|
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
|
|
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
|
|
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
|
|
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
|
|
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
|
|
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
|
|
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
|
|
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
|
|
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
|
|
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
|
|
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
|
|
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
|
|
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
|
|
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
|
|
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
|
|
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
|
|
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
|
|
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
|
|
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
|
|
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
|
|
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
|
|
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
|
|
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
|
|
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
|
|
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
|
|
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
|
|
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
|
|
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
|
|
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
|
|
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
|
|
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
|
|
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
|
|
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
|
|
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
|
|
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
|
|
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
|
|
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
|
|
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
|
|
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
|
|
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
|
|
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
|
|
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
|
|
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
|
|
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
|
|
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
|
|
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
|
|
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
|
|
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
|
|
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
|
|
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
|
|
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
|
|
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
|
|
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
|
|
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
|
|
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
|
|
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
|
|
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
|
|
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
|
|
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
|
|
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
|
|
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
|
|
},
|
|
{
|
|
0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
|
|
0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
|
|
0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
|
|
0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
|
|
0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
|
|
0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
|
|
0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
|
|
0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
|
|
0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
|
|
0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
|
|
0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
|
|
0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
|
|
0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
|
|
0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
|
|
0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
|
|
0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
|
|
0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
|
|
0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
|
|
0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
|
|
0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
|
|
0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
|
|
0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
|
|
0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
|
|
0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
|
|
0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
|
|
0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
|
|
0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
|
|
0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
|
|
0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
|
|
0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
|
|
0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
|
|
0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
|
|
0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
|
|
0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
|
|
0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
|
|
0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
|
|
0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
|
|
0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
|
|
0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
|
|
0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
|
|
0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
|
|
0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
|
|
0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
|
|
0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
|
|
0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
|
|
0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
|
|
0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
|
|
0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
|
|
0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
|
|
0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
|
|
0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
|
|
0x2d02ef8dUL
|
|
}
|
|
};
|
|
|
|
/* ========================================================================= */
|
|
#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
|
|
#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
|
|
|
|
#define EO1 crc = crc_table[1][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
|
|
#define EO8 EO1; EO1; EO1; EO1; EO1; EO1; EO1; EO1
|
|
|
|
/* ========================================================================= */
|
|
uint32_t crc32c_z(uint32_t crc, const unsigned char* buf, uint32_t len)
|
|
{
|
|
if (buf == NULL) return 0UL;
|
|
|
|
crc = crc ^ 0xffffffffUL;
|
|
while (len >= 8) {
|
|
DO8;
|
|
len -= 8;
|
|
}
|
|
if (len) do {
|
|
DO1;
|
|
} while (--len);
|
|
return crc ^ 0xffffffffUL;
|
|
}
|
|
uint32_t crc32_z(uint32_t crc, const unsigned char* buf, uint32_t len)
|
|
{
|
|
if (buf == NULL) return 0UL;
|
|
|
|
crc = crc ^ 0xffffffffUL;
|
|
while (len >= 8) {
|
|
EO8;
|
|
len -= 8;
|
|
}
|
|
if (len) do {
|
|
EO1;
|
|
} while (--len);
|
|
return crc ^ 0xffffffffUL;
|
|
}
|
|
/* ========================================================================= */
|
|
uint32_t crc32c(uint32_t crc, const unsigned char* buf, uint32_t len)
|
|
{
|
|
return crc32c_z(crc, buf, len);
|
|
}
|
|
uint32_t crc32(uint32_t crc, const unsigned char* buf, uint32_t len)
|
|
{
|
|
return crc32_z(crc, buf, len);
|
|
}
|
|
|
|
|