1/*
2 * endpoint.c
3 *
4 * Copyright (C) 2016 Aerospike, Inc.
5 *
6 * Portions may be licensed to Aerospike, Inc. under one or more contributor
7 * license agreements.
8 *
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU Affero General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option) any
12 * later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see http://www.gnu.org/licenses/
21 */
22
23#include "fabric/endpoint.h"
24
25#include <stdbool.h>
26#include <stddef.h>
27#include <stdint.h>
28#include <stdio.h>
29#include <stdlib.h>
30
31#include "citrusleaf/alloc.h"
32
33#include "fault.h"
34#include "socket.h"
35
36#include "base/cfg.h"
37
38/*----------------------------------------------------------------------------
39 * Private internal data structures.
40 *----------------------------------------------------------------------------*/
41typedef struct as_endpoint_collect_udata_s
42{
43 /**
44 * Collected endpoint pointers.
45 */
46 const as_endpoint** endpoints;
47
48 /**
49 * Collected endpoint count.
50 */
51 uint32_t collected_count;
52} as_endpoint_collect_udata;
53
54typedef struct as_endpoint_to_string_udata_s
55{
56 /**
57 * Current write pointer.
58 */
59 char* write_ptr;
60
61 /**
62 * buffer remaining capacity.
63 */
64 size_t buffer_remaining;
65
66 /**
67 * Number of endpoints converted.
68 */
69 uint32_t endpoints_converted;
70
71 /**
72 * Capabilities of endpoint.
73 */
74 uint8_t capabilities;
75
76 /**
77 * Capability mask. Set to 0 to match all the endpoints.
78 */
79 uint8_t capability_mask;
80} as_endpoint_to_string_udata;
81
82typedef struct as_endpoint_list_overlap_udata_s
83{
84 /**
85 * Indicates if there was an overlap.
86 */
87 bool overlapped;
88
89 /**
90 * Indicates if endpoint capabilities should be ignored.
91 */
92 bool ignore_capabilities;
93
94 /**
95 * The other list to compare.
96 */
97 const as_endpoint_list* other;
98} as_endpoint_list_overlap_udata;
99
100typedef struct as_endpoint_list_endpoint_find_udata_s
101{
102 /**
103 * Indicates if there was an overlap.
104 */
105 bool match_found;
106
107 /**
108 * Indicates if endpoint capabilities should be ignored.
109 */
110 bool ignore_capabilities;
111
112 /**
113 * The other list to compare.
114 */
115 const as_endpoint* to_find;
116} as_endpoint_list_endpoint_find_udata;
117
118/*----------------------------------------------------------------------------
119 * Private internal function forward declarations.
120 *----------------------------------------------------------------------------*/
121static bool endpoint_addr_type_is_valid(uint8_t type);
122static size_t endpoint_addr_binary_size(uint8_t type);
123static size_t endpoint_sizeof_by_addr_type(uint8_t addr_type);
124static as_endpoint* endpoint_allocate(uint8_t addr_type);
125static void endpoint_collect_iterate_fn(const as_endpoint* endpoint, void* udata);
126static void endpoint_to_string_iterate(const as_endpoint* endpoint, void* udata);
127static uint8_t endpoint_addr_type_from_cf_ip_addr(const cf_ip_addr* addr);
128static void endpoint_from_sock_cfg(const cf_sock_cfg* src, as_endpoint* endpoint);
129static void endpoint_list_overlap_iterate(const as_endpoint* endpoint, void* udata);
130static void endpoint_list_find_iterate(const as_endpoint* endpoint, void* udata);
131
132static bool endpoints_are_equal(const as_endpoint* endpoint1, const as_endpoint* endpoint2, const bool ignore_capabilities);
133static void endpoints_preference_sort(const as_endpoint* endpoints[], size_t n_endpoints);
134
135/*----------------------------------------------------------------------------
136 * Public API.
137 *----------------------------------------------------------------------------*/
138
139/**
140 * Get the sizeof an endpoint. Accounts for variable size of the address field.
141 * @return the size of the endpoint address. Zero if the endpoint address is
142 * invalid.
143 */
144size_t
145as_endpoint_sizeof(const as_endpoint* endpoint)
146{
147 return endpoint_sizeof_by_addr_type(endpoint->addr_type);
148}
149
150/**
151 * Enable a capability on an endpoint given its mask.
152 * @param endpoint the endpoint.
153 * @param capability_mask the capability mask.
154 */
155void
156as_endpoint_capability_enable(as_endpoint* endpoint, uint8_t capability_mask)
157{
158 endpoint->capabilities |= capability_mask;
159}
160
161/**
162 * Disable a capability on an endpoint given its mask.
163 * @param endpoint the endpoint.
164 * @param capability_mask the capability mask.
165 */
166void
167as_endpoint_capability_disable(as_endpoint* endpoint, uint8_t capability_mask)
168{
169 endpoint->capabilities &= ~capability_mask;
170}
171
172/**
173 * Connect to an endpoint.
174 *
175 * @param endpoint the peer endpoint to connect to.
176 * @param timeout the overall connect timeout.
177 * @param sock (output) will be populated if connections is successful.
178 * @return -1 on success, 0 on failure.
179 */
180int
181as_endpoint_connect(const as_endpoint* endpoint, int32_t timeout, cf_socket* sock)
182{
183 if (!endpoint_addr_type_is_valid(endpoint->addr_type)) {
184 return -1;
185 }
186
187 cf_sock_cfg cfg;
188 cf_sock_cfg_init(&cfg, CF_SOCK_OWNER_INVALID);
189 cfg.port = endpoint->port;
190 if (cf_ip_addr_from_binary(endpoint->addr, endpoint_addr_binary_size(endpoint->addr_type),
191 &cfg.addr) <= 0) {
192 return -1;
193 }
194
195 int rv = cf_socket_init_client(&cfg, timeout, sock);
196
197 // Reset the client sock config, because the config is a stack pointer.
198 sock->cfg = NULL;
199 return rv;
200}
201
202/**
203 * Connect to the best matching endpoint in the endpoint list.
204 *
205 * @param endpoint_list the list of endpoints.
206 * @param filter_fn filter function to discard incompatible endpoints. Can be
207 * NULL.
208 * @param filter_udata udata passed on as is to the filter function.
209 * @param timeout the overall connect timeout.
210 * @param sock (output) will be populated if connection is successful.
211 * @return the connected endpoint on success, NULL if no endpoint count be
212 * connected.
213 */
214const as_endpoint*
215as_endpoint_connect_any(const as_endpoint_list* endpoint_list,
216 as_endpoint_filter_fn filter_fn, void* filter_udata, int32_t timeout, cf_socket* sock)
217{
218 if (endpoint_list->n_endpoints == 0) {
219 return NULL;
220 }
221
222 const as_endpoint* ordered_endpoints[endpoint_list->n_endpoints];
223 const as_endpoint* rv = NULL;
224
225 as_endpoint_collect_udata collect_udata;
226 collect_udata.endpoints = ordered_endpoints;
227 collect_udata.collected_count = 0;
228
229 // Collect all endpoints in a pointer array.
230 as_endpoint_list_iterate(endpoint_list, endpoint_collect_iterate_fn, &collect_udata);
231
232 // Sort by descending preference.
233 endpoints_preference_sort(ordered_endpoints, endpoint_list->n_endpoints);
234
235 // TODO: Timeout individual connect or have the caller adjust based on
236 // number of endpoints
237 for (uint8_t i = 0; i < endpoint_list->n_endpoints; i++) {
238 if (filter_fn && !(filter_fn)(ordered_endpoints[i], filter_udata)) {
239 continue;
240 }
241
242 // Try this potential candidate.
243 if (as_endpoint_connect(ordered_endpoints[i], timeout, sock) == 0) {
244 // Connect succeeded.
245 rv = ordered_endpoints[i];
246 break;
247 }
248 }
249
250 return rv;
251}
252
253/**
254 * Convert a socket configuration to an endpoint in place.
255 * @return a heap allocated, converted endpoint. Should be freed using cf_free
256 * once the endpoint is no longer needed.
257 */
258void
259as_endpoint_from_sock_cfg_fill(const cf_sock_cfg* src, as_endpoint* endpoint)
260{
261 endpoint_from_sock_cfg(src, endpoint);
262}
263
264/**
265 * Convert a socket configuration to an endpoint.
266 * @return a heap allocated, converted endpoint. Should be freed using cf_free
267 * once the endpoint is no longer needed.
268 */
269as_endpoint*
270as_endpoint_from_sock_cfg(const cf_sock_cfg* src)
271{
272 uint8_t addr_type = endpoint_addr_type_from_cf_ip_addr(&src->addr);
273 as_endpoint* endpoint = endpoint_allocate(addr_type);
274 endpoint_from_sock_cfg(src, endpoint);
275 return endpoint;
276}
277
278/**
279 * Convert an endpoint to a cf_sock_addr.
280 * @param endpoint the source endpoint.
281 * @param sock_addr the target socket address.
282 * @return 0 on success, -1 on failure.
283 */
284int
285as_endpoint_to_sock_addr(const as_endpoint* endpoint, cf_sock_addr* sock_addr)
286{
287 sock_addr->port = endpoint->port;
288 return
289 cf_ip_addr_from_binary(endpoint->addr, endpoint_addr_binary_size(endpoint->addr_type),
290 &sock_addr->addr) > 0 ? 0 : -1;
291}
292
293/**
294 * Indicates if an endpoint supports listed capabilities.
295 * @return true if the endpoint supports the input capability.
296 */
297bool
298as_endpoint_capability_is_supported(const as_endpoint* endpoint, uint8_t capability_mask)
299{
300 return (endpoint->capabilities & capability_mask) > 0;
301}
302
303/**
304 * Return the in memory size in bytes of the endpoint list.
305 * @param endpoint_list the endpoint list.
306 * @param size (output) the size of the list on success.
307 * @return 0 on successful size calculation, -1 otherwise.
308 */
309int
310as_endpoint_list_sizeof(const as_endpoint_list* endpoint_list, size_t* size)
311{
312 return as_endpoint_list_nsizeof(endpoint_list, size, SIZE_MAX);
313}
314
315/**
316 * Return the in memory size in bytes of the endpoint list, but abort if the
317 * size of the read exceeds the input size.
318 * @param endpoint_list the endpoint list.
319 * @param size (output) the size of the list on success.
320 * @param size_max the maximum size until which parsing will be attempted.
321 * @return 0 on successful size calculation, -1 otherwise.
322 */
323int
324as_endpoint_list_nsizeof(const as_endpoint_list* endpoint_list, size_t* size, size_t size_max)
325{
326 if (!endpoint_list) {
327 return 0;
328 }
329
330 *size = sizeof(as_endpoint_list);
331
332 uint8_t* endpoint_ptr = (uint8_t*) endpoint_list->endpoints;
333 for (int i = 0; i < endpoint_list->n_endpoints; i++) {
334 size_t endpoint_size = as_endpoint_sizeof((as_endpoint*)endpoint_ptr);
335 if (endpoint_size == 0) {
336 // Invalid endpoint. Signal error
337 *size = 0;
338 return -1;
339 }
340
341 if (*size + endpoint_size > size_max) {
342 *size = 0;
343 return -1;
344 }
345
346 *size += endpoint_size;
347 endpoint_ptr += endpoint_size;
348 }
349
350 return 0;
351}
352
353/**
354 * Iterate over endpoints in an endpoint list and invoke the iterate function
355 * for each endpoint.
356 * @param iterate_fn the iterate function invoked for each endpoint in the list.
357 * @param udata passed as is to the iterate function. Useful for getting results
358 * out of the iteration.
359 * NULL if there is no plugin data.
360 * @return the size of the plugin data. 0 if there is no plugin data.
361 */
362void
363as_endpoint_list_iterate(const as_endpoint_list* endpoint_list,
364 const as_endpoint_iterate_fn iterate_fn, void* udata)
365{
366 if(!endpoint_list) {
367 return;
368 }
369
370 uint8_t* endpoint_ptr = (uint8_t*) endpoint_list->endpoints;
371
372 for (int i = 0; i < endpoint_list->n_endpoints; i++) {
373 if (iterate_fn) {
374 (iterate_fn)((as_endpoint*) endpoint_ptr, udata);
375 }
376 endpoint_ptr += as_endpoint_sizeof((as_endpoint*) endpoint_ptr);
377 }
378}
379
380/**
381 * Convert a server configuration to an endpoint list in place into the
382 * destination endpoint list.
383 * @param serv_cfg source server configuration.
384 * @param endpoint_list destination endpoint list.
385 */
386void
387as_endpoint_list_from_serv_cfg_fill(const cf_serv_cfg* serv_cfg, as_endpoint_list* endpoint_list)
388{
389 endpoint_list->n_endpoints = serv_cfg->n_cfgs;
390
391 uint8_t* endpoint_ptr = (uint8_t*) &endpoint_list->endpoints[0];
392 for (int i = 0; i < serv_cfg->n_cfgs; i++) {
393 as_endpoint* endpoint = (as_endpoint*) endpoint_ptr;
394 endpoint_from_sock_cfg(&serv_cfg->cfgs[i], endpoint);
395 endpoint_ptr += as_endpoint_sizeof(endpoint);
396 }
397}
398
399/**
400 * Convert a server configuration to an endpoint list.
401 * @param serv_cfg server configuration.
402 * @return a heap allocated endpoint list. Should be freed using cf_free
403 * once the endpoint is no longer needed.
404 */
405as_endpoint_list*
406as_endpoint_list_from_serv_cfg(const cf_serv_cfg* serv_cfg)
407{
408 size_t result_size = sizeof(as_endpoint_list);
409 for (int i = 0; i < serv_cfg->n_cfgs; i++) {
410 result_size += endpoint_sizeof_by_addr_type(
411 endpoint_addr_type_from_cf_ip_addr(&serv_cfg->cfgs[i].addr));
412 }
413
414 as_endpoint_list* endpoint_list = (as_endpoint_list*) cf_malloc(result_size);
415
416 as_endpoint_list_from_serv_cfg_fill(serv_cfg, endpoint_list);
417
418 return endpoint_list;
419}
420
421/**
422 * Compare two endpoint lists for equality.
423 * @param list1 the first. NULL allowed.
424 * @param list2 the second list. NULL allowed.
425 * @return true iff the lists are equals, false otherwise.
426 */
427bool
428as_endpoint_lists_are_equal(const as_endpoint_list* list1, const as_endpoint_list* list2)
429{
430 if (list1 == list2) {
431 return true;
432 }
433
434 if (!list1 || !list2) {
435 return false;
436 }
437
438 size_t size1;
439 if (as_endpoint_list_sizeof(list1, &size1) != 0) {
440 return false;
441 }
442
443 size_t size2;
444 if (as_endpoint_list_sizeof(list2, &size2) != 0) {
445 return false;
446 }
447
448 if (size1 != size2) {
449 return false;
450 }
451
452 return memcmp(list1, list2, size1) == 0;
453}
454
455/**
456 * Check if two lists overlap in at least one endpoint.
457 * @param list1 the first. NULL allowed.
458 * @param list2 the second list. NULL allowed.
459 * @param ignore_capabilities set to true if the overlap match should ignore
460 * node capabilities, false if capabilities should also be matched.
461 * @return true iff the lists are overlap, false otherwise.
462 */
463bool
464as_endpoint_lists_are_overlapping(const as_endpoint_list* list1, const as_endpoint_list* list2,
465 bool ignore_capabilities)
466{
467 if (list1 == list2) {
468 return true;
469 }
470
471 if (!list1 || !list2) {
472 return false;
473 }
474
475 as_endpoint_list_overlap_udata udata;
476 udata.overlapped = false;
477 udata.other = list2;
478 udata.ignore_capabilities = ignore_capabilities;
479
480 as_endpoint_list_iterate(list1, endpoint_list_overlap_iterate, &udata);
481
482 return udata.overlapped;
483}
484
485/**
486 * Convert an endpoint list to a string.
487 * @param endpoint_list the input list. NULL allowed.
488 * @param buffer the output buffer.
489 * @param buffer_capacity the capacity of the output buffer.
490 * @return the number of characters printed (excluding the null byte used to
491 * end output to strings)
492 */
493int
494as_endpoint_list_to_string(const as_endpoint_list* endpoint_list, char* buffer,
495 size_t buffer_capacity)
496{
497 return as_endpoint_list_to_string_match_capabilities(endpoint_list, buffer,
498 buffer_capacity, 0, 0);
499}
500
501/**
502 * Convert an endpoint list to a string matching capabilities.
503 * @param endpoint_list the input list. NULL allowed.
504 * @param buffer the output buffer.
505 * @param buffer_capacity the capacity of the output buffer.
506 * @param capability_mask specifies which bit to match.
507 * @param capabilities specifies capabilities to be match for.
508 * @return the number of characters printed (excluding the null byte used to
509 * end output to strings)
510 */
511int
512as_endpoint_list_to_string_match_capabilities(
513 const as_endpoint_list* endpoint_list, char* buffer,
514 size_t buffer_capacity, uint8_t capability_mask, uint8_t capabilities)
515{
516 if (!endpoint_list) {
517 buffer[0] = 0;
518 return 0;
519 }
520
521 as_endpoint_to_string_udata udata = { 0 };
522 udata.write_ptr = buffer;
523 udata.buffer_remaining = buffer_capacity;
524 udata.capabilities = capabilities;
525 udata.capability_mask = capability_mask;
526 as_endpoint_list_iterate(endpoint_list, endpoint_to_string_iterate, &udata);
527
528 if (udata.endpoints_converted) {
529 if (udata.endpoints_converted != endpoint_list->n_endpoints) {
530 // Truncation has happened. Add ellipses.
531 if (udata.buffer_remaining > 4) {
532 udata.buffer_remaining -= sprintf(udata.write_ptr, "...");
533 }
534 }
535 else {
536 // Remove the dangling comma from the last endpoint.
537 udata.write_ptr--;
538 udata.buffer_remaining++;
539 }
540 }
541
542 // Ensure NULL termination.
543 *udata.write_ptr = 0;
544
545 return buffer_capacity - udata.buffer_remaining;
546}
547
548/**
549 * Populate dyn buf with endpoints info
550 * @param endpoint_list the input list. NULL allowed.
551 * @param db the dynamic buffer.
552 */
553void
554as_endpoint_list_info(const as_endpoint_list* endpoint_list, cf_dyn_buf* db)
555{
556 size_t endpoint_list_size = 0;
557 as_endpoint_list_sizeof(endpoint_list, &endpoint_list_size);
558 // 4 chars for delimiters, 50 chars for ipv6 ip and port, rounded to 64
559 size_t endpoint_list_str_size = 64 * endpoint_list_size;
560
561 char endpoint_list_str[endpoint_list_str_size];
562 as_endpoint_list_to_string_match_capabilities(endpoint_list,
563 endpoint_list_str, sizeof(endpoint_list_str), AS_ENDPOINT_TLS_MASK,
564 0);
565
566 cf_dyn_buf_append_string(db, "endpoint=");
567 if (endpoint_list_str[0] != '\0') {
568 cf_dyn_buf_append_string(db, endpoint_list_str);
569 }
570 cf_dyn_buf_append_string(db, ":");
571
572 as_endpoint_list_to_string_match_capabilities(endpoint_list,
573 endpoint_list_str, sizeof(endpoint_list_str), AS_ENDPOINT_TLS_MASK,
574 AS_ENDPOINT_TLS_MASK);
575
576 cf_dyn_buf_append_string(db, "endpoint-tls=");
577 if (endpoint_list_str[0] != '\0') {
578 cf_dyn_buf_append_string(db, endpoint_list_str);
579 }
580
581}
582
583/*----------------------------------------------------------------------------
584 * Private internal functions.
585 *----------------------------------------------------------------------------*/
586/**
587 * Indicates if input address type is valid.
588 */
589static bool
590endpoint_addr_type_is_valid(uint8_t type)
591{
592 return type > AS_ENDPOINT_ADDR_TYPE_UNDEF && type < AS_ENDPOINT_ADDR_TYPE_SENTINEL;
593}
594
595/**
596 * Get the size of the binary for input address type.
597 * TODO: Move to socket API. Not if we support DNS names.
598 */
599static size_t
600endpoint_addr_binary_size(uint8_t type)
601{
602 return (type == AS_ENDPOINT_ADDR_TYPE_IPv4) ? 4 : 16;
603}
604
605/**
606 * Return the sizeof endpoint give its address type.
607 */
608static size_t
609endpoint_sizeof_by_addr_type(uint8_t addr_type)
610{
611 return sizeof(as_endpoint) + endpoint_addr_binary_size(addr_type);
612}
613
614/**
615 * Convert cf_ip address to endpoint address type.
616 */
617static uint8_t
618endpoint_addr_type_from_cf_ip_addr(const cf_ip_addr* addr)
619{
620 return cf_ip_addr_is_legacy(addr) ? AS_ENDPOINT_ADDR_TYPE_IPv4 : AS_ENDPOINT_ADDR_TYPE_IPv6;
621}
622
623/**
624 * Heap allocate an endpoint.
625 */
626static as_endpoint*
627endpoint_allocate(uint8_t addr_type)
628{
629 return cf_malloc(endpoint_sizeof_by_addr_type(addr_type));
630}
631
632/**
633 * Convert a socket to an endpoint.
634 */
635static void
636endpoint_from_sock_cfg(const cf_sock_cfg* src, as_endpoint* endpoint)
637{
638 endpoint->addr_type =
639 cf_ip_addr_is_legacy(&src->addr) ? AS_ENDPOINT_ADDR_TYPE_IPv4 : AS_ENDPOINT_ADDR_TYPE_IPv6;
640 endpoint->port = src->port;
641
642 // We will have allocated correct binary size.
643 CF_IGNORE_ERROR(
644 cf_ip_addr_to_binary(&src->addr, endpoint->addr,
645 endpoint_addr_binary_size(endpoint->addr_type)));
646
647 endpoint->capabilities = (src->owner == CF_SOCK_OWNER_HEARTBEAT_TLS ||
648 src->owner == CF_SOCK_OWNER_FABRIC_TLS) ? AS_ENDPOINT_TLS_MASK : 0;
649}
650
651/**
652 * Generate a hash for an endpoint, but salted with the a random tie breaker to
653 * generate random looking shuffles for "equal" endpoints. This is jenkins
654 * one-at-a-time hash of the tie breaker concatenated with the endpoint.
655 */
656static uint32_t
657endpoint_sort_hash(const as_endpoint* endpoint, int tie_breaker)
658{
659 uint32_t hash = 0;
660
661 // Hash the nodeid.
662 uint8_t* key = (uint8_t*)&tie_breaker;
663 for (int i = 0; i < sizeof(tie_breaker); ++i) {
664 hash += *key;
665 hash += (hash << 10);
666 hash ^= (hash >> 6);
667 key++;
668 }
669
670 // Hash the endpoint value.
671 size_t endpoint_size = as_endpoint_sizeof(endpoint);
672 key = (uint8_t*)endpoint;
673 for (int i = 0; i < endpoint_size; ++i) {
674 hash += *key;
675 hash += (hash << 10);
676 hash ^= (hash >> 6);
677 key++;
678 }
679
680 hash += (hash << 3);
681 hash ^= (hash >> 11);
682 hash += (hash << 15);
683 return hash;
684}
685
686/**
687 * Comparator to sort endpoints in descending order of preference.
688 */
689static int
690endpoint_preference_compare(const void* e1, const void* e2, void* arg)
691{
692 const as_endpoint* endpoint1 = *(as_endpoint**)e1;
693 const as_endpoint* endpoint2 = *(as_endpoint**)e2;
694 int tie_breaker = *((int*)arg);
695
696 // Prefer TLS over clear text.
697 bool endpoint1_is_tls = as_endpoint_capability_is_supported(endpoint1, AS_ENDPOINT_TLS_MASK);
698
699 bool endpoint2_is_tls = as_endpoint_capability_is_supported(endpoint2, AS_ENDPOINT_TLS_MASK);
700
701 if (endpoint1_is_tls != endpoint2_is_tls) {
702 return endpoint1_is_tls ? -1 : 1;
703 }
704
705 // If TLS capabilities match prefer IPv6.
706 bool endpoint1_is_ipv6 = endpoint1->addr_type == AS_ENDPOINT_ADDR_TYPE_IPv6;
707 bool endpoint2_is_ipv6 = endpoint2->addr_type == AS_ENDPOINT_ADDR_TYPE_IPv6;
708
709 if (endpoint1_is_ipv6 != endpoint2_is_ipv6) {
710 return endpoint1_is_ipv6 ? -1 : 1;
711 }
712
713 // Used tie breaker parameter to salt the hashes for load balancing.
714 return endpoint_sort_hash(endpoint1, tie_breaker) -
715 endpoint_sort_hash(endpoint2, tie_breaker);
716}
717
718/**
719 * Sort endpoints in place in descending order of preference.
720 * @param endpoints array of endpoint pointers.
721 */
722static void
723endpoints_preference_sort(const as_endpoint* endpoints[], size_t n_endpoints)
724{
725 // Random tie breaker to load balance between two equivalent endpoints.
726 int tie_breaker = rand();
727
728 qsort_r(endpoints, n_endpoints, sizeof(as_endpoint*),
729 endpoint_preference_compare, &tie_breaker);
730}
731
732/**
733 * Iterate and collect all endpoint addresses in passed in udata.
734 */
735static void
736endpoint_collect_iterate_fn(const as_endpoint* endpoint, void* udata)
737{
738 as_endpoint_collect_udata* endpoints_data = (as_endpoint_collect_udata*) udata;
739 endpoints_data->endpoints[endpoints_data->collected_count++] = endpoint;
740}
741
742/**
743 * Iterate over endpoints and convert them to strings.
744 */
745static void
746endpoint_to_string_iterate(const as_endpoint* endpoint, void* udata)
747{
748 as_endpoint_to_string_udata* to_string_data =
749 (as_endpoint_to_string_udata*)udata;
750
751 if ((endpoint->capabilities & to_string_data->capability_mask)
752 != (to_string_data->capabilities & to_string_data->capability_mask)) {
753 // skip as the capabilities do not match
754 to_string_data->endpoints_converted++;
755 return;
756 }
757
758 char address_buffer[1024];
759 int capacity = sizeof(address_buffer);
760 char* endpoint_str_ptr = address_buffer;
761
762 cf_sock_addr temp_addr;
763 if (cf_ip_addr_from_binary(endpoint->addr,
764 endpoint_addr_binary_size(endpoint->addr_type), &temp_addr.addr)
765 <= 0) {
766 return;
767 }
768
769 int rv = 0;
770 if (endpoint->port) {
771 temp_addr.port = endpoint->port;
772 rv = cf_sock_addr_to_string(&temp_addr, endpoint_str_ptr, capacity);
773 if (rv <= 0) {
774 return;
775 }
776
777 capacity -= rv;
778 endpoint_str_ptr += rv;
779 rv = snprintf(endpoint_str_ptr, capacity, ",");
780 }
781 else {
782 // Skip port and tls capabilities.
783 rv = cf_ip_addr_to_string(&temp_addr.addr, endpoint_str_ptr, capacity);
784 if (rv <= 0) {
785 return;
786 }
787
788 capacity -= rv;
789 endpoint_str_ptr += rv;
790 rv = snprintf(endpoint_str_ptr, capacity, ",");
791 }
792
793 if (rv == capacity) {
794 // Output truncated. Abort.
795 return;
796 }
797
798 int to_write = strnlen(address_buffer, sizeof(address_buffer));
799
800 // Ensure we leave space for the NULL terminator.
801 if (to_write + 1 <= to_string_data->buffer_remaining) {
802 sprintf(to_string_data->write_ptr, "%s", address_buffer);
803 to_string_data->buffer_remaining -= to_write;
804 to_string_data->write_ptr += to_write;
805 to_string_data->endpoints_converted++;
806 }
807}
808
809/**
810 * Compare two endpoints for equality.
811 * @param endpoint1 the first. NULL allowed.
812 * @param endpoint2 the second endpoint. NULL allowed.
813 * @param ignore_capabilities indicates if endpoint capabilities should be
814 * ignored.
815 * @return true iff the endpoints are equals, false otherwise.
816 */
817static bool
818endpoints_are_equal(const as_endpoint* endpoint1, const as_endpoint* endpoint2,
819 bool ignore_capabilities)
820{
821 if (endpoint1 == endpoint2) {
822 return true;
823 }
824
825 if (!endpoint1 || !endpoint2) {
826 return false;
827 }
828
829 size_t size1 = as_endpoint_sizeof(endpoint1);
830 if (!size1) {
831 return false;
832 }
833
834 size_t size2 = as_endpoint_sizeof(endpoint2);
835 if (!size2) {
836 return false;
837 }
838
839 if (size1 != size2) {
840 return false;
841 }
842
843 return (ignore_capabilities || endpoint1->capabilities == endpoint2->capabilities)
844 && endpoint1->port == endpoint2->port && endpoint1->addr_type == endpoint2->addr_type
845 && memcmp(endpoint1->addr, endpoint2->addr, endpoint_addr_binary_size(endpoint1->addr_type)) == 0;
846}
847
848/**
849 * Iterate function to find an overlap.
850 */
851static void
852endpoint_list_overlap_iterate(const as_endpoint* endpoint, void* udata)
853{
854 as_endpoint_list_overlap_udata* overlap_udata = (as_endpoint_list_overlap_udata*) udata;
855 as_endpoint_list_endpoint_find_udata find_udata;
856 find_udata.match_found = false;
857 find_udata.ignore_capabilities = overlap_udata->ignore_capabilities;
858 find_udata.to_find = endpoint;
859
860 as_endpoint_list_iterate(overlap_udata->other, endpoint_list_find_iterate, &find_udata);
861
862 overlap_udata->overlapped |= find_udata.match_found;
863}
864
865/**
866 * Iterate function to search for an endpoint.
867 */
868static void
869endpoint_list_find_iterate(const as_endpoint* endpoint, void* udata)
870{
871 as_endpoint_list_endpoint_find_udata* find_udata = (as_endpoint_list_endpoint_find_udata*) udata;
872
873 const as_endpoint* to_find = find_udata->to_find;
874 if (!to_find) {
875 return;
876 }
877
878 find_udata->match_found |= endpoints_are_equal(endpoint, to_find,
879 find_udata->ignore_capabilities);
880}
881