diff --git a/.pubnub.yml b/.pubnub.yml index 17640d8f..d245ed9b 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,8 +1,33 @@ name: c-core schema: 1 -version: "6.1.0" +version: "6.2.0" scm: github.com/pubnub/c-core changelog: + - date: 2025-12-10 + version: v6.2.0 + changes: + - type: feature + text: "Add working IPv6 route detection to decide the preferable connection interface." + - type: feature + text: "When built with `PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT` set to delay in milliseconds, system DNS servers' discovery will make an actual DNS query, and the value will be used as a request timeout." + - type: feature + text: "Add handling of the IPv6 proxy address provided by automated (or PAC) proxy discovery API." + - type: bug + text: "Fix issue because of which `pubnub_await` wasn't waiting after `pubnub_cancel` has been called." + - type: bug + text: "Fix `WinHttpGetProxyForUrl` usage with URL with schema instead of domain only." + - type: bug + text: "Fix issue with `/etc/resolv.conf` parsing where untrimmed string produced wrong address." + - type: bug + text: "Some Windows-related interfaces, because of different `pubnub_` struct layouts caused by different `pubnub_config.h` type aliases and pre-processing macros, accessed and modified wrong fields." + - type: bug + text: "Fix `set_from_url4proxy` for proper `lpszProxy` parsing." + - type: improvement + text: "Add defaults for `PUBNUB_ADVANCED_KEEP_ALIVE` in all related `pubnub_config.h` files." + - type: improvement + text: "When built with IPv6 support, first try to use any of the user-provided IPv6 DNS servers before falling back to the user-provided IPv4 before falling back to the address provided by a well-known DNS provider." + - type: improvement + text: "When built with IPv6 support, SDK will send two queries to receive both records and decide which will be most suitable for currently available routing." - date: 2025-11-17 version: v6.1.0 changes: @@ -1065,7 +1090,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1131,7 +1156,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1197,7 +1222,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1259,7 +1284,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1320,7 +1345,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1376,7 +1401,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" @@ -1429,7 +1454,7 @@ sdks: distribution-type: source code distribution-repository: GitHub release package-name: C-Core - location: https://github.com/pubnub/c-core/releases/tag/v6.1.0 + location: https://github.com/pubnub/c-core/releases/tag/v6.2.0 requires: - name: "miniz" diff --git a/CHANGELOG.md b/CHANGELOG.md index ea2cd4f6..fc11e5d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## v6.2.0 +December 10 2025 + +#### Added +- Add working IPv6 route detection to decide the preferable connection interface. +- When built with `PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT` set to delay in milliseconds, system DNS servers' discovery will make an actual DNS query, and the value will be used as a request timeout. +- Add handling of the IPv6 proxy address provided by automated (or PAC) proxy discovery API. + +#### Fixed +- Fix issue because of which `pubnub_await` wasn't waiting after `pubnub_cancel` has been called. +- Fix `WinHttpGetProxyForUrl` usage with URL with schema instead of domain only. +- Fix issue with `/etc/resolv.conf` parsing where untrimmed string produced wrong address. +- Some Windows-related interfaces, because of different `pubnub_` struct layouts caused by different `pubnub_config.h` type aliases and pre-processing macros, accessed and modified wrong fields. +- Fix `set_from_url4proxy` for proper `lpszProxy` parsing. + +#### Modified +- Add defaults for `PUBNUB_ADVANCED_KEEP_ALIVE` in all related `pubnub_config.h` files. +- When built with IPv6 support, first try to use any of the user-provided IPv6 DNS servers before falling back to the user-provided IPv4 before falling back to the address provided by a well-known DNS provider. +- When built with IPv6 support, SDK will send two queries to receive both records and decide which will be most suitable for currently available routing. + ## v6.1.0 November 17 2025 diff --git a/core/pubnub_dns_servers.h b/core/pubnub_dns_servers.h index 2a77f411..0ff0a3f0 100644 --- a/core/pubnub_dns_servers.h +++ b/core/pubnub_dns_servers.h @@ -21,7 +21,7 @@ struct pubnub_ipv6_address { }; /* primary, secondary(ipv4, ipv6) and default dns server */ -#define PUBNUB_MAX_DNS_SERVERS_MASK 0x10 +#define PUBNUB_MAX_DNS_SERVERS_MASK 0x10 #else /* primary, secondary(ipv4) and default dns server */ #define PUBNUB_MAX_DNS_SERVERS_MASK 0x04 @@ -122,13 +122,12 @@ PUBNUB_EXTERN int pubnub_get_dns_secondary_server_ipv4(struct pubnub_ipv4_addres @param[out] o_ipv4 The array where to put the system DNS servers. allocated by the caller for @p n elements. - @param[in] n The number of elements allocated for the @p o_ipv4 @retval -1: error, can't read DNS server configuration @retval otherwise: number of DNS servers read */ PUBNUB_EXTERN int pubnub_dns_read_system_servers_ipv4(struct pubnub_ipv4_address* o_ipv4, - size_t n); + size_t n); #if PUBNUB_USE_IPV6 /** Sets the primary DNS server IPv6 address to use when resolving the Pubnub origin, in binary form(network order). @@ -162,7 +161,7 @@ PUBNUB_EXTERN int pubnub_dns_set_secondary_server_ipv6(struct pubnub_ipv6_addres /** Sets the primary DNS server IPv6 address from the corresponding 'numbers-and-colons' notation string to use when resolving the Pubnub origin. Applies to all subsequent DNS queries, if successful. - (Note: All DNS servers are initially set to zeros) + (Note: All DNS servers are initially set to zeros) @param ipv6_str The IPv6 address string of the server to use. Set all zeros("::0", or "0::") to not use this DNS server. @@ -178,7 +177,7 @@ PUBNUB_EXTERN int pubnub_dns_set_primary_server_ipv6_str(char const* ipv6_str); Applies to all subsequent DNS queries, if successful and if using secondary server is supported. Secondary server, if used at all, is used if a query to the primary server fails. - (Note: All DNS servers are initially set to zeros) + (Note: All DNS servers are initially set to zeros) @param ipv6_str The IPv6 address string of the server to use. Set all zeros("::0", or "0::") to not use this DNS server. @@ -207,10 +206,28 @@ PUBNUB_EXTERN int pubnub_get_dns_primary_server_ipv6(struct pubnub_ipv6_address* the secondary Ipv6 DNS server */ PUBNUB_EXTERN int pubnub_get_dns_secondary_server_ipv6(struct pubnub_ipv6_address* o_ipv6); -#endif /* PUBNUB_USE_IPV6 */ -#else +/** Reads the DNS servers in the system configuration. Will read + at most @p n servers, even if more are configured. Keep in + mind that modern systems have complex configurations and + often this will yield just one DNS server which listens on the + loopback IP address, while the "real" DNS configuration is + not available through standard means. + + On POSIX systems, this will read from `/etc/resolv.conf`, + looking for `nameserver` lines. On Windows, this will use OS + functions to get the info. + @param[out] o_ipv6 The array where to put the system DNS servers. + allocated by the caller for @p n elements. + @param[in] n The number of elements allocated for the @p o_ipv6 + @retval -1: error, can't read DNS server configuration + @retval otherwise: number of DNS servers read + */ +PUBNUB_EXTERN int pubnub_dns_read_system_servers_ipv6(struct pubnub_ipv6_address* o_ipv6, + size_t n); +#endif /* PUBNUB_USE_IPV6 */ +#else /* PUBNUB_SET_DNS_SERVERS */ #define pubnub_dns_set_primary_server_ipv4(ipv4) -1 #define pubnub_dns_set_secondary_server_ipv4(ipv4) -1 #define pubnub_dns_set_primary_server_ipv4_str(ipv4_str) -1 @@ -225,6 +242,7 @@ PUBNUB_EXTERN int pubnub_get_dns_secondary_server_ipv6(struct pubnub_ipv6_addres #define pubnub_dns_set_secondary_server_ipv6_str(ipv6_str) -1 #define pubnub_get_dns_primary_server_ipv6(o_ipv6) -1 #define pubnub_get_dns_secondary_server_ipv6(o_ipv6) -1 +#define pubnub_dns_read_system_servers_ipv6(o_ipv4, n) -1 #endif /* PUBNUB_USE_IPV6 */ -#endif /* PUBNUB_SET_DNS_SERVERS */ +#endif /* !PUBNUB_SET_DNS_SERVERS */ #endif /* !defined INC_PUBNUB_DNS_SERVERS */ diff --git a/core/pubnub_internal_common.h b/core/pubnub_internal_common.h index 7288bc12..ee9b2004 100644 --- a/core/pubnub_internal_common.h +++ b/core/pubnub_internal_common.h @@ -33,7 +33,7 @@ #define PUBNUB_CHANGE_DNS_SERVERS 0 #endif -#define PUBNUB_ADNS_RETRY_AFTER_CLOSE \ +#define PUBNUB_ADNS_RETRY_AFTER_CLOSE \ (PUBNUB_CHANGE_DNS_SERVERS || PUBNUB_USE_MULTIPLE_ADDRESSES) #if !defined(PUBNUB_ONLY_PUBSUB_API) @@ -118,7 +118,7 @@ /* Maximum object length that will be sent via PATCH, or POST methods */ #define PUBNUB_MAX_OBJECT_LENGTH 30000 -/* Default value port is initialized with in case of settable origin. Only +/* Default value port is initialized with in case of settable origin. Only values different than this are taken into account when making connections */ #define INITIAL_PORT_VALUE 0 @@ -205,19 +205,44 @@ typedef struct pubnub_tcp_keepalive_ { /** The time in seconds a socket needs to be @c idle before the first keep-alive probe is sent. */ - uint8_t time; + uint8_t time; /** The number of seconds that should pass between sends of keep-alive probes if the last one wasn't acknowledged. */ - uint8_t interval; + uint8_t interval; /** The number of times a probe will be sent and not acknowledged before the connection is deemed broken. */ - uint8_t probes; + uint8_t probes; } pubnub_tcp_keepalive; +#ifdef PUBNUB_CALLBACK_API +/** DNS resolution query tracking object. */ +struct dns_queries_tracking { + /* @c A record query has been sent. */ + bool sent_a; +#if PUBNUB_USE_IPV6 + /* @c AAAA record query has been sent. */ + bool sent_aaaa; +#endif /* PUBNUB_USE_IPV6 */ + /** Whether query should be retried in case of partial completion. */ + bool need_retry; + + /* @c A record response received. */ + bool received_a; + /* Temporarily storage until @c AAAA record response will be received. */ + struct sockaddr_in dns_a_addr; +#if PUBNUB_USE_IPV6 + /* @c AAAA record response received. */ + bool received_aaaa; + /* Temporarily storage until @c A record response will be received. */ + struct sockaddr_storage dns_aaaa_addr; +#endif /* PUBNUB_USE_IPV6 */ +}; +#endif /* PUBNUB_CALLBACK_API */ + struct pubnub_options { #if PUBNUB_BLOCKING_IO_SETTABLE /** Indicates whether to use blocking I/O. Not implemented if @@ -244,12 +269,12 @@ struct pubnub_options { #endif #if PUBNUB_USE_SSL /** Should the PubNub client establish the connection to - * PubNub using SSL? */ + * PubNub using SSL? */ bool useSSL : 1; /** When SSL is enabled, should the client fallback to a - * non-SSL connection if it experiences issues handshaking - * across local proxies, firewalls, etc? - */ + * non-SSL connection if it experiences issues handshaking + * across local proxies, firewalls, etc? + */ bool fallbackSSL : 1; /** Use system certificate store (if available) */ bool use_system_certificate_store : 1; @@ -291,11 +316,11 @@ struct pubnub_flags { #define ROTATIONS_COUNT_SIZE_IN_BITS 3 /** Number of full DNS servers list rotations in single transaction to a single DNS server. - Important when DNS server doesn't answer and transaction timeout. List of DNS + Important when DNS server doesn't answer and transaction timeout. List of DNS servers should rotate to find the one which is able to respond on DNS query. Macro constant limiting number of full DNS servers list rotations. */ - int rotations_count: ROTATIONS_COUNT_SIZE_IN_BITS; + int rotations_count : ROTATIONS_COUNT_SIZE_IN_BITS; #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif }; @@ -306,8 +331,8 @@ struct pbdns_servers_check { uint8_t dns_mask; /* dns server condition bit indicators(0 - OK, 1 - Error on server). Set to zeros indicates no issues encountered while sending, or receiving - valid response from any of available dns servers(up to 8 of them, as 'uint8_t' - conains 8 bits. In practise there is up to 5 dns servers). + valid response from any of available dns servers(up to 8 of them, as + 'uint8_t' conains 8 bits. In practise there is up to 5 dns servers). */ uint8_t dns_server_check; }; @@ -315,7 +340,7 @@ struct pbdns_servers_check { #if PUBNUB_USE_MULTIPLE_ADDRESSES struct pubnub_multi_addresses { - time_t time_of_the_last_dns_query; + time_t time_of_the_last_dns_query; /* Number of spare ipv4 addresses */ int n_ipv4; /* ipv4 address index(from the array) currently used */ @@ -398,7 +423,7 @@ struct pubnub_ { #if defined PUBNUB_ORIGIN_SETTABLE char const* origin; - + uint16_t port; #endif @@ -413,7 +438,7 @@ struct pubnub_ { Takes values from enum 'pubnub_method' defined in 'pubnub_api_types.h'. */ uint8_t method; - + #if PUBNUB_ADVANCED_KEEP_ALIVE struct pubnub_keep_alive_data { time_t timeout; @@ -466,28 +491,34 @@ struct pubnub_ { pubnub_callback_t cb; void* user_data; + struct dns_queries_tracking dns_queries; #if PUBNUB_CHANGE_DNS_SERVERS struct pbdns_servers_check dns_check; -#endif +#endif #if PUBNUB_USE_MULTIPLE_ADDRESSES struct pubnub_multi_addresses spare_addresses; #endif +#if PUBNUB_USE_IPV6 + struct sockaddr_storage dns_addr; +#else /* PUBNUB_USE_IPV6 */ + struct sockaddr_in dns_addr; +#endif /* !PUBNUB_USE_IPV6 */ #endif /* defined(PUBNUB_CALLBACK_API) */ /** Subscribed channels and channel groups saved. Exist when auto heartbeat support is enabled. */ M_channelInfo() - - /** Pubnub context fields for heartbeat info used by the module for keeping presence. - Exist when auto heartbeat support is enabled. - */ - M_heartbeatInfo() + + /** Pubnub context fields for heartbeat info used by the module for + keeping presence. Exist when auto heartbeat support is enabled. + */ + M_heartbeatInfo() #if PUBNUB_PROXY_API - /** The type (protocol) of the proxy to use */ - enum pubnub_proxy_type proxy_type; + /** The type (protocol) of the proxy to use */ + enum pubnub_proxy_type proxy_type; /** Hostname (address) of the proxy server to use */ char proxy_hostname[PUBNUB_MAX_PROXY_HOSTNAME_LENGTH + 1]; @@ -507,7 +538,7 @@ struct pubnub_ { struct pubnub_ipv6_address proxy_ipv6_address; #endif #endif /* defined(PUBNUB_CALLBACK_API) */ - + /** The (TCP) port to use on the proxy. */ uint16_t proxy_port; @@ -547,17 +578,17 @@ struct pubnub_ { /** Authentication realm - received from the server */ char realm[PUBNUB_MAX_HTTP_AUTH_REALM + 1]; - /** Proxy 'authentication required' response message counter for repeating realm - within a single transaction. - At this point this field is of importance for Digest proxy authentication sheme. - See RFC 7616 - 5.4. Limited-Use Nonce Values : - ...For example, a server MAY choose to allow each nonce value to be used only once by - maintaining a record of whether, or not each recently issued nonce has been returned - and sending a next-nonce parameter in the Authentication-Info header field of every - response... - - Doing it (within the same transaction)repeatedly, without restrictions, would be a sign - of irregular behaviour. + /** Proxy 'authentication required' response message counter for repeating + realm within a single transaction. At this point this field is of + importance for Digest proxy authentication sheme. See RFC 7616 - 5.4. + Limited-Use Nonce Values : + ...For example, a server MAY choose to allow each nonce value to be used + only once by maintaining a record of whether, or not each recently issued + nonce has been returned and sending a next-nonce parameter in the + Authentication-Info header field of every response... + + Doing it (within the same transaction)repeatedly, without restrictions, + would be a sign of irregular behaviour. */ uint8_t auth_msg_count; @@ -569,7 +600,7 @@ struct pubnub_ { #endif /* PUBNUB_PROXY_API */ /** Crypto module for encryption and decryption */ - struct pubnub_crypto_provider_t *crypto_module; + struct pubnub_crypto_provider_t* crypto_module; #ifdef PUBNUB_NTF_RUNTIME_SELECTION /** The PubNub API enforcement policy. */ diff --git a/core/pubnub_netcore.c b/core/pubnub_netcore.c index 6822c208..64669e3e 100644 --- a/core/pubnub_netcore.c +++ b/core/pubnub_netcore.c @@ -40,15 +40,15 @@ #include -#define WATCH_ENUM_RESOLV_N_CONNECT(X) \ - do { \ - enum pbpal_resolv_n_connect_result x_ = (X); \ - PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, \ - __FILE__ "(%d) in %s: `" #X "` = %d (%s)\n", \ - __LINE__, \ - __FUNCTION__, \ - x_, \ - pbpal_resolv_n_connect_res_2_string(x_)); \ +#define WATCH_ENUM_RESOLV_N_CONNECT(X) \ + do { \ + enum pbpal_resolv_n_connect_result x_ = (X); \ + PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, \ + __FILE__ "(%d) in %s: `" #X "` = %d (%s)\n", \ + __LINE__, \ + __FUNCTION__, \ + x_, \ + pbpal_resolv_n_connect_res_2_string(x_)); \ } while (0) /** Each HTTP chunk has a trailining CRLF ("\r\n" in C-speak). That's @@ -78,7 +78,7 @@ bool HTTP_request_has_body(uint8_t method) { - switch(method) { + switch (method) { case pubnubSendViaGET: case pubnubUseDELETE: return false; @@ -90,8 +90,8 @@ bool HTTP_request_has_body(uint8_t method) #endif return true; default: - PUBNUB_LOG_ERROR("Error: HTTP_message_has_body(method): unhandled method: %u\n", - method); + PUBNUB_LOG_ERROR( + "Error: HTTP_message_has_body(method): unhandled method: %u\n", method); return false; } } @@ -99,7 +99,7 @@ bool HTTP_request_has_body(uint8_t method) static char const* get_method_verb_string(uint8_t method) { - switch(method) { + switch (method) { case pubnubSendViaGET: return "GET "; case pubnubSendViaPOST: @@ -115,8 +115,8 @@ static char const* get_method_verb_string(uint8_t method) case pubnubUseDELETE: return "DELETE "; default: - PUBNUB_LOG_ERROR("Error: get_method_verb_string(method): unhandled method: %u\n", - method); + PUBNUB_LOG_ERROR( + "Error: get_method_verb_string(method): unhandled method: %u\n", method); return "UNKOWN "; } } @@ -142,11 +142,12 @@ static int send_fin_head(struct pubnub_* pb) static bool should_keep_alive(struct pubnub_* pb, enum pubnub_res rslt) { - PUBNUB_LOG_DEBUG("should_keep_alive(pb=%p, rslt=%d('%s')) pb->flags.should_close = %d\n", - pb, - rslt, - pubnub_res_2_string(rslt), - (int)pb->flags.should_close); + PUBNUB_LOG_DEBUG( + "should_keep_alive(pb=%p, rslt=%d('%s')) pb->flags.should_close = %d\n", + pb, + rslt, + pubnub_res_2_string(rslt), + (int)pb->flags.should_close); if (!pb->flags.should_close) { #if PUBNUB_ADVANCED_KEEP_ALIVE time_t tt = time(NULL); @@ -261,40 +262,41 @@ static enum pubnub_res dont_parse(struct pbcc_context* p) } -static PFpbcc_parse_response_T m_aParseResponse[] = { dont_parse, - pbcc_parse_subscribe_response, - pbcc_parse_publish_response, - pbcc_parse_publish_response, /* PBTT_SIGNAL */ +static PFpbcc_parse_response_T m_aParseResponse[] = { + dont_parse, + pbcc_parse_subscribe_response, + pbcc_parse_publish_response, + pbcc_parse_publish_response, /* PBTT_SIGNAL */ #if PUBNUB_ONLY_PUBSUB_API - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse, - dont_parse + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse, + dont_parse #else pbcc_parse_presence_response, /* PBTT_LEAVE */ pbcc_parse_time_response, pbcc_parse_history_response, - pbcc_parse_presence_response, /* PBTT_HERENOW */ - pbcc_parse_presence_response, /* PBTT_GLOBAL_HERENOW */ - pbcc_parse_presence_response, /* PBTT_WHERENOW */ - pbcc_parse_presence_response, /* PBTT_SET_STATE */ - pbcc_parse_presence_response, /* PBTT_STATE_GET */ + pbcc_parse_presence_response, /* PBTT_HERENOW */ + pbcc_parse_presence_response, /* PBTT_GLOBAL_HERENOW */ + pbcc_parse_presence_response, /* PBTT_WHERENOW */ + pbcc_parse_presence_response, /* PBTT_SET_STATE */ + pbcc_parse_presence_response, /* PBTT_STATE_GET */ pbcc_parse_channel_registry_response, /* PBTT_REMOVE_CHANNEL_GROUP */ pbcc_parse_channel_registry_response, /* PBTT_REMOVE_CHANNEL_FROM_GROUP */ pbcc_parse_channel_registry_response, /* PBTT_ADD_CHANNEL_TO_GROUP */ pbcc_parse_channel_registry_response, /* PBTT_LIST_CHANNEL_GROUP */ - pbcc_parse_presence_response /* PBTT_HEARTBEAT */ + pbcc_parse_presence_response /* PBTT_HEARTBEAT */ #if PUBNUB_USE_SUBSCRIBE_V2 , pbcc_parse_subscribe_v2_response /* PBTT_SUBSCRIBE_V2 */ #endif @@ -356,7 +358,7 @@ static enum pubnub_res parse_pubnub_result(struct pubnub_* pb) else { pbauto_heartbeat_start_timer(pb); } - + return pbres; } @@ -575,7 +577,7 @@ int pbnc_fsm(struct pubnub_* pb) pb->state = PBS_WAIT_CONNECT; break; case pbpal_connect_success: - i = pbntf_got_socket(pb); + i = pbntf_got_socket(pb); pb->state = PBS_CONNECTED; break; default: @@ -724,8 +726,10 @@ int pbnc_fsm(struct pubnub_* pb) case pbtlsStartedWaitRead: case pbtlsStartedWaitWrite: pb->state = PBS_WAIT_TLS_CONNECT; - if (pbtlsStartedWaitWrite == res) pbntf_watch_out_events(pb); - if (pbtlsStartedWaitRead == res) pbntf_watch_in_events(pb); + if (pbtlsStartedWaitWrite == res) + pbntf_watch_out_events(pb); + if (pbtlsStartedWaitRead == res) + pbntf_watch_in_events(pb); return 0; case pbtlsFailed: if (pb->options.fallbackSSL) { @@ -891,7 +895,7 @@ int pbnc_fsm(struct pubnub_* pb) } else { char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; - pb->state = PBS_TX_HOST; + pb->state = PBS_TX_HOST; if ((i < 0) || (-1 == pbpal_send_str(pb, o))) { outcome_detected(pb, PNR_IO_ERROR); break; @@ -985,8 +989,7 @@ int pbnc_fsm(struct pubnub_* pb) #endif ) { char hedr[128] = "\r\n"; - pbcc_via_post_headers( - &(pb->core), hedr + 2, sizeof hedr - 2); + pbcc_via_post_headers(&(pb->core), hedr + 2, sizeof hedr - 2); PUBNUB_LOG_TRACE( "Sending HTTP 'via POST, or PATCH' headers: '%s'\n", hedr); pb->state = PBS_TX_EXTRA_HEADERS; @@ -1079,8 +1082,8 @@ int pbnc_fsm(struct pubnub_* pb) /** Reset failed request `Retry-After` HTTP header value. */ pb->http_header_retry_after = 0; #endif // #if PUBNUB_USE_RETRY_CONFIGURATION - pb->http_chunked = false; - pb->state = PBS_RX_HEADERS; + pb->http_chunked = false; + pb->state = PBS_RX_HEADERS; goto next_state; case PNR_CONNECTION_TIMEOUT: case PNR_IO_ERROR: @@ -1162,10 +1165,12 @@ int pbnc_fsm(struct pubnub_* pb) } goto next_state; } - if (pb_strncasecmp(pb->core.http_buf, h_chunked, sizeof h_chunked - 1) == 0) { + if (pb_strncasecmp(pb->core.http_buf, h_chunked, sizeof h_chunked - 1) + == 0) { pb->http_chunked = true; } - else if (pb_strncasecmp(pb->core.http_buf, h_length, sizeof h_length - 1) == 0) { + else if (pb_strncasecmp(pb->core.http_buf, h_length, sizeof h_length - 1) + == 0) { size_t len = atoi(pb->core.http_buf + sizeof h_length - 1); if (0 != pbcc_realloc_reply_buffer(&pb->core, len)) { outcome_detected(pb, PNR_REPLY_TOO_BIG); @@ -1174,15 +1179,20 @@ int pbnc_fsm(struct pubnub_* pb) pb->core.http_content_len = len; } #if PUBNUB_USE_RETRY_CONFIGURATION - else if (pb_strncasecmp(pb->core.http_buf, h_retry_after, sizeof h_retry_after - 1) == 0) { - pb->http_header_retry_after = atoi(pb->core.http_buf + sizeof h_retry_after - 1); + else if (pb_strncasecmp( + pb->core.http_buf, h_retry_after, sizeof h_retry_after - 1) + == 0) { + pb->http_header_retry_after = + atoi(pb->core.http_buf + sizeof h_retry_after - 1); } #endif // #if PUBNUB_USE_RETRY_CONFIGURATION - else if (pb_strncasecmp(pb->core.http_buf, h_close, sizeof h_close - 1) == 0) { + else if (pb_strncasecmp(pb->core.http_buf, h_close, sizeof h_close - 1) + == 0) { pb->flags.should_close = true; } #if PUBNUB_RECEIVE_GZIP_RESPONSE - else if (pb_strncasecmp(pb->core.http_buf, h_encoding, sizeof h_encoding - 1) + else if (pb_strncasecmp( + pb->core.http_buf, h_encoding, sizeof h_encoding - 1) == 0) { pb->data_compressed = compressionGZIP; } @@ -1260,7 +1270,7 @@ int pbnc_fsm(struct pubnub_* pb) } else if (0 != pbcc_realloc_reply_buffer( - &pb->core, pb->core.http_buf_len + chunk_length)) { + &pb->core, pb->core.http_buf_len + chunk_length)) { outcome_detected(pb, PNR_REPLY_TOO_BIG); } else { @@ -1379,7 +1389,7 @@ int pbnc_fsm(struct pubnub_* pb) break; } pb->state = PBS_TX_GET; - i = pbpal_send_str(pb, get_method_verb_string(pb->method)); + i = pbpal_send_str(pb, get_method_verb_string(pb->method)); if (i < 0) { pb->state = close_kept_alive_connection(pb); } @@ -1434,27 +1444,27 @@ void pbnc_stop(struct pubnub_* pbp, enum pubnub_res outcome_to_report) #if defined(PUBNUB_NTF_RUNTIME_SELECTION) if (PNA_CALLBACK == pbp->api_policy) { #endif - if (PNR_TIMEOUT == outcome_to_report) { - if ((pbp->state != PBS_WAIT_CONNECT) && + if (PNR_TIMEOUT == outcome_to_report) { + if ((pbp->state != PBS_WAIT_CONNECT) && #if PUBNUB_CHANGE_DNS_SERVERS - (pbpal_dns_rotate_server(pbp) == 0) + (pbpal_dns_rotate_server(pbp) == 0) #else (pbp->flags.sent_queries < PUBNUB_MAX_DNS_QUERIES) #endif /* PUBNUB_CHANGE_DNS_SERVERS */ - ) { - pbp->state = PBS_WAIT_CANCEL_DNS; + ) { + pbp->state = PBS_WAIT_CANCEL_DNS; + } + else { + pbp->core.last_result = (PBS_WAIT_CONNECT == pbp->state) + ? PNR_WAIT_CONNECT_TIMEOUT + : PNR_ADDR_RESOLUTION_FAILED; + pbp->state = PBS_WAIT_CANCEL; + } } else { - pbp->core.last_result = (PBS_WAIT_CONNECT == pbp->state) - ? PNR_WAIT_CONNECT_TIMEOUT - : PNR_ADDR_RESOLUTION_FAILED; pbp->state = PBS_WAIT_CANCEL; } - } - else { - pbp->state = PBS_WAIT_CANCEL; - } - pbntf_requeue_for_processing(pbp); + pbntf_requeue_for_processing(pbp); #if defined(PUBNUB_NTF_RUNTIME_SELECTION) } /* if api_policy */ #endif diff --git a/core/pubnub_ntf_sync.c b/core/pubnub_ntf_sync.c index 14b9fa50..536ba33f 100644 --- a/core/pubnub_ntf_sync.c +++ b/core/pubnub_ntf_sync.c @@ -256,6 +256,7 @@ enum pubnub_res pubnub_await(pubnub_t* pb) } #endif /* PUBNUB_NTF_RUNTIME_SELECTION */ + pb->should_stop_await = false; t0 = pbms_start(); while (!pbnc_can_start_transaction(pb)) { // Checking whether await cycle should be stopped or not. diff --git a/core/pubnub_version_internal.h b/core/pubnub_version_internal.h index 2398d35f..56beb97a 100644 --- a/core/pubnub_version_internal.h +++ b/core/pubnub_version_internal.h @@ -3,7 +3,7 @@ #define INC_PUBNUB_VERSION_INTERNAL -#define PUBNUB_SDK_VERSION "6.1.0" +#define PUBNUB_SDK_VERSION "6.2.0" #endif /* !defined INC_PUBNUB_VERSION_INTERNAL */ diff --git a/core/samples/publish_callback_subloop_sample.c b/core/samples/publish_callback_subloop_sample.c index 5ff85d38..ba03fac1 100644 --- a/core/samples/publish_callback_subloop_sample.c +++ b/core/samples/publish_callback_subloop_sample.c @@ -128,13 +128,13 @@ static void callback_sample_free(pubnub_t* pb) int main() { - const unsigned minutes_in_loop = 1; - char const* chan = "hello_world"; - pubnub_t* pbp = pubnub_alloc(); - pubnub_t* pbp_2 = pubnub_alloc(); - enum pubnub_res result; + const unsigned minutes_in_loop = 1; + char const* chan = "hello_world"; + pubnub_t* pbp = pubnub_alloc(); + pubnub_t* pbp_2 = pubnub_alloc(); + enum pubnub_res result; struct pubnub_ipv4_address o_ipv4[3]; - pubnub_subloop_t* pbsld; + pubnub_subloop_t* pbsld; if (NULL == pbp) { printf("Failed to allocate Pubnub context!\n"); @@ -148,7 +148,7 @@ int main() pubnub_register_callback(pbp_2, publish_callback, (void*)chan); - paint_text_white(); + paint_text_white(); //! [Define subscribe loop] pbsld = pubnub_subloop_define( pbp, chan, pubnub_subscribe_defopts(), subloop_callback); diff --git a/core/samples/subscribe_publish_from_callback.c b/core/samples/subscribe_publish_from_callback.c index 718ab788..17a223b4 100644 --- a/core/samples/subscribe_publish_from_callback.c +++ b/core/samples/subscribe_publish_from_callback.c @@ -27,7 +27,7 @@ static char const* m_chan = "hello_world"; static void wait_seconds(double time_in_seconds) { - time_t start = time(NULL); + time_t start = time(NULL); double time_passed_in_seconds; do { time_passed_in_seconds = difftime(time(NULL), start); @@ -36,7 +36,7 @@ static void wait_seconds(double time_in_seconds) static void wait_useconds(unsigned long time_in_microseconds) { - clock_t start = clock(); + clock_t start = clock(); unsigned long time_passed_in_microseconds; do { time_passed_in_microseconds = clock() - start; @@ -257,7 +257,7 @@ int main() printf("Failed to read system DNS server, will use default %s\n", PUBNUB_DEFAULT_DNS_SERVER); } - + pubnub_mutex_init_static(m_lock); pubnub_set_transaction_timeout(pbp, 5000); @@ -278,7 +278,7 @@ int main() /* Awaiting subscribe/connect */ do { pubnub_mutex_lock(m_lock); - if(m_first_subscribe_done) { + if (m_first_subscribe_done) { pubnub_mutex_unlock(m_lock); break; } @@ -291,9 +291,8 @@ int main() puts("-----------------------"); puts("Publishing1..."); puts("-----------------------"); - res = pubnub_publish(pbp_2, - chan1, - "\"[1]Hello world from 'subscribe-publish from callback' sample!\""); + res = pubnub_publish( + pbp_2, chan1, "\"[1]Hello world from 'subscribe-publish from callback' sample!\""); if (res != PNR_STARTED) { printf("pubnub_publish1() returned unexpected: %d('%s')\n", res, @@ -303,8 +302,8 @@ int main() } /* Time to play. Turning internet connection 'off' and then back 'on'. - Starting everything disconnected and then connecting to internet at some point, - and so on... + Starting everything disconnected and then connecting to internet at some + point, and so on... */ wait_seconds(200); diff --git a/core/test/pubnub_config.h b/core/test/pubnub_config.h index c05e9286..9fd3f2b3 100644 --- a/core/test/pubnub_config.h +++ b/core/test/pubnub_config.h @@ -1,6 +1,6 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ #if !defined INC_PUBNUB_CONFIG -#define INC_PUBNUB_CONFIG +#define INC_PUBNUB_CONFIG /* -- Next few definitions can be tweaked by the user, but with care -- */ @@ -46,7 +46,7 @@ /** If defined, the PubNub implementation will not try to catch-up on * messages it could miss while subscribe failed with an IO error or - * such. Use this if missing some messages is not a problem. + * such. Use this if missing some messages is not a problem. * * @note messages may sometimes still be lost due to potential @ref * PUBNUB_REPLY_MAXLEN overrun issue */ @@ -55,7 +55,7 @@ /** This is the URL of the Pubnub server. Change only for testing purposes. */ -#define PUBNUB_ORIGIN "pubsub.pubnub.com" +#define PUBNUB_ORIGIN "pubsub.pubnub.com" /** The maximum length (in characters) of the host name of the proxy that will be saved in the Pubnub context. @@ -108,8 +108,13 @@ /** Maximum number of consecutive retries when sending DNS query in a single transaction */ #define PUBNUB_MAX_DNS_QUERIES 3 #if PUBNUB_CHANGE_DNS_SERVERS +#if PUBNUB_USE_IPV6 +/** Maximum number of DNS servers list rotation in a single transaction */ +#define PUBNUB_MAX_DNS_ROTATION 5 +#else /* PUBNUB_USE_IPV6 */ /** Maximum number of DNS servers list rotation in a single transaction */ #define PUBNUB_MAX_DNS_ROTATION 3 +#endif /* !PUBNUB_USE_IPV6 */ #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ @@ -122,14 +127,14 @@ #define PUBNUB_USE_LOG_CALLBACK 0 #endif -#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 +#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 #define PUBNUB_MIN_TRANSACTION_TIMER 200 /** Duration of the 'wait_connect_TCP_socket' timeout set during context initialization, in milliseconds. Can be changed later by the user. */ -#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 +#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 /** Mininmal duration of the 'wait_connect_TCP_socket' timer, in milliseconds. * You can't set less than this. diff --git a/cpp/pubnub_common.hpp b/cpp/pubnub_common.hpp index 1c3c37b7..768c811b 100644 --- a/cpp/pubnub_common.hpp +++ b/cpp/pubnub_common.hpp @@ -236,10 +236,10 @@ class subscribe_options { */ class subscribe_v2_options { pubnub_subscribe_v2_options d_; - std::string d_chgrp; - std::string d_filter_expr; - std::string d_timetoken; - + std::string d_chgrp; + std::string d_filter_expr; + std::string d_timetoken; + public: subscribe_v2_options() { d_ = pubnub_subscribe_v2_defopts(); } subscribe_v2_options& channel_group(std::string const& chgroup) @@ -259,16 +259,17 @@ class subscribe_v2_options { } subscribe_v2_options& filter_expr(std::string const& filter_exp) { - d_filter_expr = filter_exp; + d_filter_expr = filter_exp; d_.filter_expr = d_filter_expr.empty() ? 0 : d_filter_expr.c_str(); return *this; } subscribe_v2_options& timetoken(std::string const& timetoken) { - d_timetoken = timetoken; + d_timetoken = timetoken; if (d_timetoken.empty()) { d_.timetoken[0] = '\0'; - } else { + } + else { std::strncpy(d_.timetoken, d_timetoken.c_str(), sizeof(d_.timetoken) - 1); d_.timetoken[d_timetoken.size()] = '\0'; } @@ -426,11 +427,14 @@ class history_options { class set_state_options { pubnub_set_state_options d_; - std::string d_channel_group; - std::string d_user_id; + std::string d_channel_group; + std::string d_user_id; public: - set_state_options() : d_(pubnub_set_state_options()) {} + set_state_options() + : d_(pubnub_set_state_options()) + { + } set_state_options& channel_group(std::string const& channel_group) { @@ -447,7 +451,7 @@ class set_state_options { set_state_options& user_id(std::string const& user_id) { - d_user_id = user_id; + d_user_id = user_id; d_.user_id = d_user_id.empty() ? 0 : d_user_id.c_str(); return *this; @@ -468,18 +472,20 @@ class set_state_options { class include_options { char d_include_c_strings_array[MAX_INCLUDE_DIMENSION][MAX_ELEM_LENGTH + 1]; size_t d_include_count; - + public: include_options() : d_include_count(0) - {} + { + } char const** include_c_strings_array() { - return (d_include_count > 0) ? (char const**)d_include_c_strings_array : NULL; + return (d_include_count > 0) ? (char const**)d_include_c_strings_array + : NULL; } char const** include_to_c_strings_array(std::vector const& inc) { - size_t n = inc.size(); + size_t n = inc.size(); unsigned i; if (n > MAX_INCLUDE_DIMENSION) { throw std::range_error("include parameter has too many elements."); @@ -499,7 +505,7 @@ class include_options { } size_t include_count() { return d_include_count; } }; - + /** A wrapper class for objects api paging option parameters, enabling a nicer usage. Something like: pbp.get_users(list_options().start(last_bookmark)); @@ -509,41 +515,48 @@ class include_options { */ class list_options { std::string d_include; - size_t d_limit; + size_t d_limit; std::string d_start; std::string d_end; - tribool d_count; + tribool d_count; public: list_options() : d_limit(0) , d_count(tribool::not_set) - {} + { + } list_options& include(std::string const& incl) { d_include = incl; return *this; } - char const* include() { return (d_include.size() > 0) ? d_include.c_str() : NULL; } + char const* include() + { + return (d_include.size() > 0) ? d_include.c_str() : NULL; + } list_options& limit(size_t lim) { d_limit = lim; return *this; } - size_t limit() { return d_limit; } + size_t limit() { return d_limit; } list_options& start(std::string const& st) { d_start = st; return *this; } - char const* start() { return (d_start.size() > 0) ? d_start.c_str() : NULL; } + char const* start() + { + return (d_start.size() > 0) ? d_start.c_str() : NULL; + } list_options& end(std::string const& e) { d_end = e; return *this; } - char const* end() { return (d_end.size() > 0) ? d_end.c_str() : NULL; } + char const* end() { return (d_end.size() > 0) ? d_end.c_str() : NULL; } list_options& count(tribool co) { d_count = co; @@ -560,43 +573,43 @@ class list_options { return pbccNotSet; } }; -#endif /* PUBNUB_USE_OBJECTS_API */ +#endif /* PUBNUB_USE_OBJECTS_API */ #if PUBNUB_CRYPTO_API /** Interface for a cryptor. It is an algorithm class that * provides encryption and decryption of arrays of bytes. * - * It is used by the C++ Pubnub context to encrypt and decrypt messages + * It is used by the C++ Pubnub context to encrypt and decrypt messages * sent and received from Pubnub. * * Note that name comes from Rust, where it is used to convert from type to type. * The pointer returned by `into_cryptor_ptr()` should be allocated with `new`. * - * @see pubnub_cryptor -*/ + * @see pubnub_cryptor + */ class into_cryptor_ptr { public: - ~into_cryptor_ptr() { } + ~into_cryptor_ptr() {} virtual pubnub_cryptor_t* into_cryptor() = 0; }; -/** Interface for a crypto provider. It is used to encrypt and decrypt - * messages sent and received from Pubnub. It makes a conversion from +/** Interface for a crypto provider. It is used to encrypt and decrypt + * messages sent and received from Pubnub. It makes a conversion from * your cryptographic module to the C interface used by the C++ Pubnub context. * - * It is used by the C++ Pubnub context to encrypt and decrypt messages + * It is used by the C++ Pubnub context to encrypt and decrypt messages * sent and received from Pubnub. * * Note that name comes from Rust, where it is used to convert from type to type. * The pointer returned by `into_provider_ptr()` should be allocated with `new`. * * @see pubnub_crypto_provider_t -*/ + */ class into_crypto_provider_ptr { public: - ~into_crypto_provider_ptr() { } + ~into_crypto_provider_ptr() {} virtual pubnub_crypto_provider_t* into_provider() = 0; }; @@ -604,12 +617,12 @@ class into_crypto_provider_ptr { /** Default implementation of the cryptoprovider. * - * It implements the "into_crypto_provider_ptr" interface. + * It implements the "into_crypto_provider_ptr" interface. * - * It is used by the C++ Pubnub context to encrypt and decrypt messages + * It is used by the C++ Pubnub context to encrypt and decrypt messages * sent and received from Pubnub. * - * It is designed to behave like the default PubNub crypto module + * It is designed to behave like the default PubNub crypto module * returned by `pubnub_crypto_aes_cbc_module_init()`, * `pubnub_crypto_legacy_module_init()` and `pubnub_crypto_module_init()` functions. * @@ -617,8 +630,8 @@ class into_crypto_provider_ptr { * @see pubnub_crypto_aes_cbc_module_init * @see pubnub_crypto_legacy_module_init * @see pubnub_crypto_module_init -*/ -class crypto_module: public into_crypto_provider_ptr { + */ +class crypto_module : public into_crypto_provider_ptr { public: /* Wrapping constructor for the `pubnub_crypto_provider_t` structure. * @@ -627,7 +640,10 @@ class crypto_module: public into_crypto_provider_ptr { * * @param crypto_module The `pubnub_crypto_provider_t` structure to wrap. */ - crypto_module(pubnub_crypto_provider_t* crypto_module) : d_module(crypto_module) {} + crypto_module(pubnub_crypto_provider_t* crypto_module) + : d_module(crypto_module) + { + } /* Constructor that translates C++ data into the `pubnub_crypto_provider_init` function. * @@ -638,21 +654,24 @@ class crypto_module: public into_crypto_provider_ptr { * * @param cryptor The `pubnub_cryptor_t` structure to wrap. */ - crypto_module(into_cryptor_ptr &default_cryptor, std::vector &additional_cryptors) + crypto_module(into_cryptor_ptr& default_cryptor, + std::vector& additional_cryptors) { - size_t size = additional_cryptors.size(); - pubnub_cryptor_t* cryptors = new pubnub_cryptor_t[sizeof(pubnub_cryptor_t*) * size]; + size_t size = additional_cryptors.size(); + pubnub_cryptor_t* cryptors = + new pubnub_cryptor_t[sizeof(pubnub_cryptor_t*) * size]; for (size_t i = 0; i < size; ++i) { pubnub_cryptor_t* cryptor = additional_cryptors[i]->into_cryptor(); - cryptors[i] = *cryptor; + cryptors[i] = *cryptor; } - this->d_module = pubnub_crypto_module_init(default_cryptor.into_cryptor(), cryptors, size); + this->d_module = pubnub_crypto_module_init( + default_cryptor.into_cryptor(), cryptors, size); } /* Constructor that creates C++ `crypto_module` object that mimics - * the `pubnub_crypto_aes_cbc_module_init` function. + * the `pubnub_crypto_aes_cbc_module_init` function. * * @param cryptor The `pubnub_cryptor_t` structure to wrap.(it doesn't own the key - keep it alive) * @@ -662,11 +681,12 @@ class crypto_module: public into_crypto_provider_ptr { */ static crypto_module aes_cbc(std::string& cipher_key) { - return crypto_module(pubnub_crypto_aes_cbc_module_init((uint8_t*)(cipher_key.c_str()))); + return crypto_module( + pubnub_crypto_aes_cbc_module_init((uint8_t*)(cipher_key.c_str()))); } /* Constructor that creates C++ `crypto_module` object that mimics - * the `pubnub_crypto_legacy_module_init` function. + * the `pubnub_crypto_legacy_module_init` function. * * @param cryptor The `pubnub_cryptor_t` structure to wrap. (it doesn't own the key - keep it alive) * @@ -676,7 +696,8 @@ class crypto_module: public into_crypto_provider_ptr { */ static crypto_module legacy(std::string& cipher_key) { - return crypto_module(pubnub_crypto_legacy_module_init((uint8_t*)(cipher_key.c_str()))); + return crypto_module( + pubnub_crypto_legacy_module_init((uint8_t*)(cipher_key.c_str()))); } /* Encrypts the @p to_encrypt buffer and returns the encrypted @@ -686,15 +707,18 @@ class crypto_module: public into_crypto_provider_ptr { * * @return The encrypted buffer */ - std::vector encrypt(std::vector& to_encrypt) { + std::vector encrypt(std::vector& to_encrypt) + { pubnub_bymebl_t to_encrypt_c; - to_encrypt_c.ptr = to_encrypt.data(); + to_encrypt_c.ptr = to_encrypt.data(); to_encrypt_c.size = to_encrypt.size(); - pubnub_bymebl_t result = this->d_module->encrypt(this->d_module, to_encrypt_c); + pubnub_bymebl_t result = + this->d_module->encrypt(this->d_module, to_encrypt_c); if (result.ptr == nullptr) { - throw std::runtime_error("crypto_module::encrypt: Encryption failed!"); + throw std::runtime_error( + "crypto_module::encrypt: Encryption failed!"); } return std::vector(result.ptr, result.ptr + result.size); @@ -707,23 +731,24 @@ class crypto_module: public into_crypto_provider_ptr { * * @return The decrypted buffer */ - std::vector decrypt(std::vector& to_decrypt) { + std::vector decrypt(std::vector& to_decrypt) + { pubnub_bymebl_t to_decrypt_c; - to_decrypt_c.ptr = to_decrypt.data(); + to_decrypt_c.ptr = to_decrypt.data(); to_decrypt_c.size = to_decrypt.size(); - pubnub_bymebl_t result = this->d_module->decrypt(this->d_module, to_decrypt_c); + pubnub_bymebl_t result = + this->d_module->decrypt(this->d_module, to_decrypt_c); if (result.ptr == nullptr) { - throw std::runtime_error("crypto_module::decrypt: Decryption failed!"); + throw std::runtime_error( + "crypto_module::decrypt: Decryption failed!"); } return std::vector(result.ptr, result.ptr + result.size); } - pubnub_crypto_provider_t* into_provider() { - return this->d_module; - } + pubnub_crypto_provider_t* into_provider() { return this->d_module; } private: pubnub_crypto_provider_t* d_module; @@ -809,7 +834,7 @@ class context { return pubnub_port_set(d_pb, port); } - /** Sets the secret key to @p secret_key. If @p secret_key is an + /** Sets the secret key to @p secret_key. If @p secret_key is an empty string, `secret_key` will not be used. @see pubnub_set_secret_key */ @@ -817,14 +842,17 @@ class context { { lock_guard lck(d_mutex); if (!pubnub_can_start_transaction(d_pb)) { - throw std::logic_error("setting 'secret_key' key while transaction in progress"); + throw std::logic_error( + "setting 'secret_key' key while transaction in progress"); } d_secret_key = secret_key; - #if PUBNUB_CRYPTO_API - pubnub_set_secret_key(d_pb, secret_key.empty() ? NULL : d_secret_key.c_str()); - #else - throw std::logic_error("PUBNUB_CRYPTO_API is not enabled to use 'secret_key'"); - #endif +#if PUBNUB_CRYPTO_API + pubnub_set_secret_key(d_pb, + secret_key.empty() ? NULL : d_secret_key.c_str()); +#else + throw std::logic_error( + "PUBNUB_CRYPTO_API is not enabled to use 'secret_key'"); +#endif } /// Returns the current `secret_key` for this context @@ -842,7 +870,8 @@ class context { { lock_guard lck(d_mutex); if (!pubnub_can_start_transaction(d_pb)) { - throw std::logic_error("setting 'auth' key while transaction in progress"); + throw std::logic_error( + "setting 'auth' key while transaction in progress"); } d_auth = auth; pubnub_set_auth(d_pb, auth.empty() ? NULL : d_auth.c_str()); @@ -862,7 +891,8 @@ class context { { lock_guard lck(d_mutex); if (!pubnub_can_start_transaction(d_pb)) { - throw std::logic_error("setting 'auth_token' while transaction in progress"); + throw std::logic_error( + "setting 'auth_token' while transaction in progress"); } d_auth_token = token; pubnub_set_auth_token(d_pb, token.empty() ? NULL : d_auth_token.c_str()); @@ -873,7 +903,7 @@ class context { lock_guard lck(d_mutex); return d_auth_token; } - + /** sets the user_id to @p uuid. if @p uuid is an empty string, user_id will not be used. @@ -898,11 +928,11 @@ class context { } /// Set the user_id with a random-generated UUID - /// + /// /// @deprecated random generated uuid/user_id is deprecated. /// /// @see pubnub_generate_uuid_v4_random - PUBNUB_DEPRECATED int set_uuid_v4_random() + PUBNUB_DEPRECATED int set_uuid_v4_random() { struct Pubnub_UUID uuid; if (0 != pubnub_generate_uuid_v4_random(&uuid)) { @@ -917,10 +947,7 @@ class context { @deprecated this is provided as a workaround for existing users. Please use `user_id` instead. */ - PUBNUB_DEPRECATED std::string const uuid() const - { - return user_id(); - } + PUBNUB_DEPRECATED std::string const uuid() const { return user_id(); } /// Returns the current user_id std::string const user_id() const @@ -953,17 +980,14 @@ class context { #if PUBNUB_USE_SUBSCRIBE_V2 /// Returns the next v2 message from the context. If there are /// none, returns an empty message structure(checked through - /// v2_mesage::is_empty()). + /// v2_mesage::is_empty()). /// @see pubnub_get_v2 - v2_message get_v2() const - { - return v2_message(pubnub_get_v2(d_pb)); - } + v2_message get_v2() const { return v2_message(pubnub_get_v2(d_pb)); } /// Returns a vector of all v2 messages from the context. std::vector get_all_v2() const { std::vector all; - v2_message msg = get_v2(); + v2_message msg = get_v2(); while (!msg.is_empty()) { all.push_back(msg); @@ -1031,13 +1055,10 @@ class context { return all; } - /// Cancels the transaction, if any is ongoing, or connection is 'kept alive'. - /// If none is ongoing, it is ignored. + /// Cancels the transaction, if any is ongoing, or connection is 'kept + /// alive'. If none is ongoing, it is ignored. /// @see pubnub_cancel - enum pubnub_cancel_res cancel() - { - return pubnub_cancel(d_pb); - } + enum pubnub_cancel_res cancel() { return pubnub_cancel(d_pb); } /// Publishes a @p message on the @p channel. The @p channel /// can have many channels separated by a comma @@ -1053,7 +1074,8 @@ class context { std::string const& message, publish_options opt) { - return doit(pubnub_publish_ex(d_pb, channel.c_str(), message.c_str(), opt.data())); + return doit(pubnub_publish_ex( + d_pb, channel.c_str(), message.c_str(), opt.data())); } /// Sends a signal @p message on the @p channel. @@ -1062,7 +1084,7 @@ class context { { return doit(pubnub_signal(d_pb, channel.c_str(), message.c_str())); } - + #if PUBNUB_CRYPTO_API /// Publishes a @p message on the @p channel encrypted with @p /// cipher_key. @@ -1121,7 +1143,8 @@ class context { /// Pass a vector of channels in the @p channel and we'll put /// commas between them. A helper function. - futres subscribe_v2(std::vector const& channel, subscribe_v2_options opt) + futres subscribe_v2(std::vector const& channel, + subscribe_v2_options opt) { return subscribe_v2(join(channel), opt); } @@ -1147,10 +1170,7 @@ class context { /// Starts a "get time" transaction /// @see pubnub_time - futres time() - { - return doit(pubnub_time(d_pb)); - } + futres time() { return doit(pubnub_time(d_pb)); } /// Starts a transaction to get message history for @p channel /// with the limit of max @p count @@ -1161,10 +1181,8 @@ class context { unsigned count = 100, bool include_token = false) { - return doit(pubnub_history(d_pb, - channel.empty() ? 0 : channel.c_str(), - count, - include_token)); + return doit(pubnub_history( + d_pb, channel.empty() ? 0 : channel.c_str(), count, include_token)); } /// Starts a "history" with extended (full) options @@ -1206,29 +1224,30 @@ class context { /// the given @p timetoken futres message_counts(std::string const& channel, std::string const& timetoken) { - return doit(pubnub_message_counts(d_pb, - channel.empty() ? 0 : channel.c_str(), - timetoken.empty() ? 0 : timetoken.c_str())); + return doit( + pubnub_message_counts(d_pb, + channel.empty() ? 0 : channel.c_str(), + timetoken.empty() ? 0 : timetoken.c_str())); } - + /// Starts 'advanced history' pubnub_message_counts operation /// for unread messages on @p channel(channel list) starting from /// the given @p timetoken futres message_counts(std::vector const& channel, - std::string const& timetoken) + std::string const& timetoken) { return message_counts(join(channel), timetoken); } - + /// Starts 'advanced history' pubnub_message_counts operation /// for unread messages on @p channel(channel list) starting from /// the given @p channel_timetokens(per channel) - futres message_counts(std::string const& channel, + futres message_counts(std::string const& channel, std::vector const& channel_timetokens) { return message_counts(channel, join(channel_timetokens)); } - + /// Starts 'advanced history' pubnub_message_counts operation /// for unread messages on @p channel(channel list) starting from /// the given @p channel_timetokens(per channel) @@ -1237,18 +1256,17 @@ class context { { return message_counts(join(channel), channel_timetokens); } - + /// Starts 'advanced history' pubnub_message_counts operation /// for unread messages on @p channel_timetokens(channel, ch_timetoken pairs) - futres message_counts( - std::vector > const& channel_timetokens) + futres message_counts(std::vector> const& channel_timetokens) { std::string ch_list(""); std::string tt_list(""); - unsigned n = channel_timetokens.empty() ? 0 : channel_timetokens.size(); - unsigned i; + unsigned n = channel_timetokens.empty() ? 0 : channel_timetokens.size(); + unsigned i; for (i = 0; i < n; i++) { - std::string separator = ((i+1) < n) ? "," : ""; + std::string separator = ((i + 1) < n) ? "," : ""; ch_list += channel_timetokens[i].first + separator; tt_list += channel_timetokens[i].second + separator; } @@ -1259,15 +1277,16 @@ class context { /// 'advanced history' pubnub_message_counts operation std::map get_channel_message_counts() { - std::map map; + std::map map; std::vector chan_msg_counters; - int i; + int i; int count = pubnub_get_chan_msg_counts_size(d_pb); if (count <= 0) { return map; } chan_msg_counters = std::vector(count); - if (pubnub_get_chan_msg_counts(d_pb, (size_t*)&count, &chan_msg_counters[0]) != 0) { + if (pubnub_get_chan_msg_counts(d_pb, (size_t*)&count, &chan_msg_counters[0]) + != 0) { return map; } for (i = 0; i < count; i++) { @@ -1277,7 +1296,7 @@ class context { } return map; } - + /// Starts a transaction to inform Pubnub we're working /// on a @p channel and/or @p channel_group /// @see pubnub_heartbeat @@ -1335,10 +1354,7 @@ class context { /// Starts a transaction to get a list of currently present /// UUIDs on all channels /// @see pubnub_global_here_now - futres global_here_now() - { - return doit(pubnub_global_here_now(d_pb)); - } + futres global_here_now() { return doit(pubnub_global_here_now(d_pb)); } /// Starts a transaction to get a list of channels the @p uuid /// is currently present on. If @p uuid is not given (or is an @@ -1373,25 +1389,25 @@ class context { return set_state(join(channel), join(channel_group), uuid, state); } - /// Starts a transaction to set the @p state JSON object with selected - /// @options for the given @p channel and/or channel groups chosen + /// Starts a transaction to set the @p state JSON object with selected + /// @options for the given @p channel and/or channel groups chosen /// in options. /// @see pubnub_set_state_ex futres set_state(std::string const& channel, std::string const& state, - set_state_options options) + set_state_options options) { char const* ch = channel.empty() ? 0 : channel.c_str(); return doit(pubnub_set_state_ex(d_pb, ch, state.c_str(), options.data())); } - - /// Starts a transaction to set the @p state JSON object with selected + + /// Starts a transaction to set the @p state JSON object with selected /// @options for the given @p channels passed as a vector and/or channel /// groups chosen in options. /// @see pubnub_set_state_ex futres set_state(std::vector const& channel, - std::string const& state, - set_state_options options) + std::string const& state, + set_state_options options) { return set_state(join(channel), state, options); } @@ -1471,12 +1487,10 @@ class context { /// @see pubnub_grant_token futres grant_token(std::string const& grant_obj) { - return doit(pubnub_grant_token( - d_pb, - grant_obj.c_str())); + return doit(pubnub_grant_token(d_pb, grant_obj.c_str())); } - /// Returns grant token + /// Returns grant token /// @see pubnub_get_grant_token() std::string get_grant_token() { @@ -1488,20 +1502,18 @@ class context { /// @see pubnub_grant_token std::string parse_token(std::string const& token) { - char* parsed_token_chars = pubnub_parse_token(d_pb, token.c_str()); + char* parsed_token_chars = pubnub_parse_token(d_pb, token.c_str()); std::string owned_parsed_token(parsed_token_chars); - - ::free(parsed_token_chars); - return owned_parsed_token; + + ::free(parsed_token_chars); + return owned_parsed_token; } #endif #if PUBNUB_USE_REVOKE_TOKEN_API futres revoke_token(std::string const& token) { - return doit( - pubnub_revoke_token(d_pb, token.c_str()) - ); + return doit(pubnub_revoke_token(d_pb, token.c_str())); } std::string get_revoke_token_result() @@ -1518,34 +1530,29 @@ class context { /// @see pubnub_getall_uuidmetadata futres getall_uuidmetadata(list_options& options) { - return doit(pubnub_getall_uuidmetadata( - d_pb, - options.include(), - options.limit(), - options.start(), - options.end(), - options.count())); + return doit(pubnub_getall_uuidmetadata(d_pb, + options.include(), + options.limit(), + options.start(), + options.end(), + options.count())); } /// Starts a transaction that creates a user with the attributes specified in @p user_obj. /// @see pubnub_set_uuidmetadata - futres set_uuidmetadata(std::string const& uuid_metadataid, std::string const& include, std::string const& user_obj) + futres set_uuidmetadata(std::string const& uuid_metadataid, + std::string const& include, + std::string const& user_obj) { return doit(pubnub_set_uuidmetadata( - d_pb, - uuid_metadataid.c_str(), - include.c_str(), - user_obj.c_str())); + d_pb, uuid_metadataid.c_str(), include.c_str(), user_obj.c_str())); } /// Starts a transaction that returns the user object specified by @p uuid. /// @see pubnub_get_uuidmetadata futres get_uuidmetadata(std::string const& uuid, std::string const& include) { - return doit(pubnub_get_uuidmetadata( - d_pb, - include.c_str(), - uuid.c_str())); + return doit(pubnub_get_uuidmetadata(d_pb, include.c_str(), uuid.c_str())); } /// Starts a transaction that deletes the user specified by @p uuid. @@ -1559,34 +1566,33 @@ class context { /// @see pubnub_getall_channelmetadata futres getall_channelmetadata(list_options& options) { - return doit(pubnub_getall_channelmetadata( - d_pb, - options.include(), - options.limit(), - options.start(), - options.end(), - options.count())); + return doit(pubnub_getall_channelmetadata(d_pb, + options.include(), + options.limit(), + options.start(), + options.end(), + options.count())); } /// Starts a transaction that creates a space with the attributes specified in @p channel_metadata_obj. /// @see pubnub_set_channelmetadata - futres set_channelmetadata(std::string const& channel_metadataid, std::string const& include, std::string const& channel_metadata_obj) + futres set_channelmetadata(std::string const& channel_metadataid, + std::string const& include, + std::string const& channel_metadata_obj) { - return doit(pubnub_set_channelmetadata( - d_pb, - channel_metadataid.c_str(), - include.c_str(), - channel_metadata_obj.c_str())); + return doit(pubnub_set_channelmetadata(d_pb, + channel_metadataid.c_str(), + include.c_str(), + channel_metadata_obj.c_str())); } /// Starts a transaction that returns the space object specified by @p space_id. /// @see pubnub_get_channelmetadata - futres get_channelmetadata(std::string const& channel_metadataid, std::string const& include) + futres get_channelmetadata(std::string const& channel_metadataid, + std::string const& include) { return doit(pubnub_get_channelmetadata( - d_pb, - include.c_str(), - channel_metadataid.c_str())); + d_pb, include.c_str(), channel_metadataid.c_str())); } /// Starts a transaction that deletes the space specified with @p channel_metadataid. @@ -1596,121 +1602,100 @@ class context { return doit(pubnub_remove_channelmetadata(d_pb, channel_metadataid.c_str())); } - /// Starts a transaction that returns the space memberships of the user specified - /// by @p user_id. + /// Starts a transaction that returns the space memberships of the user + /// specified by @p user_id. /// @see pubnub_get_memberships futres get_memberships(std::string const& metadata_uuid, list_options& options) { - return doit(pubnub_get_memberships( - d_pb, - metadata_uuid.c_str(), - options.include(), - options.limit(), - options.start(), - options.end(), - options.count())); + return doit(pubnub_get_memberships(d_pb, + metadata_uuid.c_str(), + options.include(), + options.limit(), + options.start(), + options.end(), + options.count())); } - /// Starts a transaction that updates the space memberships of the user specified - /// by @p metadata_uuid. + /// Starts a transaction that updates the space memberships of the user + /// specified by @p metadata_uuid. /// @see pubnub_set_memberships futres set_memberships(std::string const& metadata_uuid, - std::string const& set_obj, - std::string const& include) + std::string const& set_obj, + std::string const& include) { return doit(pubnub_set_memberships( - d_pb, - metadata_uuid.c_str(), - include.c_str(), - set_obj.c_str())); + d_pb, metadata_uuid.c_str(), include.c_str(), set_obj.c_str())); } - /// Starts a transaction that removes the channel memberships of the user specified - /// by @p metadata_uuid. + /// Starts a transaction that removes the channel memberships of the user + /// specified by @p metadata_uuid. /// @see pubnub_remove_memberships futres remove_memberships(std::string const& metadata_uuid, - std::string const& remove_obj, - std::string const& include) + std::string const& remove_obj, + std::string const& include) { return doit(pubnub_remove_memberships( - d_pb, - metadata_uuid.c_str(), - include.c_str(), - remove_obj.c_str())); + d_pb, metadata_uuid.c_str(), include.c_str(), remove_obj.c_str())); } /// Starts a transaction that returns all users in the space specified by @p space_id. /// @see pubnub_get_members futres get_members(std::string const& channel_metadataid, list_options& options) { - return doit(pubnub_get_members( - d_pb, - channel_metadataid.c_str(), - options.include(), - options.limit(), - options.start(), - options.end(), - options.count())); + return doit(pubnub_get_members(d_pb, + channel_metadataid.c_str(), + options.include(), + options.limit(), + options.start(), + options.end(), + options.count())); } - /// Starts a transaction that adds the list of members of the space specified - /// by @p channel_metadataid. + /// Starts a transaction that adds the list of members of the space + /// specified by @p channel_metadataid. /// @see pubnub_add_members futres add_members(std::string const& channel_metadataid, std::string const& update_obj, std::string const& include) { return doit(pubnub_add_members( - d_pb, - channel_metadataid.c_str(), - include.c_str(), - update_obj.c_str())); + d_pb, channel_metadataid.c_str(), include.c_str(), update_obj.c_str())); } - /// Starts a transaction that updates the list of members of the space specified - /// by @p channel_metadataid. + /// Starts a transaction that updates the list of members of the space + /// specified by @p channel_metadataid. /// @see pubnub_set_members futres set_members(std::string const& channel_metadataid, - std::string const& set_obj, - std::string const& include) + std::string const& set_obj, + std::string const& include) { return doit(pubnub_set_members( - d_pb, - channel_metadataid.c_str(), - include.c_str(), - set_obj.c_str())); + d_pb, channel_metadataid.c_str(), include.c_str(), set_obj.c_str())); } - /// Starts a transaction that removes the list of members of the space specified - /// by @p channel_metadataid. + /// Starts a transaction that removes the list of members of the space + /// specified by @p channel_metadataid. /// @see pubnub_remove_members futres remove_members(std::string const& channel_metadataid, std::string const& remove_obj, std::string const& include) { return doit(pubnub_remove_members( - d_pb, - channel_metadataid.c_str(), - include.c_str(), - remove_obj.c_str())); + d_pb, channel_metadataid.c_str(), include.c_str(), remove_obj.c_str())); } #endif /* PUBNUB_USE_OBJECTS_API */ #if PUBNUB_USE_ACTIONS_API - /// Starts a transaction that adds new type of message called action as a support for - /// user reactions on a published messages. + /// Starts a transaction that adds new type of message called action as a + /// support for user reactions on a published messages. /// @see pubnub_add_message_action - futres add_message_action(std::string const& channel, - std::string const& message_timetoken, + futres add_message_action(std::string const& channel, + std::string const& message_timetoken, enum pubnub_action_type actype, - std::string const& value) + std::string const& value) { return doit(pubnub_add_message_action( - d_pb, - channel.c_str(), - message_timetoken.c_str(), - actype, - value.c_str())); + d_pb, channel.c_str(), message_timetoken.c_str(), actype, value.c_str())); } /// Returns message timetoken if previous transaction had been add_action() @@ -1737,26 +1722,26 @@ class context { std::string const& action_timetoken) { return doit(pubnub_remove_message_action( - d_pb, - channel.c_str(), - pubnub_str_2_chamebl_t((char*)message_timetoken.c_str()), - pubnub_str_2_chamebl_t((char*)action_timetoken.c_str()))); + d_pb, + channel.c_str(), + pubnub_str_2_chamebl_t((char*)message_timetoken.c_str()), + pubnub_str_2_chamebl_t((char*)action_timetoken.c_str()))); } - /// Initiates transaction that returns all actions added on a given @p channel - /// between @p start and @p end action timetoken. + /// Initiates transaction that returns all actions added on a given @p + /// channel between @p start and @p end action timetoken. /// @see pubnub_get_message_actions() futres get_message_actions(std::string const& channel, std::string const& start, std::string const& end, - size_t limit=0) + size_t limit = 0) { - return doit(pubnub_get_message_actions( - d_pb, - channel.c_str(), - (start.size() > 0) ? start.c_str() : NULL, - (end.size() > 0) ? end.c_str() : NULL, - limit)); + return doit( + pubnub_get_message_actions(d_pb, + channel.c_str(), + (start.size() > 0) ? start.c_str() : NULL, + (end.size() > 0) ? end.c_str() : NULL, + limit)); } /// @see pubnub_get_message_actions_more() @@ -1765,20 +1750,20 @@ class context { return doit(pubnub_get_message_actions_more(d_pb)); } - /// Initiates transaction that returns all actions added on a given @p channel - /// between @p start and @p end message timetoken. + /// Initiates transaction that returns all actions added on a given @p + /// channel between @p start and @p end message timetoken. /// @see pubnub_history_with_message_actions() futres history_with_message_actions(std::string const& channel, std::string const& start, std::string const& end, - size_t limit=0) + size_t limit = 0) { return doit(pubnub_history_with_message_actions( - d_pb, - channel.c_str(), - (start.size() > 0) ? start.c_str() : NULL, - (end.size() > 0) ? end.c_str() : NULL, - limit)); + d_pb, + channel.c_str(), + (start.size() > 0) ? start.c_str() : NULL, + (end.size() > 0) ? end.c_str() : NULL, + limit)); } /// @see pubnub_history_with_message_actions_more() @@ -1796,8 +1781,8 @@ class context { return pubnub_enable_auto_heartbeat(d_pb, period_sec); } - /// Sets(changes) heartbeat period for keeping presence on subscribed channels - /// and channel groups. + /// Sets(changes) heartbeat period for keeping presence on subscribed + /// channels and channel groups. /// @see pubnub_set_heartbeat_period() int set_heartbeat_period(size_t period_sec) { @@ -1806,13 +1791,10 @@ class context { /// Disables keeping presence on subscribed channels and channel groups /// @see pubnub_disable_auto_heartbeat() - void disable_auto_heartbeat() - { - pubnub_disable_auto_heartbeat(d_pb); - } + void disable_auto_heartbeat() { pubnub_disable_auto_heartbeat(d_pb); } - /// Returns whether(, or not) auto heartbeat on subscribed channels and channel - /// groups is enabled + /// Returns whether(, or not) auto heartbeat on subscribed channels and + /// channel groups is enabled /// @see pubnub_is_auto_heartbeat_enabled() bool is_auto_heartbeat_enabled() { @@ -1821,33 +1803,21 @@ class context { /// Enable "smart heartbeat" for presence management. /// @see pubnub_enable_smart_heartbeat() - void enable_smart_heartbeat() - { - pubnub_enable_smart_heartbeat(d_pb); - } + void enable_smart_heartbeat() { pubnub_enable_smart_heartbeat(d_pb); } /// Disable "smart heartbeat" for presence management. /// @see pubnub_disable_smart_heartbeat() - void disable_smart_heartbeat() - { - pubnub_disable_smart_heartbeat(d_pb); - } + void disable_smart_heartbeat() { pubnub_disable_smart_heartbeat(d_pb); } /// Releases all allocated heartbeat thumpers. /// Done on any object of the class, once, suffices. /// @see pubnub_heartbeat_free_thumpers() - void heartbeat_free_thumpers() - { - pubnub_heartbeat_free_thumpers(); - } + void heartbeat_free_thumpers() { pubnub_heartbeat_free_thumpers(); } #endif /* PUBNUB_USE_AUTO_HEARTBEAT */ - + /// Return the HTTP code (result) of the last transaction. /// @see pubnub_last_http_code - int last_http_code() const - { - return pubnub_last_http_code(d_pb); - } + int last_http_code() const { return pubnub_last_http_code(d_pb); } /// Return the string of the last publish transaction. /// @see pubnub_last_publish_result @@ -1875,10 +1845,7 @@ class context { /// Return the string of the last time token. /// @see pubnub_last_time_token - std::string last_time_token() const - { - return pubnub_last_time_token(d_pb); - } + std::string last_time_token() const { return pubnub_last_time_token(d_pb); } /// Sets whether to use (non-)blocking I/O according to option @p e. /// @see pubnub_set_blocking_io, pubnub_set_non_blocking_io @@ -1917,48 +1884,30 @@ class context { /// Reuse SSL sessions, if possible (from now on). /// @see pubnub_set_reuse_ssl_session - void reuse_ssl_session() - { - pubnub_set_reuse_ssl_session(d_pb, true); - } + void reuse_ssl_session() { pubnub_set_reuse_ssl_session(d_pb, true); } /// Don't reuse SSL sessions (from now on). /// @see pubnub_set_reuse_ssl_session - void dont_reuse_ssl_session() - { - pubnub_set_reuse_ssl_session(d_pb, false); - } + void dont_reuse_ssl_session() { pubnub_set_reuse_ssl_session(d_pb, false); } /// Use HTTP Keep-Alive on the context /// @see pubnub_use_http_keep_alive - void use_http_keep_alive() - { - pubnub_use_http_keep_alive(d_pb); - } + void use_http_keep_alive() { pubnub_use_http_keep_alive(d_pb); } /// Don't Use HTTP Keep-Alive on the context /// @see pubnub_dont_use_http_keep_alive - void dont_use_http_keep_alive() - { - pubnub_dont_use_http_keep_alive(d_pb); - } + void dont_use_http_keep_alive() { pubnub_dont_use_http_keep_alive(d_pb); } /// Use of TCP Keep-Alive ("probes") on the context. /// @see pubnub_use_tcp_keep_alive - void use_tcp_keep_alive( - uint8_t time, - uint8_t interval, - uint8_t probes) + void use_tcp_keep_alive(uint8_t time, uint8_t interval, uint8_t probes) { pubnub_use_tcp_keep_alive(d_pb, time, interval, probes); } /// Don't use of TCP Keep-Alive ("probes") on the context. /// @see pubnub_dont_use_tcp_keep_alive - void dont_use_tcp_keep_alive() - { - pubnub_dont_use_tcp_keep_alive(d_pb); - } + void dont_use_tcp_keep_alive() { pubnub_dont_use_tcp_keep_alive(d_pb); } #if PUBNUB_PROXY_API /// Manually set a proxy to use @@ -1974,10 +1923,7 @@ class context { /// Sets a proxy to none /// @see pubnub_set_proxy_none - void set_proxy_none() - { - pubnub_set_proxy_none(d_pb); - } + void set_proxy_none() { pubnub_set_proxy_none(d_pb); } /// Set the proxy to use from system configuration. /// @see pubnub_set_proxy_from_system @@ -2050,6 +1996,20 @@ class context { } #endif +#if PUBNUB_USE_IPV6 + /// Use IPv4 addresses to establish connection with remote origin. + void set_ipv4_connectivity(pubnub_t* p) + { + pubnub_set_ipv4_connectivity(d_pb); + } + + /// Use IPv6 addresses to establish connection with remote origin. + void set_ipv6_connectivity(pubnub_t* p) + { + pubnub_set_ipv6_connectivity(d_pb); + } +#endif /* PUBNUB_USE_IPV6 */ + /// Frees the context and any other thing that needs to be /// freed/released. /// @see pubnub_free @@ -2104,7 +2064,7 @@ class context { /// This function sets the crypto module to be used by the context /// for encryption and decryption of messages. /// @see pubnub_set_crypto_module() - void set_crypto_module(into_crypto_provider_ptr &crypto) + void set_crypto_module(into_crypto_provider_ptr& crypto) { pubnub_set_crypto_module(d_pb, crypto.into_provider()); } @@ -2114,7 +2074,7 @@ class context { /// This function gets the crypto module used by the context /// for encryption and decryption of messages. /// @see pubnub_get_crypto_module() - pubnub_crypto_provider_t *get_crypto_module() + pubnub_crypto_provider_t* get_crypto_module() { return pubnub_get_crypto_module(d_pb); } diff --git a/freertos/pbpal_resolv_and_connect_freertos_tcp.c b/freertos/pbpal_resolv_and_connect_freertos_tcp.c index 5ca0c12d..129145a9 100644 --- a/freertos/pbpal_resolv_and_connect_freertos_tcp.c +++ b/freertos/pbpal_resolv_and_connect_freertos_tcp.c @@ -100,7 +100,7 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) #if PUBNUB_CHANGE_DNS_SERVERS int pbpal_dns_rotate_server(pubnub_t *pb) { - return (pbp->flags.sent_queries < PUBNUB_MAX_DNS_QUERIES ? 0 : 1) + return (pb->flags.sent_queries < PUBNUB_MAX_DNS_QUERIES ? 0 : 1) } #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ diff --git a/lib/pubnub_dns_codec.c b/lib/pubnub_dns_codec.c index a4bfebe1..c5638430 100644 --- a/lib/pubnub_dns_codec.c +++ b/lib/pubnub_dns_codec.c @@ -704,11 +704,11 @@ static int find_the_answer(uint8_t const* reader, return address_found ? 0 : -1; } -int pbdns_pick_resolved_addresses(uint8_t const* buf, - size_t msg_size, - struct pubnub_ipv4_address* resolved_addr_ipv4 - IPV6_ADDR_ARGUMENT_DECLARATION - PBDNS_OPTIONAL_PARAMS_DECLARATIONS) +int pbdns_pick_resolved_addresses(uint8_t const* buf, + size_t msg_size, + enum DNSqueryType* o_query_type, + struct pubnub_ipv4_address* resolved_addr_ipv4 IPV6_ADDR_ARGUMENT_DECLARATION + PBDNS_OPTIONAL_PARAMS_DECLARATIONS) { size_t q_count; size_t ans_count; @@ -725,6 +725,30 @@ int pbdns_pick_resolved_addresses(uint8_t const* buf, if (read_header(buf, msg_size, &q_count, &ans_count) != 0) { return -1; } + + if (q_count > 0) { + uint8_t const* type_reader = buf + HEADER_SIZE; + uint8_t const* _end = buf + msg_size; + + uint8_t name[256]; + size_t to_skip; + + // Decode the question name + if (dns_label_decode(name, sizeof name, type_reader, buf, msg_size, &to_skip) + == 0) { + type_reader += to_skip; + + // Check if we have enough space to read query type + if (type_reader + 2 <= _end) { + unsigned query_type = type_reader[0] * 256 + type_reader[1]; + PUBNUB_LOG_TRACE("DNS question type: %u\n", query_type); + + if (o_query_type != NULL) + *o_query_type = (enum DNSqueryType)query_type; + } + } + } + if (0 == ans_count) { return -1; } diff --git a/lib/pubnub_dns_codec.h b/lib/pubnub_dns_codec.h index 18bb1cfb..c2453da2 100644 --- a/lib/pubnub_dns_codec.h +++ b/lib/pubnub_dns_codec.h @@ -1,6 +1,6 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ #if !defined INC_PUBNUB_DNS_HANDLER -#define INC_PUBNUB_DNS_HANDLER +#define INC_PUBNUB_DNS_HANDLER #include "pubnub_internal.h" #include "core/pubnub_dns_servers.h" @@ -32,7 +32,8 @@ enum DNSqueryType { }; #if PUBNUB_USE_IPV6 -#define IPV6_ADDR_ARGUMENT_DECLARATION , struct pubnub_ipv6_address* resolved_addr_ipv6 +#define IPV6_ADDR_ARGUMENT_DECLARATION \ + , struct pubnub_ipv6_address* resolved_addr_ipv6 #define IPV6_ADDR_ARGUMENT , resolved_addr_ipv6 #else #define IPV6_ADDR_ARGUMENT_DECLARATION @@ -40,13 +41,14 @@ enum DNSqueryType { #endif /* PUBNUB_USE_IPV6 */ #if PUBNUB_USE_MULTIPLE_ADDRESSES -#define PBDNS_OPTIONAL_PARAMS_DECLARATIONS , struct pubnub_multi_addresses* spare_addresses \ - , struct pubnub_options const* options +#define PBDNS_OPTIONAL_PARAMS_DECLARATIONS \ + , struct pubnub_multi_addresses *spare_addresses, \ + struct pubnub_options const *options #define PBDNS_OPTIONAL_PARAMS , spare_addresses, options -#else +#else /* PUBNUB_USE_MULTIPLE_ADDRESSES */ #define PBDNS_OPTIONAL_PARAMS_DECLARATIONS #define PBDNS_OPTIONAL_PARAMS -#endif +#endif /* !PUBNUB_USE_MULTIPLE_ADDRESSES */ /** Prepares DNS @p query_type query request for @p host(name) in @p buf whose maximum available @@ -54,13 +56,13 @@ enum DNSqueryType { If function succeedes, @p to_send 'carries' the length of prepared message. If function reports en error, @p to_send 'keeps' the length of successfully prepared segment before error occurred. - + @retval 0 success, -1 on error */ -int pbdns_prepare_dns_request(uint8_t* buf, - size_t buf_size, - char const* host, - int *to_send, +int pbdns_prepare_dns_request(uint8_t* buf, + size_t buf_size, + char const* host, + int* to_send, enum DNSqueryType query_type); /** Picks valid resolved(Ipv4, or Ipv6) domain name addresses from the response from DNS server. @@ -72,11 +74,11 @@ int pbdns_prepare_dns_request(uint8_t* buf, @retval 0 success, -1 on error */ -int pbdns_pick_resolved_addresses(uint8_t const* buf, - size_t msg_size, - struct pubnub_ipv4_address* resolved_addr_ipv4 - IPV6_ADDR_ARGUMENT_DECLARATION - PBDNS_OPTIONAL_PARAMS_DECLARATIONS); +int pbdns_pick_resolved_addresses(uint8_t const* buf, + size_t msg_size, + enum DNSqueryType* o_query_type, + struct pubnub_ipv4_address* resolved_addr_ipv4 IPV6_ADDR_ARGUMENT_DECLARATION + PBDNS_OPTIONAL_PARAMS_DECLARATIONS); #endif /* defined INC_PUBNUB_DNS_HANDLER */ diff --git a/lib/pubnub_dns_codec_unit_test.c b/lib/pubnub_dns_codec_unit_test.c index d9207dc6..880a7ef5 100644 --- a/lib/pubnub_dns_codec_unit_test.c +++ b/lib/pubnub_dns_codec_unit_test.c @@ -303,6 +303,7 @@ Ensure(pubnub_dns_codec, decodes_strange_response_2_questions_3_answers) uint8_t data_2[] = {5,6,7,8}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); /* Assembling test message(response from DNS server) with 2 questions and 2 answers. @@ -315,13 +316,14 @@ Ensure(pubnub_dns_codec, decodes_strange_response_2_questions_3_answers) append_answer_M(encoded_piece2, RecordTypeTXT, data, 10); append_answer_M(encoded_domain_name, RecordTypeA, data_2, 100); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv4_addresses[0].ipv4, data, @@ -351,6 +353,7 @@ Ensure(pubnub_dns_codec, decodes_response_1_question_3_answers_no_ssl_fallback) struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); @@ -367,13 +370,14 @@ Ensure(pubnub_dns_codec, decodes_response_1_question_3_answers_no_ssl_fallback) append_answer_M(encoded_domain_name, RecordTypeA, data_2, 65536); append_answer_M(encoded_domain_name, RecordTypeA, data_3, 2); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv4_addresses[0].ipv4, data, @@ -400,26 +404,27 @@ Ensure(pubnub_dns_codec, decodes_strange_response_wrong_answers) uint8_t data[] = {1,2,3,4}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 2); append_question_M(just_offset); append_answer_M(encoded_domain_name, RecordTypeSRV, data, 7000); append_answer_M(encoded_abc_domain_name, RecordTypeTXT, data, 65000); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); /* Message shortened to finish within last answers label */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - 20, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - 20, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); } @@ -429,6 +434,7 @@ Ensure(pubnub_dns_codec, decodes_label_too_long_to_fit_in_modules_buffer) uint8_t data[] = {10,20,30,40}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); @@ -438,20 +444,21 @@ Ensure(pubnub_dns_codec, decodes_label_too_long_to_fit_in_modules_buffer) append_question_M(encoded_long_piece21); append_answer_M(encoded_long_piece21, RecordTypeA, data, 5); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); /* Message shortened to end within the question label */ - attest(pbdns_pick_resolved_addresses(m_buf, - 280, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + 280, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv4_addresses[0].ipv4, @@ -473,6 +480,7 @@ Ensure(pubnub_dns_codec, decodes_name_too_long_for_modules_buffer_ending_with_ba /* Resolved Ipv4 address */ uint8_t data[] = {100,200,30,40}; struct pubnub_ipv4_address resolved_addr_ipv4; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 1); /* Label finishes with bad offset format */ @@ -480,11 +488,11 @@ Ensure(pubnub_dns_codec, decodes_name_too_long_for_modules_buffer_ending_with_ba append_question_M(encoded_long_piece21); append_answer_M(encoded_long_piece21, RecordTypeA, data, 3); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); } @@ -494,6 +502,7 @@ Ensure(pubnub_dns_codec, decodes_name_too_long_for_modules_buffer_finishing_with uint8_t data[] = {100,200,30,40}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); @@ -506,13 +515,14 @@ Ensure(pubnub_dns_codec, decodes_name_too_long_for_modules_buffer_finishing_with append_question_M(encoded_long_piece21); append_answer_M(encoded_long_piece21, RecordTypeA, data, 4); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); /* Returning previous length of the last label stretch */ encoded_long_piece21[length_M(encoded_long_piece21) - 37] = '\42'; } @@ -523,6 +533,7 @@ Ensure(pubnub_dns_codec, decodes_another_spooky_response) uint8_t data[] = {4,3,2,1}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); /* Assembling test message(response from DNS server). @@ -534,13 +545,14 @@ Ensure(pubnub_dns_codec, decodes_another_spooky_response) append_answer_M(encoded_domain_name, RecordTypeA, data, 60); append_answer_M(encoded_piece2, RecordTypePTR, data, 87); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); } Ensure(pubnub_dns_codec, decodes_response_encoded_label_splitted) @@ -549,6 +561,7 @@ Ensure(pubnub_dns_codec, decodes_response_encoded_label_splitted) uint8_t data[] = {192,168,40,37}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); /* Ignores the fact that response has no questions */ @@ -562,13 +575,14 @@ Ensure(pubnub_dns_codec, decodes_response_encoded_label_splitted) PUBNUB_LOG_TRACE("------->encoded label formed:\n"); resize_msg(); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv4_addresses[0].ipv4, data, @@ -591,99 +605,92 @@ Ensure(pubnub_dns_codec, handles_response_no_usable_answer) uint8_t data2[] = {192,168,1,2}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 2); /* Message shorter than its header?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - DNS_MESSAGE_HEADER_SIZE - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + DNS_MESSAGE_HEADER_SIZE - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Reducing the offset to contaminate second answers label */ encoded_piece21[length_M(encoded_piece21) - 2] = '\300'; append_question_M(encoded_piece21); /* Message doesn't contain its first question?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - DNS_MESSAGE_HEADER_SIZE + TYPE_AND_CLASS_FIELDS_SIZE - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + DNS_MESSAGE_HEADER_SIZE + TYPE_AND_CLASS_FIELDS_SIZE - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Message doesn't contain type, nor class question fields?! - */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + */ + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Message doesn't contain complete question name(offset incomplete(1))?! - */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + */ + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Message doesn't contain complete question name(offset missing(2))?! - */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 2, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + */ + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 2, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); - /* Message doesn't contain complete question name(offset missing(2) and last character in - label stretch(1))?! + /* Message doesn't contain complete question name(offset missing(2) and last + character in label stretch(1))?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 3, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - TYPE_AND_CLASS_FIELDS_SIZE - 3, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Message doesn't contain its first answer?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size + - TYPE_AND_CLASS_FIELDS_SIZE + - TTL_FIELD_SIZE + - RECORD_DATA_LENGTH_FIELD_SIZE - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size + TYPE_AND_CLASS_FIELDS_SIZE + TTL_FIELD_SIZE + + RECORD_DATA_LENGTH_FIELD_SIZE - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); append_answer_M(encoded_long_piece1, RecordTypeAAAA, data, 11); /* Message doesn't contain complete answer name?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - - sizeof data - - TYPE_AND_CLASS_FIELDS_SIZE - - TTL_FIELD_SIZE - - RECORD_DATA_LENGTH_FIELD_SIZE - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - sizeof data - TYPE_AND_CLASS_FIELDS_SIZE + - TTL_FIELD_SIZE - RECORD_DATA_LENGTH_FIELD_SIZE - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* answer doesn't contain resource data fields */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - - sizeof data - - TYPE_AND_CLASS_FIELDS_SIZE - - TTL_FIELD_SIZE - - RECORD_DATA_LENGTH_FIELD_SIZE, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - sizeof data - TYPE_AND_CLASS_FIELDS_SIZE + - TTL_FIELD_SIZE - RECORD_DATA_LENGTH_FIELD_SIZE, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); /* Message doesn't contain complete answer?! */ - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size - 1, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size - 1, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); place_encoded_label_piece_M(offset_within_header); /* Won't find the answer due to contaminated answers label */ @@ -691,13 +698,14 @@ Ensure(pubnub_dns_codec, handles_response_no_usable_answer) place_encoded_label_piece_M(bad_offset_format); resize_msg(); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), + equals(0)); /* returning to safe offset value */ encoded_piece21[length_M(encoded_piece21) - 2] = '\301'; @@ -709,6 +717,7 @@ Ensure(pubnub_dns_codec, handles_label_offset_to_itself_preventing_infinite_loop uint8_t data[] = {192,168,1,1}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); @@ -721,59 +730,64 @@ Ensure(pubnub_dns_codec, handles_label_offset_to_itself_preventing_infinite_loop PUBNUB_LOG_TRACE("------->encoded label formed:\n"); resize_msg(); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); } Ensure(pubnub_dns_codec, handles_response_with_0_answers) { struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 0); append_question_M(encoded_piece31); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), + equals(0)); } Ensure(pubnub_dns_codec, handles_response_reporting_error) { struct pubnub_ipv4_address resolved_addr_ipv4; + enum DNSqueryType o_query_type; /* This kind of response header reports en issue: RCODE != 0 */ make_dns_header_M(QUERY, 1, 0); append_question_M(encoded_piece31); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); } Ensure(pubnub_dns_codec, handles_response_with_no_QR_flag_set) { struct pubnub_ipv4_address resolved_addr_ipv4; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 0); m_buf[OFFSET_FLAGS] ^= ResponseQueryFlagMask >> 8; append_question_M(offset_beyond_boudary); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); } @@ -783,6 +797,7 @@ Ensure(pubnub_dns_codec, handles_splitted_label_too_long_for_modules_buffer) uint8_t data[] = {192,168,2,5}; struct pubnub_ipv4_address key_addr = {{0}}; struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv4, data, sizeof key_addr.ipv4); @@ -802,13 +817,14 @@ Ensure(pubnub_dns_codec, handles_splitted_label_too_long_for_modules_buffer) append_answer_M(encoded_long_piece1, RecordTypeA, data, 0); resize_msg(); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv4, &key_addr, sizeof resolved_addr_ipv4), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(bp.spare_addresses.n_ipv4, equals(0)); attest(bp.spare_addresses.ipv4_index, equals(0)); @@ -826,14 +842,15 @@ Ensure(pubnub_dns_codec, handles_response_label_encoded_badly) /* Resolved Ipv4 address */ uint8_t data[] = {192,168,0,0}; struct pubnub_ipv4_address resolved_addr_ipv4; + enum DNSqueryType o_query_type; make_dns_header_M(RESPONSE, 1, 1); append_question_M(label_start_encoded_badly_with_offset_to_itself); append_answer_M(encoded_piece31, RecordTypeA, data, 19); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); } @@ -842,16 +859,17 @@ Ensure(pubnub_dns_codec, handles_response_RecordType_and_DataLength_mismatch) /* Resolved IpvX address */ uint8_t data[] = {255,255,0,0,0}; uint8_t data_2[] = {255,255,0,0}; + enum DNSqueryType o_query_type; struct pubnub_ipv4_address resolved_addr_ipv4; make_dns_header_M(RESPONSE, 1, 2); append_question_M(encoded_abc_domain_name); append_answer_M(encoded_piece3, RecordTypeA, data, 47); append_answer_M(encoded_piece3, RecordTypeA, data_2, 123); - attest(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + attest(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv4_addresses[0].ipv4, @@ -944,6 +962,7 @@ Ensure(pubnub_dns_codec, handles_response_RecordType_and_DataLength_mismatch_on_ /* Resolved IpvX address */ uint8_t data[] = {255,255,0,0,0}; uint8_t data_2[] = {0xAB,0xCD,0x02,0x55,0,0,0,0,0x32}; + enum DNSqueryType o_query_type; struct pubnub_ipv6_address resolved_addr_ipv6; make_dns_header_M(RESPONSE, 1, 2); append_question_M(encoded_abc_domain_name); @@ -951,9 +970,9 @@ Ensure(pubnub_dns_codec, handles_response_RecordType_and_DataLength_mismatch_on_ append_answer_M(encoded_piece3, RecordTypeA, data, 54); attest(pbdns_pick_resolved_addresses(m_buf, m_msg_size, + &o_query_type, NULL, - &resolved_addr_ipv6 - PBDNS_OPTIONAL_PARAMS_BP), + &resolved_addr_ipv6 PBDNS_OPTIONAL_PARAMS_BP), equals(-1)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(bp.spare_addresses.n_ipv4, equals(0)); @@ -973,6 +992,7 @@ Ensure(pubnub_dns_codec, handles_response_RecordTypeAAAA_and_RecordTypeA) struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; struct pubnub_ipv6_address resolved_addr_ipv6 = {{0}}; struct pubnub_ipv6_address key_addr = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv6, data_2, sizeof key_addr.ipv6); @@ -983,12 +1003,15 @@ Ensure(pubnub_dns_codec, handles_response_RecordTypeAAAA_and_RecordTypeA) append_answer_M(encoded_piece3, RecordTypeA, data, 58); attest(pbdns_pick_resolved_addresses(m_buf, m_msg_size, + &o_query_type, &resolved_addr_ipv4, - &resolved_addr_ipv6 - PBDNS_OPTIONAL_PARAMS_BP), + &resolved_addr_ipv6 PBDNS_OPTIONAL_PARAMS_BP), equals(0)); - attest(memcmp(&resolved_addr_ipv6, &key_addr, sizeof resolved_addr_ipv6), equals(0)); - attest(memcmp(&resolved_addr_ipv4.ipv4, data_zeros, sizeof resolved_addr_ipv4.ipv4), equals(0)); + attest(memcmp(&resolved_addr_ipv6, &key_addr, sizeof resolved_addr_ipv6), + equals(0)); + attest( + memcmp(&resolved_addr_ipv4.ipv4, data_zeros, sizeof resolved_addr_ipv4.ipv4), + equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv6_addresses[0].ipv6, data_2, @@ -1023,6 +1046,7 @@ Ensure(pubnub_dns_codec, handles_response_with_RecordTypeAAAA_no_ssl_fallback) struct pubnub_ipv4_address resolved_addr_ipv4 = {{0}}; struct pubnub_ipv6_address resolved_addr_ipv6 = {{0}}; struct pubnub_ipv6_address key_addr = {{0}}; + enum DNSqueryType o_query_type; memcpy(key_addr.ipv6, data_1, sizeof key_addr.ipv6); @@ -1041,12 +1065,14 @@ Ensure(pubnub_dns_codec, handles_response_with_RecordTypeAAAA_no_ssl_fallback) append_answer_M(encoded_piece3, RecordTypeAAAA, data_2, 30); attest(pbdns_pick_resolved_addresses(m_buf, m_msg_size, + &o_query_type, &resolved_addr_ipv4, - &resolved_addr_ipv6 - PBDNS_OPTIONAL_PARAMS_BP), + &resolved_addr_ipv6 PBDNS_OPTIONAL_PARAMS_BP), + equals(0)); + attest(memcmp(&resolved_addr_ipv4.ipv4, data_zeros, sizeof resolved_addr_ipv4), + equals(0)); + attest(memcmp(&resolved_addr_ipv6, &key_addr, sizeof resolved_addr_ipv6), equals(0)); - attest(memcmp(&resolved_addr_ipv4.ipv4, data_zeros, sizeof resolved_addr_ipv4), equals(0)); - attest(memcmp(&resolved_addr_ipv6, &key_addr, sizeof resolved_addr_ipv6), equals(0)); #if PUBNUB_USE_MULTIPLE_ADDRESSES attest(memcmp(bp.spare_addresses.ipv6_addresses[0].ipv6, data_1, @@ -1079,23 +1105,27 @@ Ensure(pubnub_dns_codec, fires_asserts_on_illegal_parameters) { int to_send; struct pubnub_ipv4_address resolved_addr_ipv4; + enum DNSqueryType o_query_type; pubnub_assert_set_handler((pubnub_assert_handler_t)test_assert_handler); - expect_assert_in(pbdns_prepare_dns_request(NULL, 10, "pubsub.pubnub.com", &to_send, dnsA), - "pubnub_dns_codec.c"); + expect_assert_in( + pbdns_prepare_dns_request(NULL, 10, "pubsub.pubnub.com", &to_send, dnsA), + "pubnub_dns_codec.c"); expect_assert_in(pbdns_prepare_dns_request(m_buf, 3, NULL, &to_send, dnsA), "pubnub_dns_codec.c"); - expect_assert_in(pbdns_prepare_dns_request(m_buf, 5, "pubsub.pubnub.com", NULL, dnsA), - "pubnub_dns_codec.c"); - expect_assert_in(pbdns_pick_resolved_addresses(NULL, - m_msg_size, - &resolved_addr_ipv4 - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), - "pubnub_dns_codec.c"); - expect_assert_in(pbdns_pick_resolved_addresses(m_buf, - m_msg_size, - NULL - IPV6_NULL_ARGUMENT - PBDNS_OPTIONAL_PARAMS_BP), + expect_assert_in( + pbdns_prepare_dns_request(m_buf, 5, "pubsub.pubnub.com", NULL, dnsA), + "pubnub_dns_codec.c"); + expect_assert_in( + pbdns_pick_resolved_addresses( + NULL, + m_msg_size, + &o_query_type, + &resolved_addr_ipv4 IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), + "pubnub_dns_codec.c"); + expect_assert_in(pbdns_pick_resolved_addresses( + m_buf, + m_msg_size, + &o_query_type, + NULL IPV6_NULL_ARGUMENT PBDNS_OPTIONAL_PARAMS_BP), "pubnub_dns_codec.c"); } diff --git a/lib/pubnub_parse_ipv6_addr.c b/lib/pubnub_parse_ipv6_addr.c index 3af1663a..e7319f1c 100644 --- a/lib/pubnub_parse_ipv6_addr.c +++ b/lib/pubnub_parse_ipv6_addr.c @@ -94,12 +94,12 @@ int pubnub_parse_ipv6_addr(char const* addr, struct pubnub_ipv6_address* p) previous_colon = true; continue; } - else if (isdigit(*pos)) { + else if (isdigit((unsigned char)*pos)) { digit_value = *pos - '0'; } else if ((('a' <= *pos) && (*pos <= 'f')) || (('A' <= *pos) && (*pos <= 'F'))) { - digit_value = toupper(*pos) - 'A' + 10; + digit_value = toupper((unsigned char)*pos) - 'A' + 10; } else { PUBNUB_LOG_ERROR("Error :pubnub_parse_ipv6_addr('%s') - " diff --git a/lib/sockets/pbpal_adns_sockets.c b/lib/sockets/pbpal_adns_sockets.c index 1994a26b..e400408b 100644 --- a/lib/sockets/pbpal_adns_sockets.c +++ b/lib/sockets/pbpal_adns_sockets.c @@ -1,7 +1,7 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ -#include "pubnub_internal.h" - #include "lib/sockets/pbpal_adns_sockets.h" + +#include "pubnub_internal.h" #include "core/pubnub_assert.h" #include "core/pubnub_log.h" @@ -39,48 +39,84 @@ #endif -int send_dns_query(pb_socket_t skt, - struct sockaddr const* dest, - char const* host, - enum DNSqueryType query_type) +int send_dns_query(pb_socket_t skt, + struct sockaddr const* dest, + char const* host, + struct dns_queries_tracking* tracking) { uint8_t buf[4096]; int to_send; int sent_to; size_t sockaddr_size; + bool any_blocked = false; + + if (!tracking) { + PUBNUB_LOG_ERROR("send_dns_query: tracking required\n"); + return -1; + } switch (dest->sa_family) { case AF_INET: - sockaddr_size = sizeof(struct sockaddr_in); + sockaddr_size = sizeof(struct sockaddr_in); ((struct sockaddr_in*)dest)->sin_port = htons(DNS_PORT); break; #if PUBNUB_USE_IPV6 case AF_INET6: - sockaddr_size = sizeof(struct sockaddr_in6); + sockaddr_size = sizeof(struct sockaddr_in6); ((struct sockaddr_in6*)dest)->sin6_port = htons(DNS_PORT); break; #endif /* PUBNUB_USE_IPV6 */ default: PUBNUB_LOG_ERROR("send_dns_query(socket=%d): invalid address family " - "dest->sa_family =%uh\n", + "dest->sa_family=%u\n", skt, dest->sa_family); return -1; } - if (-1 == pbdns_prepare_dns_request(buf, sizeof buf, host, &to_send, query_type)) { - PUBNUB_LOG_ERROR("Couldn't prepare dns request! : #prepared bytes=%d\n", - to_send); - return -1; - } - TRACE_SOCKADDR("Sending DNS query to: ", dest, sockaddr_size); - sent_to = sendto(skt, (char*)buf, to_send, 0, dest, sockaddr_size); - if (sent_to <= 0) { - return socket_would_block() ? +1 : -1; + + if (!tracking->sent_a) { + if (pbdns_prepare_dns_request(buf, sizeof buf, host, &to_send, dnsA) == 0) { + TRACE_SOCKADDR("Sending DNS A query to: ", dest, sockaddr_size); + sent_to = sendto(skt, (char*)buf, to_send, 0, dest, sockaddr_size); + if (sent_to > 0 && to_send == sent_to) + tracking->sent_a = true; + else if (sent_to <= 0 && socket_would_block()) + any_blocked = true; + else + PUBNUB_LOG_WARNING("Failed to send A query: %d\n", sent_to); + } } - else if (to_send != sent_to) { - PUBNUB_LOG_ERROR("sendto() sent %d out of %d bytes!\n", sent_to, to_send); - return -1; + +#if PUBNUB_USE_IPV6 + if (!tracking->sent_aaaa) { + if (pbdns_prepare_dns_request(buf, sizeof buf, host, &to_send, dnsAAAA) + == 0) { + TRACE_SOCKADDR("Sending DNS AAAA query to: ", dest, sockaddr_size); + sent_to = sendto(skt, (char*)buf, to_send, 0, dest, sockaddr_size); + if (sent_to > 0 && to_send == sent_to) + tracking->sent_aaaa = true; + else if (sent_to <= 0 && socket_would_block()) + any_blocked = true; + else + PUBNUB_LOG_WARNING("Failed to send AAAA query: %d\n", sent_to); + } } +#endif /* PUBNUB_USE_IPV6 */ + tracking->need_retry = !tracking->sent_a +#if PUBNUB_USE_IPV6 + || !tracking->sent_aaaa +#endif + ; + + if (!tracking->sent_a +#if PUBNUB_USE_IPV6 + && !tracking->sent_aaaa +#endif + ) + return any_blocked ? +1 : -1; + if (tracking->need_retry && any_blocked) + return +1; + return 0; } @@ -91,66 +127,139 @@ int send_dns_query(pb_socket_t skt, #endif -int read_dns_response(pb_socket_t skt, +int read_dns_response(pb_socket_t skt, struct sockaddr* dest, - struct sockaddr* resolved_addr - PBDNS_OPTIONAL_PARAMS_DECLARATIONS) + struct dns_queries_tracking* tracking PBDNS_OPTIONAL_PARAMS_DECLARATIONS) { - uint8_t buf[8192]; - int msg_size; - unsigned sockaddr_size; - struct pubnub_ipv4_address addr_ipv4 = {{0}}; -#if PUBNUB_USE_IPV6 - struct pubnub_ipv6_address addr_ipv6 = {{0}}; -#endif + uint8_t buf[8192]; + int msg_size; + unsigned sockaddr_size; + int responses_received = 0; + int expected_responses = 0; + PUBNUB_ASSERT(SOCKET_INVALID != skt); + if (tracking->sent_a) + expected_responses++; + if (tracking->received_a) + responses_received++; +#if PUBNUB_USE_IPV6 + if (tracking->sent_aaaa) + expected_responses++; + if (tracking->received_aaaa) + responses_received++; +#endif /* PUBNUB_USE_IPV6 */ + + if (expected_responses == 0) { + PUBNUB_LOG_ERROR("read_dns_response: No queries were sent!\n"); + return -1; + } + + if (responses_received >= expected_responses) { + PUBNUB_LOG_WARNING( + "read_dns_response: Already received all responses\n"); + return 0; + } + + PUBNUB_LOG_TRACE("Expecting %d response(s), already received %d\n", + expected_responses, + responses_received); + switch (dest->sa_family) { case AF_INET: - sockaddr_size = sizeof(struct sockaddr_in); + sockaddr_size = sizeof(struct sockaddr_in); ((struct sockaddr_in*)dest)->sin_port = htons(DNS_PORT); break; #if PUBNUB_USE_IPV6 case AF_INET6: - sockaddr_size = sizeof(struct sockaddr_in6); + sockaddr_size = sizeof(struct sockaddr_in6); ((struct sockaddr_in6*)dest)->sin6_port = htons(DNS_PORT); break; #endif /* PUBNUB_USE_IPV6 */ default: PUBNUB_LOG_ERROR("read_dns_response(socket=%d): invalid address family " - "dest->sa_family =%uh\n", + "dest->sa_family =%u\n", skt, dest->sa_family); return -1; } - msg_size = recvfrom(skt, (char*)buf, sizeof buf, 0, dest, CAST & sockaddr_size); - if (msg_size <= 0) { - return socket_would_block() ? +1 : -1; - } + while (responses_received < expected_responses) { + msg_size = + recvfrom(skt, (char*)buf, sizeof buf, 0, dest, CAST & sockaddr_size); + if (msg_size <= 0) + return socket_would_block() ? +1 : -1; + + PUBNUB_LOG_TRACE("Received DNS response packet (%d bytes)\n", msg_size); #if PUBNUB_USE_MULTIPLE_ADDRESSES - time(&spare_addresses->time_of_the_last_dns_query); + if (responses_received == 0) + time(&spare_addresses->time_of_the_last_dns_query); +#endif /* PUBNUB_USE_MULTIPLE_ADDRESSES */ + enum DNSqueryType question_type; + struct pubnub_ipv4_address this_ipv4 = { { 0 } }; +#if PUBNUB_USE_IPV6 + struct pubnub_ipv6_address this_ipv6 = { { 0 } }; #endif - if (pbdns_pick_resolved_addresses(buf, - (size_t)msg_size, - &addr_ipv4 - P_ADDR_IPV6_ARGUMENT - PBDNS_OPTIONAL_PARAMS) != 0) { - return -1; - } - if (addr_ipv4.ipv4[0] != 0) { - memcpy(&((struct sockaddr_in*)resolved_addr)->sin_addr.s_addr, - addr_ipv4.ipv4, - sizeof addr_ipv4.ipv4); - resolved_addr->sa_family = AF_INET; - } + + if (pbdns_pick_resolved_addresses(buf, + (size_t)msg_size, + &question_type, + &this_ipv4 #if PUBNUB_USE_IPV6 - else { - memcpy(((struct sockaddr_in6*)resolved_addr)->sin6_addr.s6_addr, - addr_ipv6.ipv6, - sizeof addr_ipv6.ipv6); - resolved_addr->sa_family = AF_INET6; - } + , + &this_ipv6 +#endif + PBDNS_OPTIONAL_PARAMS) + == 0) { + if (dnsA == question_type && !tracking->received_a) { + if (this_ipv4.ipv4[0] != 0) { + memcpy(&tracking->dns_a_addr.sin_addr.s_addr, + this_ipv4.ipv4, + sizeof this_ipv4.ipv4); + tracking->dns_a_addr.sin_family = AF_INET; + } + tracking->received_a = true; + responses_received++; + } +#if PUBNUB_USE_IPV6 + else if (dnsAAAA == question_type && !tracking->received_aaaa) { + if (this_ipv6.ipv6[0] != 0 || this_ipv6.ipv6[1] != 0) { + struct sockaddr_in6* sin6 = + (struct sockaddr_in6*)&tracking->dns_aaaa_addr; + memcpy(sin6->sin6_addr.s6_addr, + this_ipv6.ipv6, + sizeof this_ipv6.ipv6); + sin6->sin6_family = AF_INET6; + } + tracking->received_aaaa = true; + responses_received++; + } +#endif + else { + PUBNUB_LOG_WARNING( + "Received duplicate or unexpected DNS response\n"); + } + } + else { + responses_received++; + if (dnsA == question_type) { + tracking->received_a = true; + PUBNUB_LOG_WARNING( + "There are no 'A' records for requested domain.\n"); + } +#if PUBNUB_USE_IPV6 + else if (dnsAAAA == question_type) { + tracking->received_aaaa = true; + PUBNUB_LOG_WARNING( + "There are no 'AAAA' records for requested domain.\n"); + } #endif /* PUBNUB_USE_IPV6 */ + else + PUBNUB_LOG_WARNING("Failed to parse DNS response.\n"); + } + } + + PUBNUB_LOG_TRACE("All %d DNS responses received\n", expected_responses); + return 0; } @@ -169,7 +278,9 @@ int main() struct sockaddr_in dest; struct sockaddr_in6 dest6; struct sockaddr_storage resolved_addr; - + struct dns_queries_tracking tracking; + memset(&tracking, 0, sizeof(tracking)); + #if defined(_WIN32) WSADATA wsaData; int iResult; @@ -188,7 +299,7 @@ int main() PUBNUB_LOG_ERROR("Error: Couldnt't get Ipv4 socket.\n"); return -1; } - + #if !defined(_WIN32) int flags = fcntl(skt, F_GETFL, 0); fcntl(skt, F_SETFL, flags | O_NONBLOCK); @@ -197,7 +308,7 @@ int main() dest.sin_port = htons(53); inet_pton(AF_INET, "208.67.222.222", &(dest.sin_addr.s_addr)); - if (-1 == send_dns_query(skt, (struct sockaddr*)&dest, "facebook.com", dnsANY)) { + if (-1 == send_dns_query(skt, (struct sockaddr*)&dest, "facebook.com", &tracking)) { PUBNUB_LOG_ERROR("Error: Couldn't send datagram(Ipv4).\n"); return -1; } @@ -221,7 +332,7 @@ int main() timev.tv_sec, timev.tv_usec); #endif - read_dns_response(skt, (struct sockaddr*)&dest, (struct sockaddr*)&resolved_addr); + read_dns_response(skt, (struct sockaddr*)&dest, &tracking); #if !defined(_WIN32) } else { @@ -247,7 +358,7 @@ int main() dest6.sin6_port = htons(53); inet_pton(AF_INET6, "2001:470:20::2", dest6.sin6_addr.s6_addr); - if (-1 == send_dns_query(skt, (struct sockaddr*)&dest6, "facebook.com", dnsANY)) { + if (-1 == send_dns_query(skt, (struct sockaddr*)&dest6, "facebook.com", &tracking)) { PUBNUB_LOG_ERROR("Error: Couldn't send datagram(Ipv6).\n"); return -1; @@ -268,7 +379,7 @@ int main() timev.tv_sec, timev.tv_usec); #endif - read_dns_response(skt, (struct sockaddr*)&dest6, (struct sockaddr*)&resolved_addr); + read_dns_response(skt, (struct sockaddr*)&dest6, &tracking); #if !defined(_WIN32) } else { diff --git a/lib/sockets/pbpal_adns_sockets.h b/lib/sockets/pbpal_adns_sockets.h index 378fb120..358ba36c 100644 --- a/lib/sockets/pbpal_adns_sockets.h +++ b/lib/sockets/pbpal_adns_sockets.h @@ -1,25 +1,25 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ #if !defined INC_PBPAL_ANDS_SOCKETS -#define INC_PBPAL_ANDS_SOCKETS +#define INC_PBPAL_ANDS_SOCKETS #include "lib/pubnub_dns_codec.h" struct sockaddr; +struct dns_queries_tracking; /** * Perform a DNS query by sending a packet to the DNS server @p dest. */ -int send_dns_query(pb_socket_t skt, - struct sockaddr const *dest, - char const *host, - enum DNSqueryType query_type); +int send_dns_query(pb_socket_t skt, + struct sockaddr const* dest, + char const* host, + struct dns_queries_tracking* tracking); /** Reads response from DNS server @p dest, putting it into @p resolved addr. */ -int read_dns_response(pb_socket_t skt, - struct sockaddr *dest, - struct sockaddr *resolved_addr - PBDNS_OPTIONAL_PARAMS_DECLARATIONS); - +int read_dns_response(pb_socket_t skt, + struct sockaddr* dest, + struct dns_queries_tracking* tracking + PBDNS_OPTIONAL_PARAMS_DECLARATIONS); #endif /* defined INC_PBPAL_ANDS_SOCKETS */ diff --git a/lib/sockets/pbpal_resolv_and_connect_sockets.c b/lib/sockets/pbpal_resolv_and_connect_sockets.c index cb170c13..ce8934a0 100644 --- a/lib/sockets/pbpal_resolv_and_connect_sockets.c +++ b/lib/sockets/pbpal_resolv_and_connect_sockets.c @@ -16,19 +16,27 @@ #if defined(_WIN32) #include "windows/pubnub_get_native_socket.h" #include +#include +typedef ADDRESS_FAMILY sa_family_t; #else #include "posix/pubnub_get_native_socket.h" #include #endif +#define PUBNUB_DEFAULT_IPV4_DNS_SERVER "8.8.8.8" +#define PUBNUB_DEFAULT_IPV6_DNS_SERVER "2001:4860:4860:0000:0000:0000:0000:8888" #define HTTP_PORT 80 - #define TLS_PORT 443 #ifndef PUBNUB_CALLBACK_API #define send_dns_query(x, y, z, v) -1 -#define read_response(x, y, z, v) -1 -#else +#if PUBNUB_USE_MULTIPLE_ADDRESSES +#define read_dns_response(x, y, z, v, p) -1 +#else /* PUBNUB_USE_MULTIPLE_ADDRESSES */ +#define read_dns_response(x, y, z) -1 +#endif /* !PUBNUB_USE_MULTIPLE_ADDRESSES */ + +#else /* !PUBNUB_CALLBACK_API */ /** Considering the size of bit field for DNS queries sent */ PUBNUB_STATIC_ASSERT(PUBNUB_MAX_DNS_QUERIES < (1 << SENT_QUERIES_SIZE_IN_BITS), PUBNUB_MAX_DNS_QUERIES_is_too_big); @@ -39,17 +47,76 @@ PUBNUB_STATIC_ASSERT(PUBNUB_MAX_DNS_ROTATION < (1 << ROTATIONS_COUNT_SIZE_IN_BIT #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #if PUBNUB_USE_IPV6 typedef struct sockaddr_storage sockaddr_inX_t; -#define QUERY_TYPE pb->options.ipv6_connectivity ? dnsAAAA : dnsA -#else +#else /* PUBNUB_USE_IPV6 */ typedef struct sockaddr_in sockaddr_inX_t; -#define QUERY_TYPE dnsA -#endif +#endif /* !PUBNUB_USE_IPV6 */ #endif /* PUBNUB_CALLBACK_API */ +#if PUBNUB_USE_IPV6 +/** Check whether kernel has any IPv6 routing data or not. + * + * @return @c true if kernel knows about routing for @c AF_INET6, @c false + * otherwise. + */ +static bool is_ipv6_supported(void) +{ + struct sockaddr_in6 sin6 = { 0 }; + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(53); + inet_pton(AF_INET6, PUBNUB_DEFAULT_IPV6_DNS_SERVER, &sin6.sin6_addr); + + const pb_socket_t udp_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (SOCKET_INVALID == udp_socket) + return false; + + int result = connect(udp_socket, (struct sockaddr*)&sin6, sizeof(sin6)); + socket_close(udp_socket); + + return 0 == result; +} -static void prepare_port_and_hostname(pubnub_t* pb, - uint16_t* p_port, - char const** p_origin) +#ifdef PUBNUB_CALLBACK_API +/** Check whether provided address is a IPv4 mapped IPv6 address or not. + * + * @param addr Address which should be checked to be IPv4 mapped IPv6 address. + * @return @c true if IPv4 mapped IPv6 address provided, @c false otherwise + */ +static bool is_ipv4_mapped_ipv6(const struct sockaddr* addr) +{ + if (addr->sa_family != AF_INET6) + return false; + + const uint8_t prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + return (memcmp(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr, prefix, 12) + == 0); +} + +/** Remap IPv6 encoded IPv4 address back to IPv4. + * + * @param[in,out] addr IPv4 mapped IPv6 address which should be mapped back to + * IPv4. + */ +static void remap_ipv6_to_ipv4(struct sockaddr* addr) +{ + if (addr->sa_family != AF_INET6) + return; + + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; + struct sockaddr_in sin4 = { 0 }; + + sin4.sin_family = AF_INET; + sin4.sin_port = sin6->sin6_port; + memcpy(&sin4.sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12], 4); + + memset(addr, 0, sizeof(sockaddr_inX_t)); + memcpy(addr, &sin4, sizeof(struct sockaddr_in)); +} +#endif /* PUBNUB_USE_IPV6 */ +#endif /* PUBNUB_CALLBACK_API */ + +static void prepare_port_and_hostname(const pubnub_t* pb, + uint16_t* p_port, + char const** p_origin) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_DNS_SEND)); @@ -65,7 +132,9 @@ static void prepare_port_and_hostname(pubnub_t* pb, *p_port = pb->port; } *p_origin = pb->origin ? pb->origin : PUBNUB_ORIGIN; -#endif +#else /* PUBNUB_ORIGIN_SETTABLE */ + *p_origin = PUBNUB_ORIGIN; +#endif /* !PUBNUB_ORIGIN_SETTABLE */ #if PUBNUB_PROXY_API switch (pb->proxy_type) { case pbproxyHTTP_CONNECT: @@ -82,107 +151,187 @@ static void prepare_port_and_hostname(pubnub_t* pb, default: break; } -#endif - return; +#endif /* PUBNUB_PROXY_API */ } - #ifdef PUBNUB_CALLBACK_API +static void get_default_ipv4_dns_ip(struct pubnub_ipv4_address* addr) +{ + if (pubnub_dns_read_system_servers_ipv4(addr, 1) != 1) + inet_pton(AF_INET, PUBNUB_DEFAULT_IPV4_DNS_SERVER, addr); +} -static void get_default_dns_ip(struct pubnub_ipv4_address *addr) { - if (pubnub_dns_read_system_servers_ipv4(addr, 1) != 1) { - inet_pton(AF_INET, PUBNUB_DEFAULT_DNS_SERVER, addr); - } +#if PUBNUB_USE_IPV6 +static void get_default_ipv6_dns_ip(struct pubnub_ipv6_address* addr) +{ + if (pubnub_dns_read_system_servers_ipv6(addr, 1) != 1) + inet_pton(AF_INET6, PUBNUB_DEFAULT_IPV6_DNS_SERVER, addr); } +#endif /* PUBNUB_USE_IPV6 */ #if PUBNUB_SET_DNS_SERVERS #if PUBNUB_CHANGE_DNS_SERVERS -static void get_dns_ip(struct pbdns_servers_check* dns_check, struct sockaddr* addr) +/** Retrieve DNS server suitable for preferred routing address family. + * + * @param family System preferred routing address family (prefer IPv6 over IPv4 + * if able to connect using IPv6). + * @param[in,out] dns_check Check structure used to rotate through + * user-provided DNS server addresses. + * @param[in,out] addr Decided DNS server for origin DNS query. + */ +static void get_dns_ip(sa_family_t family, + struct pbdns_servers_check* dns_check, + struct sockaddr* addr) { - void* p = &(((struct sockaddr_in*)addr)->sin_addr.s_addr); -#if PUBNUB_USE_IPV6 - void* pv6 = ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; -#endif - addr->sa_family = AF_INET; dns_check->dns_mask = 1; - if ((pubnub_get_dns_primary_server_ipv4((struct pubnub_ipv4_address*)p) == -1) - || (dns_check->dns_server_check & dns_check->dns_mask)) { - dns_check->dns_mask <<= 1; - if ((pubnub_get_dns_secondary_server_ipv4((struct pubnub_ipv4_address*)p) +#if PUBNUB_USE_IPV6 + bool user_provided_ipv6_dns = false; + if (AF_INET6 == family) { + addr->sa_family = AF_INET6; + struct in6_addr* pv6 = &((struct sockaddr_in6*)addr)->sin6_addr; + + // Try to use user-provided IPv6 DNS addresses (if any). + if ((pubnub_get_dns_primary_server_ipv6((struct pubnub_ipv6_address*)pv6->s6_addr) == -1) || (dns_check->dns_server_check & dns_check->dns_mask)) { dns_check->dns_mask <<= 1; -#if PUBNUB_USE_IPV6 - addr->sa_family = AF_INET6; -#else - get_default_dns_ip((struct pubnub_ipv4_address*)p); -#endif /* PUBNUB_USE_IPV6 */ + + if ((pubnub_get_dns_secondary_server_ipv6( + (struct pubnub_ipv6_address*)pv6->s6_addr) + == -1) + || (dns_check->dns_server_check & dns_check->dns_mask)) { + dns_check->dns_mask <<= 1; + } } + user_provided_ipv6_dns = !IN6_IS_ADDR_UNSPECIFIED(pv6); } -#if PUBNUB_USE_IPV6 - if (AF_INET6 == addr->sa_family) { - if ((pubnub_get_dns_primary_server_ipv6((struct pubnub_ipv6_address*)pv6) - == -1) + // For IPv4 preferred family or uninitialized IPv6 address. + if (AF_INET == family || (AF_INET6 == family && !user_provided_ipv6_dns)) { +#endif /* PUBNUB_USE_IPV6 */ + addr->sa_family = AF_INET; + void* p = &(((struct sockaddr_in*)addr)->sin_addr.s_addr); + + // Try to use user-provided IPv4 DNS addresses (if any). + if ((pubnub_get_dns_primary_server_ipv4((struct pubnub_ipv4_address*)p) == -1) || (dns_check->dns_server_check & dns_check->dns_mask)) { dns_check->dns_mask <<= 1; - if ((pubnub_get_dns_secondary_server_ipv6((struct pubnub_ipv6_address*)pv6) + if ((pubnub_get_dns_secondary_server_ipv4((struct pubnub_ipv4_address*)p) == -1) || (dns_check->dns_server_check & dns_check->dns_mask)) { dns_check->dns_mask <<= 1; - addr->sa_family = AF_INET; - get_default_dns_ip((struct pubnub_ipv4_address*)p); + if (AF_INET == family) { + // If only IPv4 supported we don't have to fall back to the IPv6. + get_default_ipv4_dns_ip((struct pubnub_ipv4_address*)p); + addr->sa_family = AF_INET; + } } } +#if PUBNUB_USE_IPV6 + } + // Fallback to the default (well-known DNS provider) IPv6 DNS address. + if (AF_INET6 == family + && (AF_INET == addr->sa_family + && 0 == ((struct sockaddr_in*)addr)->sin_addr.s_addr)) { + void* pv6 = ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; + get_default_ipv6_dns_ip((struct pubnub_ipv6_address*)pv6); + addr->sa_family = AF_INET6; + + if (is_ipv4_mapped_ipv6(addr)) + remap_ipv6_to_ipv4(addr); } #endif /* PUBNUB_USE_IPV6 */ } -#else -static void get_dns_ip(struct sockaddr* addr) +#else /* PUBNUB_CHANGE_DNS_SERVERS */ +/** Retrieve DNS server suitable for preferred routing address family. + * + * @param family System preferred routing address family (prefer IPv6 over IPv4 + * if able to connect using IPv6). + * @param[in,out] addr Decided DNS server for origin DNS query. + */ +static void get_dns_ip(sa_family_t family, struct sockaddr* addr) { - void* p = &(((struct sockaddr_in*)addr)->sin_addr.s_addr); -#if PUBNUB_USE_IPV6 - void* pv6 = ((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; -#endif - addr->sa_family = AF_INET; - if ((pubnub_get_dns_primary_server_ipv4((struct pubnub_ipv4_address*)p) == -1) - && (pubnub_get_dns_secondary_server_ipv4((struct pubnub_ipv4_address*)p) - == -1)) { #if PUBNUB_USE_IPV6 - addr->sa_family = AF_INET6; -#else - get_default_dns_ip((struct pubnub_ipv4_address*)p); -#endif /* PUBNUB_USE_IPV6 */ + bool user_provided_ipv6_dns = false; + if (AF_INET6 == family) { + addr->sa_family = AF_INET6; + struct in6_addr* pv6 = &((struct sockaddr_in6*)addr)->sin6_addr; + + // Try to use user-provided IPv6 DNS addresses (if any). + if (pubnub_get_dns_primary_server_ipv6((struct pubnub_ipv6_address*)pv6->s6_addr) + == -1) { + pubnub_get_dns_secondary_server_ipv6( + (struct pubnub_ipv6_address*)pv6->s6_addr); + } + user_provided_ipv6_dns = !IN6_IS_ADDR_UNSPECIFIED(pv6); } -#if PUBNUB_USE_IPV6 - if (AF_INET6 == addr->sa_family) { - if ((pubnub_get_dns_primary_server_ipv6((struct pubnub_ipv6_address*)pv6) - == -1) - && (pubnub_get_dns_secondary_server_ipv6((struct pubnub_ipv6_address*)pv6) - == -1)) { + // For IPv4 preferred family or uninitialized IPv6 address. + if (AF_INET == family || (AF_INET6 == family && !user_provided_ipv6_dns)) { +#endif /* PUBNUB_USE_IPV6 */ + addr->sa_family = AF_INET; + void* p = &(((struct sockaddr_in*)addr)->sin_addr.s_addr); + + // Try to use user-provided IPv4 DNS addresses (if any). + if ((pubnub_get_dns_primary_server_ipv4((struct pubnub_ipv4_address*)p) == -1) + && (pubnub_get_dns_secondary_server_ipv4((struct pubnub_ipv4_address*)p) + == -1) + && AF_INET == family) { + // If only IPv4 supported we don't have to fall back to the IPv6. + get_default_ipv4_dns_ip((struct pubnub_ipv4_address*)p); addr->sa_family = AF_INET; - get_default_dns_ip((struct pubnub_ipv4_address*)p); } +#if PUBNUB_USE_IPV6 } -#endif + // Fallback to the default (well-known DNS provider) IPv6 DNS address. + if (AF_INET6 == family + && (AF_INET == addr->sa_family + && 0 == ((struct sockaddr_in*)addr)->sin_addr.s_addr)) { + struct in6_addr* pv6 = &((struct sockaddr_in6*)addr)->sin6_addr; + get_default_ipv6_dns_ip((struct pubnub_ipv6_address*)pv6->s6_addr); + addr->sa_family = AF_INET6; + + if (is_ipv4_mapped_ipv6(addr)) + remap_ipv6_to_ipv4(addr); + } +#endif /* PUBNUB_USE_IPV6 */ } -#endif /* PUBNUB_CHANGE_DNS_SERVERS */ -#else -static void get_dns_ip(struct sockaddr* addr) +#endif /* !PUBNUB_CHANGE_DNS_SERVERS */ +#else /* PUBNUB_SET_DNS_SERVERS */ +/** Retrieve default DNS server suitable for preferred routing address family. + * + * @param family System preferred routing address family (prefer IPv6 over IPv4 + * if able to connect using IPv6). + * @param[in,out] addr Decided DNS server for origin DNS query. + */ +static void get_dns_ip(sa_family_t family, struct sockaddr* addr) { - addr->sa_family = AF_INET; - struct pubnub_ipv4_address* p = (struct pubnub_ipv4_address*)&(((struct sockaddr_in*)addr)->sin_addr.s_addr); - get_default_dns_ip(p); + addr->sa_family = family; + if (AF_INET == family) { + struct pubnub_ipv4_address* p = (struct pubnub_ipv4_address*)&( + ((struct sockaddr_in*)addr)->sin_addr.s_addr); + get_default_ipv4_dns_ip(p); + addr->sa_family = AF_INET; + } +#if PUBNUB_USE_IPV6 + else { + struct in6_addr* pv6 = &((struct sockaddr_in6*)addr)->sin6_addr; + get_default_ipv6_dns_ip((struct pubnub_ipv6_address*)pv6->s6_addr); + addr->sa_family = AF_INET6; + + if (is_ipv4_mapped_ipv6(addr)) + remap_ipv6_to_ipv4(addr); + } +#endif /* PUBNUB_USE_IPV6 */ } -#endif /* PUBNUB_SET_DNS_SERVERS */ +#endif /* !PUBNUB_SET_DNS_SERVERS */ -static enum pbpal_resolv_n_connect_result -connect_TCP_socket(pb_socket_t* skt, - struct pubnub_options* options, - struct sockaddr* dest, - const uint16_t port) +static enum pbpal_resolv_n_connect_result connect_TCP_socket(pubnub_t* pb, + struct sockaddr* dest, + const uint16_t port) { - size_t sockaddr_size; + pb_socket_t* skt = &pb->pal.socket; + struct pubnub_options* options = &pb->options; + size_t sockaddr_size; PUBNUB_ASSERT_OPT(dest != NULL); @@ -208,7 +357,7 @@ connect_TCP_socket(pb_socket_t* skt, default: PUBNUB_LOG_ERROR( "connect_TCP_socket(socket=%ld): invalid internet protocol " - "dest->sa_family =%uh\n", + "dest->sa_family =%u\n", (long)*skt, dest->sa_family); return pbpal_connect_failed; @@ -217,6 +366,18 @@ connect_TCP_socket(pb_socket_t* skt, if (SOCKET_INVALID == *skt) { return pbpal_connect_resource_failure; } +#if defined(_WIN32) + const BOOL enabled = + pbccTrue == options->tcp_keepalive.enabled ? TRUE : FALSE; + (void)setsockopt( + *skt, SOL_SOCKET, SO_KEEPALIVE, (const char*)&enabled, sizeof(enabled)); + // The most reliable time to set idle/interval/count + // (pbpal_set_tcp_keepalive) is after connection completion. +#else /* defined(_WIN32) */ + const int enabled = pbccTrue == options->tcp_keepalive.enabled ? 1 : 0; + (void)setsockopt(*skt, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); + pbpal_set_tcp_keepalive(pb); +#endif /* !defined(_WIN32) */ options->use_blocking_io = false; pbpal_set_socket_blocking_io(*skt, options->use_blocking_io); socket_disable_SIGPIPE(*skt); @@ -241,26 +402,25 @@ static void if_no_retry_close_socket(pb_socket_t* skt, struct pubnub_flags* flag #if PUBNUB_CHANGE_DNS_SERVERS -int pbpal_dns_rotate_server(pubnub_t *pb) +int pbpal_dns_rotate_server(pubnub_t* pb) { struct pbdns_servers_check* dns_check = &pb->dns_check; - struct pubnub_flags* flags = &pb->flags; + struct pubnub_flags* flags = &pb->flags; dns_check->dns_server_check |= dns_check->dns_mask; if ((dns_check->dns_mask >= PUBNUB_MAX_DNS_SERVERS_MASK) - && (flags->rotations_count < PUBNUB_MAX_DNS_ROTATION)) - { + && (flags->rotations_count < PUBNUB_MAX_DNS_ROTATION)) { dns_check->dns_server_check = 0; /** Update how many times all DNS servers has been tried to process query */ flags->rotations_count++; } - + if (flags->rotations_count >= PUBNUB_MAX_DNS_ROTATION) { flags->retry_after_close = false; - flags->rotations_count = 1; + flags->rotations_count = 1; return 1; } - + /** Going with new DNS server, after retry, brings new set of queries */ flags->sent_queries = 0; @@ -274,7 +434,7 @@ static void check_dns_server_error(struct pbdns_servers_check* dns_check, dns_check->dns_server_check |= dns_check->dns_mask; if (dns_check->dns_mask < PUBNUB_MAX_DNS_SERVERS_MASK) { /** Going with new DNS server, after retry, brings new set of queries */ - flags->sent_queries = 0; + flags->sent_queries = 0; flags->retry_after_close = true; } } @@ -291,54 +451,109 @@ void pbpal_multiple_addresses_reset_counters(struct pubnub_multi_addresses* spar #endif } - static enum pbpal_resolv_n_connect_result -try_TCP_connect_spare_address(pb_socket_t* skt, - struct pubnub_multi_addresses* spare_addresses, - struct pubnub_options* options, - struct pubnub_flags* flags, - const uint16_t port) +try_TCP_connect_spare_address(pubnub_t* pb, sa_family_t family, const uint16_t port) { + struct pubnub_multi_addresses* spare_addresses = &pb->spare_addresses; + struct pubnub_options* options = &pb->options; + struct pubnub_flags* flags = &pb->flags; + pb_socket_t* skt = &pb->pal.socket; enum pbpal_resolv_n_connect_result rslt = pbpal_resolv_resource_failure; time_t tt = time(NULL); - if (spare_addresses->ipv4_index < spare_addresses->n_ipv4 #if PUBNUB_USE_IPV6 - && !options->ipv6_connectivity + if (AF_INET6 == family) { + if (spare_addresses->ipv6_index < spare_addresses->n_ipv6) { + PUBNUB_LOG_TRACE("spare_addresses->ipv6_index = %d, " + "spare_addresses->n_ipv6 = %d.\n", + spare_addresses->ipv6_index, + spare_addresses->n_ipv6); + /* Need at least a second to live */ + if (spare_addresses->ttl_ipv6[spare_addresses->ipv6_index] - 2 + > tt - spare_addresses->time_of_the_last_dns_query) { + struct sockaddr_in6 dest = { 0 }; + dest.sin6_family = AF_INET6; + PUBNUB_LOG_TRACE( + "spare_addresses->ttl_ipv6[spare_addresses->ipv6_index]=%" + "ld > (time() - " + "spare_addresses->time_of_the_last_dns_query)=%ld\n", + (long)(spare_addresses->ttl_ipv6[spare_addresses->ipv6_index]), + (long)(tt - spare_addresses->time_of_the_last_dns_query)); + memcpy( + dest.sin6_addr.s6_addr, + spare_addresses->ipv6_addresses[spare_addresses->ipv6_index].ipv6, + sizeof dest.sin6_addr.s6_addr); + rslt = connect_TCP_socket(pb, (struct sockaddr*)&dest, port); + if (pbpal_connect_failed == rslt) { + pbpal_report_error_from_environment(NULL, __FILE__, __LINE__); + } +#if defined(_WIN32) + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ + } + else { + char str[INET6_ADDRSTRLEN]; + uint8_t* ipv6 = + spare_addresses->ipv6_addresses[spare_addresses->ipv6_index].ipv6; + PUBNUB_LOG_TRACE("Spare IPv6: %s address expired.\n", + inet_ntop(AF_INET6, ipv6, str, INET6_ADDRSTRLEN)); + rslt = pbpal_connect_failed; + } + + if (pbpal_connect_failed == rslt) { + flags->retry_after_close = + (++spare_addresses->ipv6_index < spare_addresses->n_ipv6); + if_no_retry_close_socket(skt, flags); +#if PUBNUB_USE_SSL + flags->trySSL = options->useSSL; +#endif + } + } + } #endif /* PUBNUB_USE_IPV6 */ - ) { - PUBNUB_LOG_TRACE( - "spare_addresses->ipv4_index = %d, spare_addresses->n_ipv4 = %d.\n", - spare_addresses->ipv4_index, - spare_addresses->n_ipv4); + + if ((AF_INET == family || pbpal_connect_failed == rslt) + && spare_addresses->ipv4_index < spare_addresses->n_ipv4) { + PUBNUB_LOG_TRACE("spare_addresses->ipv4_index = %d, " + "spare_addresses->n_ipv4 = %d.\n", + spare_addresses->ipv4_index, + spare_addresses->n_ipv4); /* Need at least a second to live */ if (spare_addresses->ttl_ipv4[spare_addresses->ipv4_index] - 2 > tt - spare_addresses->time_of_the_last_dns_query) { struct sockaddr_in dest = { 0 }; PUBNUB_LOG_TRACE( - "spare_addresses->ttl_ipv4[spare_addresses->ipv4_index]=%ld > " - "(time() - spare_addresses->time_of_the_last_dns_query)=%ld\n", + "spare_addresses->ttl_ipv4[spare_addresses->ipv4_index]=%" + "ld > " + "(time() - " + "spare_addresses->time_of_the_last_dns_query)=%ld\n", (long)(spare_addresses->ttl_ipv4[spare_addresses->ipv4_index]), (long)(tt - spare_addresses->time_of_the_last_dns_query)); memcpy(&(dest.sin_addr.s_addr), spare_addresses->ipv4_addresses[spare_addresses->ipv4_index].ipv4, sizeof dest.sin_addr.s_addr); dest.sin_family = AF_INET; - rslt = connect_TCP_socket(skt, options, (struct sockaddr*)&dest, port); + rslt = connect_TCP_socket(pb, (struct sockaddr*)&dest, port); if (pbpal_connect_failed == rslt) { pbpal_report_error_from_environment(NULL, __FILE__, __LINE__); } +#if defined(_WIN32) + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ } else { + char str[INET_ADDRSTRLEN]; uint8_t* ipv4 = spare_addresses->ipv4_addresses[spare_addresses->ipv4_index].ipv4; - PUBNUB_LOG_TRACE("Spare IPv4: %d.%d.%d.%d address expired.\n", - ipv4[0], - ipv4[1], - ipv4[2], - ipv4[3]); + PUBNUB_LOG_TRACE("Spare IPv4: %s address expired.\n", + inet_ntop(AF_INET, ipv4, str, INET_ADDRSTRLEN)); rslt = pbpal_connect_failed; } + if (pbpal_connect_failed == rslt) { flags->retry_after_close = (++spare_addresses->ipv4_index < spare_addresses->n_ipv4); @@ -348,58 +563,9 @@ try_TCP_connect_spare_address(pb_socket_t* skt, #endif } } -#if PUBNUB_USE_IPV6 - else if (spare_addresses->ipv6_index < spare_addresses->n_ipv6) { - PUBNUB_LOG_TRACE( - "spare_addresses->ipv6_index = %d, spare_addresses->n_ipv6 = %d.\n", - spare_addresses->ipv6_index, - spare_addresses->n_ipv6); - /* Need at least a second to live */ - if (spare_addresses->ttl_ipv6[spare_addresses->ipv6_index] - 2 - > tt - spare_addresses->time_of_the_last_dns_query) { - struct sockaddr_in6 dest = { 0 }; - PUBNUB_LOG_TRACE( - "spare_addresses->ttl_ipv6[spare_addresses->ipv6_index]=%ld > " - "(time() - spare_addresses->time_of_the_last_dns_query)=%ld\n", - (long)(spare_addresses->ttl_ipv6[spare_addresses->ipv6_index]), - (long)(tt - spare_addresses->time_of_the_last_dns_query)); - memcpy(dest.sin6_addr.s6_addr, - spare_addresses->ipv6_addresses[spare_addresses->ipv6_index].ipv6, - sizeof dest.sin6_addr.s6_addr); - dest.sin6_family = AF_INET6; - rslt = connect_TCP_socket(skt, options, (struct sockaddr*)&dest, port); - if (pbpal_connect_failed == rslt) { - pbpal_report_error_from_environment(NULL, __FILE__, __LINE__); - } - } - else { - uint8_t* ipv6 = - spare_addresses->ipv6_addresses[spare_addresses->ipv6_index].ipv6; - PUBNUB_LOG_TRACE( - "Spare IPv6: %X:%X:%X:%X:%X:%X:%X:%X address expired.\n", - ipv6[0] * 256 + ipv6[1], - ipv6[2] * 256 + ipv6[3], - ipv6[4] * 256 + ipv6[5], - ipv6[6] * 256 + ipv6[7], - ipv6[8] * 256 + ipv6[9], - ipv6[10] * 256 + ipv6[11], - ipv6[12] * 256 + ipv6[13], - ipv6[14] * 256 + ipv6[15]); - rslt = pbpal_connect_failed; - } - if (pbpal_connect_failed == rslt) { - flags->retry_after_close = - (++spare_addresses->ipv6_index < spare_addresses->n_ipv6); - if_no_retry_close_socket(skt, flags); -#if PUBNUB_USE_SSL - flags->trySSL = options->useSSL; -#endif - } - } -#endif /* PUBNUB_USE_IPV6 */ - else { + else pbpal_multiple_addresses_reset_counters(spare_addresses); - } + if ((pbpal_connect_failed == rslt) && !flags->retry_after_close) { rslt = pbpal_resolv_resource_failure; pbpal_multiple_addresses_reset_counters(spare_addresses); @@ -410,192 +576,224 @@ try_TCP_connect_spare_address(pb_socket_t* skt, #endif /* PUBNUB_USE_MULTIPLE_ADDRESSES */ #endif /* PUBNUB_CALLBACK_API */ - enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t* pb) { int error; uint16_t port = HTTP_PORT; char const* origin; +#if PUBNUB_USE_IPV6 + bool use_ipv6_addresses = is_ipv6_supported(); + sa_family_t family = use_ipv6_addresses ? AF_INET6 : AF_INET; +#else /* PUBNUB_USE_IPV6 */ + sa_family_t family = AF_INET; +#endif /* !PUBNUB_USE_IPV6 */ #ifdef PUBNUB_NTF_RUNTIME_SELECTION - if (PNA_CALLBACK == pb->api_policy) { // if policy + if (PNA_CALLBACK == pb->api_policy) { // if policy #endif #ifdef PUBNUB_CALLBACK_API - sockaddr_inX_t dest = { 0 }; + sockaddr_inX_t dest = { 0 }; #if PUBNUB_PROXY_API #if PUBNUB_USE_IPV6 - const bool has_ipv6_proxy = 0 != pb->proxy_ipv6_address.ipv6[0] - || 0 != pb->proxy_ipv6_address.ipv6[1]; + const bool has_ipv6_proxy = 0 != pb->proxy_ipv6_address.ipv6[0] + || 0 != pb->proxy_ipv6_address.ipv6[1]; +#else /* !PUBNUB_USE_IPV6 */ + const bool has_ipv6_proxy = false; #endif /* PUBNUB_USE_IPV6 */ #endif /* PUBNUB_PROXY_API */ - prepare_port_and_hostname(pb, &port, &origin); + prepare_port_and_hostname(pb, &port, &origin); #if PUBNUB_PROXY_API - if (0 != pb->proxy_ipv4_address.ipv4[0] #if PUBNUB_USE_IPV6 - && (!pb->options.ipv6_connectivity || !has_ipv6_proxy) -#endif /* PUBNUB_USE_IPV6 */ - ) { - struct sockaddr_in dest = { 0 }; - PUBNUB_LOG_TRACE("(0 != pb->proxy_ipv4_address.ipv4[0]) - "); - memcpy(&(dest.sin_addr.s_addr), - pb->proxy_ipv4_address.ipv4, - sizeof dest.sin_addr.s_addr); - dest.sin_family = AF_INET; -#if defined(_WIN32) - enum pbpal_resolv_n_connect_result rslt = connect_TCP_socket( - &pb->pal.socket, &pb->options, (struct sockaddr*)&dest, port); - if (pbpal_connect_success == rslt) pbpal_set_tcp_keepalive(pb); -#else - return connect_TCP_socket( - &pb->pal.socket, &pb->options, (struct sockaddr*)&dest, port); -#endif - } -#if PUBNUB_USE_IPV6 - if (has_ipv6_proxy) { - struct sockaddr_in6 dest = { 0 }; - PUBNUB_LOG_TRACE("(0 != pb->proxy_ipv6_address.ipv6[0]) ||" - " (0 != pb->proxy_ipv6_address.ipv6[1]) - "); - memcpy(dest.sin6_addr.s6_addr, - pb->proxy_ipv6_address.ipv6, - sizeof dest.sin6_addr.s6_addr); - dest.sin6_family = AF_INET6; + if (AF_INET6 == family && has_ipv6_proxy) { + struct sockaddr_in6 dest = { 0 }; + dest.sin6_family = AF_INET6; + PUBNUB_LOG_TRACE("(0 != pb->proxy_ipv6_address.ipv6[0]) ||" + " (0 != pb->proxy_ipv6_address.ipv6[1]) - "); + memcpy(dest.sin6_addr.s6_addr, + pb->proxy_ipv6_address.ipv6, + sizeof dest.sin6_addr.s6_addr); + enum pbpal_resolv_n_connect_result rslt = + connect_TCP_socket(pb, (struct sockaddr*)&dest, port); #if defined(_WIN32) - enum pbpal_resolv_n_connect_result rslt = connect_TCP_socket( - &pb->pal.socket, &pb->options, (struct sockaddr*)&dest, port); - if (pbpal_connect_success == rslt) pbpal_set_tcp_keepalive(pb); -#else - return connect_TCP_socket( - &pb->pal.socket, &pb->options, (struct sockaddr*)&dest, port); -#endif - } + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ + return rslt; + } #endif /* PUBNUB_USE_IPV6 */ + if (0 != pb->proxy_ipv4_address.ipv4[0] + && (AF_INET6 != family || !has_ipv6_proxy)) { + struct sockaddr_in dest = { 0 }; + dest.sin_family = AF_INET; + PUBNUB_LOG_TRACE("(0 != pb->proxy_ipv4_address.ipv4[0]) - "); + memcpy(&(dest.sin_addr.s_addr), + pb->proxy_ipv4_address.ipv4, + sizeof dest.sin_addr.s_addr); + enum pbpal_resolv_n_connect_result rslt = + connect_TCP_socket(pb, (struct sockaddr*)&dest, port); +#if defined(_WIN32) + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ + return rslt; + } #endif /* PUBNUB_PROXY_API */ #if PUBNUB_USE_MULTIPLE_ADDRESSES - { - enum pbpal_resolv_n_connect_result rslt; - rslt = try_TCP_connect_spare_address( - &pb->pal.socket, &pb->spare_addresses, &pb->options, &pb->flags, port); + { + enum pbpal_resolv_n_connect_result rslt; + rslt = try_TCP_connect_spare_address(pb, family, port); #if defined(_WIN32) - if (pbpal_connect_success == rslt) pbpal_set_tcp_keepalive(pb); -#endif - if (rslt != pbpal_resolv_resource_failure) { - return rslt; + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ + if (rslt != pbpal_resolv_resource_failure) + return rslt; } - } -#endif +#endif /* !defined(_WIN32) */ + if (SOCKET_INVALID == pb->pal.socket) { + /* Reset DNS server address at the start of new resolution */ + memset(&pb->dns_queries, 0, sizeof(pb->dns_queries)); + memset(&pb->dns_addr, 0, sizeof(pb->dns_addr)); + #if PUBNUB_CHANGE_DNS_SERVERS - get_dns_ip(&pb->dns_check, (struct sockaddr*)&dest); -#else - get_dns_ip((struct sockaddr*)&dest); -#endif - if (SOCKET_INVALID == pb->pal.socket) { - pb->pal.socket = - socket(((struct sockaddr*)&dest)->sa_family, SOCK_DGRAM, IPPROTO_UDP); - } - if (SOCKET_INVALID == pb->pal.socket) { - return pbpal_resolv_resource_failure; - } - pb->options.use_blocking_io = false; - pbpal_set_blocking_io(pb); - error = - send_dns_query(pb->pal.socket, (struct sockaddr*)&dest, origin, QUERY_TYPE); - if (error < 0) { + get_dns_ip(family, &pb->dns_check, (struct sockaddr*)&dest); +#else /* PUBNUB_CHANGE_DNS_SERVERS */ + get_dns_ip(family, (struct sockaddr*)&dest); +#endif /* !PUBNUB_CHANGE_DNS_SERVERS */ + /* Store DNS server address for later use in pbpal_check_resolv_and_connect */ + memcpy(&pb->dns_addr, &dest, sizeof(dest)); + + pb->pal.socket = socket( + ((struct sockaddr*)&dest)->sa_family, SOCK_DGRAM, IPPROTO_UDP); + } + if (SOCKET_INVALID == pb->pal.socket) { + return pbpal_resolv_resource_failure; + } + pb->options.use_blocking_io = false; + pbpal_set_blocking_io(pb); + error = send_dns_query(pb->pal.socket, + (struct sockaddr*)&pb->dns_addr, + origin, + &pb->dns_queries); + if (error < 0) { #if PUBNUB_CHANGE_DNS_SERVERS - check_dns_server_error(&pb->dns_check, &pb->flags); - if_no_retry_close_socket(&pb->pal.socket, &pb->flags); + check_dns_server_error(&pb->dns_check, &pb->flags); + if_no_retry_close_socket(&pb->pal.socket, &pb->flags); #endif - return pbpal_resolv_failed_send; - } - if (error > 0) { - return pbpal_resolv_send_wouldblock; - } - pb->flags.sent_queries++; - - return pbpal_resolv_sent; + return pbpal_resolv_failed_send; + } + if (error > 0) { + return pbpal_resolv_send_wouldblock; + } + pb->flags.sent_queries++; + + return pbpal_resolv_sent; #endif /* PUBNUB_CALLBACK_API */ #ifdef PUBNUB_NTF_RUNTIME_SELECTION - } else { // if policy + } + else { // if policy #endif #if !defined PUBNUB_CALLBACK_API || defined PUBNUB_NTF_RUNTIME_SELECTION - char port_string[20]; - struct addrinfo* result; - struct addrinfo* it; - struct addrinfo hint; - - hint.ai_socktype = SOCK_STREAM; - hint.ai_family = AF_UNSPEC; - hint.ai_protocol = hint.ai_flags = hint.ai_addrlen = 0; - hint.ai_addr = NULL; - hint.ai_canonname = NULL; - hint.ai_next = NULL; - - prepare_port_and_hostname(pb, &port, &origin); - snprintf(port_string, sizeof port_string, "%hu", port); - error = getaddrinfo(origin, port_string, &hint, &result); - if (error != 0) { - return pbpal_resolv_failed_processing; - } + char port_string[20]; + struct addrinfo* result; + struct addrinfo* it; + struct addrinfo hint; + + hint.ai_socktype = SOCK_STREAM; + hint.ai_family = AF_UNSPEC; + hint.ai_protocol = hint.ai_flags = hint.ai_addrlen = 0; + hint.ai_addr = NULL; + hint.ai_canonname = NULL; + hint.ai_next = NULL; + + prepare_port_and_hostname(pb, &port, &origin); + snprintf(port_string, sizeof port_string, "%hu", port); + error = getaddrinfo(origin, port_string, &hint, &result); + if (error != 0) { + return pbpal_resolv_failed_processing; + } #if PUBNUB_USE_IPV6 - for (int pass = 0; pass < 2; ++pass) { - bool prioritize_ipv6 = pass == 0 && pb->options.ipv6_connectivity; + for (int pass = 0; pass < 2; ++pass) { + bool prioritize_ipv6 = pass == 0 && AF_INET6 == family; #endif /* PUBNUB_USE_IPV6 */ - for (it = result; it != NULL; it = it->ai_next) { + for (it = result; it != NULL; it = it->ai_next) { #if PUBNUB_USE_IPV6 - if (prioritize_ipv6 && it->ai_family != AF_INET6) { continue; } - if (!prioritize_ipv6 && it->ai_family != AF_INET) { continue; } + if (prioritize_ipv6 && it->ai_family != AF_INET6) + continue; + if (!prioritize_ipv6 && it->ai_family != AF_INET) + continue; #endif /* PUBNUB_USE_IPV6 */ - pb->pal.socket = - socket(it->ai_family, it->ai_socktype, it->ai_protocol); - if (pb->pal.socket == SOCKET_INVALID) { - continue; - } - - pbpal_set_blocking_io(pb); -#ifndef _WIN32 - const int enabled = pbccTrue == pb->options.tcp_keepalive.enabled ? 1 : 0; - (void)setsockopt(pb->pal.socket, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); - pbpal_set_tcp_keepalive(pb); -#endif - if (connect(pb->pal.socket, it->ai_addr, it->ai_addrlen) == SOCKET_ERROR) { - if (socket_would_block()) { - error = 1; - break; - } - else { - PUBNUB_LOG_WARNING("socket connect() failed, will try " - "another IP address, if available\n"); - socket_close(pb->pal.socket); - pb->pal.socket = SOCKET_INVALID; + pb->pal.socket = + socket(it->ai_family, it->ai_socktype, it->ai_protocol); + if (pb->pal.socket == SOCKET_INVALID) { continue; } +#if defined(_WIN32) + const BOOL enabled = + pbccTrue == pb->options.tcp_keepalive.enabled ? TRUE : FALSE; + (void)setsockopt(pb->pal.socket, + SOL_SOCKET, + SO_KEEPALIVE, + (const char*)&enabled, + sizeof(enabled)); + // The most reliable time to set idle/interval/count + // (pbpal_set_tcp_keepalive) is after connection completion. +#else /* defined(_WIN32) */ + const int enabled = + pbccTrue == pb->options.tcp_keepalive.enabled ? 1 : 0; + (void)setsockopt( + pb->pal.socket, SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)); + pbpal_set_tcp_keepalive(pb); +#endif /* !defined(_WIN32) */ + + pbpal_set_blocking_io(pb); + if (connect(pb->pal.socket, it->ai_addr, it->ai_addrlen) + == SOCKET_ERROR) { + if (socket_would_block()) { + error = 1; + break; + } + else { + PUBNUB_LOG_WARNING( + "socket connect() failed, will try " + "another IP address, if available\n"); + socket_close(pb->pal.socket); + pb->pal.socket = SOCKET_INVALID; + continue; + } + } + break; } - break; - } #if PUBNUB_USE_IPV6 - if (1 == error) break; - } + if (1 == error) + break; + } #endif /* PUBNUB_USE_IPV6 */ - freeaddrinfo(result); + freeaddrinfo(result); - if (NULL == it) { - return pbpal_connect_failed; - } + if (NULL == it) { + return pbpal_connect_failed; + } - socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); - socket_disable_SIGPIPE(pb->pal.socket); + socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); + socket_disable_SIGPIPE(pb->pal.socket); #if defined(_WIN32) - if (!error) pbpal_set_tcp_keepalive(pb); + if (!error) + pbpal_set_tcp_keepalive(pb); #endif - return error ? pbpal_connect_wouldblock : pbpal_connect_success; + return error ? pbpal_connect_wouldblock : pbpal_connect_success; #endif /* !defined PUBNUB_CALLBACK_API || defined PUBNUB_NTF_RUNTIME_SELECTION */ #ifdef PUBNUB_NTF_RUNTIME_SELECTION - } // if policy + } // if policy #endif } @@ -608,85 +806,102 @@ enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t* pb) enum pbpal_resolv_n_connect_result pbpal_check_resolv_and_connect(pubnub_t* pb) { #ifdef PUBNUB_NTF_RUNTIME_SELECTION - if (PNA_CALLBACK == pb->api_policy) { // if policy + if (PNA_CALLBACK == pb->api_policy) { // if policy #endif #ifdef PUBNUB_CALLBACK_API + sockaddr_inX_t dest = { 0 }; + uint16_t port = HTTP_PORT; + enum pbpal_resolv_n_connect_result rslt; - sockaddr_inX_t dns_server = { 0 }; - sockaddr_inX_t dest = { 0 }; - uint16_t port = HTTP_PORT; - enum pbpal_resolv_n_connect_result rslt; - - PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); - PUBNUB_ASSERT_OPT(pb->state == PBS_WAIT_DNS_RCV); + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + PUBNUB_ASSERT_OPT(pb->state == PBS_WAIT_DNS_RCV); #if PUBNUB_USE_SSL - if (pb->flags.trySSL) { - PUBNUB_ASSERT(pb->options.useSSL); - port = TLS_PORT; - } + if (pb->flags.trySSL) { + PUBNUB_ASSERT(pb->options.useSSL); + port = TLS_PORT; + } #endif #if PUBNUB_PROXY_API - if (pbproxyNONE != pb->proxy_type) { - port = pb->proxy_port; - } -#endif -#if PUBNUB_CHANGE_DNS_SERVERS - get_dns_ip(&pb->dns_check, (struct sockaddr*)&dns_server); -#else - get_dns_ip((struct sockaddr*)&dns_server); + if (pbproxyNONE != pb->proxy_type) { + port = pb->proxy_port; + } #endif - switch (read_dns_response(pb->pal.socket, - (struct sockaddr*)&dns_server, - (struct sockaddr*)&dest PBDNS_OPTIONAL_PARAMS_PB)) { - case -1: + /* Reuse the DNS server address that was stored when sending the query */ + switch (read_dns_response(pb->pal.socket, + (struct sockaddr*)&pb->dns_addr, + &pb->dns_queries PBDNS_OPTIONAL_PARAMS_PB)) { + case -1: #if PUBNUB_CHANGE_DNS_SERVERS - check_dns_server_error(&pb->dns_check, &pb->flags); + check_dns_server_error(&pb->dns_check, &pb->flags); #endif - return pbpal_resolv_failed_rcv; - case +1: - return pbpal_resolv_rcv_wouldblock; - case 0: + return pbpal_resolv_failed_rcv; + case +1: + return pbpal_resolv_rcv_wouldblock; + case 0: #if PUBNUB_CHANGE_DNS_SERVERS - pb->flags.rotations_count = 0; + pb->flags.rotations_count = 0; #endif /* PUBNUB_CHANGE_DNS_SERVERS */ - break; - } - socket_close(pb->pal.socket); - - rslt = connect_TCP_socket( - &pb->pal.socket, &pb->options, (struct sockaddr*)&dest, port); -#if PUBNUB_USE_MULTIPLE_ADDRESSES - if (pbpal_connect_failed == rslt) { - if (AF_INET == ((struct sockaddr*)&dest)->sa_family) { - pb->flags.retry_after_close = - (++pb->spare_addresses.ipv4_index < pb->spare_addresses.n_ipv4); + break; } + socket_close(pb->pal.socket); + #if PUBNUB_USE_IPV6 - else if (AF_INET6 == ((struct sockaddr*)&dest)->sa_family) { - pb->flags.retry_after_close = - (++pb->spare_addresses.ipv6_index < pb->spare_addresses.n_ipv6); + if (is_ipv6_supported() + && !IN6_IS_ADDR_UNSPECIFIED( + &((struct sockaddr_in6*)&pb->dns_queries.dns_aaaa_addr)->sin6_addr)) { + ((struct sockaddr_in6*)&dest)->sin6_family = AF_INET6; + memcpy(((struct sockaddr_in6*)&dest)->sin6_addr.s6_addr, + ((struct sockaddr_in6*)&pb->dns_queries.dns_aaaa_addr) + ->sin6_addr.s6_addr, + 16); + } + else { + ((struct sockaddr_in*)&dest)->sin_family = AF_INET; + memcpy(&((struct sockaddr_in*)&dest)->sin_addr.s_addr, + &pb->dns_queries.dns_a_addr.sin_addr.s_addr, + 4); } +#else /* PUBNUB_USE_IPV6 */ + dest.sin_family = AF_INET; + memcpy(&dest.sin_addr.s_addr, &pb->dns_queries.dns_a_addr.sin_addr.s_addr, 4); +#endif /* !PUBNUB_USE_IPV6 */ + + rslt = connect_TCP_socket(pb, (struct sockaddr*)&dest, port); +#if PUBNUB_USE_MULTIPLE_ADDRESSES + if (pbpal_connect_failed == rslt) { + if (AF_INET == ((struct sockaddr*)&dest)->sa_family) { + pb->flags.retry_after_close = (++pb->spare_addresses.ipv4_index + < pb->spare_addresses.n_ipv4); + } +#if PUBNUB_USE_IPV6 + else if (AF_INET6 == ((struct sockaddr*)&dest)->sa_family) { + pb->flags.retry_after_close = (++pb->spare_addresses.ipv6_index + < pb->spare_addresses.n_ipv6); + } #endif #if PUBNUB_USE_SSL - pb->flags.trySSL = pb->options.useSSL; + pb->flags.trySSL = pb->options.useSSL; #endif - } + } #endif /* PUBNUB_USE_MULTIPLE_ADDRESSES */ #if defined(_WIN32) - if (pbpal_connect_success == rslt) pbpal_set_tcp_keepalive(pb); -#endif - return rslt; + // Complete TCP Keep-Alive configuration if connection established. + if (pbpal_connect_success == rslt) + pbpal_set_tcp_keepalive(pb); +#endif /* defined(_WIN32) */ + return rslt; #endif /* PUBNUB_CALLBACK_API */ #ifdef PUBNUB_NTF_RUNTIME_SELECTION - } else { // if policy + } + else { // if policy #endif #if !defined PUBNUB_CALLBACK_API || defined PUBNUB_NTF_RUNTIME_SELECTION - PUBNUB_UNUSED(pb); + PUBNUB_UNUSED(pb); - /* Under regular BSD-ish sockets, this function should not be - called unless using async DNS, so this is an error */ - return pbpal_connect_failed; + /* Under regular BSD-ish sockets, this function should not be + called unless using async DNS, so this is an error */ + return pbpal_connect_failed; #endif /* !defined PUBNUB_CALLBACK_API || defined PUBNUB_NTF_RUNTIME_SELECTION */ #ifdef PUBNUB_NTF_RUNTIME_SELECTION @@ -707,14 +922,17 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t* pb) PUBNUB_ASSERT_OPT(pb->state == PBS_WAIT_CONNECT); #if defined(_WIN32) - rslt = getsockopt( - pb->pal.socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, (int*)&error_code_size); + rslt = getsockopt(pb->pal.socket, + SOL_SOCKET, + SO_ERROR, + (char*)&error_code, + (int*)&error_code_size); if (rslt == SOCKET_ERROR) { - PUBNUB_LOG_ERROR( - "Error: pbpal_check_connect(pb=%p)---> getsockopt() == SOCKET_ERROR\n" - " WSAGetLastError()=%d\n", - pb, - WSAGetLastError()); + PUBNUB_LOG_ERROR("Error: pbpal_check_connect(pb=%p)---> getsockopt() " + "== SOCKET_ERROR\n" + " WSAGetLastError()=%d\n", + pb, + WSAGetLastError()); return pbpal_connect_failed; } #else @@ -727,11 +945,11 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t* pb) } #endif /* defined(_WIN32) */ if (error_code != 0) { - PUBNUB_LOG_ERROR( - "Error: pbpal_check_connect(pb=%p)--> getsockopt(&error_code)--> " - "error_code=%d\n", - pb, - error_code); + PUBNUB_LOG_ERROR("Error: pbpal_check_connect(pb=%p)--> " + "getsockopt(&error_code)--> " + "error_code=%d\n", + pb, + error_code); #if PUBNUB_USE_MULTIPLE_ADDRESSES PUBNUB_LOG_ERROR( " time_since_the_last_dns_query = %ld seconds\n", @@ -761,40 +979,39 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t* pb) else if (rslt > 0) { PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n"); #if defined(_WIN32) + // Complete TCP Keep-Alive configuration if connection established. pbpal_set_tcp_keepalive(pb); -#endif +#endif /* defined(_WIN32) */ return pbpal_connect_success; } PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n"); return pbpal_connect_wouldblock; } -void pbpal_set_tcp_keepalive(const pubnub_t *pb) +void pbpal_set_tcp_keepalive(const pubnub_t* pb) { - if (pb->pal.socket == SOCKET_INVALID) return; + if (pb->pal.socket == SOCKET_INVALID) + return; const pubnub_tcp_keepalive keepalive = pb->options.tcp_keepalive; - const pb_socket_t skt = pb->pal.socket; - -#if defined(_WIN32) - const BOOL enabled = pbccTrue == keepalive.enabled ? TRUE : FALSE; - (void)setsockopt(skt, SOL_SOCKET, SO_KEEPALIVE, (const char*)&enabled, sizeof(enabled)); -#endif + const pb_socket_t skt = pb->pal.socket; - if (pbccTrue != keepalive.enabled || - (0 == keepalive.time && 0 == keepalive.interval)) return; + if (pbccTrue != keepalive.enabled + || (0 == keepalive.time && 0 == keepalive.interval)) + return; #if defined(_WIN32) struct tcp_keepalive alive; - DWORD bytes = 0; - alive.onoff = 1; - alive.keepaliveinterval = (keepalive.interval > 0) ? (ULONG)keepalive.interval * 1000UL : 0; - alive.keepalivetime = (keepalive.time > 0) ? (ULONG)keepalive.time * 1000UL : 0; - (void)WSAIoctl(skt, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), - NULL, 0, &bytes, NULL, NULL); + DWORD bytes = 0; + alive.onoff = 1; + alive.keepaliveinterval = + (keepalive.interval > 0) ? (ULONG)keepalive.interval * 1000UL : 0; + alive.keepalivetime = (keepalive.time > 0) ? (ULONG)keepalive.time * 1000UL : 0; + (void)WSAIoctl( + skt, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, &bytes, NULL, NULL); #else const int interval = keepalive.interval; - const int probes = keepalive.probes; - const int time = keepalive.time; + const int probes = keepalive.probes; + const int time = keepalive.time; if (time > 0) { #if defined(__APPLE__) @@ -805,7 +1022,8 @@ void pbpal_set_tcp_keepalive(const pubnub_t *pb) } if (interval > 0) - (void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); + (void)setsockopt( + skt, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval)); if (probes > 0) (void)setsockopt(skt, IPPROTO_TCP, TCP_KEEPCNT, &probes, sizeof(probes)); diff --git a/microchip_harmony/pbpal_resolv_and_connect_harmony_tcp.c b/microchip_harmony/pbpal_resolv_and_connect_harmony_tcp.c index 0ed5148b..5ef6433a 100644 --- a/microchip_harmony/pbpal_resolv_and_connect_harmony_tcp.c +++ b/microchip_harmony/pbpal_resolv_and_connect_harmony_tcp.c @@ -95,7 +95,7 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) #if PUBNUB_USE_SSL bool connected = !NET_PRES_SocketIsNegotiatingEncryption(pb->pal.socket) && NET_PRES_SocketIsSecure(pb->pal.socket); - return bool ? pbpal_connect_success : pbpal_connect_wouldblock; + return connected ? pbpal_connect_success : pbpal_connect_wouldblock; #else return TCPIP_TCP_IsConnected(pb->pal.socket) ? pbpal_connect_success : pbpal_connect_wouldblock; #endif @@ -105,7 +105,7 @@ enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) #if PUBNUB_CHANGE_DNS_SERVERS int pbpal_dns_rotate_server(pubnub_t *pb) { - return (pbp->flags.sent_queries < PUBNUB_MAX_DNS_QUERIES ? 0 : 1) + return (pb->flags.sent_queries < PUBNUB_MAX_DNS_QUERIES ? 0 : 1) } #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ diff --git a/openssl/pbpal_connect_openssl.c b/openssl/pbpal_connect_openssl.c index 931d861d..f9809ead 100644 --- a/openssl/pbpal_connect_openssl.c +++ b/openssl/pbpal_connect_openssl.c @@ -121,8 +121,6 @@ static char pubnub_cert_Starfield[] = #if defined(PUBNUB_USE_LETS_ENCRYPT_CERTIFICATE) || defined(_WIN32) /* ISRG Root X1 (Let's Encrypt) - Used for testing and validation purposes with badssl.com and other Let's Encrypt domains. - Note: This is NOT used by PubNub production infrastructure. Subject: C=US, O=Internet Security Research Group, CN=ISRG Root X1 Issuer: C=US, O=Internet Security Research Group, CN=ISRG Root X1 @@ -204,7 +202,7 @@ static int add_pubnub_cert(SSL_CTX* sslCtx) 3. ISRG Root X1 - For testing with Let's Encrypt domains (badssl.com, etc.) */ int rslt = add_pem_cert(sslCtx, pubnub_cert_Amazon_Root_CA_1); - rslt = rslt || add_pem_cert(sslCtx, pubnub_cert_Starfield); + rslt = rslt || add_pem_cert(sslCtx, pubnub_cert_Starfield); #if defined(PUBNUB_USE_LETS_ENCRYPT_CERTIFICATE) || defined(_WIN32) rslt = rslt || add_pem_cert(sslCtx, pubnub_cert_ISRG_Root_X1); #endif @@ -277,27 +275,27 @@ enum pbpal_tls_result pbpal_start_tls(pubnub_t* pb) /** Enable SNI (Server Name Indication) for virtual hosting support. */ if (!SSL_set_tlsext_host_name(ssl, pb->origin)) { - PUBNUB_LOG_WARNING( - "pb=%p: SSL_set_tlsext_host_name() failed to set SNI hostname '%s'\n", - pb, - pb->origin); + PUBNUB_LOG_WARNING("pb=%p: SSL_set_tlsext_host_name() failed to set " + "SNI hostname '%s'\n", + pb, + pb->origin); ERR_print_errors_cb(print_to_pubnub_log, pb); return pbtlsFailed; } /** Enable hostname verification. */ - X509_VERIFY_PARAM *param = SSL_get0_param(ssl); + X509_VERIFY_PARAM* param = SSL_get0_param(ssl); if (param != NULL) { char const* hostname = pb->origin; if (!X509_VERIFY_PARAM_set1_host(param, hostname, 0)) { - PUBNUB_LOG_WARNING( - "pb=%p: X509_VERIFY_PARAM_set1_host() failed to set hostname '%s' for verification\n", - pb, - hostname); + PUBNUB_LOG_WARNING("pb=%p: X509_VERIFY_PARAM_set1_host() failed to " + "set hostname '%s' for verification\n", + pb, + hostname); return pbtlsFailed; } - PUBNUB_LOG_DEBUG("pb=%p: Hostname verification configured for '%s'\n", - pb, hostname); + PUBNUB_LOG_TRACE( + "pb=%p: Hostname verification configured for '%s'\n", pb, hostname); } PUBNUB_LOG_TRACE("pb=%p: Got SSL\n", pb); SSL_set_fd(ssl, pb->pal.socket); @@ -318,8 +316,8 @@ enum pbpal_tls_result pbpal_start_tls(pubnub_t* pb) */ enum pbpal_tls_result pbpal_check_tls(pubnub_t* pb) { - SSL* ssl; - int rslt; + SSL* ssl; + int rslt; X509* cert; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); @@ -331,19 +329,23 @@ enum pbpal_tls_result pbpal_check_tls(pubnub_t* pb) bool needRead = false, needWrite = false; rslt = SSL_connect(ssl); - rslt = pbpal_handle_socket_condition(rslt, pb, __FILE__, __LINE__, &needRead, &needWrite); + rslt = pbpal_handle_socket_condition( + rslt, pb, __FILE__, __LINE__, &needRead, &needWrite); if (PNR_OK != rslt) { - if (rslt != PNR_IN_PROGRESS) return pbtlsFailed; - return needRead ? pbtlsStartedWaitRead : needWrite ? pbtlsStartedWaitWrite : pbtlsStarted; + if (rslt != PNR_IN_PROGRESS) + return pbtlsFailed; + return needRead ? pbtlsStartedWaitRead + : needWrite ? pbtlsStartedWaitWrite + : pbtlsStarted; } PUBNUB_LOG_TRACE("pb=%p: SSL connected\n", pb); socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); - #if OPENSSL_VERSION_NUMBER < 0x30000000L +#if OPENSSL_VERSION_NUMBER < 0x30000000L cert = SSL_get_peer_certificate(ssl); - #else +#else cert = SSL_get1_peer_certificate(ssl); - #endif +#endif if (cert != NULL) { rslt = SSL_get_verify_result(ssl); X509_free(cert); diff --git a/openssl/pubnub_config.h b/openssl/pubnub_config.h index 924911d8..0d2fb366 100644 --- a/openssl/pubnub_config.h +++ b/openssl/pubnub_config.h @@ -1,6 +1,6 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ #if !defined INC_PUBNUB_CONFIG -#define INC_PUBNUB_CONFIG +#define INC_PUBNUB_CONFIG /* -- Next few definitions can be tweaked by the user, but with care -- */ @@ -62,7 +62,7 @@ /** This is the URL of the Pubnub server. Change only for testing purposes. */ -#define PUBNUB_ORIGIN "pubsub.pubnub.com" +#define PUBNUB_ORIGIN "pubsub.pubnub.com" /** Set to 0 to disable changing the origin from the default #PUBNUB_ORIGIN. Set to anything != 0 to enable changing the @@ -71,15 +71,15 @@ #define PUBNUB_ORIGIN_SETTABLE 1 /** Duration of the transaction timeout set during context initialization, - in milliseconds. Timeout dration in the context can be changed by the + in milliseconds. Timeout dration in the context can be changed by the user after initialization. */ -#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 +#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 /** Duration of the 'wait_connect_TCP_socket' timeout set during context initialization, in milliseconds. Can be changed later by the user. */ -#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 +#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 /** Mininmal duration of the 'wait_connect_TCP_socket' timer, in milliseconds. * You can't set less than this. @@ -97,12 +97,12 @@ #endif #if defined(PUBNUB_CALLBACK_API) -/** The size of the stack (in kilobytes) for the "polling" thread, when using - the callback interface. We don't need much, so, if you want to conserve - memory, you can try small values. It's hard to say what is the minumum, - as it depends on the OS functions we call, but, you probably +/** The size of the stack (in kilobytes) for the "polling" thread, when using + the callback interface. We don't need much, so, if you want to conserve + memory, you can try small values. It's hard to say what is the minumum, + as it depends on the OS functions we call, but, you probably shouldn't try less than 64 KB. - + Set to `0` to use the default stack size. */ #define PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB 0 @@ -139,8 +139,13 @@ /** Maximum number of consecutive retries when sending DNS query in a single transaction */ #define PUBNUB_MAX_DNS_QUERIES 3 #if PUBNUB_CHANGE_DNS_SERVERS +#if PUBNUB_USE_IPV6 +/** Maximum number of DNS servers list rotation in a single transaction */ +#define PUBNUB_MAX_DNS_ROTATION 5 +#else /* PUBNUB_USE_IPV6 */ /** Maximum number of DNS servers list rotation in a single transaction */ #define PUBNUB_MAX_DNS_ROTATION 3 +#endif /* !PUBNUB_USE_IPV6 */ #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ @@ -205,9 +210,9 @@ #if !defined(PUBNUB_USE_OBJECTS_API) /** If true (!=0) will enable using the objects API, which is a - collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) - on two new pubnub objects: User and Space, as well as manipulating connections - between them. */ + collection of rest API features that enables "CRUD"(Create, Read, Update and + Delete) on two new pubnub objects: User and Space, as well as manipulating + connections between them. */ #define PUBNUB_USE_OBJECTS_API 1 #endif @@ -236,6 +241,15 @@ these things all by himself using pubnub_heartbeat() transaction */ #define PUBNUB_USE_AUTO_HEARTBEAT 1 #endif + +#if !defined(PUBNUB_ADVANCED_KEEP_ALIVE) +/** If true (!=0) will enable use of advanced keep-alive parameters, + see pubnub_set_keep_alive_param() and will observe the + `Connection: close` if received from server or proxy. +*/ +#define PUBNUB_ADVANCED_KEEP_ALIVE 1 +#endif + #define PUBNUB_MAX_URL_PARAMS 12 #ifndef PUBNUB_RAND_INIT_VECTOR diff --git a/posix/pubnub_config.h b/posix/pubnub_config.h index 74bb94bb..bf7979e2 100644 --- a/posix/pubnub_config.h +++ b/posix/pubnub_config.h @@ -1,6 +1,6 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ #if !defined INC_PUBNUB_CONFIG -#define INC_PUBNUB_CONFIG +#define INC_PUBNUB_CONFIG /* -- Next few definitions can be tweaked by the user, but with care -- */ @@ -58,7 +58,7 @@ /** This is the URL of the Pubnub server. Change only for testing purposes. */ -#define PUBNUB_ORIGIN "ps.pndsn.com" +#define PUBNUB_ORIGIN "ps.pndsn.com" /** Set to 0 to disable changing the origin from the default #PUBNUB_ORIGIN. Set to anything != 0 to enable changing the @@ -69,12 +69,12 @@ /** Duration of the transaction timeout set during context initialization, in milliseconds. Can be changed later by the user. */ -#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 +#define PUBNUB_DEFAULT_TRANSACTION_TIMER 310000 /** Duration of the 'wait_connect_TCP_socket' timeout set during context initialization, in milliseconds. Can be changed later by the user. */ -#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 +#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 /** Mininmal duration of the 'wait_connect_TCP_socket' timer, in milliseconds. * You can't set less than this. @@ -89,12 +89,12 @@ #endif #if defined(PUBNUB_CALLBACK_API) -/** The size of the stack (in kilobytes) for the "polling" thread, when using - the callback interface. We don't need much, so, if you want to conserve - memory, you can try small values. It's hard to say what is the minumum, - as it depends on the OS functions we call, but, you probably +/** The size of the stack (in kilobytes) for the "polling" thread, when using + the callback interface. We don't need much, so, if you want to conserve + memory, you can try small values. It's hard to say what is the minumum, + as it depends on the OS functions we call, but, you probably shouldn't try less than 64 KB. - + Set to `0` to use the default stack size. */ #define PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB 0 @@ -131,8 +131,13 @@ /** Maximum number of consecutive retries when sending DNS query in a single transaction */ #define PUBNUB_MAX_DNS_QUERIES 3 #if PUBNUB_CHANGE_DNS_SERVERS +#if PUBNUB_USE_IPV6 +/** Maximum number of DNS servers list rotation in a single transaction */ +#define PUBNUB_MAX_DNS_ROTATION 5 +#else /* PUBNUB_USE_IPV6 */ /** Maximum number of DNS servers list rotation in a single transaction */ #define PUBNUB_MAX_DNS_ROTATION 3 +#endif /* !PUBNUB_USE_IPV6 */ #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ @@ -196,9 +201,9 @@ #if !defined(PUBNUB_USE_OBJECTS_API) /** If true (!=0) will enable using the objects API, which is a - collection of Rest API features that enables "CRUD"(Create, Read, Update and Delete) - on two new pubnub objects: User and Space, as well as manipulating connections - between them. */ + collection of Rest API features that enables "CRUD"(Create, Read, Update and + Delete) on two new pubnub objects: User and Space, as well as manipulating + connections between them. */ #define PUBNUB_USE_OBJECTS_API 1 #endif @@ -228,6 +233,14 @@ #define PUBNUB_USE_AUTO_HEARTBEAT 1 #endif +#if !defined(PUBNUB_ADVANCED_KEEP_ALIVE) +/** If true (!=0) will enable use of advanced keep-alive parameters, + see pubnub_set_keep_alive_param() and will observe the + `Connection: close` if received from server or proxy. +*/ +#define PUBNUB_ADVANCED_KEEP_ALIVE 1 +#endif + #define PUBNUB_MAX_URL_PARAMS 12 #ifndef PUBNUB_RAND_INIT_VECTOR diff --git a/posix/pubnub_dns_system_servers.c b/posix/pubnub_dns_system_servers.c index b9689429..0592c30f 100644 --- a/posix/pubnub_dns_system_servers.c +++ b/posix/pubnub_dns_system_servers.c @@ -3,17 +3,21 @@ #include "core/pubnub_log.h" #include "core/pubnub_dns_servers.h" #include "lib/pubnub_parse_ipv4_addr.h" +#if PUBNUB_USE_IPV6 +#include "lib/pubnub_parse_ipv6_addr.h" +#endif #include #include #include +#include int pubnub_dns_read_system_servers_ipv4(struct pubnub_ipv4_address* o_ipv4, size_t n) { - FILE* fp; - char buffer[255]; - unsigned i = 0; - bool found = false; + FILE* fp; + char buffer[255]; + unsigned i = 0; + bool found = false; PUBNUB_ASSERT_OPT(n > 0); PUBNUB_ASSERT_OPT(o_ipv4 != NULL); @@ -28,7 +32,17 @@ int pubnub_dns_read_system_servers_ipv4(struct pubnub_ipv4_address* o_ipv4, size /* Reads new line */ fgets(buffer, sizeof buffer, fp); if (strncmp(buffer, "nameserver", 10) == 0) { - if (pubnub_parse_ipv4_addr(buffer + 10, &o_ipv4[i]) != 0) { + char* addr_start = buffer + 10; + while (*addr_start == ' ' || *addr_start == '\t') { + addr_start++; + } + char* end = addr_start; + while (*end && *end != ' ' && *end != '\n' && *end != '\r' && *end != '\t') { + end++; + } + *end = '\0'; + + if (pubnub_parse_ipv4_addr(addr_start, &o_ipv4[i]) != 0) { PUBNUB_LOG_ERROR( "pubnub_dns_read_system_servers_ipv4():" "- ipv4 'numbers-and-dots' notation string(%s)" @@ -54,3 +68,90 @@ int pubnub_dns_read_system_servers_ipv4(struct pubnub_ipv4_address* o_ipv4, size return i; } + +#if PUBNUB_USE_IPV6 +int pubnub_dns_read_system_servers_ipv6(struct pubnub_ipv6_address* o_ipv6, size_t n) +{ + FILE* fp; + char buffer[255]; + unsigned dns_count = 0; + bool has_ipv4_addresses = false; + + PUBNUB_ASSERT_OPT(n > 0); + PUBNUB_ASSERT_OPT(o_ipv6 != NULL); + + fp = fopen("/etc/resolv.conf", "r"); + if (NULL == fp) { + PUBNUB_LOG_ERROR( + "Can't open file:'/etc/resolv.conf' for reading!-errno:%d\n", errno); + return -1; + } + while ((dns_count < n) && !feof(fp)) { + /* Reads new line */ + fgets(buffer, sizeof buffer, fp); + if (strncmp(buffer, "nameserver", 10) == 0) { + bool is_ipv4_address = false; + uint8_t* dns_bytes = NULL; + char* addr_start = buffer + 10; + while (*addr_start == ' ' || *addr_start == '\t') { + addr_start++; + } + + char* end = addr_start; + while (*end && *end != ' ' && *end != '\n' && *end != '\r' && *end != '\t') { + end++; + } + *end = '\0'; + + /* Try parsing as IPv6 first */ + if (pubnub_parse_ipv6_addr(addr_start, &o_ipv6[dns_count]) == 0) { + dns_bytes = o_ipv6[dns_count].ipv6; + ++dns_count; + } + else { + struct pubnub_ipv4_address ipv4_addr; + if (pubnub_parse_ipv4_addr(addr_start, &ipv4_addr) == 0) { + has_ipv4_addresses = true; + is_ipv4_address = true; + memset(o_ipv6[dns_count].ipv6, 0, 10); + o_ipv6[dns_count].ipv6[10] = 0xff; + o_ipv6[dns_count].ipv6[11] = 0xff; + memcpy(&o_ipv6[dns_count].ipv6[12], ipv4_addr.ipv4, 4); + dns_bytes = o_ipv6[dns_count].ipv6; + ++dns_count; + } + else { + PUBNUB_LOG_WARNING("pubnub_dns_read_system_servers_ipv6(): " + "Invalid nameserver address '%s' in " + "/etc/resolv.conf, skipping.\n", + addr_start); + } + } + + if (NULL != dns_bytes) { + char str[INET6_ADDRSTRLEN]; + PUBNUB_LOG_TRACE( + "Added %s DNS (%s).\n", + is_ipv4_address ? "IPv4-mapped IPv6" : "IPv6", + inet_ntop(AF_INET6, dns_bytes, str, INET6_ADDRSTRLEN)); + } + } + } + if (fclose(fp) != 0) { + PUBNUB_LOG_ERROR("Error closing file: '/etc/resolv.conf'! errno:%d\n", + errno); + return -1; + } + if (0 == dns_count) { + PUBNUB_LOG_WARNING( + "Couldn't find any DNS servers in file:'/etc/resolv.conf'!"); + return 0; + } + + PUBNUB_LOG_DEBUG("Discovered %u %s DNS server(s)\n", + dns_count, + has_ipv4_addresses ? "IPv4-mapped IPv6/IPv6" : "IPv6"); + + return dns_count; +} +#endif /* PUBNUB_USE_IPV6 */ diff --git a/windows/pbpal_windows_blocking_io.c b/windows/pbpal_windows_blocking_io.c index 744f031f..e159f11d 100644 --- a/windows/pbpal_windows_blocking_io.c +++ b/windows/pbpal_windows_blocking_io.c @@ -4,7 +4,11 @@ #include "core/pubnub_assert.h" +#if PUBNUB_USE_SSL +#include "openssl/pubnub_internal.h" +#else #include "pubnub_internal.h" +#endif #include diff --git a/windows/pubnub_config.h b/windows/pubnub_config.h index ea4f6a67..b0bb02f2 100644 --- a/windows/pubnub_config.h +++ b/windows/pubnub_config.h @@ -58,7 +58,7 @@ /** This is the URL of the Pubnub server. Change only for testing purposes. */ -#define PUBNUB_ORIGIN "pubsub.pubnub.com" +#define PUBNUB_ORIGIN "ps.pndsn.com" /** Set to 0 to disable changing the origin from the default #PUBNUB_ORIGIN. Set to anything != 0 to enable changing the @@ -75,7 +75,7 @@ /** Duration of the 'wait_connect_TCP_socket' timeout set during context initialization, in milliseconds. Can be changed later by the user. */ -#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 +#define PUBNUB_DEFAULT_WAIT_CONNECT_TIMER 10000 /** Mininmal duration of the 'wait_connect_TCP_socket' timer, in milliseconds. * You can't set less than this. @@ -130,8 +130,13 @@ /** Maximum number of consecutive retries when sending DNS query in a single transaction */ #define PUBNUB_MAX_DNS_QUERIES 3 #if PUBNUB_CHANGE_DNS_SERVERS +#if PUBNUB_USE_IPV6 +/** Maximum number of DNS servers list rotation in a single transaction */ +#define PUBNUB_MAX_DNS_ROTATION 5 +#else /* PUBNUB_USE_IPV6 */ /** Maximum number of DNS servers list rotation in a single transaction */ #define PUBNUB_MAX_DNS_ROTATION 3 +#endif /* !PUBNUB_USE_IPV6 */ #endif /* PUBNUB_CHANGE_DNS_SERVERS */ #endif /* defined(PUBNUB_CALLBACK_API) */ @@ -148,7 +153,7 @@ /** If true (!=0) will use Windows SSPI (for NTLM and such). Otherwise, will use own implementation, if available. */ -#ifndef PUBNUB_USE_WIN_SSPI +#ifndef PUBNUB_USE_WIN_SSPI #define PUBNUB_USE_WIN_SSPI 1 #endif @@ -158,7 +163,7 @@ #define PUBNUB_MAX_PROXY_HOSTNAME_LENGTH 63 /** If true (!=0), enable support for message encryption/decryption */ -#ifndef PUBNUB_CRYPTO_API +#ifndef PUBNUB_CRYPTO_API #define PUBNUB_CRYPTO_API 0 #endif @@ -200,9 +205,9 @@ #define PUBNUB_USE_FETCH_HISTORY 1 /** If true (!=0) will enable using the objects API, which is a - collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) - on two new pubnub objects: User and Space, as well as manipulating connections - between them. */ + collection of rest API features that enables "CRUD"(Create, Read, Update and + Delete) on two new pubnub objects: User and Space, as well as manipulating + connections between them. */ #define PUBNUB_USE_OBJECTS_API 1 /** If true (!=0) will enable using the Actions API, which is a collection diff --git a/windows/pubnub_dns_system_servers.c b/windows/pubnub_dns_system_servers.c index 10187688..a9199e13 100644 --- a/windows/pubnub_dns_system_servers.c +++ b/windows/pubnub_dns_system_servers.c @@ -1,14 +1,30 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ -#include "pubnub_internal.h" +/** + * @file pubnub_dns_system_servers.c + * @brief Windows DNS server discovery using GetAdaptersAddresses. + * + * This implementation is designed for Windows 8+ and provides: + * - Thread-safe DNS server discovery + * - Proper handling of stale VPN connections via DNS validation + * - IPv4 and IPv6 support with GetBestRoute2 + * - Adapter prioritization based on routing metrics + * + * Minimum Windows version: Windows 8 (NT 6.2) + */ +#if PUBNUB_USE_SSL +#include "openssl/pubnub_internal.h" +#else +#include "pubnub_internal.h" +#endif #include "core/pubnub_dns_servers.h" #include "core/pubnub_assert.h" #include "core/pubnub_log.h" #include +#include #include #include -#include #include #include @@ -18,454 +34,704 @@ #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -/* Maximum retry attempts for GetAdaptersAddresses buffer allocation */ +/** Maximum retry attempts for GetAdaptersAddresses */ #define MAX_ADAPTER_ADDRESS_RETRIES 3 +/** Maximum number of DNS servers per adapter */ +#define MAX_DNS_SERVERS_PER_ADAPTER 8 -/* Public DNS servers to use for GetBestInterface routing test */ -#define PUBLIC_DNS_TEST_IP_1 "8.8.8.8" /* Google Public DNS */ -#define PUBLIC_DNS_TEST_IP_2 "1.1.1.1" /* Cloudflare DNS */ - -/* Timeout for DNS server connectivity check (milliseconds) */ -#define DNS_CONNECTIVITY_TIMEOUT_MS 2000 - -/** Helper structure to track adapters with their metrics for sorting */ -struct adapter_with_metric { - IP_ADAPTER_ADDRESSES* adapter; - ULONG metric; - bool is_best_route; +/** DNS server validation timeout in milliseconds. + * DNS servers will be validated only if timeout is larget than @b 0. + */ +#ifndef PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT +#define PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT 0 +#endif /* PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT */ + +/** Adapter info for sorting with verified DNS servers. */ +struct adapter_info { + IP_ADAPTER_ADDRESSES* adapter; + ULONG metric; + bool is_best_route; + size_t dns_count; + struct sockaddr_storage dns_servers[MAX_DNS_SERVERS_PER_ADAPTER]; }; -/** Check whether provided address is valid (not loopback, not APIPA, not zero). +/** Check if @c IPv4 address is valid (not loopback, APIPA, or zero). * - * @param host_addr Address in host byte order. - * @return @c true if proper non-internal address has been provided, - * @c false otherwise. + * @param addr IPv4 address to check (4 bytes, network order). + * @return @c true if valid, @c false otherwise. */ -static bool pubnub_ipv4_address_valid(DWORD host_addr) +static bool is_valid_ipv4(const uint8_t addr[4]) { - return !(host_addr == 0 || ((host_addr >> 24) & 0xFF) == 127 || - (((host_addr >> 24) & 0xFF) == 169 && ((host_addr >> 16) & 0xFF) == 254)); + if (addr[0] == 0 || addr[0] == 127 || (addr[0] == 169 && addr[1] == 254)) + return false; + + return true; } -/** Comparison function for qsort to sort adapters by priority. - * Priority order: - * 1. Adapters on the best route (lowest priority number) - * 2. Lower interface metric +#if PUBNUB_USE_IPV6 +/** Check if @c IPv6 address is valid (not loopback, or zero). * - * @param a Left-hand @c adapter_with_metric which will be used in comparison. - * @param b Right-hand @c adapter_with_metric which will be used in comparison. - * @return @b -1 when the left-hand adapter has higher priority than the - * right-hand; @b 0 when both adapters have the same priority; @b 1 when the - * right-hand adapter has higher priority than the left-hand. + * @param addr IPv6 address to check (16 bytes, network order). + * @return @c true if valid, @c false otherwise. */ -static int compare_adapters_by_priority(const void* a, const void* b) +static bool is_valid_ipv6(const uint8_t addr[16]) { - const struct adapter_with_metric* aa = (const struct adapter_with_metric*)a; - const struct adapter_with_metric* bb = (const struct adapter_with_metric*)b; + bool loopback = addr[15] == 1; + bool all_zero = true; + + for (int i = 0; i < 16 && all_zero; i++) { + if (addr[i] != 0) + all_zero = false; + } - /* Best route adapters come first */ - if (aa->is_best_route && !bb->is_best_route) return -1; - if (!aa->is_best_route && bb->is_best_route) return 1; + for (int i = 0; i < 15 && loopback; i++) { + if (addr[i] != 0) + loopback = false; + } - /* Then sort by metric (lower is better) */ - if (aa->metric < bb->metric) return -1; - if (aa->metric > bb->metric) return 1; + if (all_zero || loopback || (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80)) + return false; - return 0; + return true; } +#endif /* PUBNUB_USE_IPV6 */ -/** Test if a DNS server is reachable by attempting a TCP connection to port 53. - * This is used to detect stale VPN connections where the adapter appears UP - * but the DNS server is actually unreachable. +/** Check address is valid (not loopback, APIPA, or zero) for specified family. * - * @param dns_addr_net DNS server address in network byte order. - * @return @c true if DNS server is reachable, @c false otherwise. + * @param family @c AF_INET or @c AF_INET6 family. + * @param addr Address to check (4 bytes for @c IPv4, 16 bytes for @c IPv6, + * network order). + * @return @c true if valid, @c false otherwise. */ -static bool pubnub_dns_server_reachable(DWORD dns_addr_net) +static bool is_valid_address(ULONG family, const uint8_t* addr) { - SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) return false; + if (family == AF_INET) + return is_valid_ipv4(addr); +#if PUBNUB_USE_IPV6 + if (family == AF_INET6) + return is_valid_ipv6(addr); +#endif /* PUBNUB_USE_IPV6 */ + return false; +} - /* Set socket to non-blocking mode for timeout control */ - u_long mode = 1; - if (ioctlsocket(sock, FIONBIO, &mode) != 0) { - closesocket(sock); +/** Check if adapter is suitable for DNS queries (unified for IPv4/IPv6). + * + * @param family Address family (@c AF_INET or @c AF_INET6). + * @param aa Adapter to check. + * @return @c true if suitable. + */ +static bool is_adapter_suitable(ULONG family, IP_ADAPTER_ADDRESSES* aa) +{ + if (!aa || aa->OperStatus != IfOperStatusUp + || aa->IfType == IF_TYPE_SOFTWARE_LOOPBACK) return false; - } - - struct sockaddr_in dns_addr; - memset(&dns_addr, 0, sizeof(dns_addr)); - dns_addr.sin_family = AF_INET; - dns_addr.sin_port = htons(53); - dns_addr.sin_addr.s_addr = dns_addr_net; - - int result = connect(sock, (struct sockaddr*)&dns_addr, sizeof(dns_addr)); - - if (result == SOCKET_ERROR) { - if (WSAGetLastError() != WSAEWOULDBLOCK) { - closesocket(sock); - return false; - } - - /* Wait for socket's in-progress connection complete with timeout. */ - struct timeval timeout; - timeout.tv_sec = DNS_CONNECTIVITY_TIMEOUT_MS / 1000; - timeout.tv_usec = (DNS_CONNECTIVITY_TIMEOUT_MS % 1000) * 1000; - - fd_set writefds, exceptfds; - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); - FD_SET(sock, &writefds); - FD_SET(sock, &exceptfds); - result = select(0, NULL, &writefds, &exceptfds, &timeout); + if (AF_INET == family && !(aa->Flags & IP_ADAPTER_IPV4_ENABLED)) + return false; - if (result == SOCKET_ERROR || result == 0 || - FD_ISSET(sock, &exceptfds) || !FD_ISSET(sock, &writefds)) { - closesocket(sock); - return false; +#if PUBNUB_USE_IPV6 + if (AF_INET6 == family && !(aa->Flags & IP_ADAPTER_IPV6_ENABLED)) + return false; +#endif /* PUBNUB_USE_IPV6 */ + + /* For IPv4: check if it has valid gateway */ + if (AF_INET == family) { + for (IP_ADAPTER_GATEWAY_ADDRESS* gw = aa->FirstGatewayAddress; gw != NULL; + gw = gw->Next) { + if (gw->Address.lpSockaddr + && gw->Address.lpSockaddr->sa_family == family) { + struct sockaddr_in* sin = + (struct sockaddr_in*)gw->Address.lpSockaddr; + const uint8_t* bytes = (uint8_t*)&sin->sin_addr.s_addr; + if (is_valid_address(AF_INET, bytes)) + return true; + } } - /* Ensure that connect didn't end up with an error. */ - int so_error = 0; - int len = sizeof(so_error); - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&so_error, &len) != 0 || - so_error != 0) { - closesocket(sock); + /* Accept physical Ethernet or Wi-Fi adapters with valid unicast address. + * This handles WSL2, Hyper-V, Docker scenarios where gateway info may + * be missing but the adapter is still functional for local DNS. + * + * Skip adapter in other case without unicast address verification. + */ + if (aa->IfType != IF_TYPE_ETHERNET_CSMACD + && aa->IfType != IF_TYPE_IEEE80211 && aa->IfType != 71) return false; - } } - closesocket(sock); - return true; -} - -/** Copy IPv4 address from network byte order DWORD to array, checking for - * duplicates. - * - * @param array Pointer to the array of @c pubnub_ipv4_address structures with - * previously discovered DNS server addresses. - * @param count Current number of entries in @b array. - * @param n_addr DNS server address in network byte order. - * @param out Target entry in @b array where @b n_addr should be copied (if not - * present yet in @b array). - * @return @c true if the provided address in network byte order was acceptable - * and copied to the list of discovered DNS server addresses, @c false otherwise. - */ -static bool pubnub_copy_ipv4_bytes_from_be_dword( - const struct pubnub_ipv4_address* array, - const size_t count, - DWORD n_addr, - unsigned char out[4]) -{ - DWORD host_addr = ntohl(n_addr); - unsigned char temp_ip[4]; - - temp_ip[0] = (unsigned char)((host_addr >> 24) & 0xFF); - temp_ip[1] = (unsigned char)((host_addr >> 16) & 0xFF); - temp_ip[2] = (unsigned char)((host_addr >> 8) & 0xFF); - temp_ip[3] = (unsigned char)(host_addr & 0xFF); + /* Must have at least one valid unicast address */ + /* For IPv4: required if no gateway, for IPv6: always required */ + for (IP_ADAPTER_UNICAST_ADDRESS* ua = aa->FirstUnicastAddress; ua != NULL; + ua = ua->Next) { + if (!ua->Address.lpSockaddr || ua->Address.lpSockaddr->sa_family != family) + continue; - /* Filter out invalid addresses */ - if (!pubnub_ipv4_address_valid(host_addr)) return false; + uint8_t* bytes = NULL; + if (AF_INET == family) { + struct sockaddr_in* sin = (struct sockaddr_in*)ua->Address.lpSockaddr; + bytes = (uint8_t*)&sin->sin_addr.s_addr; + } +#if PUBNUB_USE_IPV6 + else if (AF_INET6 == family) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ua->Address.lpSockaddr; + bytes = (uint8_t*)&sin6->sin6_addr; + } +#endif /* PUBNUB_USE_IPV6 */ - /* Check for duplicates */ - for (size_t i = 0; i < count; i++) { - if (memcmp(array[i].ipv4, temp_ip, 4) == 0) return false; + if (is_valid_address(family, bytes)) + return true; } - out[0] = temp_ip[0]; - out[1] = temp_ip[1]; - out[2] = temp_ip[2]; - out[3] = temp_ip[3]; - - return true; + return false; } -/** Get adapter addresses with retry logic to handle race conditions. +/** Retrieve adapter addresses for specified address family. * - * @param aa Pointer to the list of @c IP_ADAPTER_ADDRESSES structs. - * @return @c true if @p aa has been populated with active adapter, - * @c false otherwise. + * @param family Address family (@c AF_INET or @c AF_INET6). + * @return Pointer to adapter addresses on success, @c NULL on error. + * Caller must @c FREE the returned pointer. */ -static bool pubnub_adapter_addresses_get(IP_ADAPTER_ADDRESSES** aa) +static IP_ADAPTER_ADDRESSES* get_adapter_addresses(ULONG family) { - if (NULL == aa) return false; - - /* Free existing allocation if present */ - if (*aa) { - FREE(*aa); - *aa = NULL; - } - - ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_GATEWAYS; - ULONG buflen = 15000; /* Start with reasonable size to avoid extra call */ + IP_ADAPTER_ADDRESSES* addrs = NULL; + ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST + | GAA_FLAG_INCLUDE_GATEWAYS; + ULONG buflen = 15000; + int retries = 0; DWORD ret; - int retries = 0; - /* Retry loop to handle race conditions where adapters change between calls */ do { - IP_ADAPTER_ADDRESSES* buffer = (IP_ADAPTER_ADDRESSES*)MALLOC(buflen); - if (NULL == buffer) { - PUBNUB_LOG_ERROR("OOM allocating %lu bytes for GetAdaptersAddresses\n", - (unsigned long)buflen); - return false; + addrs = (IP_ADAPTER_ADDRESSES*)MALLOC(buflen); + if (!addrs) { + PUBNUB_LOG_ERROR( + "Failed to allocate %lu bytes for adapter info (family=%lu)\n", + (unsigned long)buflen, + family); + return NULL; } - ret = GetAdaptersAddresses(AF_INET, flags, NULL, buffer, &buflen); - - if (ret == NO_ERROR) { - *aa = buffer; - return true; - } + ret = GetAdaptersAddresses(family, flags, NULL, addrs, &buflen); - FREE(buffer); + if (ret == NO_ERROR) + break; - if (ret == ERROR_BUFFER_OVERFLOW) { - if (++retries >= MAX_ADAPTER_ADDRESS_RETRIES) { - PUBNUB_LOG_ERROR("GetAdaptersAddresses buffer overflow after " - "%d retries\n", retries); - return false; - } - /* `GetAdaptersAddresses` call failed but updated `buflen` value. */ - continue; + FREE(addrs); + addrs = NULL; + + if (ret != ERROR_BUFFER_OVERFLOW + || ++retries >= MAX_ADAPTER_ADDRESS_RETRIES) { + PUBNUB_LOG_ERROR("GetAdaptersAddresses(family=%lu) failed: %lu\n", + (unsigned long)family, + (unsigned long)ret); + return NULL; } + } while (ret == ERROR_BUFFER_OVERFLOW); - PUBNUB_LOG_ERROR("GetAdaptersAddresses failed: %lu\n", (unsigned long)ret); - return false; - - } while (ret == ERROR_BUFFER_OVERFLOW && retries < MAX_ADAPTER_ADDRESS_RETRIES); - - return false; + return addrs; } -/** Check if an adapter is suitable for DNS queries. - * An adapter is suitable if it is: - * - Operationally UP, AND - * - Not loopback or tunnel, AND - * - IPv4 enabled, AND - * - Has a valid gateway OR physical adapters with valid unicast address. + +#if PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT +/** Validate DNS server by sending a UDP probe packet. * - * @param aa Pointer to the adapter structure which should be checked. - * @return @c true if provided adapter fulfill requirements ("alive"), - * @c false otherwise. + * @param addr_family Address family (@c AF_INET or @c AF_INET6). + * @param dns_addr DNS server address (4 bytes for @c IPv4, 16 bytes for + * @c IPv6, network order). + * @param if_index Interface index to bind query to (0 for any). + * @return @c true if DNS server responds successfully. */ -static bool pubnub_adapter_is_suitable(IP_ADAPTER_ADDRESSES* aa) +static bool validate_dns_server_udp(int addr_family, + const uint8_t dns_addr[], + DWORD if_index) { - if (NULL == aa) return false; + /* Minimal DNS query packet */ + const uint8_t query[12] = { 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + SOCKET sock = socket(addr_family, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) { + PUBNUB_LOG_ERROR("Failed to create validation socket: %d\n", + WSAGetLastError()); + return false; + } - if (aa->OperStatus != IfOperStatusUp || - aa->IfType == IF_TYPE_SOFTWARE_LOOPBACK || - aa->IfType == IF_TYPE_TUNNEL || - !(aa->Flags & IP_ADAPTER_IPV4_ENABLED)) { + /* Set receive timeout */ + DWORD timeout_ms = PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_ms, sizeof(timeout_ms)) + != 0) { + PUBNUB_LOG_WARNING("Failed to set socket timeout: %d\n", WSAGetLastError()); + } + + /* Prepare DNS server address */ + struct sockaddr_storage dns_server; + ZeroMemory(&dns_server, sizeof(dns_server)); + int addr_len; + if (addr_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)&dns_server; + sin->sin_family = AF_INET; + sin->sin_port = htons(53); + memcpy(&sin->sin_addr.s_addr, dns_addr, 4); + addr_len = sizeof(struct sockaddr_in); + PUBNUB_LOG_TRACE("Validating DNS server: %u.%u.%u.%u\n", + dns_addr[0], + dns_addr[1], + dns_addr[2], + dns_addr[3]); + } +#if PUBNUB_USE_IPV6 + else if (addr_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&dns_server; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(53); + memcpy(sin6->sin6_addr.s6_addr, dns_addr, 16); + addr_len = sizeof(struct sockaddr_in6); + PUBNUB_LOG_TRACE( + "Validating DNS server: %02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + dns_addr[0], + dns_addr[1], + dns_addr[2], + dns_addr[3], + dns_addr[4], + dns_addr[5], + dns_addr[6], + dns_addr[7], + dns_addr[8], + dns_addr[9], + dns_addr[10], + dns_addr[11], + dns_addr[12], + dns_addr[13], + dns_addr[14], + dns_addr[15]); + } +#endif + else { + PUBNUB_LOG_ERROR("Invalid address family: %d\n", addr_family); + socket_close(sock); return false; } - /* Check for valid gateway. */ - for (IP_ADAPTER_GATEWAY_ADDRESS* gw = aa->FirstGatewayAddress; gw != NULL; gw = gw->Next) { - if (gw->Address.lpSockaddr && gw->Address.lpSockaddr->sa_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)gw->Address.lpSockaddr; - if (pubnub_ipv4_address_valid(ntohl(sin->sin_addr.S_un.S_addr))) - return true; - } + /* Send minimal DNS query to probe server */ + int sent = sendto( + sock, (char*)query, sizeof(query), 0, (struct sockaddr*)&dns_server, addr_len); + + if (sent <= 0) { + PUBNUB_LOG_DEBUG("DNS validation sendto() failed: %d\n", WSAGetLastError()); + socket_close(sock); + return false; } - /* Fallback: Accept physical Ethernet or WiFi adapters with valid unicast address - * This handles WSL2, Hyper-V, Docker scenarios where gateway info may be missing - * but the adapter is still functional for local DNS. - */ - if (aa->IfType == IF_TYPE_ETHERNET_CSMACD || - aa->IfType == IF_TYPE_IEEE80211 || - aa->IfType == 71) { - - for (IP_ADAPTER_UNICAST_ADDRESS* ua = aa->FirstUnicastAddress; ua != NULL; ua = ua->Next) { - if (ua->Address.lpSockaddr && ua->Address.lpSockaddr->sa_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)ua->Address.lpSockaddr; - if (pubnub_ipv4_address_valid(ntohl(sin->sin_addr.S_un.S_addr))) - return true; - } - } + /* Try to receive any response (even error response means server is alive) */ + uint8_t response[512]; + int recvd = recvfrom(sock, (char*)response, sizeof(response), 0, NULL, NULL); + + if (recvd > 0) { + PUBNUB_LOG_TRACE("DNS server responded (%d bytes) - validation OK\n", recvd); + socket_close(sock); + return true; + } + + int err = WSAGetLastError(); + if (err == WSAETIMEDOUT) { + PUBNUB_LOG_DEBUG("DNS validation timeout - server unreachable\n"); + } + else { + PUBNUB_LOG_DEBUG("DNS validation recvfrom() failed: %d\n", err); } + socket_close(sock); return false; } +#endif /* PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT */ -/** Get the best interface for reaching the public Internet (for DNS queries). - * Detect the index of the adapter that is used by Windows for request routing. +/** Get best interface index for public Internet routing using GetBestRoute2. * - * @return Non-zero index of the adapter that can route to the public Internet, - * zero otherwise. + * @param family Address family (@c AF_INET or @c AF_INET6). + * @return Interface index, or @c 0 if index cannot determine. */ -static DWORD pubnub_get_best_interface_index(void) +static DWORD get_best_interface_index(ADDRESS_FAMILY family) { - DWORD best_if_index = 0; - struct in_addr dest_addr; + SOCKADDR_INET dest_addr; + SOCKADDR_INET source_addr; + ZeroMemory(&dest_addr, sizeof(dest_addr)); + ZeroMemory(&source_addr, sizeof(source_addr)); + dest_addr.si_family = family; + + if (family == AF_INET) { + if (inet_pton(AF_INET, "8.8.8.8", &dest_addr.Ipv4.sin_addr) != 1) + return 0; + } +#if PUBNUB_USE_IPV6 + else if (family == AF_INET6) { + if (inet_pton(AF_INET6, "2001:4860:4860::8888", &dest_addr.Ipv6.sin6_addr) + != 1) + return 0; + } +#endif + else { + return 0; + } - if (inet_pton(AF_INET, PUBLIC_DNS_TEST_IP_1, &dest_addr) == 1) { - if (GetBestInterface(dest_addr.S_un.S_addr, &best_if_index) == NO_ERROR && best_if_index != 0) { - return best_if_index; - } + MIB_IPFORWARD_ROW2 route; + DWORD ret = GetBestRoute2(NULL, 0, NULL, &dest_addr, 0, &route, &source_addr); + + if (ret != NO_ERROR) { + PUBNUB_LOG_DEBUG("GetBestRoute2(family=%lu) failed: %lu\n", + family, + (unsigned long)ret); + return 0; } - if (inet_pton(AF_INET, PUBLIC_DNS_TEST_IP_2, &dest_addr) == 1) { - if (GetBestInterface(dest_addr.S_un.S_addr, &best_if_index) == NO_ERROR && best_if_index != 0) { - return best_if_index; - } + NET_LUID luid = route.InterfaceLuid; + NET_IFINDEX if_index; + ret = ConvertInterfaceLuidToIndex(&luid, &if_index); + + if (ret != NO_ERROR) { + PUBNUB_LOG_DEBUG( + "ConvertInterfaceLuidToIndex(family=%lu) failed: %lu\n", + family, + (unsigned long)ret); + return 0; } - return 0; + PUBNUB_LOG_TRACE("Best interface index for %s: %lu\n", + family == AF_INET ? "IPv4" : "IPv6", + (unsigned long)if_index); + + return (DWORD)if_index; } -/** Check if DNS server is reachable from the adapter. -* A DNS server is reachable if: -* 1. It's on the same subnet as one of the adapter's unicast addresses, OR -* 2. The adapter has a valid gateway (can route to it) -* -* @param dns_addr_net DNS address in network byte order. -* @param aa Pointer to the adapter that should be tested for subnet unicast -* match with provided DNS server address. -* @return @c true if the adapter can route the request to the provided DNS -* server address, @c false otherwise. -*/ -static bool pubnub_dns_reachable_from_adapter(DWORD dns_addr_net, IP_ADAPTER_ADDRESSES* aa) + +/** Comparison function for qsort - prioritize adapters. */ +static int compare_adapter_priority(const void* a, const void* b) { - DWORD dns_addr = ntohl(dns_addr_net); + const struct adapter_info* aa = (const struct adapter_info*)a; + const struct adapter_info* bb = (const struct adapter_info*)b; - /* Check if DNS is on the same subnet as any unicast address */ - for (IP_ADAPTER_UNICAST_ADDRESS* ua = aa->FirstUnicastAddress; - ua != NULL; - ua = ua->Next) { - if (ua->Address.lpSockaddr && ua->Address.lpSockaddr->sa_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)ua->Address.lpSockaddr; - DWORD if_addr = ntohl(sin->sin_addr.S_un.S_addr); + /* Best route adapters first */ + if (aa->is_best_route && !bb->is_best_route) + return -1; + if (!aa->is_best_route && bb->is_best_route) + return 1; - /* Get subnet mask from prefix length */ - ULONG prefix_len = ua->OnLinkPrefixLength; - if (prefix_len > 0 && prefix_len <= 32) { - DWORD mask = (prefix_len == 32) ? 0xFFFFFFFF : ~((1UL << (32 - prefix_len)) - 1); + /* Then by metric (lower is better) */ + if (aa->metric < bb->metric) + return -1; + if (aa->metric > bb->metric) + return 1; - /* Check if DNS is on same subnet */ - if ((dns_addr & mask) == (if_addr & mask)) { - return true; - } - } - } - } - - /* Check if adapter has a valid gateway (can route off-subnet) */ - for (IP_ADAPTER_GATEWAY_ADDRESS* gw = aa->FirstGatewayAddress; gw != NULL; gw = gw->Next) { - if (gw->Address.lpSockaddr && gw->Address.lpSockaddr->sa_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)gw->Address.lpSockaddr; - if (pubnub_ipv4_address_valid(ntohl(sin->sin_addr.S_un.S_addr))) - return true; - } - } - - return false; + return 0; } -/** Retrieve DNS servers from network adapters, prioritized by routing metrics. -* - * @param o_ipv4 Pointer to the array of @c pubnub_ipv4_address structures with - * previously discovered DNS server addresses. - * @param n Maximum number of discovered DNS server addresses. - * @return Number of discovered system DNS servers, @c zero otherwise. +/** Process provided list of adapter addresses to find useful DNS server + * addresses. + * + * @param family Adapter address family (@c AF_INET or @c AF_INET6). + * @param addrs List of @c family adapter addresses. + * @param n Maximum number of required DNS server addresses. + * @param[out] count Number of verified adapters with DNS servers. + * @return List of @c adapter_info with suitable adapters. */ -int pubnub_dns_read_system_servers_ipv4( - struct pubnub_ipv4_address* o_ipv4, - size_t n) +struct adapter_info* pubnub_dns_read_system_servers(ULONG family, + IP_ADAPTER_ADDRESSES* addrs, + const size_t n, + size_t* count) { - if (!o_ipv4 || n == 0) return 0; - - IP_ADAPTER_ADDRESSES* addrs = NULL; - if (!pubnub_adapter_addresses_get(&addrs)) return 0; + if (!addrs) + return NULL; - /* Get the best interface index for routing to public Internet. */ - DWORD best_if_index = pubnub_get_best_interface_index(); - - /* Count suitable adapters. */ size_t adapter_count = 0; for (IP_ADAPTER_ADDRESSES* aa = addrs; aa != NULL; aa = aa->Next) { - if (pubnub_adapter_is_suitable(aa)) adapter_count++; + adapter_count++; } if (adapter_count == 0) { - FREE(addrs); - return 0; + PUBNUB_LOG_WARNING("No suitable adapters found (family=%lu)\n", family); + *count = 0; + return NULL; } - /* Build array of adapters with their metrics for sorting. */ - struct adapter_with_metric* adapter_list = - (struct adapter_with_metric*)MALLOC(adapter_count * sizeof(struct adapter_with_metric)); - + struct adapter_info* adapter_list = + (struct adapter_info*)MALLOC(adapter_count * sizeof(struct adapter_info)); if (!adapter_list) { - FREE(addrs); - return 0; + *count = 0; + return NULL; } - size_t idx = 0; + DWORD best_if_index = get_best_interface_index(family); + size_t idx = 0; for (IP_ADAPTER_ADDRESSES* aa = addrs; aa != NULL; aa = aa->Next) { - if (pubnub_adapter_is_suitable(aa)) { + if (is_adapter_suitable(family, aa)) { adapter_list[idx].adapter = aa; - adapter_list[idx].metric = aa->Ipv4Metric; + adapter_list[idx].metric = + AF_INET == family ? aa->Ipv4Metric : aa->Ipv6Metric; adapter_list[idx].is_best_route = (best_if_index != 0 && aa->IfIndex == best_if_index); + adapter_list[idx].dns_count = 0; idx++; } } - /* Sort adapters by priority. */ - qsort(adapter_list, adapter_count, sizeof(struct adapter_with_metric), - compare_adapters_by_priority); + adapter_count = idx; + if (adapter_count == 0) { + PUBNUB_LOG_WARNING("No suitable adapters found (family=%lu)\n", family); + FREE(adapter_list); + *count = 0; + return NULL; + } - /* Extract DNS servers from adapters in priority order. */ - unsigned dns_count = 0; - for (size_t i = 0; i < adapter_count && dns_count < n; i++) { + qsort(adapter_list, + adapter_count, + sizeof(struct adapter_info), + compare_adapter_priority); + + size_t total_dns_count = 0; + for (size_t i = 0; i < adapter_count && total_dns_count < n; i++) { IP_ADAPTER_ADDRESSES* aa = adapter_list[i].adapter; - PUBNUB_LOG_TRACE("Processing adapter: Index=%lu, Metric=%lu, IsBestRoute=%d\n", - (unsigned long)aa->IfIndex, - (unsigned long)adapter_list[i].metric, - adapter_list[i].is_best_route); + PUBNUB_LOG_TRACE( + "Processing adapter idx=%lu metric=%lu best=%d (family=%lu)\n", + (unsigned long)aa->IfIndex, + (unsigned long)adapter_list[i].metric, + adapter_list[i].is_best_route, + family); + + for (IP_ADAPTER_DNS_SERVER_ADDRESS* ds = aa->FirstDnsServerAddress; + ds && adapter_list[i].dns_count < MAX_DNS_SERVERS_PER_ADAPTER + && total_dns_count < n; + ds = ds->Next) { - for (IP_ADAPTER_DNS_SERVER_ADDRESS* ds = aa->FirstDnsServerAddress; ds && dns_count < n; ds = ds->Next) { - if (!ds->Address.lpSockaddr || ds->Address.lpSockaddr->sa_family != AF_INET) + if (!ds->Address.lpSockaddr) continue; - const struct sockaddr_in* sin = (const struct sockaddr_in*)ds->Address.lpSockaddr; - DWORD net_addr = sin->sin_addr.S_un.S_addr; - if (net_addr == 0) continue; + ULONG dns_family = ds->Address.lpSockaddr->sa_family; - /* Verifying adapter can be used to reach DNS server. */ - if (!pubnub_dns_reachable_from_adapter(net_addr, aa)) { - PUBNUB_LOG_DEBUG("Skipping DNS server - not reachable from " - "adapter\n"); + /* IPv4 can't handle IPv6 addresses and should skip such. */ + if (AF_INET == family && dns_family != family) continue; + + uint8_t* dns_bytes = NULL; + + if (AF_INET == dns_family) { + struct sockaddr_in* sin = + (struct sockaddr_in*)ds->Address.lpSockaddr; + dns_bytes = (uint8_t*)&sin->sin_addr.s_addr; + } +#if PUBNUB_USE_IPV6 + else if (AF_INET6 == dns_family) { + struct sockaddr_in6* sin6 = + (struct sockaddr_in6*)ds->Address.lpSockaddr; + dns_bytes = (uint8_t*)&sin6->sin6_addr; } +#endif /* PUBNUB_USE_IPV6 */ - /* Verify DNS server is actually reachable via TCP connection test. - * This detects stale VPN connections where the adapter appears UP but - * the DNS server is unreachable. This is critical because GetBestInterface - * may return a stale VPN adapter if Windows routing table hasn't been updated. - * We test ALL adapters to catch this scenario. - */ - if (!pubnub_dns_server_reachable(net_addr)) { - PUBNUB_LOG_WARNING("Skipping DNS server - TCP connection test " - "failed (possibly stale VPN)\n"); + if (!dns_bytes || !is_valid_address(dns_family, dns_bytes)) continue; + + /* Check for duplicates across all already verified DNS servers */ + bool is_duplicate = false; + for (size_t j = 0; j <= i; j++) { + for (size_t k = 0; k < adapter_list[j].dns_count; k++) { + struct sockaddr_storage* stored = + &adapter_list[j].dns_servers[k]; + if (AF_INET == dns_family && stored->ss_family == AF_INET) { + struct sockaddr_in* stored_sin = (struct sockaddr_in*)stored; + if (memcmp(&stored_sin->sin_addr.s_addr, dns_bytes, 4) == 0) { + is_duplicate = true; + break; + } + } +#if PUBNUB_USE_IPV6 + else if (AF_INET6 == dns_family + && stored->ss_family == AF_INET6) { + struct sockaddr_in6* stored_sin6 = + (struct sockaddr_in6*)stored; + if (memcmp(&stored_sin6->sin6_addr, dns_bytes, 16) == 0) { + is_duplicate = true; + break; + } + } +#endif + } + if (is_duplicate) + break; + } + + /* Skipping already added DNS server. */ + if (is_duplicate) + continue; + +#if PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT + if (!validate_dns_server_udp(dns_family, dns_bytes, aa->IfIndex)) { + PUBNUB_LOG_WARNING( + "Skipping DNS - validation failed (family=%lu)\n", family); + continue; + } +#endif /* PUBNUB_DNS_SERVERS_VALIDATION_TIMEOUT */ + + /* Store the verified DNS server */ + struct sockaddr_storage* verified = + &adapter_list[i].dns_servers[adapter_list[i].dns_count]; + ZeroMemory(verified, sizeof(struct sockaddr_storage)); + + if (AF_INET == dns_family) { + struct sockaddr_in* sin_out = (struct sockaddr_in*)verified; + sin_out->sin_family = AF_INET; + sin_out->sin_port = htons(53); + memcpy(&sin_out->sin_addr.s_addr, dns_bytes, 4); } +#if PUBNUB_USE_IPV6 + else if (AF_INET6 == dns_family) { + struct sockaddr_in6* sin6_out = (struct sockaddr_in6*)verified; + sin6_out->sin6_family = AF_INET6; + sin6_out->sin6_port = htons(53); + memcpy(&sin6_out->sin6_addr, dns_bytes, 16); + } +#endif /* PUBNUB_USE_IPV6 */ + + adapter_list[i].dns_count++; + total_dns_count++; + } + } + + *count = adapter_count; + return adapter_list; +} + + +/** + * Read system DNS servers for IPv4. + * + * Thread-safe implementation using GetAdaptersAddresses with optional + * DNS validation to filter out stale VPN servers. + * + * @param o_ipv4 Output array (must be allocated by caller). + * @param n Maximum number of DNS servers to return. + * @return Number of DNS servers found, or -1 on error. + */ +int pubnub_dns_read_system_servers_ipv4(struct pubnub_ipv4_address* o_ipv4, size_t n) +{ + PUBNUB_ASSERT_OPT(o_ipv4 != NULL); + PUBNUB_ASSERT_OPT(n > 0); + + if (!o_ipv4 || n == 0) + return -1; + + IP_ADAPTER_ADDRESSES* addrs = get_adapter_addresses(AF_INET); + if (!addrs) + return -1; + + size_t count = 0; + struct adapter_info* adapter_list = + pubnub_dns_read_system_servers(AF_INET, addrs, n, &count); + if (!adapter_list || 0 == count) { + FREE(addrs); + if (adapter_list) + FREE(adapter_list); + return -1; + } + + unsigned dns_count = 0; + for (size_t i = 0; i < count; i++) { + if (!adapter_list[i].dns_count) + continue; + for (size_t d = 0; d < adapter_list[i].dns_count; d++) { + struct sockaddr_in* sin = + (struct sockaddr_in*)&adapter_list[i].dns_servers[d]; + uint8_t* dns_bytes = (uint8_t*)&sin->sin_addr.s_addr; + memcpy(o_ipv4[dns_count].ipv4, dns_bytes, 4); + + PUBNUB_LOG_TRACE("Added IPv4 DNS: %u.%u.%u.%u\n", + dns_bytes[0], + dns_bytes[1], + dns_bytes[2], + dns_bytes[3]); + dns_count++; + } + } - /* Add to output if not duplicate */ - if (pubnub_copy_ipv4_bytes_from_be_dword(o_ipv4, dns_count, net_addr, o_ipv4[dns_count].ipv4)) { - PUBNUB_LOG_TRACE("Added DNS server from adapter index %lu\n", - (unsigned long)aa->IfIndex); - ++dns_count; + FREE(adapter_list); + FREE(addrs); + + if (dns_count == 0) { + PUBNUB_LOG_ERROR("No valid DNS servers found\n"); + return -1; + } + + PUBNUB_LOG_DEBUG("Discovered %u DNS server(s)\n", dns_count); + return (int)dns_count; +} + + +#if PUBNUB_USE_IPV6 +int pubnub_dns_read_system_servers_ipv6(struct pubnub_ipv6_address* o_ipv6, size_t n) +{ + PUBNUB_ASSERT_OPT(o_ipv6 != NULL); + PUBNUB_ASSERT_OPT(n > 0); + + if (!o_ipv6 || n == 0) + return -1; + + IP_ADAPTER_ADDRESSES* addrs = get_adapter_addresses(AF_INET6); + if (!addrs) + return -1; + + size_t count = 0; + struct adapter_info* adapter_list = + pubnub_dns_read_system_servers(AF_INET6, addrs, n, &count); + if (!adapter_list || 0 == count) { + FREE(addrs); + if (adapter_list) + FREE(adapter_list); + return -1; + } + + bool has_ipv4_addresses = false; + unsigned dns_count = 0; + for (size_t i = 0; i < count; i++) { + if (!adapter_list[i].dns_count) + continue; + for (size_t d = 0; d < adapter_list[i].dns_count; d++) { + struct sockaddr_storage* stored = &adapter_list[i].dns_servers[d]; + uint8_t dns_bytes_buffer[16]; + uint8_t* dns_bytes = NULL; + + if (AF_INET == stored->ss_family) { + struct sockaddr_in* sin = (struct sockaddr_in*)stored; + memset(dns_bytes_buffer, 0, 10); + dns_bytes_buffer[10] = 0xff; + dns_bytes_buffer[11] = 0xff; + memcpy(&dns_bytes_buffer[12], &sin->sin_addr.s_addr, 4); + dns_bytes = dns_bytes_buffer; + has_ipv4_addresses = true; + } + else { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)stored; + dns_bytes = (uint8_t*)&sin6->sin6_addr; } + memcpy(o_ipv6[dns_count].ipv6, dns_bytes, 16); + + char str[INET6_ADDRSTRLEN]; + PUBNUB_LOG_TRACE( + "Added %s DNS (%s).\n", + AF_INET == stored->ss_family ? "IPv4-mapped IPv6" : "IPv6", + inet_ntop(AF_INET6, dns_bytes, str, INET6_ADDRSTRLEN)); + dns_count++; } } FREE(adapter_list); FREE(addrs); + if (dns_count == 0) { + PUBNUB_LOG_WARNING("No IPv6 DNS servers found\n"); + return 0; + } + + PUBNUB_LOG_DEBUG("Discovered %u %s DNS server(s)\n", + dns_count, + has_ipv4_addresses ? "IPv4-mapped IPv6/IPv6" : "IPv6"); return (int)dns_count; } +#endif /* PUBNUB_USE_IPV6 */ diff --git a/windows/pubnub_get_native_socket.c b/windows/pubnub_get_native_socket.c index 07dc1cd5..d518c872 100644 --- a/windows/pubnub_get_native_socket.c +++ b/windows/pubnub_get_native_socket.c @@ -1,5 +1,9 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if PUBNUB_USE_SSL +#include "openssl/pubnub_internal.h" +#else #include "pubnub_internal.h" +#endif #include "pubnub_get_native_socket.h" #include "core/pubnub_log.h" diff --git a/windows/pubnub_ntf_callback_windows.c b/windows/pubnub_ntf_callback_windows.c index c70e4970..9e33d3bb 100644 --- a/windows/pubnub_ntf_callback_windows.c +++ b/windows/pubnub_ntf_callback_windows.c @@ -1,5 +1,9 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if PUBNUB_USE_SSL +#include "openssl/pubnub_internal.h" +#else #include "pubnub_internal.h" +#endif #include "core/pubnub_ntf_callback.h" #include "pbtimespec_elapsed_ms.h" diff --git a/windows/pubnub_set_proxy_from_system_windows.c b/windows/pubnub_set_proxy_from_system_windows.c index 9e8839e9..b02c71b9 100644 --- a/windows/pubnub_set_proxy_from_system_windows.c +++ b/windows/pubnub_set_proxy_from_system_windows.c @@ -1,7 +1,14 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ -#include "core/pubnub_proxy.h" +/* When building with OpenSSL, we need the OpenSSL version of pubnub_internal.h + because it has a different struct pubnub_pal definition. The build system + should define PUBNUB_USE_SSL=1 for OpenSSL builds. */ +#if PUBNUB_USE_SSL +#include "openssl/pubnub_internal.h" +#else #include "pubnub_internal.h" +#endif +#include "core/pubnub_proxy.h" #include "core/pubnub_log.h" #include "core/pubnub_assert.h" @@ -45,40 +52,93 @@ static int set_from_url4proxy(pubnub_t *p, wchar_t *url4proxy) PUBNUB_LOG_TRACE("set_from_url4proxy(url4proxy=%S)\n", url4proxy); for (it = url4proxy; *end != '\0'; it = end + 1) { wchar_t *port; - size_t separator_position = wcscspn(it, L"; "); + wchar_t *hostname_start; + wchar_t *hostname_end; + const size_t separator_position = wcscspn(it, L"; "); end = it + separator_position; - port = wmemchr(it, L':', end - it); - if (port != NULL) { - if (port[1] == L'/') { - it = port + 3; - port = wmemchr(it, L':', end - it); - if (NULL == port) { - port = end; - } + + // Check for protocol prefix format (e.g., "http=127.0.0.1:8888" or "https=[::1]:8888") + wchar_t *equals_sign = wmemchr(it, L'=', end - it); + if (equals_sign != NULL) { + // Skip the protocol prefix (e.g., "http=" or "https=") + it = equals_sign + 1; + } + + // Skipping schema (if 'http://' or 'https://' is present). + if (end - it > 3 && it[0] != L'[') { + wchar_t *scheme_end = wmemchr(it, L':', end - it); + if (scheme_end != NULL && scheme_end[1] == L'/') + it = scheme_end + 3; + } + + if (it[0] == L'[') { + // IPv6 format: [2001:db8::1]:8080 or [2001:db8::1] + hostname_start = it + 1; + hostname_end = wmemchr(hostname_start, L']', end - hostname_start); + + if (hostname_end == NULL) { + PUBNUB_LOG_WARNING("Malformed IPv6 proxy address (missing ']'): %S\n", it); + continue; + } + + if (hostname_end < end && hostname_end[1] == L':') + port = hostname_end + 1; + else + port = end; + } else { + // IPv4 or hostname format: 192.168.1.1:8080 or test.proxy.com:3128 + hostname_start = it; + port = NULL; + + for (wchar_t *p_scan = it; p_scan < end; p_scan++) { + if (*p_scan == L':') + port = p_scan; } + + hostname_end = (port != NULL) ? port : end; } - else { - port = end; + + int bytes_written = WideCharToMultiByte(CP_UTF8, + 0, + hostname_start, + hostname_end - hostname_start, + p->proxy_hostname, + sizeof(p->proxy_hostname) - 1, + NULL, + NULL); + + if (bytes_written == 0) { + PUBNUB_LOG_WARNING("WideCharToMultiByte failed for proxy hostname: %lu\n", + GetLastError()); + continue; } - if (0 == WideCharToMultiByte( - CP_UTF8, 0, it, port - it, p->proxy_hostname, - sizeof p->proxy_hostname / sizeof p->proxy_hostname[0], - NULL, NULL)) { + if (bytes_written >= sizeof(p->proxy_hostname)) { + PUBNUB_LOG_WARNING("Proxy hostname too long (needs %d bytes)\n", + bytes_written); continue; } - p->proxy_hostname[port - it] = '\0'; + + p->proxy_hostname[bytes_written] = '\0'; + PUBNUB_LOG_TRACE("Set proxy_hostname = %s\n", p->proxy_hostname); - if (port != end) { + + if (port != NULL && port < end) { + const wchar_t saved_char = *end; *end = L'\0'; p->proxy_port = (uint16_t)wcstol(port + 1, NULL, 10); + *end = saved_char; + PUBNUB_LOG_TRACE("Set proxy_port = %hu\n", p->proxy_port); - if (0 == p->proxy_port) { + if (0 == p->proxy_port) continue; - } } else { +#if PUBNUB_USE_SSL + p->proxy_port = p->options.useSSL ? 443 : 80; +#else /* PUBNUB_USE_SSL */ p->proxy_port = 80; +#endif /* !PUBNUB_USE_SSL */ } return 0; @@ -90,6 +150,17 @@ static int set_from_url4proxy(pubnub_t *p, wchar_t *url4proxy) int pubnub_set_proxy_from_system(pubnub_t *p, enum pubnub_proxy_type protocol) { + PUBNUB_ASSERT_OPT(p != NULL); + + switch (protocol) { + case pbproxyHTTP_GET: + case pbproxyHTTP_CONNECT: + break; + default: + /* other proxy protocols not yet supported */ + return -1; + } + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_cfg = {0}; WINHTTP_AUTOPROXY_OPTIONS autoproxy_opts = {0}; WINHTTP_PROXY_INFO proxy_info = {0}; @@ -99,7 +170,7 @@ int pubnub_set_proxy_from_system(pubnub_t *p, enum pubnub_proxy_type protocol) if (WinHttpGetIEProxyConfigForCurrentUser(&ie_proxy_cfg)) { if (ie_proxy_cfg.lpszProxy != NULL) { - PUBNUB_LOG_INFO("Found proxy in Registry: %S", + PUBNUB_LOG_INFO("Found proxy in Registry: %S\n", ie_proxy_cfg.lpszProxy); url4proxy = ie_proxy_cfg.lpszProxy; /* This may be overriden if auto-detect is also on */ @@ -116,15 +187,34 @@ int pubnub_set_proxy_from_system(pubnub_t *p, enum pubnub_proxy_type protocol) } } + pubnub_mutex_lock(p->monitor); if (use_auto_proxy) { char const *origin = PUBNUB_ORIGIN_SETTABLE ? p->origin : PUBNUB_ORIGIN; - wchar_t wide_origin[PUBNUB_MAX_PROXY_HOSTNAME_LENGTH + 1]; + char url_for_proxy[256]; + wchar_t wide_origin[256]; HINTERNET winhttp; + int url_len; + +#if PUBNUB_USE_SSL + char const *scheme = p->options.useSSL ? "https" : "http"; +#else /* PUBNUB_USE_SSL */ + char const *scheme = "http"; +#endif /* !PUBNUB_USE_SSL */ + + url_len = snprintf(url_for_proxy, sizeof(url_for_proxy), + "%s://%s", scheme, origin); + if (url_len < 0 || url_len >= sizeof(url_for_proxy)) { + PUBNUB_LOG_ERROR("Origin URL too long: '%s'\n", origin); + pubnub_mutex_unlock(p->monitor); + return -1; + } if (0 == - MultiByteToWideChar(CP_UTF8, 0, origin, -1, wide_origin, + MultiByteToWideChar(CP_UTF8, 0, url_for_proxy, -1, wide_origin, sizeof wide_origin / sizeof wide_origin[0])) { - PUBNUB_LOG_ERROR("Origin '%s' to wide string failed\n", origin); + PUBNUB_LOG_ERROR("Origin '%s' to wide string failed: %lu\n", + url_for_proxy, GetLastError()); + pubnub_mutex_unlock(p->monitor); return -1; } winhttp = WinHttpOpen(L"C-core", WINHTTP_ACCESS_TYPE_NO_PROXY, @@ -165,8 +255,32 @@ int pubnub_set_proxy_from_system(pubnub_t *p, enum pubnub_proxy_type protocol) WATCH_INT(rslt); if (0 == rslt) { p->proxy_type = protocol; +#if defined(PUBNUB_CALLBACK_API) +#if defined(PUBNUB_NTF_RUNTIME_SELECTION) + if (PNA_CALLBACK == p->api_policy) { +#endif + /* If we haven't got numerical address for proxy we'll have to do DNS resolution(from proxy + host name) later on, but in order to do that we have to have all proxy addresses(on the + given context) set to zeros. + */ + if (0 != pubnub_parse_ipv4_addr(p->proxy_hostname, &(p->proxy_ipv4_address))) { + memset(&(p->proxy_ipv4_address), 0, sizeof p->proxy_ipv4_address); +#if PUBNUB_USE_IPV6 + if (0 != pubnub_parse_ipv6_addr(p->proxy_hostname, &(p->proxy_ipv6_address))) { + memset(&(p->proxy_ipv6_address), 0, sizeof p->proxy_ipv6_address); + } +#endif + } +#if PUBNUB_USE_MULTIPLE_ADDRESSES + pbpal_multiple_addresses_reset_counters(&p->spare_addresses); +#endif +#if defined(PUBNUB_NTF_RUNTIME_SELECTION) + } /* if (PNA_CALLBACK == p->api_policy) */ +#endif +#endif /* defined(PUBNUB_CALLBACK_API) */ } free_winhttp_stuff(&ie_proxy_cfg, &proxy_info); + pubnub_mutex_unlock(p->monitor); return rslt; }