| /* 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: |