/** * Copyright (c) 2020 Paul-Louis Ageneau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #ifndef JUICE_AGENT_H #define JUICE_AGENT_H #include "addr.h" #include "conn.h" #include "ice.h" #include "juice.h" #include "stun.h" #include "thread.h" #include "timestamp.h" #include "turn.h" #include #include // RFC 8445: Agents MUST NOT use an RTO value smaller than 500 ms. #define MIN_STUN_RETRANSMISSION_TIMEOUT 500 // msecs #define MAX_STUN_RETRANSMISSION_COUNT 5 // count (exponential backoff, will give ~30s) // #define MAX_STUN_RETRANSMISSION_COUNT 2 // count (exponential backoff, will give ~30s) // RFC 8445: ICE agents SHOULD use a default Ta value, 50 ms, but MAY use // another value based on the characteristics of the associated data. #define STUN_PACING_TIME 50 // msecs // RFC 8445: Agents SHOULD use a Tr value of 15 seconds. Agents MAY use a bigger value but MUST NOT // use a value smaller than 15 seconds. #define STUN_KEEPALIVE_PERIOD 15000 // msecs // Consent freshness // RFC 7675: Consent expires after 30 seconds. #define CONSENT_TIMEOUT 30000 // msecs // RFC 7675: To prevent synchronization of consent checks, each interval MUST be randomized from // between 0.8 and 1.2 times the basic period. Implementations SHOULD set a default interval of 5 // seconds, resulting in a period between checks of 4 to 6 seconds. Implementations MUST NOT set the // period between checks to less than 4 seconds. #define MIN_CONSENT_CHECK_PERIOD 4000 // msecs #define MAX_CONSENT_CHECK_PERIOD 6000 // msecs // TURN refresh period #define TURN_LIFETIME 600000 // msecs, 10 min #define TURN_REFRESH_PERIOD (TURN_LIFETIME - 60000) // msecs, lifetime - 1 min // ICE trickling timeout #define ICE_FAIL_TIMEOUT 30000 // msecs // Max STUN and TURN server entries #define MAX_SERVER_ENTRIES_COUNT 2 // max STUN server entries #define MAX_RELAY_ENTRIES_COUNT 2 // max TURN server entries // Max TURN redirections for ALTERNATE-SERVER mechanism #define MAX_TURN_REDIRECTIONS 1 // Compute max candidates and entries count #define MAX_STUN_SERVER_RECORDS_COUNT MAX_SERVER_ENTRIES_COUNT #define MAX_HOST_CANDIDATES_COUNT ((ICE_MAX_CANDIDATES_COUNT - MAX_STUN_SERVER_RECORDS_COUNT) / 2) #define MAX_PEER_REFLEXIVE_CANDIDATES_COUNT MAX_HOST_CANDIDATES_COUNT #define MAX_CANDIDATE_PAIRS_COUNT (ICE_MAX_CANDIDATES_COUNT * (1 + MAX_RELAY_ENTRIES_COUNT)) #define MAX_STUN_ENTRIES_COUNT (MAX_CANDIDATE_PAIRS_COUNT + MAX_STUN_SERVER_RECORDS_COUNT) #define AGENT_TURN_MAP_SIZE ICE_MAX_CANDIDATES_COUNT typedef enum agent_mode { AGENT_MODE_UNKNOWN, AGENT_MODE_CONTROLLED, AGENT_MODE_CONTROLLING } agent_mode_t; typedef enum agent_stun_entry_type { AGENT_STUN_ENTRY_TYPE_EMPTY, AGENT_STUN_ENTRY_TYPE_SERVER, AGENT_STUN_ENTRY_TYPE_RELAY, AGENT_STUN_ENTRY_TYPE_CHECK } agent_stun_entry_type_t; typedef enum agent_stun_entry_state { AGENT_STUN_ENTRY_STATE_PENDING, AGENT_STUN_ENTRY_STATE_CANCELLED, AGENT_STUN_ENTRY_STATE_FAILED, AGENT_STUN_ENTRY_STATE_SUCCEEDED, AGENT_STUN_ENTRY_STATE_SUCCEEDED_KEEPALIVE, AGENT_STUN_ENTRY_STATE_IDLE } agent_stun_entry_state_t; typedef struct agent_turn_state { turn_map_t map; stun_credentials_t credentials; const char *password; } agent_turn_state_t; typedef struct agent_stun_entry { agent_stun_entry_type_t type; agent_stun_entry_state_t state; agent_mode_t mode; ice_candidate_pair_t *pair; addr_record_t record; addr_record_t relayed; uint8_t transaction_id[STUN_TRANSACTION_ID_SIZE]; timestamp_t next_transmission; timediff_t retransmission_timeout; int retransmissions; // TURN agent_turn_state_t *turn; unsigned int turn_redirections; struct agent_stun_entry *relay_entry; } agent_stun_entry_t; struct juice_agent { juice_config_t config; juice_state_t state; agent_mode_t mode; ice_description_t local; ice_description_t remote; ice_candidate_pair_t candidate_pairs[MAX_CANDIDATE_PAIRS_COUNT]; ice_candidate_pair_t *ordered_pairs[MAX_CANDIDATE_PAIRS_COUNT]; ice_candidate_pair_t *selected_pair; int candidate_pairs_count; agent_stun_entry_t entries[MAX_STUN_ENTRIES_COUNT]; int entries_count; atomic_ptr(agent_stun_entry_t) selected_entry; uint64_t ice_tiebreaker; timestamp_t fail_timestamp; bool gathering_done; int conn_index; void *conn_impl; thread_t resolver_thread; bool resolver_thread_started; }; juice_agent_t *agent_create(const juice_config_t *config); void agent_destroy(juice_agent_t *agent); int agent_gather_candidates(juice_agent_t *agent); int agent_resolve_servers(juice_agent_t *agent); int agent_get_local_description(juice_agent_t *agent, char *buffer, size_t size); int agent_set_remote_description(juice_agent_t *agent, const char *sdp); int agent_add_remote_candidate(juice_agent_t *agent, const char *sdp); int agent_set_remote_gathering_done(juice_agent_t *agent); int agent_send(juice_agent_t *agent, const char *data, size_t size, int ds); int agent_direct_send(juice_agent_t *agent, const addr_record_t *dst, const char *data, size_t size, int ds); int agent_relay_send(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *dst, const char *data, size_t size, int ds); int agent_channel_send(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *dst, const char *data, size_t size, int ds); juice_state_t agent_get_state(juice_agent_t *agent); int agent_get_selected_candidate_pair(juice_agent_t *agent, ice_candidate_t *local, ice_candidate_t *remote); int agent_conn_recv(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src); int agent_conn_update(juice_agent_t *agent, timestamp_t *next_timestamp); int agent_conn_fail(juice_agent_t *agent); int agent_input(juice_agent_t *agent, char *buf, size_t len, const addr_record_t *src, const addr_record_t *relayed); // relayed may be NULL int agent_bookkeeping(juice_agent_t *agent, timestamp_t *next_timestamp); void agent_change_state(juice_agent_t *agent, juice_state_t state); int agent_verify_stun_binding(juice_agent_t *agent, void *buf, size_t size, const stun_message_t *msg); int agent_verify_credentials(juice_agent_t *agent, const agent_stun_entry_t *entry, void *buf, size_t size, stun_message_t *msg); int agent_dispatch_stun(juice_agent_t *agent, void *buf, size_t size, stun_message_t *msg, const addr_record_t *src, const addr_record_t *relayed); // relayed may be NULL int agent_process_stun_binding(juice_agent_t *agent, const stun_message_t *msg, agent_stun_entry_t *entry, const addr_record_t *src, const addr_record_t *relayed); // relayed may be NULL int agent_send_stun_binding(juice_agent_t *agent, agent_stun_entry_t *entry, stun_class_t msg_class, unsigned int error_code, const uint8_t *transaction_id, const addr_record_t *mapped); int agent_process_turn_allocate(juice_agent_t *agent, const stun_message_t *msg, agent_stun_entry_t *entry); int agent_send_turn_allocate_request(juice_agent_t *agent, const agent_stun_entry_t *entry, stun_method_t method); int agent_process_turn_create_permission(juice_agent_t *agent, const stun_message_t *msg, agent_stun_entry_t *entry); int agent_send_turn_create_permission_request(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *record, int ds); int agent_process_turn_channel_bind(juice_agent_t *agent, const stun_message_t *msg, agent_stun_entry_t *entry); int agent_send_turn_channel_bind_request(juice_agent_t *agent, agent_stun_entry_t *entry, const addr_record_t *record, int ds, uint16_t *out_channel); // out_channel may be NULL int agent_process_turn_data(juice_agent_t *agent, const stun_message_t *msg, agent_stun_entry_t *entry); int agent_process_channel_data(juice_agent_t *agent, agent_stun_entry_t *entry, char *buf, size_t len); int agent_add_local_relayed_candidate(juice_agent_t *agent, const addr_record_t *record); int agent_add_local_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type, const addr_record_t *record); int agent_add_remote_reflexive_candidate(juice_agent_t *agent, ice_candidate_type_t type, uint32_t priority, const addr_record_t *record); int agent_add_candidate_pair(juice_agent_t *agent, ice_candidate_t *local, ice_candidate_t *remote); // local may be NULL int agent_add_candidate_pairs_for_remote(juice_agent_t *agent, ice_candidate_t *remote); int agent_unfreeze_candidate_pair(juice_agent_t *agent, ice_candidate_pair_t *pair); void agent_arm_keepalive(juice_agent_t *agent, agent_stun_entry_t *entry); void agent_arm_transmission(juice_agent_t *agent, agent_stun_entry_t *entry, timediff_t delay); void agent_update_gathering_done(juice_agent_t *agent); void agent_update_candidate_pairs(juice_agent_t *agent); void agent_update_ordered_pairs(juice_agent_t *agent); agent_stun_entry_t *agent_find_entry_from_transaction_id(juice_agent_t *agent, const uint8_t *transaction_id); agent_stun_entry_t * agent_find_entry_from_record(juice_agent_t *agent, const addr_record_t *record, const addr_record_t *relayed); // relayed may be NULL void agent_translate_host_candidate_entry(juice_agent_t *agent, agent_stun_entry_t *entry); #endif