99dbe82404d3ac7b7dd6dcc2ab5cce4fdb5299b3
[vlendec/samba-autobuild/.git] / ctdb / protocol / protocol_util.c
1 /*
2    CTDB protocol marshalling
3
4    Copyright (C) Amitay Isaacs  2015
5
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.
10
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.
15
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/>.
18 */
19
20 #include "replace.h"
21 #include "system/network.h"
22
23 #include <talloc.h>
24
25 #include "common/line.h"
26
27 #include "protocol.h"
28 #include "protocol_util.h"
29 #include "lib/util/util.h"
30
31 static struct {
32         enum ctdb_runstate runstate;
33         const char * label;
34 } runstate_map[] = {
35         { CTDB_RUNSTATE_UNKNOWN, "UNKNOWN" },
36         { CTDB_RUNSTATE_INIT, "INIT" },
37         { CTDB_RUNSTATE_SETUP, "SETUP" },
38         { CTDB_RUNSTATE_FIRST_RECOVERY, "FIRST_RECOVERY" },
39         { CTDB_RUNSTATE_STARTUP, "STARTUP" },
40         { CTDB_RUNSTATE_RUNNING, "RUNNING" },
41         { CTDB_RUNSTATE_SHUTDOWN, "SHUTDOWN" },
42         { -1, NULL },
43 };
44
45 const char *ctdb_runstate_to_string(enum ctdb_runstate runstate)
46 {
47         int i;
48
49         for (i=0; runstate_map[i].label != NULL; i++) {
50                 if (runstate_map[i].runstate == runstate) {
51                         return runstate_map[i].label;
52                 }
53         }
54
55         return runstate_map[0].label;
56 }
57
58 enum ctdb_runstate ctdb_runstate_from_string(const char *runstate_str)
59 {
60         int i;
61
62         for (i=0; runstate_map[i].label != NULL; i++) {
63                 if (strcasecmp(runstate_map[i].label,
64                                runstate_str) == 0) {
65                         return runstate_map[i].runstate;
66                 }
67         }
68
69         return CTDB_RUNSTATE_UNKNOWN;
70 }
71
72 static struct {
73         enum ctdb_event event;
74         const char *label;
75 } event_map[] = {
76         { CTDB_EVENT_INIT, "init" },
77         { CTDB_EVENT_SETUP, "setup" },
78         { CTDB_EVENT_STARTUP, "startup" },
79         { CTDB_EVENT_START_RECOVERY, "startrecovery" },
80         { CTDB_EVENT_RECOVERED, "recovered" },
81         { CTDB_EVENT_TAKE_IP, "takeip" },
82         { CTDB_EVENT_RELEASE_IP, "releaseip" },
83         { CTDB_EVENT_MONITOR, "monitor" },
84         { CTDB_EVENT_SHUTDOWN, "shutdown" },
85         { CTDB_EVENT_UPDATE_IP, "updateip" },
86         { CTDB_EVENT_IPREALLOCATED, "ipreallocated" },
87         { CTDB_EVENT_MAX, "all" },
88         { -1, NULL },
89 };
90
91 const char *ctdb_event_to_string(enum ctdb_event event)
92 {
93         int i;
94
95         for (i=0; event_map[i].label != NULL; i++) {
96                 if (event_map[i].event == event) {
97                         return event_map[i].label;
98                 }
99         }
100
101         return "unknown";
102 }
103
104 enum ctdb_event ctdb_event_from_string(const char *event_str)
105 {
106         int i;
107
108         for (i=0; event_map[i].label != NULL; i++) {
109                 if (strcmp(event_map[i].label, event_str) == 0) {
110                         return event_map[i].event;
111                 }
112         }
113
114         return CTDB_EVENT_MAX;
115 }
116
117 int ctdb_sock_addr_to_buf(char *buf, socklen_t buflen,
118                           ctdb_sock_addr *addr, bool with_port)
119 {
120         const char *t;
121
122         switch (addr->sa.sa_family) {
123         case AF_INET:
124                 t = inet_ntop(addr->ip.sin_family, &addr->ip.sin_addr,
125                               buf, buflen);
126                 if (t == NULL) {
127                         return errno;
128                 }
129                 break;
130
131         case AF_INET6:
132                 t = inet_ntop(addr->ip6.sin6_family, &addr->ip6.sin6_addr,
133                               buf, buflen);
134                 if (t == NULL) {
135                         return errno;
136                 }
137                 break;
138
139         default:
140                 return EAFNOSUPPORT;
141                 break;
142         }
143
144         if (with_port) {
145                 size_t len = strlen(buf);
146                 int ret;
147
148                 ret = snprintf(buf+len, buflen-len,
149                                ":%u", ctdb_sock_addr_port(addr));
150                 if (ret >= buflen-len) {
151                         return ENOSPC;
152                 }
153         }
154
155         return 0;
156 }
157
158 const char *ctdb_sock_addr_to_string(TALLOC_CTX *mem_ctx,
159                                      ctdb_sock_addr *addr, bool with_port)
160 {
161         size_t len = 64;
162         char *cip;
163         int ret;
164
165         cip = talloc_size(mem_ctx, len);
166
167         if (cip == NULL) {
168                 return NULL;
169         }
170
171         ret = ctdb_sock_addr_to_buf(cip, len, addr, with_port);
172         if (ret != 0) {
173                 talloc_free(cip);
174                 return NULL;
175         }
176
177         return cip;
178 }
179
180 static int ipv4_from_string(const char *str, struct sockaddr_in *ip)
181 {
182         int ret;
183
184         *ip = (struct sockaddr_in) {
185                 .sin_family = AF_INET,
186         };
187
188         ret = inet_pton(AF_INET, str, &ip->sin_addr);
189         if (ret != 1) {
190                 return EINVAL;
191         }
192
193 #ifdef HAVE_SOCK_SIN_LEN
194         ip->sin_len = sizeof(*ip);
195 #endif
196         return 0;
197 }
198
199 static int ipv6_from_string(const char *str, struct sockaddr_in6 *ip6)
200 {
201         int ret;
202
203         *ip6 = (struct sockaddr_in6) {
204                 .sin6_family   = AF_INET6,
205         };
206
207         ret = inet_pton(AF_INET6, str, &ip6->sin6_addr);
208         if (ret != 1) {
209                 return EINVAL;
210         }
211
212 #ifdef HAVE_SOCK_SIN6_LEN
213         ip6->sin6_len = sizeof(*ip6);
214 #endif
215         return 0;
216 }
217
218 static int ip_from_string(const char *str, ctdb_sock_addr *addr)
219 {
220         char *p;
221         int ret;
222
223         if (addr == NULL) {
224                 return EINVAL;
225         }
226
227         ZERO_STRUCTP(addr); /* valgrind :-) */
228
229         /* IPv4 or IPv6 address?
230          *
231          * Use rindex() because we need the right-most ':' below for
232          * IPv4-mapped IPv6 addresses anyway...
233          */
234         p = rindex(str, ':');
235         if (p == NULL) {
236                 ret = ipv4_from_string(str, &addr->ip);
237         } else {
238                 uint8_t ipv4_mapped_prefix[12] = {
239                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff
240                 };
241
242                 ret = ipv6_from_string(str, &addr->ip6);
243                 if (ret != 0) {
244                         return ret;
245                 }
246
247                 /*
248                  * Check for IPv4-mapped IPv6 address
249                  * (e.g. ::ffff:192.0.2.128) - reparse as IPv4 if
250                  * necessary
251                  */
252                 if (memcmp(&addr->ip6.sin6_addr.s6_addr[0],
253                            ipv4_mapped_prefix,
254                            sizeof(ipv4_mapped_prefix)) == 0) {
255                         /* Reparse as IPv4 */
256                         ret = ipv4_from_string(p+1, &addr->ip);
257                 }
258         }
259
260         return ret;
261 }
262
263 int ctdb_sock_addr_from_string(const char *str,
264                                ctdb_sock_addr *addr, bool with_port)
265 {
266         char *p;
267         char s[64]; /* Much longer than INET6_ADDRSTRLEN */
268         unsigned port;
269         char *endp = NULL;
270         size_t len;
271         int ret;
272
273         if (! with_port) {
274                 ret = ip_from_string(str, addr);
275                 return ret;
276         }
277
278         /* Parse out port number and then IP address */
279
280         len = strlcpy(s, str, sizeof(s));
281         if (len >= sizeof(s)) {
282                 return EINVAL;
283         }
284
285         p = rindex(s, ':');
286         if (p == NULL) {
287                 return EINVAL;
288         }
289
290         port = strtoul_err(p+1, &endp, 10, &ret);
291         if (endp == p+1 || *endp != '\0' || ret != 0) {
292                 /* Empty string or trailing garbage */
293                 return EINVAL;
294         }
295
296         *p = '\0';
297         ret = ip_from_string(s, addr);
298
299         ctdb_sock_addr_set_port(addr, port);
300
301         return ret;
302 }
303
304 int ctdb_sock_addr_mask_from_string(const char *str,
305                                     ctdb_sock_addr *addr,
306                                     unsigned int *mask)
307 {
308         char *p;
309         char s[64]; /* Much longer than INET6_ADDRSTRLEN */
310         unsigned int m;
311         char *endp = NULL;
312         ssize_t len;
313         int ret = 0;
314
315         if (addr == NULL || mask == NULL) {
316                 return EINVAL;
317         }
318
319         len = strlcpy(s, str, sizeof(s));
320         if (len >= sizeof(s)) {
321                 return EINVAL;
322         }
323
324         p = rindex(s, '/');
325         if (p == NULL) {
326                 return EINVAL;
327         }
328
329         m = strtoul_err(p+1, &endp, 10, &ret);
330         if (endp == p+1 || *endp != '\0' || ret != 0) {
331                 /* Empty string or trailing garbage */
332                 return EINVAL;
333         }
334
335         *p = '\0';
336         ret = ip_from_string(s, addr);
337
338         if (ret == 0) {
339                 *mask = m;
340         }
341
342         return ret;
343 }
344
345 unsigned int ctdb_sock_addr_port(ctdb_sock_addr *addr)
346 {
347         switch (addr->sa.sa_family) {
348         case AF_INET:
349                 return ntohs(addr->ip.sin_port);
350                 break;
351         case AF_INET6:
352                 return ntohs(addr->ip6.sin6_port);
353                 break;
354         default:
355                 return 0;
356         }
357 }
358
359 void ctdb_sock_addr_set_port(ctdb_sock_addr *addr, unsigned int port)
360 {
361         switch (addr->sa.sa_family) {
362         case AF_INET:
363                 addr->ip.sin_port = htons(port);
364                 break;
365         case AF_INET6:
366                 addr->ip6.sin6_port = htons(port);
367                 break;
368         default:
369                 break;
370         }
371 }
372
373 static int ctdb_sock_addr_cmp_family(const ctdb_sock_addr *addr1,
374                                      const ctdb_sock_addr *addr2)
375 {
376         /* This is somewhat arbitrary.  However, when used for sorting
377          * it just needs to be consistent.
378          */
379         if (addr1->sa.sa_family < addr2->sa.sa_family) {
380                 return -1;
381         }
382         if (addr1->sa.sa_family > addr2->sa.sa_family) {
383                 return 1;
384         }
385
386         return 0;
387 }
388
389 int ctdb_sock_addr_cmp_ip(const ctdb_sock_addr *addr1,
390                           const ctdb_sock_addr *addr2)
391 {
392         int ret;
393
394         ret = ctdb_sock_addr_cmp_family(addr1, addr2);
395         if (ret != 0) {
396                 return ret;
397         }
398
399         switch (addr1->sa.sa_family) {
400         case AF_INET:
401                 ret = memcmp(&addr1->ip.sin_addr.s_addr,
402                              &addr2->ip.sin_addr.s_addr, 4);
403                 break;
404
405         case AF_INET6:
406                 ret = memcmp(addr1->ip6.sin6_addr.s6_addr,
407                              addr2->ip6.sin6_addr.s6_addr, 16);
408                 break;
409
410         default:
411                 ret = -1;
412         }
413
414         return ret;
415 }
416
417 int ctdb_sock_addr_cmp(const ctdb_sock_addr *addr1,
418                        const ctdb_sock_addr *addr2)
419 {
420         int ret = 0;
421
422         ret = ctdb_sock_addr_cmp_ip(addr1, addr2);
423         if (ret != 0) {
424                 return ret;
425         }
426
427         switch (addr1->sa.sa_family) {
428         case AF_INET:
429                 if (addr1->ip.sin_port < addr2->ip.sin_port) {
430                         ret = -1;
431                 } else if (addr1->ip.sin_port > addr2->ip.sin_port) {
432                         ret = 1;
433                 }
434                 break;
435
436         case AF_INET6:
437                 if (addr1->ip6.sin6_port < addr2->ip6.sin6_port) {
438                         ret = -1;
439                 } else if (addr1->ip6.sin6_port > addr2->ip6.sin6_port) {
440                         ret = 1;
441                 }
442                 break;
443
444         default:
445                 ret = -1;
446         }
447
448         return ret;
449 }
450
451 bool ctdb_sock_addr_same_ip(const ctdb_sock_addr *addr1,
452                             const ctdb_sock_addr *addr2)
453 {
454         return (ctdb_sock_addr_cmp_ip(addr1, addr2) == 0);
455 }
456
457 bool ctdb_sock_addr_same(const ctdb_sock_addr *addr1,
458                          const ctdb_sock_addr *addr2)
459 {
460         return (ctdb_sock_addr_cmp(addr1, addr2) == 0);
461 }
462
463 int ctdb_connection_to_buf(char *buf, size_t buflen,
464                            struct ctdb_connection *conn, bool client_first)
465 {
466         char server[64], client[64];
467         int ret;
468
469         ret = ctdb_sock_addr_to_buf(server, sizeof(server),
470                                     &conn->server, true);
471         if (ret != 0) {
472                 return ret;
473         }
474
475         ret = ctdb_sock_addr_to_buf(client, sizeof(client),
476                                     &conn->client, true);
477         if (ret != 0) {
478                 return ret;
479         }
480
481         if (! client_first) {
482                 ret = snprintf(buf, buflen, "%s %s", server, client);
483         } else {
484                 ret = snprintf(buf, buflen, "%s %s", client, server);
485         }
486         if (ret >= buflen) {
487                 return ENOSPC;
488         }
489
490         return 0;
491 }
492
493 const char *ctdb_connection_to_string(TALLOC_CTX *mem_ctx,
494                                       struct ctdb_connection *conn,
495                                       bool client_first)
496 {
497         const size_t len = 128;
498         char *out;
499         int ret;
500
501         out = talloc_size(mem_ctx, len);
502         if (out == NULL) {
503                 return NULL;
504         }
505
506         ret = ctdb_connection_to_buf(out, len, conn, client_first);
507         if (ret != 0) {
508                 talloc_free(out);
509                 return NULL;
510         }
511
512         return out;
513 }
514
515 int ctdb_connection_from_string(const char *str, bool client_first,
516                                 struct ctdb_connection *conn)
517 {
518         char s[128];
519         char *t1 = NULL, *t2 = NULL;
520         size_t len;
521         ctdb_sock_addr *first = (client_first ? &conn->client : &conn->server);
522         ctdb_sock_addr *second = (client_first ? &conn->server : &conn->client);
523         int ret;
524
525         len = strlcpy(s, str, sizeof(s));
526         if (len >= sizeof(s)) {
527                 return EINVAL;
528         }
529
530         t1 = strtok(s, " \t\n");
531         if (t1 == NULL) {
532                 return EINVAL;
533         }
534
535         t2 = strtok(NULL, " \t\n\0");
536         if (t2 == NULL) {
537                 return EINVAL;
538         }
539
540         ret = ctdb_sock_addr_from_string(t1, first, true);
541         if (ret != 0) {
542                 return ret;
543         }
544
545         ret = ctdb_sock_addr_from_string(t2, second, true);
546         if (ret != 0) {
547                 return ret;
548         }
549
550         ret = ctdb_sock_addr_cmp_family(first, second);
551         if (ret != 0) {
552                 return EINVAL;
553         }
554
555         return 0;
556 }
557
558 int ctdb_connection_list_add(struct ctdb_connection_list *conn_list,
559                              struct ctdb_connection *conn)
560 {
561         uint32_t len;
562
563         if (conn_list == NULL) {
564                 return EINVAL;
565         }
566
567         /* Ensure array is big enough */
568         len = talloc_array_length(conn_list->conn);
569         if (conn_list->num == len) {
570                 conn_list->conn = talloc_realloc(conn_list, conn_list->conn,
571                                                  struct ctdb_connection,
572                                                  len+128);
573                 if (conn_list->conn == NULL) {
574                         return ENOMEM;
575                 }
576         }
577
578         conn_list->conn[conn_list->num] = *conn;
579         conn_list->num++;
580
581         return 0;
582 }
583
584 static int connection_cmp(const void *a, const void *b)
585 {
586         const struct ctdb_connection *conn_a = a;
587         const struct ctdb_connection *conn_b = b;
588         int ret;
589
590         ret = ctdb_sock_addr_cmp(&conn_a->server, &conn_b->server);
591         if (ret == 0) {
592                 ret = ctdb_sock_addr_cmp(&conn_a->client, &conn_b->client);
593         }
594
595         return ret;
596 }
597
598 int ctdb_connection_list_sort(struct ctdb_connection_list *conn_list)
599 {
600         if (conn_list == NULL) {
601                 return EINVAL;
602         }
603
604         if (conn_list->num > 0) {
605                 qsort(conn_list->conn, conn_list->num,
606                       sizeof(struct ctdb_connection), connection_cmp);
607         }
608
609         return 0;
610 }
611
612 const char *ctdb_connection_list_to_string(
613         TALLOC_CTX *mem_ctx,
614         struct ctdb_connection_list *conn_list, bool client_first)
615 {
616         uint32_t i;
617         char *out;
618
619         out = talloc_strdup(mem_ctx, "");
620         if (out == NULL) {
621                 return NULL;
622         }
623
624         if (conn_list == NULL || conn_list->num == 0) {
625                 return out;
626         }
627
628         for (i = 0; i < conn_list->num; i++) {
629                 char buf[128];
630                 int ret;
631
632                 ret = ctdb_connection_to_buf(buf, sizeof(buf),
633                                              &conn_list->conn[i], client_first);
634                 if (ret != 0) {
635                         talloc_free(out);
636                         return NULL;
637                 }
638
639                 out = talloc_asprintf_append(out, "%s\n", buf);
640                 if (out == NULL) {
641                         return NULL;
642                 }
643         }
644
645         return out;
646 }
647
648 struct ctdb_connection_list_read_state {
649         struct ctdb_connection_list *list;
650         bool client_first;
651 };
652
653 static int ctdb_connection_list_read_line(char *line, void *private_data)
654 {
655         struct ctdb_connection_list_read_state *state =
656                 (struct ctdb_connection_list_read_state *)private_data;
657         struct ctdb_connection conn;
658         int ret;
659
660         /* Skip empty lines */
661         if (line[0] == '\0') {
662                 return 0;
663         }
664
665         /* Comment */
666         if (line[0] == '#') {
667                 return 0;
668         }
669
670         ret = ctdb_connection_from_string(line, state->client_first, &conn);
671         if (ret != 0) {
672                 return ret;
673         }
674
675         ret = ctdb_connection_list_add(state->list, &conn);
676         if (ret != 0) {
677                 return ret;
678         }
679
680         return 0;
681 }
682
683 int ctdb_connection_list_read(TALLOC_CTX *mem_ctx,
684                               int fd,
685                               bool client_first,
686                               struct ctdb_connection_list **conn_list)
687 {
688         struct ctdb_connection_list_read_state state;
689         int ret;
690
691         if (conn_list == NULL) {
692                 return EINVAL;
693         }
694
695         state.list = talloc_zero(mem_ctx, struct ctdb_connection_list);
696         if (state.list == NULL) {
697                 return ENOMEM;
698         }
699
700         state.client_first = client_first;
701
702         ret = line_read(fd,
703                         128,
704                         mem_ctx,
705                         ctdb_connection_list_read_line,
706                         &state,
707                         NULL);
708
709         *conn_list = state.list;
710
711         return ret;
712 }