blob: 73fc41dd09eef68f25f4b946a1bbbd1776c70026 [file] [log] [blame]
/* srp-replication.h
*
* Copyright (c) 2020-2023 Apple Computer, Inc. All rights reserved.
*
* 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.
*
* This file contains structure definitions and external definitions for the SRP Replication code.
*/
#ifndef __SRP_REPLICATION_H__
#define __SRP_REPLICATION_H__
// States: each state has a function, which bears the name of the state. The function takes an
// srpl_connection_t and an srpl_event_t. The function is called once on entering the
// state, once on leaving the state, and once whenever an event arrives while in the state.
//
// Whenever this function is called, it returns a next state. If the next state is
// "invalid," that means that there is no state change.
//
// Events: Events can be triggered by connection activities, e.g. connect, disconnect, add_address, etc.
// They can also be triggered by the arrival of messages on connections.
// They can also be triggered by happenings on the srp server.
// Events are never sent by state actions.
// Each event has a single function which sends the event. That function may in some cases force
// a state change (e.g., a disconnect event). This is the exception, not the rule. Events are
// otherwise handled by the state action function for the current state.
//
// In some cases, an event that's expected to be delivered asynchronously arrives synchronously because
// no asynchronous action was required. In this case, the action that can trigger this synchronous event
// has to also handle the event. Since the event is normally expected to be delivered asynchronously,
// the right solution in this case is to queue the event for later delivery.
//
// An example of this pattern is in srpl_advertise_finished_event_defer. Because events are normally automatic
// variables, any event that needs to be deferred has to be allocated and its contents (if any) copied. The
// srpl_connection_t is stashed on the event; srpl_deferred_event_deliver is then called asynchronously to deliver the
// event, release the reference to the srpl_connection_t, and free the event data structure.
enum srpl_state {
srpl_state_invalid = 0, // Only as a return value, means do not move to a new state
// Connection-related states
srpl_state_disconnected,
srpl_state_next_address_get,
srpl_state_connect,
srpl_state_idle,
srpl_state_reconnect_wait,
srpl_state_retry_delay_send,
srpl_state_disconnect,
srpl_state_disconnect_wait,
srpl_state_connecting,
// Session establishment
srpl_state_session_send,
srpl_state_session_response_wait,
srpl_state_session_evaluate,
srpl_state_sync_wait,
// Requesting and getting remote candidate list
srpl_state_send_candidates_send,
srpl_state_send_candidates_wait,
// Waiting for candidate to arrive
srpl_state_candidate_check,
// Waiting for a host to arrive after requesting it
srpl_state_candidate_host_wait,
srpl_state_candidate_host_prepare,
srpl_state_candidate_host_contention_wait,
srpl_state_candidate_host_re_evaluate,
srpl_state_candidate_host_apply,
srpl_state_candidate_host_apply_wait,
// Getting request for candidate list and sending them.
srpl_state_send_candidates_received,
srpl_state_send_candidates_remaining_check,
srpl_state_next_candidate_send,
srpl_state_next_candidate_send_wait,
srpl_state_candidate_host_send,
srpl_state_candidate_host_response_wait,
// When we're done sending candidates
srpl_state_send_candidates_response_send,
// Ready states
srpl_state_ready,
srpl_state_srp_client_update_send,
srpl_state_srp_client_ack_evaluate,
srpl_state_stashed_host_check,
srpl_state_stashed_host_apply,
srpl_state_stashed_host_finished,
// States for connections received by this server
srpl_state_session_message_wait,
srpl_state_session_response_send,
srpl_state_send_candidates_message_wait,
};
enum srpl_event_type {
srpl_event_invalid = 0,
srpl_event_address_add,
srpl_event_address_remove,
srpl_event_server_disconnect,
srpl_event_reconnect_timer_expiry,
srpl_event_disconnected,
srpl_event_connected,
srpl_event_session_response_received,
srpl_event_send_candidates_response_received,
srpl_event_candidate_received,
srpl_event_host_message_received,
srpl_event_srp_client_update_finished,
srpl_event_advertise_finished,
srpl_event_candidate_response_received,
srpl_event_host_response_received,
srpl_event_session_message_received,
srpl_event_send_candidates_message_received,
srpl_event_do_sync,
};
enum srpl_candidate_disposition { srpl_candidate_yes, srpl_candidate_no, srpl_candidate_conflict };
typedef struct srpl_instance_service srpl_instance_service_t;
typedef struct srpl_connection srpl_connection_t;
typedef struct srpl_instance srpl_instance_t;
typedef struct srpl_domain srpl_domain_t;
typedef struct address_query address_query_t;
typedef struct unclaimed_connection unclaimed_connection_t;
typedef enum srpl_state srpl_state_t;
typedef enum srpl_event_type srpl_event_type_t;
typedef struct srpl_event srpl_event_t;
typedef struct srpl_candidate srpl_candidate_t;
typedef enum srpl_candidate_disposition srpl_candidate_disposition_t;
typedef struct srpl_srp_client_queue_entry srpl_srp_client_queue_entry_t;
typedef struct srpl_srp_client_update_result srpl_srp_client_update_result_t;
typedef struct srpl_host_update srpl_host_update_t;
typedef struct srpl_advertise_finished_result srpl_advertise_finished_result_t;
typedef struct srpl_session srpl_session_t;
typedef void (*address_change_callback_t)(void *NULLABLE context, addr_t *NULLABLE address, bool added, int err);
typedef void (*address_query_cancel_callback_t)(void *NULLABLE context);
typedef enum {
address_query_next_address_gotten, // success
address_query_next_address_empty, // no addresses at all
address_query_cycle_complete // all addresses have been tried
} address_query_result_t;
#define ADDRESS_QUERY_MAX_ADDRESSES 20
struct address_query {
int ref_count;
dnssd_txn_t *NULLABLE aaaa_query, *NULLABLE a_query;
addr_t addresses[ADDRESS_QUERY_MAX_ADDRESSES]; // If there are more than this many viable addresses, too bad?
uint32_t address_interface[ADDRESS_QUERY_MAX_ADDRESSES];
int num_addresses, cur_address;
address_change_callback_t NULLABLE change_callback;
address_query_cancel_callback_t NULLABLE cancel_callback;
void *NULLABLE context;
char *NONNULL hostname;
};
struct srpl_candidate {
dns_label_t *NULLABLE name;
uint32_t key_id; // key id from adv_host_t
uint32_t update_offset; // Offset in seconds before the time candidate message was sent that update was received.
time_t update_time; // the time of registration received from remote
time_t local_time; // our time of registration when we fetched the host
message_t *NULLABLE message; // The SRP message.
adv_host_t *NULLABLE host; // the host, when it's been fetched
};
struct srpl_advertise_finished_result {
char *NULLABLE hostname;
srp_server_t *NULLABLE server_state;
int rcode;
};
// 1: local > remote
// 0: local == remote
// -1: local < remote
// -2: undefined result.
enum {
EQUAL = 0,
LOCAL_LARGER = 1,
LOCAL_SMALLER = -1,
UNDEFINED = -2,
};
typedef enum {
srpl_event_content_type_none = 0,
srpl_event_content_type_address,
srpl_event_content_type_session,
srpl_event_content_type_candidate,
srpl_event_content_type_rcode,
srpl_event_content_type_candidate_disposition,
srpl_event_content_type_host_update,
srpl_event_content_type_client_result,
srpl_event_content_type_advertise_finished_result,
} srpl_event_content_type_t;
typedef srpl_state_t (*srpl_action_t)(srpl_connection_t *NONNULL connection, srpl_event_t *NULLABLE event);
struct srpl_srp_client_update_result {
adv_host_t *NONNULL host;
int rcode;
};
struct srpl_host_update {
message_t *NULLABLE *NULLABLE messages;
intptr_t orig_buffer;
uint64_t server_stable_id;
dns_name_t *NULLABLE hostname;
uint32_t update_offset;
int num_messages, max_messages, messages_processed;
int rcode;
unsigned num_bytes;
};
struct srpl_session {
uint64_t partner_id;
dns_name_t *NULLABLE domain_name;
uint16_t remote_version;
bool new_partner;
};
struct srpl_event {
char *NONNULL name;
srpl_event_content_type_t content_type;
union {
addr_t address;
srpl_session_t session;
srpl_srp_client_update_result_t client_result;
srpl_candidate_t *NULLABLE candidate;
int rcode;
srpl_candidate_disposition_t disposition;
srpl_host_update_t host_update;
srpl_advertise_finished_result_t advertise_finished;
} content;
message_t *NULLABLE message;
srpl_event_type_t event_type;
srpl_connection_t *NULLABLE srpl_connection; // if the event's been deferred, otherwise ALWAYS NULL.
};
struct srpl_srp_client_queue_entry {
srpl_srp_client_queue_entry_t *NULLABLE next;
adv_host_t *NONNULL host;
bool sent;
};
struct srpl_connection {
int ref_count;
uint64_t remote_partner_id;
char *NONNULL name;
char *NONNULL state_name;
comm_t *NULLABLE connection;
addr_t connected_address;
srpl_candidate_t *NULLABLE candidate;
dso_state_t *NULLABLE dso;
srpl_instance_t *NULLABLE instance;
wakeup_t *NULLABLE reconnect_wakeup;
message_t *NULLABLE message;
adv_host_t *NULLABLE *NULLABLE candidates;
srpl_host_update_t stashed_host;
srpl_srp_client_queue_entry_t *NULLABLE client_update_queue;
wakeup_t *NULLABLE keepalive_send_wakeup;
wakeup_t *NULLABLE keepalive_receive_wakeup;
time_t last_message_sent;
time_t last_message_received;
int num_candidates;
int current_candidate;
int retry_delay; // How long to send when we send a retry_delay message
int keepalive_interval;
srpl_state_t state, next_state;
uint32_t variation_mask; // Protocol variations to support pre-standard TLV formats
bool is_server;
bool new_partner;
bool database_synchronized;
bool candidates_not_generated; // If this is true, we haven't generated a candidates list yet.
};
struct srpl_instance_service {
int ref_count;
srpl_instance_t *NULLABLE instance;
dnssd_txn_t *NULLABLE txt_txn;
dnssd_txn_t *NULLABLE srv_txn;
srpl_instance_service_t *NULLABLE next;
srpl_domain_t *NONNULL domain;
wakeup_t *NULLABLE resolve_wakeup;
wakeup_t *NULLABLE discontinue_timeout;
uint8_t *NULLABLE txt_rdata;
uint8_t *NULLABLE srv_rdata;
uint8_t *NULLABLE ptr_rdata;
uint8_t *NULLABLE addr_rdata;
char *NULLABLE host_name;
char *NULLABLE full_service_name;
address_query_t *NULLABLE address_query;
int num_copies; // Tracks adds and deletes from the DNSServiceBrowse for this instance.
uint32_t ifindex;
uint16_t outgoing_port;
uint16_t txt_length;
uint16_t srv_length;
uint16_t ptr_length;
bool have_srv_record, have_txt_record;
// True if we've already started a resolve for this instance, to prevent starting a second resolve if the instance
// is seen on more than one interface.
bool resolve_started;
bool discontinuing; // True if we are in the process of discontinuing this instance.
bool got_new_info; // True if we have received new information since the last time we did a reconfirm.
};
struct srpl_instance {
int ref_count;
srpl_instance_t *NULLABLE next;
srpl_domain_t *NONNULL domain;
srpl_connection_t *NULLABLE connection;
wakeup_t *NULLABLE reconnect_timeout;
char *NULLABLE instance_name;
srpl_instance_service_t *NONNULL services;
uint64_t partner_id;
uint64_t dataset_id;
bool have_partner_id;
bool have_dataset_id;
bool sync_to_join; // True if sync with the remote partner is required to join the replication
bool is_me;
bool discontinuing; // True if we are in the process of discontinuing this instance.
bool unmatched; // True if this is an incoming connection that hasn't been associated with a real instance.
};
typedef enum {
SRPL_OPSTATE_STARTUP = 0,
SRPL_OPSTATE_ROUTINE = 1
} srpl_opstate_t;
struct srpl_domain {
uint64_t partner_id; // SRP replication partner ID
uint64_t dataset_id;
bool have_dataset_id;
bool partner_discovery_pending;
int ref_count;
srpl_opstate_t srpl_opstate;
srpl_domain_t *NULLABLE next;
char *NONNULL name;
srpl_instance_t *NULLABLE instances;
srpl_instance_service_t *NULLABLE unresolved_services;
dnssd_txn_t *NULLABLE query;
srp_server_t *NULLABLE server_state;
dnssd_txn_t *NULLABLE srpl_advertise_txn;
wakeup_t *NULLABLE srpl_register_wakeup;
wakeup_t *NULLABLE partner_discovery_timeout;
};
#define SRP_THREAD_DOMAIN "thread.home.arpa."
#define DSO_TLV_HEADER_SIZE 4 // opcode (u16) + length (u16)
#define DSO_MESSAGE_MIN_LENGTH DNS_HEADER_SIZE + DSO_TLV_HEADER_SIZE + 1
#define SRPL_RETRY_DELAY_LENGTH DSO_MESSAGE_MIN_LENGTH + sizeof(uint32_t)
#define SRPL_SESSION_MESSAGE_LENGTH (DSO_MESSAGE_MIN_LENGTH + \
sizeof(uint64_t) + \
DNS_MAX_NAME_SIZE + DSO_TLV_HEADER_SIZE + \
DSO_TLV_HEADER_SIZE + sizeof(uint16_t) + \
DSO_TLV_HEADER_SIZE + sizeof(uint16_t))
#define SRPL_SEND_CANDIDATES_LENGTH DSO_MESSAGE_MIN_LENGTH
#define SRPL_CANDIDATE_MESSAGE_LENGTH (DSO_MESSAGE_MIN_LENGTH + \
DNS_MAX_NAME_SIZE + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE)
#define SRPL_KEEPALIVE_MESSAGE_LENGTH (DSO_MESSAGE_MIN_LENGTH + \
DNS_MAX_NAME_SIZE + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE)
#define SRPL_CANDIDATE_RESPONSE_LENGTH DSO_MESSAGE_MIN_LENGTH + DSO_TLV_HEADER_SIZE
#define SRPL_HOST_MESSAGE_LENGTH (DSO_MESSAGE_MIN_LENGTH + \
DNS_MAX_NAME_SIZE + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE + \
sizeof(uint32_t) + DSO_TLV_HEADER_SIZE)
#define SRPL_HOST_RESPONSE_LENGTH DSO_MESSAGE_MIN_LENGTH
#define SRPL_UPDATE_JITTER_WINDOW 10
#define MIN_PARTNER_DISCOVERY_INTERVAL 4000 // minimum partner discovery time interval in milliseconds
#define MAX_PARTNER_DISCOVERY_INTERVAL 7500 // maximum partner discovery time interval in milliseconds
#define PARTNER_DISCOVERY_INTERVAL_RANGE (MAX_PARTNER_DISCOVERY_INTERVAL - \
MIN_PARTNER_DISCOVERY_INTERVAL + 1)
#define DEFAULT_KEEPALIVE_WAKEUP_EXPIRY (5 * 60 * 1000) // five minutes
#define PARTNER_ID_BITS 64
#define LOWER56_BIT_MASK 0xFFFFFFFFFFFFFFULL
// SRP Replication protocol versioning
#define SRPL_VERSION_MULTI_HOST_MESSAGE 1
#define SRPL_VERSION_ANYCAST 2
#define SRPL_CURRENT_VERSION SRPL_VERSION_ANYCAST
// Variation bits.
#define SRPL_VARIATION_MULTI_HOST_MESSAGE 1
#define SRPL_SUPPORTS(srpl_connection, variation) \
(((srpl_connection)->variation_mask & ~(variation)) != 0)
// Exported functions...
void srpl_startup(srp_server_t *NONNULL srp_server);
void srpl_shutdown(srp_server_t *NONNULL server_state);
void srpl_disable(srp_server_t *NONNULL srp_server);
void srpl_drop_srpl_connection(srp_server_t *NONNULL srp_server);
void srpl_undrop_srpl_connection(srp_server_t *NONNULL srp_server);
void srpl_drop_srpl_advertisement(srp_server_t *NONNULL srp_server);
void srpl_undrop_srpl_advertisement(srp_server_t *NONNULL srp_server);
void srpl_dso_server_message(comm_t *NONNULL connection, message_t *NULLABLE message, dso_state_t *NONNULL dso,
srp_server_t *NONNULL server_state);
void srpl_advertise_finished_event_send(char *NONNULL host, int rcode, srp_server_t *NONNULL server_state);
void srpl_srp_client_update_finished_event_send(adv_host_t *NONNULL host, int rcode);
#define srpl_connection_release(connection) srpl_connection_release_(connection, __FILE__, __LINE__)
void srpl_connection_release_(srpl_connection_t *NONNULL srpl_connection, const char *NONNULL file, int line);
#define srpl_connection_retain(connection) srpl_connection_retain_(connection, __FILE__, __LINE__)
void srpl_connection_retain_(srpl_connection_t *NONNULL srpl_connection, const char *NONNULL file, int line);
#endif // __SRP_REPLICATION_H__
// Local Variables:
// mode: C
// tab-width: 4
// c-file-style: "bsd"
// c-basic-offset: 4
// fill-column: 120
// indent-tabs-mode: nil
// End: