Add ROLE_IPA_DC into two more places
[Samba.git] / ctdb / protocol / protocol_util.c
blobfe757658f482f7404158f54c9cadb398ef1ada59
1 /*
2 CTDB protocol marshalling
4 Copyright (C) Amitay Isaacs 2015
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "replace.h"
21 #include "system/network.h"
23 #include <talloc.h>
25 #include "common/line.h"
27 #include "protocol.h"
28 #include "protocol_util.h"
29 #include "lib/util/util.h"
30 #include "lib/util/smb_strtox.h"
32 static struct {
33 enum ctdb_runstate runstate;
34 const char * label;
35 } runstate_map[] = {
36 { CTDB_RUNSTATE_UNKNOWN, "UNKNOWN" },
37 { CTDB_RUNSTATE_INIT, "INIT" },
38 { CTDB_RUNSTATE_SETUP, "SETUP" },
39 { CTDB_RUNSTATE_FIRST_RECOVERY, "FIRST_RECOVERY" },
40 { CTDB_RUNSTATE_STARTUP, "STARTUP" },
41 { CTDB_RUNSTATE_RUNNING, "RUNNING" },
42 { CTDB_RUNSTATE_SHUTDOWN, "SHUTDOWN" },
43 { -1, NULL },
46 const char *ctdb_runstate_to_string(enum ctdb_runstate runstate)
48 int i;
50 for (i=0; runstate_map[i].label != NULL; i++) {
51 if (runstate_map[i].runstate == runstate) {
52 return runstate_map[i].label;
56 return runstate_map[0].label;
59 enum ctdb_runstate ctdb_runstate_from_string(const char *runstate_str)
61 int i;
63 for (i=0; runstate_map[i].label != NULL; i++) {
64 if (strcasecmp(runstate_map[i].label,
65 runstate_str) == 0) {
66 return runstate_map[i].runstate;
70 return CTDB_RUNSTATE_UNKNOWN;
73 static struct {
74 enum ctdb_event event;
75 const char *label;
76 } event_map[] = {
77 { CTDB_EVENT_INIT, "init" },
78 { CTDB_EVENT_SETUP, "setup" },
79 { CTDB_EVENT_STARTUP, "startup" },
80 { CTDB_EVENT_START_RECOVERY, "startrecovery" },
81 { CTDB_EVENT_RECOVERED, "recovered" },
82 { CTDB_EVENT_TAKE_IP, "takeip" },
83 { CTDB_EVENT_RELEASE_IP, "releaseip" },
84 { CTDB_EVENT_MONITOR, "monitor" },
85 { CTDB_EVENT_SHUTDOWN, "shutdown" },
86 { CTDB_EVENT_UPDATE_IP, "updateip" },
87 { CTDB_EVENT_IPREALLOCATED, "ipreallocated" },
88 { CTDB_EVENT_MAX, "all" },
89 { -1, NULL },
92 const char *ctdb_event_to_string(enum ctdb_event event)
94 int i;
96 for (i=0; event_map[i].label != NULL; i++) {
97 if (event_map[i].event == event) {
98 return event_map[i].label;
102 return "unknown";
105 enum ctdb_event ctdb_event_from_string(const char *event_str)
107 int i;
109 for (i=0; event_map[i].label != NULL; i++) {
110 if (strcmp(event_map[i].label, event_str) == 0) {
111 return event_map[i].event;
115 return CTDB_EVENT_MAX;
118 int ctdb_sock_addr_to_buf(char *buf, socklen_t buflen,
119 ctdb_sock_addr *addr, bool with_port)
121 const char *t;
122 size_t len = 0;
124 switch (addr->sa.sa_family) {
125 case AF_INET:
126 t = inet_ntop(addr->ip.sin_family, &addr->ip.sin_addr,
127 buf, buflen);
128 if (t == NULL) {
129 return errno;
131 if (with_port) {
132 len = strlen(buf);
134 break;
136 case AF_INET6: {
137 char tmp[INET6_ADDRSTRLEN];
139 t = inet_ntop(addr->ip6.sin6_family,
140 &addr->ip6.sin6_addr,
141 tmp,
142 sizeof(tmp));
143 if (t == NULL) {
144 return errno;
147 if (with_port) {
148 int ret = snprintf(buf, buflen, "[%s]", tmp);
149 if (ret < 0) {
150 return ENOSPC;
152 len = (size_t)ret;
153 } else {
154 len = strlcpy(buf, tmp, buflen);
156 if (len >= buflen){
157 return ENOSPC;
159 break;
162 default:
163 return EAFNOSUPPORT;
164 break;
167 if (with_port) {
168 int ret;
170 ret = snprintf(buf+len, buflen-len,
171 ":%u", ctdb_sock_addr_port(addr));
172 if (ret < 0 || (size_t)ret >= buflen-len) {
173 return ENOSPC;
177 return 0;
180 char *ctdb_sock_addr_to_string(TALLOC_CTX *mem_ctx,
181 ctdb_sock_addr *addr,
182 bool with_port)
184 size_t len = 64;
185 char *cip;
186 int ret;
188 cip = talloc_size(mem_ctx, len);
190 if (cip == NULL) {
191 return NULL;
194 ret = ctdb_sock_addr_to_buf(cip, len, addr, with_port);
195 if (ret != 0) {
196 talloc_free(cip);
197 return NULL;
200 return cip;
203 static int ipv4_from_string(const char *str, struct sockaddr_in *ip)
205 int ret;
207 *ip = (struct sockaddr_in) {
208 .sin_family = AF_INET,
211 ret = inet_pton(AF_INET, str, &ip->sin_addr);
212 if (ret != 1) {
213 return EINVAL;
216 #ifdef HAVE_SOCK_SIN_LEN
217 ip->sin_len = sizeof(*ip);
218 #endif
219 return 0;
222 static int ipv6_from_string(const char *str, struct sockaddr_in6 *ip6)
224 int ret;
226 *ip6 = (struct sockaddr_in6) {
227 .sin6_family = AF_INET6,
230 ret = inet_pton(AF_INET6, str, &ip6->sin6_addr);
231 if (ret != 1) {
232 return EINVAL;
235 #ifdef HAVE_SOCK_SIN6_LEN
236 ip6->sin6_len = sizeof(*ip6);
237 #endif
238 return 0;
241 static int ip_from_string(const char *str, ctdb_sock_addr *addr)
243 char *p;
244 int ret;
246 if (addr == NULL) {
247 return EINVAL;
250 ZERO_STRUCTP(addr); /* valgrind :-) */
252 /* IPv4 or IPv6 address?
254 * Use strrchr() because we need the right-most ':' below for
255 * IPv4-mapped IPv6 addresses anyway...
257 p = strrchr(str, ':');
258 if (p == NULL) {
259 ret = ipv4_from_string(str, &addr->ip);
260 } else {
261 static const uint8_t ipv4_mapped_prefix[12] = {
262 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff
264 size_t len = strlen(str);
265 char s[64];
267 len = strlcpy(s, str, sizeof(s));
268 if (len >= sizeof(s)) {
269 return EINVAL;
272 if ((len >= 2) && (s[0] == '[') && (s[len-1] == ']')) {
273 s[len-1] = '\0';
274 str = s+1;
275 p = strrchr(str, ':');
278 ret = ipv6_from_string(str, &addr->ip6);
279 if (ret != 0) {
280 return ret;
284 * Check for IPv4-mapped IPv6 address
285 * (e.g. ::ffff:192.0.2.128) - reparse as IPv4 if
286 * necessary
288 if (memcmp(&addr->ip6.sin6_addr.s6_addr[0],
289 ipv4_mapped_prefix,
290 sizeof(ipv4_mapped_prefix)) == 0) {
291 /* Initialize addr struct to zero before reparsing as IPV4 */
292 ZERO_STRUCTP(addr);
294 /* Reparse as IPv4 */
295 ret = ipv4_from_string(p+1, &addr->ip);
299 return ret;
302 int ctdb_sock_addr_from_string(const char *str,
303 ctdb_sock_addr *addr, bool with_port)
305 char *p;
306 char s[64]; /* Much longer than INET6_ADDRSTRLEN */
307 unsigned port;
308 size_t len;
309 int ret;
311 if (! with_port) {
312 ret = ip_from_string(str, addr);
313 return ret;
316 /* Parse out port number and then IP address */
318 len = strlcpy(s, str, sizeof(s));
319 if (len >= sizeof(s)) {
320 return EINVAL;
323 p = strrchr(s, ':');
324 if (p == NULL) {
325 return EINVAL;
328 port = smb_strtoul(p+1, NULL, 10, &ret, SMB_STR_FULL_STR_CONV);
329 if (ret != 0) {
330 /* Empty string or trailing garbage */
331 return EINVAL;
334 *p = '\0';
335 ret = ip_from_string(s, addr);
337 ctdb_sock_addr_set_port(addr, port);
339 return ret;
342 int ctdb_sock_addr_mask_from_string(const char *str,
343 ctdb_sock_addr *addr,
344 unsigned int *mask)
346 char *p;
347 char s[64]; /* Much longer than INET6_ADDRSTRLEN */
348 unsigned int m;
349 size_t len;
350 int ret = 0;
352 if (addr == NULL || mask == NULL) {
353 return EINVAL;
356 len = strlcpy(s, str, sizeof(s));
357 if (len >= sizeof(s)) {
358 return EINVAL;
361 p = strrchr(s, '/');
362 if (p == NULL) {
363 return EINVAL;
366 m = smb_strtoul(p+1, NULL, 10, &ret, SMB_STR_FULL_STR_CONV);
367 if (ret != 0) {
368 /* Empty string or trailing garbage */
369 return EINVAL;
372 *p = '\0';
373 ret = ip_from_string(s, addr);
375 if (ret == 0) {
376 *mask = m;
379 return ret;
382 unsigned int ctdb_sock_addr_port(ctdb_sock_addr *addr)
384 switch (addr->sa.sa_family) {
385 case AF_INET:
386 return ntohs(addr->ip.sin_port);
387 break;
388 case AF_INET6:
389 return ntohs(addr->ip6.sin6_port);
390 break;
391 default:
392 return 0;
396 void ctdb_sock_addr_set_port(ctdb_sock_addr *addr, unsigned int port)
398 switch (addr->sa.sa_family) {
399 case AF_INET:
400 addr->ip.sin_port = htons(port);
401 break;
402 case AF_INET6:
403 addr->ip6.sin6_port = htons(port);
404 break;
405 default:
406 break;
410 static int ctdb_sock_addr_cmp_family(const ctdb_sock_addr *addr1,
411 const ctdb_sock_addr *addr2)
413 /* This is somewhat arbitrary. However, when used for sorting
414 * it just needs to be consistent.
416 if (addr1->sa.sa_family < addr2->sa.sa_family) {
417 return -1;
419 if (addr1->sa.sa_family > addr2->sa.sa_family) {
420 return 1;
423 return 0;
426 int ctdb_sock_addr_cmp_ip(const ctdb_sock_addr *addr1,
427 const ctdb_sock_addr *addr2)
429 int ret;
431 ret = ctdb_sock_addr_cmp_family(addr1, addr2);
432 if (ret != 0) {
433 return ret;
436 switch (addr1->sa.sa_family) {
437 case AF_INET:
438 ret = memcmp(&addr1->ip.sin_addr.s_addr,
439 &addr2->ip.sin_addr.s_addr, 4);
440 break;
442 case AF_INET6:
443 ret = memcmp(addr1->ip6.sin6_addr.s6_addr,
444 addr2->ip6.sin6_addr.s6_addr, 16);
445 break;
447 default:
448 ret = -1;
451 return ret;
454 int ctdb_sock_addr_cmp(const ctdb_sock_addr *addr1,
455 const ctdb_sock_addr *addr2)
457 int ret = 0;
459 ret = ctdb_sock_addr_cmp_ip(addr1, addr2);
460 if (ret != 0) {
461 return ret;
464 switch (addr1->sa.sa_family) {
465 case AF_INET:
466 if (addr1->ip.sin_port < addr2->ip.sin_port) {
467 ret = -1;
468 } else if (addr1->ip.sin_port > addr2->ip.sin_port) {
469 ret = 1;
471 break;
473 case AF_INET6:
474 if (addr1->ip6.sin6_port < addr2->ip6.sin6_port) {
475 ret = -1;
476 } else if (addr1->ip6.sin6_port > addr2->ip6.sin6_port) {
477 ret = 1;
479 break;
481 default:
482 ret = -1;
485 return ret;
488 bool ctdb_sock_addr_same_ip(const ctdb_sock_addr *addr1,
489 const ctdb_sock_addr *addr2)
491 return (ctdb_sock_addr_cmp_ip(addr1, addr2) == 0);
494 bool ctdb_sock_addr_same(const ctdb_sock_addr *addr1,
495 const ctdb_sock_addr *addr2)
497 return (ctdb_sock_addr_cmp(addr1, addr2) == 0);
500 int ctdb_connection_to_buf(char *buf,
501 size_t buflen,
502 struct ctdb_connection *conn,
503 bool client_first,
504 const char *sep)
506 char server[64], client[64];
507 int ret;
509 ret = ctdb_sock_addr_to_buf(server, sizeof(server),
510 &conn->server, true);
511 if (ret != 0) {
512 return ret;
515 ret = ctdb_sock_addr_to_buf(client, sizeof(client),
516 &conn->client, true);
517 if (ret != 0) {
518 return ret;
521 if (! client_first) {
522 ret = snprintf(buf, buflen, "%s%s%s", server, sep, client);
523 } else {
524 ret = snprintf(buf, buflen, "%s%s%s", client, sep, server);
526 if (ret < 0 || (size_t)ret >= buflen) {
527 return ENOSPC;
530 return 0;
533 char *ctdb_connection_to_string(TALLOC_CTX *mem_ctx,
534 struct ctdb_connection *conn,
535 bool client_first)
537 const size_t len = 128;
538 char *out;
539 int ret;
541 out = talloc_size(mem_ctx, len);
542 if (out == NULL) {
543 return NULL;
546 ret = ctdb_connection_to_buf(out, len, conn, client_first, " ");
547 if (ret != 0) {
548 talloc_free(out);
549 return NULL;
552 return out;
555 int ctdb_connection_from_string(const char *str, bool client_first,
556 struct ctdb_connection *conn)
558 char s[128];
559 char *t1 = NULL, *t2 = NULL;
560 size_t len;
561 ctdb_sock_addr *first = (client_first ? &conn->client : &conn->server);
562 ctdb_sock_addr *second = (client_first ? &conn->server : &conn->client);
563 int ret;
565 len = strlcpy(s, str, sizeof(s));
566 if (len >= sizeof(s)) {
567 return EINVAL;
570 t1 = strtok(s, " \t\n");
571 if (t1 == NULL) {
572 return EINVAL;
575 t2 = strtok(NULL, " \t\n\0");
576 if (t2 == NULL) {
577 return EINVAL;
580 ret = ctdb_sock_addr_from_string(t1, first, true);
581 if (ret != 0) {
582 return ret;
585 ret = ctdb_sock_addr_from_string(t2, second, true);
586 if (ret != 0) {
587 return ret;
590 ret = ctdb_sock_addr_cmp_family(first, second);
591 if (ret != 0) {
592 return EINVAL;
595 return 0;
598 int ctdb_connection_list_add(struct ctdb_connection_list *conn_list,
599 struct ctdb_connection *conn)
601 uint32_t len;
603 if (conn_list == NULL) {
604 return EINVAL;
607 /* Ensure array is big enough */
608 len = talloc_array_length(conn_list->conn);
609 if (conn_list->num == len) {
610 conn_list->conn = talloc_realloc(conn_list, conn_list->conn,
611 struct ctdb_connection,
612 len+128);
613 if (conn_list->conn == NULL) {
614 return ENOMEM;
618 conn_list->conn[conn_list->num] = *conn;
619 conn_list->num++;
621 return 0;
624 static int connection_cmp(const void *a, const void *b)
626 const struct ctdb_connection *conn_a = a;
627 const struct ctdb_connection *conn_b = b;
628 int ret;
630 ret = ctdb_sock_addr_cmp(&conn_a->server, &conn_b->server);
631 if (ret == 0) {
632 ret = ctdb_sock_addr_cmp(&conn_a->client, &conn_b->client);
635 return ret;
638 int ctdb_connection_list_sort(struct ctdb_connection_list *conn_list)
640 if (conn_list == NULL) {
641 return EINVAL;
644 if (conn_list->num > 0) {
645 qsort(conn_list->conn, conn_list->num,
646 sizeof(struct ctdb_connection), connection_cmp);
649 return 0;
652 char *ctdb_connection_list_to_string(
653 TALLOC_CTX *mem_ctx,
654 struct ctdb_connection_list *conn_list, bool client_first)
656 uint32_t i;
657 char *out;
659 out = talloc_strdup(mem_ctx, "");
660 if (out == NULL) {
661 return NULL;
664 if (conn_list == NULL || conn_list->num == 0) {
665 return out;
668 for (i = 0; i < conn_list->num; i++) {
669 char buf[128];
670 int ret;
672 ret = ctdb_connection_to_buf(buf,
673 sizeof(buf),
674 &conn_list->conn[i],
675 client_first,
676 " ");
677 if (ret != 0) {
678 talloc_free(out);
679 return NULL;
682 out = talloc_asprintf_append(out, "%s\n", buf);
683 if (out == NULL) {
684 return NULL;
688 return out;
691 struct ctdb_connection_list_read_state {
692 struct ctdb_connection_list *list;
693 bool client_first;
696 static int ctdb_connection_list_read_line(char *line, void *private_data)
698 struct ctdb_connection_list_read_state *state =
699 (struct ctdb_connection_list_read_state *)private_data;
700 struct ctdb_connection conn;
701 int ret;
703 /* Skip empty lines */
704 if (line[0] == '\0') {
705 return 0;
708 /* Comment */
709 if (line[0] == '#') {
710 return 0;
713 ret = ctdb_connection_from_string(line, state->client_first, &conn);
714 if (ret != 0) {
715 return ret;
718 ret = ctdb_connection_list_add(state->list, &conn);
719 if (ret != 0) {
720 return ret;
723 return 0;
726 int ctdb_connection_list_read(TALLOC_CTX *mem_ctx,
727 int fd,
728 bool client_first,
729 struct ctdb_connection_list **conn_list)
731 struct ctdb_connection_list_read_state state;
732 int ret;
734 if (conn_list == NULL) {
735 return EINVAL;
738 state.list = talloc_zero(mem_ctx, struct ctdb_connection_list);
739 if (state.list == NULL) {
740 return ENOMEM;
743 state.client_first = client_first;
745 ret = line_read(fd,
746 128,
747 mem_ctx,
748 ctdb_connection_list_read_line,
749 &state,
750 NULL);
752 *conn_list = state.list;
754 return ret;