| /* dso.h |
| * |
| * Copyright (c) 2018-2023 Apple 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 |
| * |
| * https://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. |
| * |
| */ |
| |
| #ifndef __DSO_H |
| #define __DSO_H |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include "nullability.h" |
| |
| // Maximum number of additional TLVs we support in a DSO message. |
| #define MAX_ADDITLS 2 |
| |
| // Use 0 to represent an invalid ID for the object dso_connect_t. |
| #define DSO_STATE_INVALID_SERIAL 0 |
| |
| typedef enum { |
| // When we get a DSO query or response with no primary TLV, the TLV type will be "No Primary TLV," which is not |
| // otherwise a valid DSO TLV type. |
| kDSOType_NoPrimaryTLV = 0, |
| // Standard DSO Types from RFC 8490 |
| kDSOType_Keepalive = 1, |
| kDSOType_RetryDelay = 2, |
| kDSOType_EncryptionPadding = 3, |
| |
| // Standard DSO Types from RFC 8765 |
| kDSOType_DNSPushSubscribe = 0x40, |
| kDSOType_DNSPushUpdate = 0x41, |
| kDSOType_DNSPushUnsubscribe = 0x42, |
| kDSOType_DNSPushReconfirm = 0x43, |
| |
| // Experimental types, taken from the experimental number space, for the discovery relay. |
| kDSOType_mDNSLinkRequest = 0xF901, |
| kDSOType_mDNSLinkDiscontinue = 0xF902, |
| kDSOType_mDNSMessage = 0xF903, |
| kDSOType_LinkIdentifier = 0xF904, |
| kDSOType_L2SourceAddress = 0xF905, |
| kDSOType_IPSourceAddress = 0xF906, |
| kDSOType_mDNSReportLinkChanges = 0xF907, |
| kDSOType_mDNSStopLinkChanges = 0xF908, |
| kDSOType_mDNSLinkAvailable = 0xF900, |
| kDSOType_mDNSLinkUnavailable = 0xF90a, |
| kDSOType_LinkPrefix = 0xF90b, |
| |
| // Experimental types, taken from the experimental number space, for SRP Replication. |
| kDSOType_SRPLSession = 0xF90c, |
| kDSOType_SRPLSendCandidates = 0xF90d, |
| kDSOType_SRPLCandidate = 0xF90e, |
| kDSOType_SRPLHost = 0xF90f, |
| kDSOType_SRPLServerID = 0xF910, |
| kDSOType_SRPLCandidateYes = 0xF911, |
| kDSOType_SRPLCandidateNo = 0xF912, |
| kDSOType_SRPLConflict = 0xF913, |
| kDSOType_SRPLHostname = 0xF914, |
| kDSOType_SRPLHostMessage = 0xF915, |
| kDSOType_SRPLTimeOffset = 0xF916, |
| kDSOType_SRPLKeyID = 0xF917, |
| kDSOType_SRPLServerStableID = 0xF918, |
| kDSOType_SRPLVersion = 0xF919, |
| kDSOType_SRPLDomainName = 0xF91a, |
| kDSOType_SRPLNewPartner = 0xF91b, |
| } dso_message_types_t; |
| |
| // When a DSO message arrives, or one that was sent is acknowledged, or the state of the DSO connection |
| // changes, we need to call the user of the DSO connection. |
| typedef enum { |
| kDSOEventType_DNSMessage, // A DNS message that is not a DSO message |
| kDSOEventType_DNSResponse, // A DNS response that is not a DSO response |
| kDSOEventType_DSOMessage, // DSOState.primary and DSOState.additl will contain the message TLVs; |
| // header will contain the DNS header |
| kDSOEventType_Finalize, // The DSO connection to the other DSO endpoint has terminated and we are |
| // in the idle loop. |
| kDSOEventType_DSOResponse, // DSOState.primary and DSOState.additl contain any TLVs in the response; |
| // header contains the DNS header |
| kDSOEventType_Connected, // We succeeded in making a connection |
| kDSOEventType_ConnectFailed, // We failed to get a connection |
| kDSOEventType_Disconnected, // We were connected, but have disconnected or been disconnected |
| kDSOEventType_ShouldReconnect, // We are disconnected, and a scheduled reconnect timer has gone off. |
| // Recipient is responsible for reconnecting, or deciding not to. |
| kDSOEventType_Inactive, // We went inactive and the inactivity timeout expired, so it's time to drop the connection. |
| kDSOEventType_Keepalive, // It's time to send a keepalive message, here are the values to send |
| kDSOEventType_KeepaliveRcvd, // We just received a keepalive from a client, here are the values. |
| kDSOEventType_RetryDelay // We got a RetryDelay from the server. Have to shut down. |
| } dso_event_type_t; |
| |
| typedef struct dso_outstanding_query { |
| uint16_t id; |
| void *context; |
| } dso_outstanding_query_t; |
| |
| typedef struct dso_outstanding_query_state { |
| int outstanding_query_count; |
| int max_outstanding_queries; |
| dso_outstanding_query_t queries[0]; |
| } dso_outstanding_query_state_t; |
| |
| typedef struct dso_query_receive_context { |
| void *query_context; |
| void *message_context; |
| uint16_t rcode; |
| } dso_query_receive_context_t; |
| |
| typedef struct dso_disconnect_context { |
| uint32_t reconnect_delay; |
| } dso_disconnect_context_t; |
| |
| typedef struct dso_keepalive_context { |
| uint32_t inactivity_timeout; |
| uint32_t keepalive_interval; |
| uint16_t xid; |
| bool send_response; |
| } dso_keepalive_context_t; |
| |
| // Structure to represent received DSO TLVs |
| typedef struct dsotlv { |
| uint16_t opcode; |
| uint16_t length; |
| const uint8_t *payload; |
| } dso_tlv_t; |
| |
| // DSO message under construction |
| typedef struct dso_message { |
| uint8_t *buf; // The buffer in which we are constructing the message |
| size_t max; // Size of the buffer |
| size_t cur; // Current position in the buffer |
| bool building_tlv; // True if we have started and not finished building a TLV |
| int outstanding_query_number; // Number of the outstanding query state entry for this message, or -1 |
| size_t tlv_len; // Current length of the TLV we are building. |
| size_t tlv_len_offset; // Where to store the length of the current TLV when finished. |
| const uint8_t *no_copy_bytes; // One TLV can have data that isn't copied into the buffer |
| size_t no_copy_bytes_len; // Length of that data, if any. |
| size_t no_copy_bytes_offset; // Where in the buffer the data should be interposed. |
| } dso_message_t; |
| |
| // Record of ongoing activity |
| typedef struct dso_activity dso_activity_t; |
| struct dso_activity { |
| dso_activity_t *next; |
| void (*finalize)(dso_activity_t *activity); |
| const char *activity_type; // Name of the activity type, must be the same pointer for all activities of a type. |
| void *context; // Activity implementation's context (if any). |
| char *name; // Name of the individual activity. |
| }; |
| |
| typedef struct dso_transport dso_transport_t; |
| typedef struct dso_state dso_state_t; |
| typedef int32_t event_time_t; |
| |
| typedef void (*dso_event_callback_t)(void *context, void *header, |
| dso_state_t *dso, dso_event_type_t eventType); |
| typedef void (*dso_transport_finalize_t)(dso_transport_t *transport, const char *whence); |
| |
| typedef enum { |
| // When the object is created and holds a reference to the context, the callback(see below) is called with |
| // dso_life_cycle_create. |
| dso_life_cycle_create, |
| // When the object is canceled, the callback(see below) is called with dso_life_cycle_cancel to provide a chance |
| // for the context to do the corresponding cleaning work(cancel or release/free). |
| dso_life_cycle_cancel, |
| // When the object is freed, the callback(see below) is called with dso_life_cycle_free to provide a chance for the |
| // context to clean anything remains allocated. |
| dso_life_cycle_free |
| } dso_life_cycle_t; |
| |
| typedef bool (*dso_life_cycle_context_callback_t)(const dso_life_cycle_t life_cycle, void *const context, |
| dso_state_t *const dso); |
| |
| // DNS Stateless Operations state |
| struct dso_state { |
| dso_state_t *next; |
| void *context; // The context of the next layer up (e.g., a Discovery Proxy) |
| // The callback gets called when dso_state_t is created, canceled or freed. |
| dso_life_cycle_context_callback_t context_callback; |
| dso_event_callback_t cb; // Called when an event happens |
| |
| // Transport state; handled separately for reusability |
| dso_transport_t *transport; // The transport (e.g., dso-transport.c or other). |
| dso_transport_finalize_t transport_finalize; |
| |
| uint32_t serial; // Unique serial number which can be used after the DSO has been dropped. |
| bool is_server; // True if the endpoint represented by this DSO state is a server |
| // (according to the DSO spec) |
| bool has_session; // True if DSO session establishment has happened for this DSO endpoint |
| event_time_t response_awaited; // If we are waiting for a session-establishing response, when it's |
| // expected; otherwise zero. |
| uint32_t keepalive_interval; // Time between keepalives (to be sent, on client, expected, on server) |
| uint32_t inactivity_timeout; // Session can't be inactive more than this amount of time. |
| event_time_t keepalive_due; // When the next keepalive is due (to be received or sent) |
| event_time_t inactivity_due; // When next activity has to happen for connection to remain active |
| dso_activity_t *activities; // Outstanding DSO activities. |
| |
| dso_tlv_t primary; // Primary TLV for current message |
| dso_tlv_t *additl; // Additional TLVs |
| unsigned num_additls; // Number of additional TLVs in this message |
| unsigned max_additls; // Maximum number of additional TLVs this DSO state can represent |
| dso_tlv_t additl_buf[MAX_ADDITLS]; // Initial buffer for additional TLVs. |
| |
| char *remote_name; |
| |
| dso_outstanding_query_state_t *outstanding_queries; |
| }; |
| |
| // Provided by dso.c |
| dso_state_t *dso_state_create(bool is_server, int max_outstanding_queries, const char *remote_name, |
| dso_event_callback_t callback, void *const context, |
| const dso_life_cycle_context_callback_t context_callback, |
| dso_transport_t *transport); |
| dso_state_t *dso_find_by_serial(uint32_t serial); |
| void dso_state_cancel(dso_state_t *dso); |
| void dso_cleanup(bool call_callbacks); |
| int32_t dso_idle(void *context, int32_t now, int32_t next_timer_event); |
| void dso_set_event_context(dso_state_t *dso, void *context); |
| void dso_set_event_callback(dso_state_t *dso, dso_event_callback_t callback); |
| void dso_set_life_cycle_callback(dso_state_t *dso, dso_life_cycle_context_callback_t callback); |
| void dso_start_tlv(dso_message_t *state, int opcode); |
| void dso_add_tlv_bytes(dso_message_t *state, const uint8_t *bytes, size_t len); |
| void dso_add_tlv_bytes_no_copy(dso_message_t *state, const uint8_t *bytes, size_t len); |
| void dso_add_tlv_byte(dso_message_t *state, uint8_t byte); |
| void dso_add_tlv_u16(dso_message_t *state, uint16_t u16); |
| void dso_add_tlv_u32(dso_message_t *state, uint32_t u32); |
| void dso_finish_tlv(dso_message_t *state); |
| dso_activity_t *dso_find_activity(dso_state_t *dso, const char *name, const char *activity_type, void *context); |
| dso_activity_t *dso_add_activity(dso_state_t *dso, const char *name, const char *activity_type, |
| void *context, void (*finalize)(dso_activity_t *)); |
| void dso_drop_activity(dso_state_t *dso, dso_activity_t *activity); |
| uint32_t dso_ignore_further_responses(dso_state_t *dso, const void *context); |
| uint32_t dso_connections_reset_outstanding_query_context(const void *context); |
| bool dso_make_message(dso_message_t *state, uint8_t *outbuf, size_t outbuf_size, dso_state_t *dso, |
| bool unidirectional, bool response, uint16_t xid, int rcode, void *callback_state); |
| size_t dso_message_length(dso_message_t *state); |
| void dso_retry_delay(dso_state_t *dso, const DNSMessageHeader *header); |
| void dso_keepalive(dso_state_t *dso, const DNSMessageHeader *header); |
| void dso_message_received(dso_state_t *dso, const uint8_t *message, size_t message_length, void *context); |
| void dns_message_received(dso_state_t *dso, const uint8_t *message, size_t message_length, void *context); |
| |
| const char *dso_event_type_to_string(dso_event_type_t dso_event_type); |
| |
| // Provided by DSO transport implementation for use by dso.c: |
| int32_t dso_transport_idle(void *context, int32_t now, int32_t next_timer_event); |
| bool dso_send_simple_response(dso_state_t *dso, int rcode, const DNSMessageHeader *header, const char *pres); |
| bool dso_send_not_implemented(dso_state_t *dso, const DNSMessageHeader *header); |
| bool dso_send_refused(dso_state_t *dso, const DNSMessageHeader *header); |
| bool dso_send_formerr(dso_state_t *dso, const DNSMessageHeader *header); |
| bool dso_send_servfail(dso_state_t *dso, const DNSMessageHeader *header); |
| bool dso_send_name_error(dso_state_t *dso, const DNSMessageHeader *header); |
| bool dso_send_no_error(dso_state_t *dso, const DNSMessageHeader *header); |
| #endif // !defined(__DSO_H) |
| |
| // Local Variables: |
| // mode: C |
| // tab-width: 4 |
| // c-file-style: "bsd" |
| // c-basic-offset: 4 |
| // fill-column: 108 |
| // indent-tabs-mode: nil |
| // End: |