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