Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 :
15 : #include "msettings.h"
16 : #include "mstring.h"
17 :
18 : #include <assert.h>
19 : #include <ctype.h>
20 : #include <limits.h>
21 : #include <stdarg.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #ifdef HAVE_STRINGS_H
26 : #include <strings.h>
27 : #endif
28 :
29 : #define FATAL() do { fprintf(stderr, "\n\n abort in msettings.c: %s\n\n", __func__); abort(); } while (0)
30 :
31 : static const char * const MALLOC_FAILED = "malloc failed";
32 :
33 : bool
34 0 : msettings_malloc_failed(msettings_error err)
35 : {
36 0 : return ((const char*)err == (const char*)MALLOC_FAILED);
37 : }
38 :
39 :
40 :
41 862 : int msetting_parse_bool(const char *text)
42 : {
43 862 : static struct { const char *word; bool value; } variants[] = {
44 : { "true", true },
45 : { "false", false },
46 : { "yes", true },
47 : { "no", false },
48 : { "on", true },
49 : { "off", false },
50 : };
51 4170 : for (size_t i = 0; i < sizeof(variants) / sizeof(variants[0]); i++)
52 4156 : if (strcasecmp(text, variants[i].word) == 0)
53 848 : return variants[i].value;
54 : return -1;
55 : }
56 :
57 : char* allocprintf(const char *fmt, ...)
58 : __attribute__((__format__(__printf__, 1, 2)));
59 :
60 : char *
61 680 : allocprintf(const char *fmt, ...)
62 : {
63 680 : va_list ap;
64 680 : char *buf = NULL;
65 680 : size_t pos = 0, cap = 0;
66 680 : int n;
67 :
68 680 : va_start(ap, fmt);
69 680 : n = vreallocprintf(&buf, &pos, &cap, fmt, ap);
70 680 : va_end(ap);
71 :
72 680 : if (n >= 0)
73 680 : return buf;
74 0 : free(buf);
75 0 : return NULL;
76 : }
77 :
78 :
79 :
80 :
81 : static const struct { const char *name; mparm parm; }
82 : by_name[] = {
83 : { .name="autocommit", .parm=MP_AUTOCOMMIT },
84 : { .name="binary", .parm=MP_BINARY },
85 : { .name="cert", .parm=MP_CERT },
86 : { .name="certhash", .parm=MP_CERTHASH },
87 : { .name="client_application", .parm=MP_CLIENT_APPLICATION },
88 : { .name="client_info", .parm=MP_CLIENT_INFO },
89 : { .name="client_remark", .parm=MP_CLIENT_REMARK },
90 : { .name="clientcert", .parm=MP_CLIENTCERT },
91 : { .name="clientkey", .parm=MP_CLIENTKEY },
92 : { .name="connect_timeout", .parm=MP_CONNECT_TIMEOUT },
93 : { .name="database", .parm=MP_DATABASE },
94 : { .name="host", .parm=MP_HOST },
95 : { .name="language", .parm=MP_LANGUAGE },
96 : { .name="map_to_long_varchar", .parm=MP_MAPTOLONGVARCHAR },
97 : { .name="password", .parm=MP_PASSWORD },
98 : { .name="port", .parm=MP_PORT },
99 : { .name="reply_timeout", .parm=MP_REPLY_TIMEOUT },
100 : { .name="replysize", .parm=MP_REPLYSIZE },
101 : { .name="fetchsize", .parm=MP_REPLYSIZE },
102 : { .name="schema", .parm=MP_SCHEMA },
103 : { .name="sock", .parm=MP_SOCK },
104 : { .name="sockdir", .parm=MP_SOCKDIR},
105 : { .name="table", .parm=MP_TABLE },
106 : { .name="tableschema", .parm=MP_TABLESCHEMA },
107 : { .name="timezone", .parm=MP_TIMEZONE },
108 : { .name="tls", .parm=MP_TLS },
109 : { .name="user", .parm=MP_USER },
110 : //
111 : { .name="logfile", .parm=MP_LOGFILE },
112 : //
113 : { .name="hash", .parm=MP_IGNORE },
114 : { .name="debug", .parm=MP_IGNORE },
115 : };
116 :
117 : mparm
118 521 : mparm_parse(const char *name)
119 : {
120 521 : int n = sizeof(by_name) / sizeof(by_name[0]);
121 : // could use a binary search but this is not going to be a bottleneck
122 7970 : for (int i = 0; i < n; i++)
123 7965 : if (strcmp(by_name[i].name, name) == 0)
124 516 : return by_name[i].parm;
125 :
126 5 : return strchr(name, '_') ? MP_IGNORE : MP_UNKNOWN;
127 : }
128 :
129 : mparm
130 0 : mparm_enumerate(int i)
131 : {
132 0 : int n = sizeof(by_name) / sizeof(by_name[0]);
133 0 : if (i < 0 || i >= n)
134 : return MP_UNKNOWN;
135 0 : return by_name[i].parm;
136 : }
137 :
138 : const char *
139 24 : mparm_name(mparm parm)
140 : {
141 24 : switch (parm) {
142 : case MP_AUTOCOMMIT: return "autocommit";
143 0 : case MP_BINARY: return "binary";
144 0 : case MP_CERT: return "cert";
145 10 : case MP_CERTHASH: return "certhash";
146 0 : case MP_CLIENT_APPLICATION: return "client_application";
147 0 : case MP_CLIENT_INFO: return "client_info";
148 0 : case MP_CLIENT_REMARK: return "client_remark";
149 0 : case MP_CLIENTCERT: return "clientcert";
150 0 : case MP_CLIENTKEY: return "clientkey";
151 0 : case MP_CONNECT_TIMEOUT: return "connect_timeout";
152 1 : case MP_DATABASE: return "database";
153 1 : case MP_HOST: return "host";
154 0 : case MP_LANGUAGE: return "language";
155 0 : case MP_LOGFILE: return "logfile";
156 0 : case MP_MAPTOLONGVARCHAR: return "map_to_long_varchar";
157 0 : case MP_PASSWORD: return "password";
158 1 : case MP_PORT: return "port";
159 0 : case MP_REPLY_TIMEOUT: return "reply_timeout"; // underscore present means specific to this client library
160 2 : case MP_REPLYSIZE: return "replysize"; // no underscore means mandatory for all client libraries
161 0 : case MP_SCHEMA: return "schema";
162 0 : case MP_SOCK: return "sock";
163 0 : case MP_SOCKDIR: return "sockdir";
164 1 : case MP_TABLE: return "table";
165 1 : case MP_TABLESCHEMA: return "tableschema";
166 2 : case MP_TIMEZONE: return "timezone";
167 1 : case MP_TLS: return "tls";
168 0 : case MP_USER: return "user";
169 0 : default: FATAL();
170 : }
171 : }
172 :
173 : bool
174 960 : mparm_is_core(mparm parm)
175 : {
176 960 : switch (parm) {
177 : case MP_TLS:
178 : case MP_HOST:
179 : case MP_PORT:
180 : case MP_DATABASE:
181 : case MP_TABLESCHEMA:
182 : case MP_TABLE:
183 : return true;
184 831 : default:
185 831 : return false;
186 : }
187 : }
188 :
189 : struct string {
190 : char *str;
191 : bool must_free;
192 : };
193 :
194 : struct msettings {
195 : // Must match EXACTLY the order of enum mparm
196 : bool dummy_start_bool;
197 : bool tls;
198 : bool autocommit;
199 : bool client_info;
200 : bool dummy_end_bool;
201 :
202 : // Must match EXACTLY the order of enum mparm
203 : long dummy_start_long;
204 : long port;
205 : long timezone;
206 : long replysize;
207 : long map_to_long_varchar;
208 : long connect_timeout;
209 : long reply_timeout;
210 : long dummy_end_long;
211 :
212 : // Must match EXACTLY the order of enum mparm
213 : struct string dummy_start_string;
214 : struct string sock;
215 : struct string sockdir;
216 : struct string cert;
217 : struct string clientkey;
218 : struct string clientcert;
219 : struct string host;
220 : struct string database;
221 : struct string tableschema;
222 : struct string table;
223 : struct string certhash;
224 : struct string user;
225 : struct string password;
226 : struct string language;
227 : struct string schema;
228 : struct string binary;
229 : struct string logfile;
230 : struct string client_application;
231 : struct string client_remark;
232 : struct string dummy_end_string;
233 :
234 : char **unknown_parameters;
235 : size_t nr_unknown;
236 :
237 : bool lang_is_mal;
238 : bool lang_is_sql;
239 : long user_generation;
240 : long password_generation;
241 : char *unix_sock_name_buffer;
242 : char certhash_digits_buffer[64 + 2 + 1]; // fit more than required plus trailing '\0'
243 : bool validated;
244 : const char* (*localizer)(const void *data, mparm parm);
245 : void *localizer_data;
246 : char error_message[256];
247 : };
248 :
249 : static
250 : const msettings msettings_default_values = {
251 : .tls = false,
252 : .autocommit = true,
253 : .client_info = true,
254 :
255 : .port = -1 ,
256 : .timezone = 0,
257 : .replysize = 100,
258 :
259 : .sockdir = { "/tmp", false },
260 : .binary = { "on", false },
261 :
262 : .unknown_parameters = NULL,
263 : .nr_unknown = 0,
264 :
265 : .lang_is_mal = false,
266 : .lang_is_sql = true,
267 : .unix_sock_name_buffer = NULL,
268 :
269 : .validated = false,
270 : };
271 :
272 : const msettings *msettings_default = &msettings_default_values;
273 :
274 564 : msettings *msettings_create(void)
275 : {
276 564 : msettings *mp = malloc(sizeof(*mp));
277 564 : if (!mp) {
278 : return NULL;
279 : }
280 564 : *mp = msettings_default_values;
281 564 : return mp;
282 : }
283 :
284 85 : msettings *msettings_clone(const msettings *orig)
285 : {
286 85 : msettings *mp = malloc(sizeof(*mp));
287 85 : char **unknowns = orig->nr_unknown > 0 ? calloc(2 * orig->nr_unknown, sizeof(char*)) : NULL;
288 85 : const char *namebuf = orig->unix_sock_name_buffer;
289 85 : char *cloned_name_buffer = namebuf ? strdup(namebuf) : NULL;
290 85 : if (!mp || (orig->nr_unknown > 0 && !unknowns) || (namebuf && !cloned_name_buffer)) {
291 0 : free(mp);
292 0 : free(unknowns);
293 0 : free(cloned_name_buffer);
294 0 : return NULL;
295 : }
296 85 : *mp = *orig;
297 85 : mp->unknown_parameters = unknowns;
298 85 : mp->unix_sock_name_buffer = cloned_name_buffer;
299 :
300 : // now we have to very carefully duplicate the strings.
301 : // taking care to only free our own ones if that fails
302 :
303 85 : struct string *start = &mp->dummy_start_string;
304 85 : struct string *end = &mp->dummy_end_string;
305 85 : struct string *p = start;
306 1700 : while (p < end) {
307 1615 : if (p->must_free) {
308 148 : p->str = strdup(p->str);
309 148 : if (p->str == NULL)
310 0 : goto bailout;
311 : }
312 1615 : p++;
313 : }
314 :
315 85 : for (size_t i = 0; i < 2 * mp->nr_unknown; i++) {
316 0 : assert(orig->unknown_parameters[i]);
317 0 : char *u = strdup(orig->unknown_parameters[i]);
318 0 : if (u == NULL)
319 0 : goto bailout;
320 0 : mp->unknown_parameters[i] = u;
321 : }
322 :
323 : return mp;
324 :
325 0 : bailout:
326 0 : for (struct string *q = start; q < p; q++)
327 0 : if (q->must_free)
328 0 : free(q->str);
329 0 : for (size_t i = 0; i < 2 * mp->nr_unknown; i++)
330 0 : free(mp->unknown_parameters[i]);
331 0 : free(mp->unix_sock_name_buffer);
332 0 : free(mp);
333 0 : return NULL;
334 : }
335 :
336 : void
337 32 : msettings_reset(msettings *mp)
338 : {
339 : // free modified string settings
340 32 : struct string *start = &mp->dummy_start_string;
341 32 : struct string *end = &mp->dummy_end_string;
342 640 : for (struct string *p = start; p < end; p++) {
343 608 : if (p->must_free)
344 131 : free(p->str);
345 : }
346 :
347 : // free unknown parameters
348 32 : if (mp->nr_unknown) {
349 0 : for (size_t i = 0; i < 2 * mp->nr_unknown; i++)
350 0 : free(mp->unknown_parameters[i]);
351 0 : free(mp->unknown_parameters);
352 : }
353 :
354 : // free the buffer
355 32 : free(mp->unix_sock_name_buffer);
356 :
357 : // keep the localizer
358 32 : void *localizer = mp->localizer;
359 32 : void *localizer_data = mp->localizer_data;
360 :
361 : // now overwrite the whole thing
362 32 : *mp = *msettings_default;
363 32 : mp->localizer = localizer;
364 32 : mp->localizer_data = localizer_data;
365 32 : }
366 :
367 : msettings *
368 658 : msettings_destroy(msettings *mp)
369 : {
370 658 : if (mp == NULL)
371 : return NULL;
372 :
373 11590 : for (struct string *p = &mp->dummy_start_string + 1; p < &mp->dummy_end_string; p++) {
374 10980 : if (p->must_free)
375 2265 : free(p->str);
376 : }
377 616 : for (size_t i = 0; i < mp->nr_unknown; i++) {
378 6 : free(mp->unknown_parameters[2 * i]);
379 6 : free(mp->unknown_parameters[2 * i + 1]);
380 : }
381 610 : free(mp->unknown_parameters);
382 610 : free(mp->unix_sock_name_buffer);
383 610 : free(mp);
384 :
385 610 : return NULL;
386 : }
387 :
388 : static const char *format_error(msettings *mp, const char *fmt, ...)
389 : __attribute__((__format__(__printf__, 2, 3)));
390 :
391 : static const char *
392 25 : format_error(msettings *mp, const char *fmt, ...)
393 : {
394 25 : va_list ap;
395 25 : va_start(ap, fmt);
396 25 : vsnprintf(mp->error_message, sizeof(mp->error_message), fmt, ap);
397 25 : va_end(ap);
398 :
399 25 : return mp->error_message;
400 : }
401 :
402 24 : const char *msetting_parm_name(const msettings *mp, mparm parm)
403 : {
404 24 : const char *localized = NULL;
405 24 : if (mp->localizer)
406 0 : localized = (mp->localizer)(mp->localizer_data, parm);
407 24 : return localized ? localized : mparm_name(parm);
408 : }
409 :
410 41 : void msettings_set_localizer(msettings *mp, const char* (*localizer)(const void *data, mparm parm), void *data)
411 : {
412 41 : mp->localizer = localizer;
413 41 : mp->localizer_data = data;
414 41 : }
415 :
416 :
417 :
418 : const char*
419 29970 : msetting_string(const msettings *mp, mparm parm)
420 : {
421 29970 : if (mparm_classify(parm) != MPCLASS_STRING) {
422 1 : FATAL();
423 : return "";
424 : }
425 29969 : int i = parm - MP__STRING_START;
426 29969 : struct string const *p = &mp->dummy_start_string + 1 + i;
427 29969 : if (p >= &mp->dummy_end_string) {
428 0 : FATAL();
429 : return "";
430 : }
431 29969 : char *s = p->str;
432 :
433 29969 : if (s == NULL) {
434 15588 : if (parm == MP_LANGUAGE)
435 : return "sql";
436 15544 : else if (parm == MP_BINARY)
437 : return "on";
438 15544 : return "";
439 : }
440 : return s;
441 : }
442 :
443 :
444 : msettings_error
445 3749 : msetting_set_string(msettings *mp, mparm parm, const char* value)
446 : {
447 3749 : assert(value != NULL);
448 :
449 3749 : if (mparm_classify(parm) != MPCLASS_STRING)
450 0 : FATAL();
451 3750 : int i = parm - MP__STRING_START;
452 3750 : struct string *p = &mp->dummy_start_string + 1 + i;
453 3750 : if (p >= &mp->dummy_end_string)
454 0 : FATAL();
455 :
456 3750 : char *v = strdup(value);
457 3750 : if (!v)
458 : return MALLOC_FAILED;
459 3750 : if (p->must_free)
460 1289 : free(p->str);
461 3750 : p->str = v;
462 3750 : p->must_free = true;
463 :
464 3750 : switch (parm) {
465 459 : case MP_USER:
466 459 : mp->user_generation++;
467 459 : break;
468 663 : case MP_PASSWORD:
469 663 : mp->password_generation++;
470 663 : break;
471 396 : case MP_LANGUAGE:
472 396 : mp->lang_is_mal = false;
473 396 : mp->lang_is_sql = false;
474 : // Tricky logic, a mixture of strstr==val and strcmp
475 : // strstr==val is a clever way to compute 'startswith'
476 396 : if (strcmp(value, "mal") == 0 || strcmp(value, "msql") == 0)
477 201 : mp->lang_is_mal = true;
478 195 : else if (strstr(value, "sql") == value)
479 193 : mp->lang_is_sql = true;
480 : break;
481 : default:
482 : break;
483 : }
484 :
485 3750 : mp->validated = false;
486 3750 : return NULL;
487 : }
488 :
489 :
490 : long
491 41426 : msetting_long(const msettings *mp, mparm parm)
492 : {
493 41426 : if (mparm_classify(parm) != MPCLASS_LONG)
494 0 : FATAL();
495 41426 : int i = parm - MP__LONG_START;
496 41426 : const long * p = &mp->dummy_start_long + 1 + i;
497 41426 : if (p >= &mp->dummy_end_long)
498 0 : FATAL();
499 :
500 41426 : return *p;
501 : }
502 :
503 :
504 : msettings_error
505 1530 : msetting_set_long(msettings *mp, mparm parm, long value)
506 : {
507 1530 : if (mparm_classify(parm) != MPCLASS_LONG)
508 0 : FATAL();
509 1530 : int i = parm - MP__LONG_START;
510 1530 : long *p = &mp->dummy_start_long + 1 + i;
511 1530 : if (p >= &mp->dummy_end_long)
512 0 : FATAL();
513 :
514 1530 : *p = value;
515 :
516 1530 : mp->validated = false;
517 1530 : return NULL;
518 : }
519 :
520 :
521 : bool
522 8259 : msetting_bool(const msettings *mp, mparm parm)
523 : {
524 8259 : if (mparm_classify(parm) != MPCLASS_BOOL)
525 0 : FATAL();
526 8259 : int i = parm - MP__BOOL_START;
527 8259 : const bool *p = &mp->dummy_start_bool + 1 + i;
528 8259 : if (p >= &mp->dummy_end_bool)
529 0 : FATAL();
530 8259 : return *p;
531 : }
532 :
533 :
534 : msettings_error
535 1035 : msetting_set_bool(msettings *mp, mparm parm, bool value)
536 : {
537 1035 : if (mparm_classify(parm) != MPCLASS_BOOL)
538 0 : FATAL();
539 1035 : int i = parm - MP__BOOL_START;
540 1035 : bool *p = &mp->dummy_start_bool + 1 + i;
541 1035 : if (p >= &mp->dummy_end_bool)
542 0 : FATAL();
543 1035 : *p = value;
544 :
545 1035 : mp->validated = false;
546 1035 : return NULL;
547 : }
548 :
549 : msettings_error
550 1279 : msetting_parse(msettings *mp, mparm parm, const char *text)
551 : {
552 1279 : int b; // int not bool because we need to allow for parse errors
553 1279 : switch (mparm_classify(parm)) {
554 : case MPCLASS_BOOL:
555 26 : b = msetting_parse_bool(text);
556 26 : if (b < 0)
557 4 : return format_error(mp, "%s: invalid boolean value", msetting_parm_name(mp, parm));
558 22 : return msetting_set_bool(mp, parm, b);
559 : case MPCLASS_LONG:
560 92 : if (text[0] == '\0')
561 3 : return format_error(mp, "%s: integer parameter cannot be empty string", msetting_parm_name(mp, parm));
562 89 : char *end;
563 89 : long l = strtol(text, &end, 10);
564 89 : if (*end != '\0')
565 1 : return format_error(mp, "%s: invalid integer", msetting_parm_name(mp, parm));
566 88 : return msetting_set_long(mp, parm, l);
567 : case MPCLASS_STRING:
568 1161 : return msetting_set_string(mp, parm, text);
569 : default:
570 : assert(0 && "unreachable");
571 : return "internal error, unclassified parameter type";
572 : }
573 : }
574 :
575 : char *
576 1634 : msetting_as_string(const msettings *mp, mparm parm)
577 : {
578 1634 : bool b;
579 1634 : long l;
580 1634 : const char *s;
581 1634 : switch (mparm_classify(parm)) {
582 : case MPCLASS_BOOL:
583 215 : b = msetting_bool(mp, parm);
584 259 : return strdup(b ? "true" : "false");
585 : case MPCLASS_LONG:
586 473 : l = msetting_long(mp, parm);
587 473 : int n = 40;
588 473 : char *buf = malloc(n);
589 473 : if (!buf)
590 : return NULL;
591 473 : snprintf(buf, n, "%ld", l);
592 473 : return buf;
593 : case MPCLASS_STRING:
594 946 : s = msetting_string(mp, parm);
595 946 : return strdup(s);
596 : default:
597 : assert(0 && "unreachable");
598 : return NULL;
599 : }
600 : }
601 :
602 : msettings_error
603 6 : msetting_set_ignored(msettings *mp, const char *key, const char *value)
604 : {
605 6 : char *my_key = strdup(key);
606 6 : char *my_value = strdup(value);
607 :
608 6 : size_t n = mp->nr_unknown;
609 6 : size_t new_size = (2 * n + 2) * sizeof(char*);
610 6 : char **new_unknowns = realloc(mp->unknown_parameters, new_size);
611 :
612 6 : if (!my_key || !my_value || !new_unknowns) {
613 0 : free(my_key);
614 0 : free(my_value);
615 0 : free(new_unknowns);
616 0 : return MALLOC_FAILED;
617 : }
618 :
619 6 : new_unknowns[2 * n] = my_key;
620 6 : new_unknowns[2 * n + 1] = my_value;
621 6 : mp->unknown_parameters = new_unknowns;
622 6 : mp->nr_unknown += 1;
623 :
624 6 : return NULL;
625 : }
626 :
627 : /* store named parameter */
628 : msettings_error
629 283 : msetting_set_named(msettings *mp, bool allow_core, const char *key, const char *value)
630 : {
631 283 : mparm parm = mparm_parse(key);
632 283 : if (parm == MP_UNKNOWN)
633 1 : return format_error(mp, "%s: unknown parameter", key);
634 :
635 282 : if (parm == MP_IGNORE)
636 6 : return msetting_set_ignored(mp, key, value);
637 :
638 276 : if (!allow_core && mparm_is_core(parm))
639 6 : return format_error(mp, "%s: parameter not allowed here", msetting_parm_name(mp, parm));
640 :
641 270 : return msetting_parse(mp, parm, value);
642 : }
643 :
644 :
645 : static bool
646 5711 : empty(const msettings *mp, mparm parm)
647 : {
648 5711 : const char *value = msetting_string(mp, parm);
649 5711 : assert(value);
650 5711 : return *value == '\0';
651 : }
652 :
653 : static bool
654 4236 : nonempty(const msettings *mp, mparm parm)
655 : {
656 675 : return !empty(mp, parm);
657 : }
658 :
659 : static msettings_error
660 679 : validate_certhash(msettings *mp)
661 : {
662 679 : mp->certhash_digits_buffer[0] = '\0';
663 :
664 679 : const char *full_certhash = msetting_string(mp, MP_CERTHASH);
665 679 : const char *certhash = full_certhash;
666 679 : if (*certhash == '\0')
667 : return NULL;
668 :
669 28 : if (strncmp(certhash, "sha256:", 7) == 0) {
670 19 : certhash += 7;
671 : } else {
672 9 : return format_error(mp, "%s: expected to start with 'sha256:'", msetting_parm_name(mp, MP_CERTHASH));
673 : }
674 :
675 19 : size_t i = 0;
676 367 : for (const char *r = certhash; *r != '\0'; r++) {
677 349 : if (*r == ':')
678 21 : continue;
679 328 : if (!isxdigit(*r))
680 1 : return format_error(mp, "%s: invalid hex digit", msetting_parm_name(mp, MP_CERTHASH));
681 327 : if (i < sizeof(mp->certhash_digits_buffer) - 1)
682 287 : mp->certhash_digits_buffer[i++] = tolower(*r);
683 : }
684 18 : mp->certhash_digits_buffer[i] = '\0';
685 18 : if (i == 0)
686 0 : return format_error(mp, "%s: need at least one digit", msetting_parm_name(mp, MP_CERTHASH));
687 :
688 : return NULL;
689 : }
690 :
691 : static bool
692 1970 : validate_identifier(const char *name)
693 : {
694 1970 : int first = name[0];
695 1970 : if (first == '\0')
696 : return true;
697 559 : if (first != '_' && !isalpha(first))
698 : return false;
699 10644 : for (const char *p = name; *p; p++) {
700 10105 : bool ok = (isalnum(*p) || *p == '.' || *p == '-' || *p == '_');
701 10105 : if (!ok)
702 : return false;
703 : }
704 : return true;
705 : }
706 :
707 : bool
708 1865 : msettings_validate(msettings *mp, char **errmsg)
709 : {
710 1865 : if (mp->validated)
711 : return true;
712 :
713 : // 1. The parameters have the types listed in the table in [Section
714 : // Parameters](#parameters).
715 : // (this has already been checked)
716 :
717 : // 2. At least one of **sock** and **host** must be empty.
718 721 : if (nonempty(mp, MP_SOCK) && nonempty(mp, MP_HOST)) {
719 8 : *errmsg = allocprintf(
720 : "With sock='%s', host must be 'localhost', not '%s'",
721 : msetting_string(mp, MP_SOCK),
722 : msetting_string(mp, MP_HOST));
723 8 : return false;
724 : }
725 :
726 : // 3. The string parameter **binary** must either parse as a boolean or as a
727 : // non-negative integer.
728 : // (pretend valid so we can use msettings_connect_binary() to see if it parses)
729 687 : mp->validated = true;
730 687 : long level = msettings_connect_binary(mp);
731 687 : mp->validated = false;
732 687 : if (level < 0) {
733 4 : *errmsg = allocprintf("invalid value '%s' for parameter 'binary'", msetting_string(mp, MP_BINARY));
734 4 : return false;
735 : }
736 :
737 : // 4. If **sock** is not empty, **tls** must be 'off'.
738 683 : if (nonempty(mp, MP_SOCK) && msetting_bool(mp, MP_TLS)) {
739 4 : *errmsg = allocprintf("TLS cannot be used with Unix domain sockets");
740 4 : return false;
741 : }
742 :
743 : // 5. If **certhash** is not empty, it must be of the form `sha256:hexdigits`
744 : // where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons.
745 679 : const char *certhash_msg = validate_certhash(mp);
746 679 : if (certhash_msg) {
747 10 : *errmsg = strdup(certhash_msg);
748 10 : return false;
749 : }
750 : // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well.
751 1318 : if (nonempty(mp, MP_CERT) || nonempty(mp, MP_CERTHASH))
752 32 : if (!msetting_bool(mp, MP_TLS)) {
753 5 : *errmsg = strdup("'cert' and 'certhash' can only be used with monetdbs://");
754 5 : return false;
755 : }
756 :
757 : // 7. Parameters **database**, **tableschema** and **table** must consist only of
758 : // upper- and lowercase letters, digits, dashes and underscores. They must not
759 : // start with a dash.
760 664 : const char *database = msetting_string(mp, MP_DATABASE);
761 664 : if (!validate_identifier(database)) {
762 8 : *errmsg = allocprintf("invalid database name '%s'", database);
763 8 : return false;
764 : }
765 656 : const char *tableschema = msetting_string(mp, MP_TABLESCHEMA);
766 656 : if (!validate_identifier(tableschema)) {
767 6 : *errmsg = allocprintf("invalid schema name '%s'", tableschema);
768 6 : return false;
769 : }
770 650 : const char *table = msetting_string(mp, MP_TABLE);
771 650 : if (!validate_identifier(table)) {
772 6 : *errmsg = allocprintf("invalid table name '%s'", table);
773 6 : return false;
774 : }
775 :
776 : // 8. Parameter **port** must be -1 or in the range 1-65535.
777 644 : long port = msetting_long(mp, MP_PORT);
778 644 : bool port_ok = (port == -1 || (port >= 1 && port <= 65535));
779 644 : if (!port_ok) {
780 3 : *errmsg = allocprintf("invalid port '%ld'", port);
781 3 : return false;
782 : }
783 :
784 : // 9. If **clientcert** is set, **clientkey** must also be set.
785 641 : if (nonempty(mp, MP_CLIENTCERT) && empty(mp, MP_CLIENTKEY)) {
786 1 : *errmsg = allocprintf("clientcert can only be set together with clientkey");
787 1 : return false;
788 : }
789 :
790 : // compute this here so the getter function can take const msettings*
791 640 : const char *sockdir = msetting_string(mp, MP_SOCKDIR);
792 640 : long effective_port = msettings_connect_port(mp);
793 640 : free(mp->unix_sock_name_buffer);
794 640 : mp->unix_sock_name_buffer = allocprintf("%s/.s.monetdb.%ld", sockdir, effective_port);
795 640 : if (mp->unix_sock_name_buffer == NULL)
796 : return false;
797 :
798 640 : mp->validated = true;
799 640 : return true;
800 : }
801 :
802 : bool
803 1469 : msettings_connect_scan(const msettings *mp)
804 : {
805 1469 : if (empty(mp, MP_DATABASE))
806 : return false;
807 439 : if (nonempty(mp, MP_SOCK))
808 : return false;
809 434 : if (nonempty(mp, MP_HOST))
810 : return false;
811 83 : long port = msetting_long(mp, MP_PORT);
812 83 : if (port != -1)
813 : return false;
814 26 : bool tls = msetting_bool(mp, MP_TLS);
815 26 : if (tls)
816 : return false;
817 :
818 : return true;
819 : }
820 :
821 : const char *
822 1523 : msettings_connect_unix(const msettings *mp)
823 : {
824 1523 : assert(mp->validated);
825 1523 : const char *sock = msetting_string(mp, MP_SOCK);
826 1523 : const char *host = msetting_string(mp, MP_HOST);
827 1523 : bool tls = msetting_bool(mp, MP_TLS);
828 :
829 1523 : if (*sock)
830 : return sock;
831 1512 : if (tls)
832 : return "";
833 1488 : if (*host == '\0') {
834 : // This was precomputed in msettings_validate(),
835 : // {sockdir}/.s.monetdb.{port}
836 108 : return mp->unix_sock_name_buffer;
837 : }
838 : return "";
839 : }
840 :
841 :
842 : const char *
843 2868 : msettings_connect_tcp(const msettings *mp)
844 : {
845 2868 : assert(mp->validated);
846 2868 : const char *sock = msetting_string(mp, MP_SOCK);
847 2868 : const char *host = msetting_string(mp, MP_HOST);
848 : // bool tls = msetting_bool(mp, MP_TLS);
849 :
850 2868 : if (*sock)
851 : return "";
852 2858 : if (!*host)
853 105 : return "localhost";
854 : return host;
855 : }
856 :
857 : long
858 2050 : msettings_connect_port(const msettings *mp)
859 : {
860 2050 : long port = msetting_long(mp, MP_PORT);
861 2050 : if (port == -1)
862 : return 50000;
863 : else
864 1837 : return port;
865 : }
866 :
867 : enum msetting_tls_verify
868 36 : msettings_connect_tls_verify(const msettings *mp)
869 : {
870 36 : assert(mp->validated);
871 36 : bool tls = msetting_bool(mp, MP_TLS);
872 36 : const char *cert = msetting_string(mp, MP_CERT);
873 36 : const char *certhash = msetting_string(mp, MP_CERTHASH);
874 :
875 36 : if (!tls)
876 : return verify_none;
877 35 : if (*certhash) // certhash comes before cert
878 : return verify_hash;
879 24 : if (*cert)
880 17 : return verify_cert;
881 : return verify_system;
882 : }
883 :
884 : const char*
885 15 : msettings_connect_clientkey(const msettings *mp)
886 : {
887 15 : return msetting_string(mp, MP_CLIENTKEY);
888 : }
889 :
890 : const char*
891 15 : msettings_connect_clientcert(const msettings *mp)
892 : {
893 15 : const char *cert = msetting_string(mp, MP_CLIENTCERT);
894 15 : if (*cert)
895 : return cert;
896 : else
897 13 : return msetting_string(mp, MP_CLIENTKEY);
898 : }
899 :
900 : const char*
901 4 : msettings_connect_certhash_digits(const msettings *mp)
902 : {
903 4 : return mp->certhash_digits_buffer;
904 : }
905 :
906 : // also used as a validator, returns < 0 on invalid
907 : long
908 698 : msettings_connect_binary(const msettings *mp)
909 : {
910 698 : const long sufficiently_large = 65535;
911 698 : const char *binary = msetting_string(mp, MP_BINARY);
912 :
913 : // may be bool
914 698 : int b = msetting_parse_bool(binary);
915 698 : if (b == 0)
916 : return 0;
917 692 : if (b == 1)
918 : return sufficiently_large;
919 10 : assert(b < 0);
920 :
921 10 : char *end;
922 10 : long level = strtol(binary, &end, 10);
923 10 : if (end != binary && *end == '\0')
924 7 : return level;
925 :
926 : return -1;
927 : }
928 :
929 :
930 : /* automatically incremented each time the corresponding field is updated */
931 : long
932 777 : msettings_user_generation(const msettings *mp)
933 : {
934 777 : return mp->user_generation;
935 : }
936 :
937 : /* automatically incremented each time the corresponding field is updated */
938 : long
939 777 : msettings_password_generation(const msettings *mp)
940 : {
941 777 : return mp->password_generation;
942 : }
943 :
944 :
945 : bool
946 12 : msettings_lang_is_mal(const msettings *mp)
947 : {
948 12 : return mp->lang_is_mal;
949 : }
950 :
951 : bool
952 576552 : msettings_lang_is_sql(const msettings *mp)
953 : {
954 576552 : return mp->lang_is_sql;
955 : }
|