Code

07511ce070767d4a3f2e4c08655c490764301ffe
[sysdb.git] / src / utils / proto.c
1 /*
2  * SysDB - src/utils/proto.c
3  * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif
32 #include "core/data.h"
33 #include "core/store.h"
34 #include "core/time.h"
35 #include "utils/error.h"
36 #include "utils/proto.h"
38 #include <assert.h>
39 #include <errno.h>
41 #include <arpa/inet.h>
42 #include <limits.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
48 #include <sys/select.h>
50 /*
51  * private helper functions
52  */
54 /* swap endianess of the specified 64-bit value */
55 static uint64_t
56 endian_swap64(uint64_t v)
57 {
58         return
59                   (((v & 0xff00000000000000LL) >> 56) & 0x00000000000000ffLL)
60                 | (((v & 0x00ff000000000000LL) >> 40) & 0x000000000000ff00LL)
61                 | (((v & 0x0000ff0000000000LL) >> 24) & 0x0000000000ff0000LL)
62                 | (((v & 0x000000ff00000000LL) >> 8)  & 0x00000000ff000000LL)
63                 | (((v & 0x00000000ff000000LL) << 8)  & 0x000000ff00000000LL)
64                 | (((v & 0x0000000000ff0000LL) << 24) & 0x0000ff0000000000LL)
65                 | (((v & 0x000000000000ff00LL) << 40) & 0x00ff000000000000LL)
66                 | (((v & 0x00000000000000ffLL) << 56) & 0xff00000000000000LL);
67 } /* endian_swap64 */
69 static unsigned char *
70 memdup(const unsigned char *d, size_t length)
71 {
72         unsigned char *v = malloc(length);
73         if (! v)
74                 return NULL;
75         memcpy(v, d, length);
76         return v;
77 } /* memdup */
79 /* In case there's not enough buffer space, the marshal functions have to
80  * return the number of bytes that would have been written if enough space had
81  * been available. */
83 static ssize_t
84 marshal_int32(char *buf, size_t buf_len, uint32_t v)
85 {
86         if (buf_len >= sizeof(v)) {
87                 v = htonl(v);
88                 memcpy(buf, &v, sizeof(v));
89         }
90         return sizeof(v);
91 } /* marshal_int32 */
93 static ssize_t
94 marshal_int64(char *buf, size_t buf_len, int64_t v)
95 {
96         if (buf_len >= sizeof(v)) {
97 #if __BYTE_ORDER != __BIG_ENDIAN
98                 v = endian_swap64(v);
99 #endif
100                 memcpy(buf, &v, sizeof(v));
101         }
102         return sizeof(v);
103 } /* marshal_int64 */
105 static ssize_t
106 unmarshal_int64(const char *buf, size_t len, int64_t *v)
108         if (len < sizeof(*v))
109                 return -1;
110         if (v) {
111                 memcpy(v, buf, sizeof(*v));
112 #if __BYTE_ORDER != __BIG_ENDIAN
113                 *v = endian_swap64(*v);
114 #endif
115         }
116         return sizeof(*v);
117 } /* unmarshal_int64 */
119 static ssize_t
120 marshal_double(char *buf, size_t buf_len, double v)
122         uint64_t t = 0;
123         assert(sizeof(v) == sizeof(t));
124         if (buf_len >= sizeof(t)) {
125                 memcpy(&t, &v, sizeof(v));
126 #if IEEE754_DOUBLE_BYTE_ORDER != IEEE754_DOUBLE_BIG_ENDIAN
127                 t = endian_swap64(t);
128 #endif
129                 memcpy(buf, &t, sizeof(t));
130         }
131         return sizeof(t);
132 } /* marshal_double */
134 static ssize_t
135 unmarshal_double(const char *buf, size_t len, double *v)
137         uint64_t t;
138         assert(sizeof(*v) == sizeof(t));
139         if (len < sizeof(*v))
140                 return -1;
141         if (v) {
142                 memcpy(&t, buf, sizeof(t));
143 #if IEEE754_DOUBLE_BYTE_ORDER != IEEE754_DOUBLE_BIG_ENDIAN
144                 t = endian_swap64(t);
145 #endif
146                 memcpy(v, &t, sizeof(t));
147         }
148         return sizeof(*v);
149 } /* unmarshal_double */
151 static ssize_t
152 marshal_datetime(char *buf, size_t buf_len, sdb_time_t v)
154         return marshal_int64(buf, buf_len, (int64_t)v);
155 } /* marshal_datetime */
157 static ssize_t
158 unmarshal_datetime(const char *buf, size_t len, sdb_time_t *v)
160         return unmarshal_int64(buf, len, (int64_t *)v);
161 } /* unmarshal_datetime */
163 static ssize_t
164 marshal_binary(char *buf, size_t buf_len, size_t len, const unsigned char *v)
166         uint32_t tmp;
167         if (buf_len >= sizeof(tmp) + len) {
168                 tmp = htonl((uint32_t)len);
169                 memcpy(buf, &tmp, sizeof(tmp));
170                 memcpy(buf + sizeof(tmp), v, len);
171         }
172         return sizeof(tmp) + len;
173 } /* marshal_binary */
175 static ssize_t
176 unmarshal_binary(const char *buf, size_t len,
177                 size_t *v_len, const unsigned char **v)
179         uint32_t l;
180         ssize_t n;
182         if ((n = sdb_proto_unmarshal_int32(buf, len, &l)) < 0)
183                 return -1;
184         buf += n; len -= n;
185         if (len < (size_t)l)
186                 return -1;
188         if (v_len)
189                 *v_len = (size_t)l;
190         if (v && (l > 0))
191                 *v = (const unsigned char *)buf;
192         else if (v)
193                 *v = NULL;
194         return sizeof(l) + (ssize_t)l;
195 } /* unmarshal_binary */
197 static ssize_t
198 marshal_string(char *buf, size_t buf_len, const char *v)
200         /* The actual string including the terminating null byte. */
201         size_t len = strlen(v) + 1;
202         if (buf_len >= len)
203                 memcpy(buf, v, len);
204         return len;
205 } /* marshal_string */
207 static ssize_t
208 unmarshal_string(const char *buf, size_t len, const char **v)
210         size_t l = 0;
212         for (l = 0; l < len; ++l)
213                 if (buf[l] == '\0')
214                         break;
215         if (l == len)
216                 return -1;
217         if (v)
218                 *v = buf;
219         return l + 1;
220 } /* unmarshal_string */
222 #define OBJ_HEADER_LEN (sizeof(uint32_t) + sizeof(sdb_time_t))
223 static ssize_t
224 marshal_obj_header(char *buf, size_t buf_len,
225                 int type, sdb_time_t last_update)
227         ssize_t n;
229         if (buf_len < OBJ_HEADER_LEN)
230                 return OBJ_HEADER_LEN;
232         n = marshal_int32(buf, buf_len, (uint32_t)type);
233         buf += n; buf_len -= n;
234         marshal_datetime(buf, buf_len, last_update);
235         return OBJ_HEADER_LEN;
236 } /* marshal_obj_header */
238 /*
239  * public API
240  */
242 ssize_t
243 sdb_proto_marshal(char *buf, size_t buf_len, uint32_t code,
244                 uint32_t msg_len, const char *msg)
246         size_t len = 2 * sizeof(uint32_t) + msg_len;
247         uint32_t tmp;
249         if (buf_len < 2 * sizeof(uint32_t))
250                 return -1;
251         if (buf_len < len) /* crop message */
252                 msg_len -= (uint32_t)(len - buf_len);
254         tmp = htonl(code);
255         memcpy(buf, &tmp, sizeof(tmp));
256         tmp = htonl(msg_len);
257         memcpy(buf + sizeof(tmp), &tmp, sizeof(tmp));
259         if (msg_len)
260                 memcpy(buf + 2 * sizeof(tmp), msg, msg_len);
261         return len;
262 } /* sdb_proto_marshal */
264 ssize_t
265 sdb_proto_marshal_data(char *buf, size_t buf_len, const sdb_data_t *datum)
267         ssize_t len = 0, n = 0;
268         uint32_t tmp;
269         size_t i;
270         int type;
272         if (buf_len >= sizeof(tmp)) {
273                 tmp = htonl((uint32_t)datum->type);
274                 memcpy(buf, &tmp, sizeof(tmp));
275                 buf += sizeof(tmp);
276                 buf_len -= sizeof(tmp);
277         }
278         else
279                 buf_len = 0;
280         len += sizeof(tmp);
282         if (datum->type == SDB_TYPE_NULL)
283                 return len;
285         if (datum->type == SDB_TYPE_INTEGER)
286                 n = marshal_int64(buf, buf_len, datum->data.integer);
287         else if (datum->type == SDB_TYPE_DECIMAL)
288                 n = marshal_double(buf, buf_len, datum->data.decimal);
289         else if (datum->type == SDB_TYPE_STRING)
290                 n = marshal_string(buf, buf_len, datum->data.string);
291         else if (datum->type == SDB_TYPE_DATETIME)
292                 n = marshal_datetime(buf, buf_len, datum->data.datetime);
293         else if (datum->type == SDB_TYPE_BINARY)
294                 n = marshal_binary(buf, buf_len,
295                                 datum->data.binary.length, datum->data.binary.datum);
296         else if (datum->type == SDB_TYPE_REGEX)
297                 n = marshal_string(buf, buf_len, datum->data.re.raw);
299         if (n < 0)
300                 return n;
301         else if (n > 0)
302                 return len + n;
304         if (! (datum->type & SDB_TYPE_ARRAY)) {
305                 errno = EINVAL;
306                 return -1;
307         }
309         /* arrays */
310         if (buf_len >= sizeof(tmp)) {
311                 tmp = htonl((uint32_t)datum->data.array.length);
312                 memcpy(buf, &tmp, sizeof(tmp));
313                 buf += sizeof(tmp);
314                 buf_len -= sizeof(tmp);
315         }
316         else
317                 buf_len = 0;
318         len += sizeof(tmp);
320         type = datum->type & 0xff;
321         for (i = 0; i < datum->data.array.length; ++i) {
322                 if (type == SDB_TYPE_INTEGER) {
323                         int64_t *v = datum->data.array.values;
324                         n = marshal_int64(buf, buf_len, v[i]);
325                 }
326                 else if (type == SDB_TYPE_DECIMAL) {
327                         double *v = datum->data.array.values;
328                         n = marshal_double(buf, buf_len, v[i]);
329                 }
330                 else if (type == SDB_TYPE_STRING) {
331                         char **v = datum->data.array.values;
332                         n = marshal_string(buf, buf_len, v[i]);
333                 }
334                 else if (type == SDB_TYPE_DATETIME) {
335                         sdb_time_t *v = datum->data.array.values;
336                         n = marshal_datetime(buf, buf_len, v[i]);
337                 }
338                 else if (type == SDB_TYPE_BINARY) {
339                         struct {
340                                 size_t length;
341                                 unsigned char *datum;
342                         } *v = datum->data.array.values;
343                         n = marshal_binary(buf, buf_len, v[i].length, v[i].datum);
344                 }
345                 else if (type == SDB_TYPE_REGEX) {
346                         struct {
347                                 char *raw;
348                                 regex_t regex;
349                         } *v = datum->data.array.values;
350                         n = marshal_string(buf, buf_len, v[i].raw);
351                 }
352                 else {
353                         errno = EINVAL;
354                         return -1;
355                 }
357                 if (n < 0)
358                         return -1;
359                 if (buf_len >= (size_t)n) {
360                         buf += n;
361                         buf_len -= n;
362                 }
363                 else
364                         buf_len = 0;
365                 len += n;
366         }
367         return len;
368 } /* sdb_proto_marshal_data */
370 ssize_t
371 sdb_proto_marshal_host(char *buf, size_t buf_len,
372                 const sdb_proto_host_t *host)
374         size_t len;
375         ssize_t n;
377         if ((! host) || (! host->name))
378                 return -1;
380         len = OBJ_HEADER_LEN + strlen(host->name) + 1;
381         if (buf_len < len)
382                 return len;
384         n = marshal_obj_header(buf, buf_len, SDB_HOST, host->last_update);
385         buf += n; buf_len -= n;
386         marshal_string(buf, buf_len, host->name);
387         return len;
388 } /* sdb_proto_marshal_host */
390 ssize_t
391 sdb_proto_marshal_service(char *buf, size_t buf_len,
392                 const sdb_proto_service_t *svc)
394         size_t len;
395         ssize_t n;
397         if ((! svc) || (! svc->hostname) || (! svc->name))
398                 return -1;
400         len = OBJ_HEADER_LEN + strlen(svc->hostname) + strlen(svc->name) + 2;
401         if (buf_len < len)
402                 return len;
404         n = marshal_obj_header(buf, buf_len, SDB_SERVICE, svc->last_update);
405         buf += n; buf_len -= n;
406         n = marshal_string(buf, buf_len, svc->hostname);
407         buf += n; buf_len -= n;
408         marshal_string(buf, buf_len, svc->name);
409         return len;
410 } /* sdb_proto_marshal_service */
412 ssize_t
413 sdb_proto_marshal_metric(char *buf, size_t buf_len,
414                 const sdb_proto_metric_t *metric)
416         size_t len;
417         ssize_t n;
419         if ((! metric) || (! metric->hostname) || (! metric->name))
420                 return -1;
422         len = OBJ_HEADER_LEN + strlen(metric->hostname) + strlen(metric->name) + 2;
423         if (metric->store_type && metric->store_id)
424                 len += strlen(metric->store_type) + strlen(metric->store_id) + 2;
425         if (buf_len < len)
426                 return len;
428         n = marshal_obj_header(buf, buf_len, SDB_METRIC, metric->last_update);
429         buf += n; buf_len -= n;
430         n = marshal_string(buf, buf_len, metric->hostname);
431         buf += n; buf_len -= n;
432         n = marshal_string(buf, buf_len, metric->name);
433         buf += n; buf_len -= n;
434         if (metric->store_type && metric->store_id) {
435                 n = marshal_string(buf, buf_len, metric->store_type);
436                 buf += n; buf_len -= n;
437                 marshal_string(buf, buf_len, metric->store_id);
438         }
439         return len;
440 } /* sdb_proto_marshal_metric */
442 ssize_t
443 sdb_proto_marshal_attribute(char *buf, size_t buf_len,
444                 const sdb_proto_attribute_t *attr)
446         size_t len;
447         ssize_t n;
449         if ((! attr) || (! attr->parent) || (! attr->key) || (! attr->value)
450                         || ((attr->parent_type != SDB_HOST) && (! attr->hostname))
451                         || ((attr->parent_type != SDB_HOST)
452                                 && (attr->parent_type != SDB_SERVICE)
453                                 && (attr->parent_type != SDB_METRIC)))
454                 return -1;
456         n = sdb_proto_marshal_data(NULL, 0, attr->value);
457         if (n < 0)
458                 return -1;
460         len = OBJ_HEADER_LEN
461                 + strlen(attr->parent) + strlen(attr->key) + 2 + (size_t)n;
462         if (attr->parent_type != SDB_HOST)
463                 len += strlen(attr->hostname) + 1;
464         if (buf_len < len)
465                 return len;
467         n = marshal_obj_header(buf, buf_len,
468                         attr->parent_type | SDB_ATTRIBUTE, attr->last_update);
469         buf += n; buf_len -= n;
470         if (attr->parent_type != SDB_HOST) {
471                 n = marshal_string(buf, buf_len, attr->hostname);
472                 buf += n; buf_len -= n;
473         }
474         n = marshal_string(buf, buf_len, attr->parent);
475         buf += n; buf_len -= n;
476         n = marshal_string(buf, buf_len, attr->key);
477         buf += n; buf_len -= n;
478         sdb_proto_marshal_data(buf, buf_len, attr->value);
479         return len;
480 } /* sdb_proto_marshal_attribute */
482 ssize_t
483 sdb_proto_unmarshal_header(const char *buf, size_t buf_len,
484                 uint32_t *code, uint32_t *msg_len)
486         uint32_t tmp;
487         ssize_t n;
489         if (buf_len < 2 * sizeof(uint32_t))
490                 return -1;
492         n = sdb_proto_unmarshal_int32(buf, buf_len, &tmp);
493         if (code)
494                 *code = tmp;
495         buf += n; buf_len -= n;
496         sdb_proto_unmarshal_int32(buf, buf_len, &tmp);
497         if (msg_len)
498                 *msg_len = tmp;
499         return 2 * sizeof(uint32_t);
500 } /* sdb_proto_unmarshal_header */
502 ssize_t
503 sdb_proto_unmarshal_int32(const char *buf, size_t buf_len, uint32_t *v)
505         uint32_t n;
507         /* not enough data to read */
508         if (buf_len < sizeof(n))
509                 return -1;
511         memcpy(&n, buf, sizeof(n));
512         if (v)
513                 *v = ntohl(n);
514         return sizeof(n);
515 } /* sdb_proto_unmarshal_int32 */
517 ssize_t
518 sdb_proto_unmarshal_data(const char *buf, size_t len, sdb_data_t *datum)
520         sdb_data_t d = SDB_DATA_INIT;
521         ssize_t l = 0, n;
522         uint32_t tmp;
523         size_t i;
525         if ((n = sdb_proto_unmarshal_int32(buf, len, &tmp)) < 0)
526                 return -1;
527         d.type = (int)tmp;
528         if (d.type == SDB_TYPE_NULL)
529                 return sizeof(tmp);
530         buf += n; len -= n; l += n;
532 /* Don't populate 'd' if 'datum' is NULL. */
533 #define D(field) (datum ? &d.data.field : NULL)
534         if (d.type == SDB_TYPE_INTEGER)
535                 n = unmarshal_int64(buf, len, D(integer));
536         else if (d.type == SDB_TYPE_DECIMAL)
537                 n = unmarshal_double(buf, len, D(decimal));
538         else if (d.type == SDB_TYPE_STRING) {
539                 const char *str = NULL;
540                 n = unmarshal_string(buf, len, &str);
541                 if ((n > 0) && datum)
542                         if (! (d.data.string = strdup(str)))
543                                 n = -1;
544         }
545         else if (d.type == SDB_TYPE_DATETIME)
546                 n = unmarshal_datetime(buf, len, D(datetime));
547         else if (d.type == SDB_TYPE_BINARY) {
548                 const unsigned char *data = NULL;
549                 n = unmarshal_binary(buf, len, D(binary.length), &data);
550                 if ((n > 0) && datum)
551                         if (! (d.data.binary.datum = memdup(data, d.data.binary.length)))
552                                 n = -1;
553         }
554         else if (d.type == SDB_TYPE_REGEX) {
555                 if (datum) {
556                         const char *str = NULL;
557                         n = unmarshal_string(buf, len, &str);
558                         if (sdb_data_parse(str, SDB_TYPE_REGEX, &d))
559                                 n = -1;
560                 }
561                 else
562                         n = unmarshal_string(buf, len, NULL);
563         }
564         else
565                 n = 0;
566 #undef D
568         if (n < 0)
569                 return n;
570         else if (n > 0) {
571                 if (datum)
572                         *datum = d;
573                 return l + n;
574         }
576         if (! (d.type & SDB_TYPE_ARRAY)) {
577                 errno = EINVAL;
578                 return -1;
579         }
581         /* arrays */
582         if ((n = sdb_proto_unmarshal_int32(buf, len, &tmp)) < 0)
583                 return -1;
584         buf += n; len -= n; l += n;
585         d.data.array.length = (size_t)tmp;
587 #define V(field) (datum ? &v[i]field : NULL)
588         if (datum)
589                 d.data.array.values = calloc(d.data.array.length,
590                                 sdb_data_sizeof(d.type & 0xff));
591         for (i = 0; i < d.data.array.length; ++i) {
592                 if ((d.type & 0xff) == SDB_TYPE_INTEGER) {
593                         int64_t *v = d.data.array.values;
594                         n = unmarshal_int64(buf, len, V());
595                 }
596                 else if ((d.type & 0xff) == SDB_TYPE_DECIMAL) {
597                         double *v = d.data.array.values;
598                         n = unmarshal_double(buf, len, V());
599                 }
600                 else if ((d.type & 0xff) == SDB_TYPE_STRING) {
601                         char **v = d.data.array.values;
602                         const char *str = NULL;
603                         n = unmarshal_string(buf, len, &str);
604                         if ((n > 0) && datum)
605                                 if (! (v[i] = strdup(str)))
606                                         n = -1;
607                 }
608                 else if ((d.type & 0xff) == SDB_TYPE_DATETIME) {
609                         sdb_time_t *v = d.data.array.values;
610                         n = unmarshal_datetime(buf, len, V());
611                 }
612                 else if ((d.type & 0xff) == SDB_TYPE_BINARY) {
613                         struct {
614                                 size_t length;
615                                 unsigned char *datum;
616                         } *v = d.data.array.values;
617                         const unsigned char *data = NULL;
619                         n = unmarshal_binary(buf, len, V(.length), &data);
620                         if ((n > 0) && datum)
621                                 if (! (v[i].datum = memdup(data, v[i].length)))
622                                         n = -1;
623                 }
624                 else if ((d.type & 0xff) == SDB_TYPE_REGEX) {
625                         struct {
626                                 char *raw;
627                                 regex_t regex;
628                         } *v = d.data.array.values;
629                         if (datum) {
630                                 sdb_data_t t = SDB_DATA_INIT;
631                                 const char *str = NULL;
632                                 n = unmarshal_string(buf, len, &str);
633                                 if (! sdb_data_parse(str, SDB_TYPE_REGEX, &t)) {
634                                         v[i].raw = t.data.re.raw;
635                                         v[i].regex = t.data.re.regex;
636                                 }
637                                 else
638                                         n = -1;
639                         }
640                         else
641                                 n = unmarshal_string(buf, len, NULL);
642                 }
643                 else {
644                         if (datum)
645                                 sdb_data_free_datum(&d);
646                         errno = EINVAL;
647                         return -1;
648                 }
650                 if (n < 0) {
651                         if (datum)
652                                 sdb_data_free_datum(&d);
653                         return -1;
654                 }
655                 if (len >= (size_t)n) {
656                         buf += n;
657                         len -= n;
658                         l += n;
659                 }
660                 else
661                         return -1;
662         }
663 #undef V
665         if (datum)
666                 *datum = d;
667         return l;
668 } /* sdb_proto_unmarshal_data */
670 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */