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 863 : int msetting_parse_bool(const char *text)
42 : {
43 863 : 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 4175 : for (size_t i = 0; i < sizeof(variants) / sizeof(variants[0]); i++)
52 4161 : if (strcasecmp(text, variants[i].word) == 0)
53 849 : 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 681 : allocprintf(const char *fmt, ...)
62 : {
63 681 : va_list ap;
64 681 : char *buf = NULL;
65 681 : size_t pos = 0, cap = 0;
66 681 : int n;
67 :
68 681 : va_start(ap, fmt);
69 681 : n = vreallocprintf(&buf, &pos, &cap, fmt, ap);
70 681 : va_end(ap);
71 :
72 681 : if (n >= 0)
73 681 : 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 580 : msettings *msettings_create(void)
275 : {
276 580 : msettings *mp = malloc(sizeof(*mp));
277 580 : if (!mp) {
278 : return NULL;
279 : }
280 580 : *mp = msettings_default_values;
281 580 : 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 690 : msettings_destroy(msettings *mp)
369 : {
370 690 : if (mp == NULL)
371 : return NULL;
372 :
373 12198 : for (struct string *p = &mp->dummy_start_string + 1; p < &mp->dummy_end_string; p++) {
374 11556 : if (p->must_free)
375 2425 : free(p->str);
376 : }
377 648 : 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 642 : free(mp->unknown_parameters);
382 642 : free(mp->unix_sock_name_buffer);
383 642 : free(mp);
384 :
385 642 : 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 30333 : msetting_string(const msettings *mp, mparm parm)
420 : {
421 30333 : if (mparm_classify(parm) != MPCLASS_STRING) {
422 0 : FATAL();
423 : return "";
424 : }
425 30333 : int i = parm - MP__STRING_START;
426 30333 : struct string const *p = &mp->dummy_start_string + 1 + i;
427 30333 : if (p >= &mp->dummy_end_string) {
428 0 : FATAL();
429 : return "";
430 : }
431 30333 : char *s = p->str;
432 :
433 30333 : if (s == NULL) {
434 15842 : if (parm == MP_LANGUAGE)
435 : return "sql";
436 15798 : else if (parm == MP_BINARY)
437 : return "on";
438 15798 : return "";
439 : }
440 : return s;
441 : }
442 :
443 :
444 : msettings_error
445 3836 : msetting_set_string(msettings *mp, mparm parm, const char* value)
446 : {
447 3836 : assert(value != NULL);
448 :
449 3836 : if (mparm_classify(parm) != MPCLASS_STRING)
450 0 : FATAL();
451 3848 : int i = parm - MP__STRING_START;
452 3848 : struct string *p = &mp->dummy_start_string + 1 + i;
453 3848 : if (p >= &mp->dummy_end_string)
454 0 : FATAL();
455 :
456 3848 : char *v = strdup(value);
457 3848 : if (!v)
458 : return MALLOC_FAILED;
459 3848 : if (p->must_free)
460 1318 : free(p->str);
461 3848 : p->str = v;
462 3848 : p->must_free = true;
463 :
464 3848 : switch (parm) {
465 476 : case MP_USER:
466 476 : mp->user_generation++;
467 476 : break;
468 713 : case MP_PASSWORD:
469 713 : mp->password_generation++;
470 713 : break;
471 413 : case MP_LANGUAGE:
472 413 : mp->lang_is_mal = false;
473 413 : 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 413 : if (strcmp(value, "mal") == 0 || strcmp(value, "msql") == 0)
477 201 : mp->lang_is_mal = true;
478 212 : else if (strstr(value, "sql") == value)
479 210 : mp->lang_is_sql = true;
480 : else if (strcmp(value, "`"))
481 : break;
482 : default:
483 : break;
484 : }
485 :
486 3848 : mp->validated = false;
487 3848 : return NULL;
488 : }
489 :
490 :
491 : long
492 47346 : msetting_long(const msettings *mp, mparm parm)
493 : {
494 47346 : if (mparm_classify(parm) != MPCLASS_LONG)
495 0 : FATAL();
496 47346 : int i = parm - MP__LONG_START;
497 47346 : const long * p = &mp->dummy_start_long + 1 + i;
498 47346 : if (p >= &mp->dummy_end_long)
499 0 : FATAL();
500 :
501 47346 : return *p;
502 : }
503 :
504 :
505 : msettings_error
506 1611 : msetting_set_long(msettings *mp, mparm parm, long value)
507 : {
508 1611 : if (mparm_classify(parm) != MPCLASS_LONG)
509 0 : FATAL();
510 1611 : int i = parm - MP__LONG_START;
511 1611 : long *p = &mp->dummy_start_long + 1 + i;
512 1611 : if (p >= &mp->dummy_end_long)
513 0 : FATAL();
514 :
515 1611 : *p = value;
516 :
517 1611 : mp->validated = false;
518 1611 : return NULL;
519 : }
520 :
521 :
522 : bool
523 8385 : msetting_bool(const msettings *mp, mparm parm)
524 : {
525 8385 : if (mparm_classify(parm) != MPCLASS_BOOL)
526 0 : FATAL();
527 8385 : int i = parm - MP__BOOL_START;
528 8385 : const bool *p = &mp->dummy_start_bool + 1 + i;
529 8385 : if (p >= &mp->dummy_end_bool)
530 0 : FATAL();
531 8385 : return *p;
532 : }
533 :
534 :
535 : msettings_error
536 1066 : msetting_set_bool(msettings *mp, mparm parm, bool value)
537 : {
538 1066 : if (mparm_classify(parm) != MPCLASS_BOOL)
539 0 : FATAL();
540 1066 : int i = parm - MP__BOOL_START;
541 1066 : bool *p = &mp->dummy_start_bool + 1 + i;
542 1066 : if (p >= &mp->dummy_end_bool)
543 0 : FATAL();
544 1066 : *p = value;
545 :
546 1066 : mp->validated = false;
547 1066 : return NULL;
548 : }
549 :
550 : msettings_error
551 1272 : msetting_parse(msettings *mp, mparm parm, const char *text)
552 : {
553 1272 : int b; // int not bool because we need to allow for parse errors
554 1272 : switch (mparm_classify(parm)) {
555 : case MPCLASS_BOOL:
556 26 : b = msetting_parse_bool(text);
557 26 : if (b < 0)
558 4 : return format_error(mp, "%s: invalid boolean value", msetting_parm_name(mp, parm));
559 22 : return msetting_set_bool(mp, parm, b);
560 : case MPCLASS_LONG:
561 92 : if (text[0] == '\0')
562 3 : return format_error(mp, "%s: integer parameter cannot be empty string", msetting_parm_name(mp, parm));
563 89 : char *end;
564 89 : long l = strtol(text, &end, 10);
565 89 : if (*end != '\0')
566 1 : return format_error(mp, "%s: invalid integer", msetting_parm_name(mp, parm));
567 88 : return msetting_set_long(mp, parm, l);
568 : case MPCLASS_STRING:
569 1154 : return msetting_set_string(mp, parm, text);
570 : default:
571 : assert(0 && "unreachable");
572 : return "internal error, unclassified parameter type";
573 : }
574 : }
575 :
576 : char *
577 1634 : msetting_as_string(const msettings *mp, mparm parm)
578 : {
579 1634 : bool b;
580 1634 : long l;
581 1634 : const char *s;
582 1634 : switch (mparm_classify(parm)) {
583 : case MPCLASS_BOOL:
584 215 : b = msetting_bool(mp, parm);
585 259 : return strdup(b ? "true" : "false");
586 : case MPCLASS_LONG:
587 473 : l = msetting_long(mp, parm);
588 473 : int n = 40;
589 473 : char *buf = malloc(n);
590 473 : if (!buf)
591 : return NULL;
592 473 : snprintf(buf, n, "%ld", l);
593 473 : return buf;
594 : case MPCLASS_STRING:
595 946 : s = msetting_string(mp, parm);
596 946 : return strdup(s);
597 : default:
598 : assert(0 && "unreachable");
599 : return NULL;
600 : }
601 : }
602 :
603 : msettings_error
604 6 : msetting_set_ignored(msettings *mp, const char *key, const char *value)
605 : {
606 6 : char *my_key = strdup(key);
607 6 : char *my_value = strdup(value);
608 :
609 6 : size_t n = mp->nr_unknown;
610 6 : size_t new_size = (2 * n + 2) * sizeof(char*);
611 6 : char **new_unknowns = realloc(mp->unknown_parameters, new_size);
612 :
613 6 : if (!my_key || !my_value || !new_unknowns) {
614 0 : free(my_key);
615 0 : free(my_value);
616 0 : free(new_unknowns);
617 0 : return MALLOC_FAILED;
618 : }
619 :
620 6 : new_unknowns[2 * n] = my_key;
621 6 : new_unknowns[2 * n + 1] = my_value;
622 6 : mp->unknown_parameters = new_unknowns;
623 6 : mp->nr_unknown += 1;
624 :
625 6 : return NULL;
626 : }
627 :
628 : /* store named parameter */
629 : msettings_error
630 283 : msetting_set_named(msettings *mp, bool allow_core, const char *key, const char *value)
631 : {
632 283 : mparm parm = mparm_parse(key);
633 283 : if (parm == MP_UNKNOWN)
634 1 : return format_error(mp, "%s: unknown parameter", key);
635 :
636 282 : if (parm == MP_IGNORE)
637 6 : return msetting_set_ignored(mp, key, value);
638 :
639 276 : if (!allow_core && mparm_is_core(parm))
640 6 : return format_error(mp, "%s: parameter not allowed here", msetting_parm_name(mp, parm));
641 :
642 270 : return msetting_parse(mp, parm, value);
643 : }
644 :
645 :
646 : static bool
647 5767 : empty(const msettings *mp, mparm parm)
648 : {
649 5767 : const char *value = msetting_string(mp, parm);
650 5767 : assert(value);
651 5767 : return *value == '\0';
652 : }
653 :
654 : static bool
655 4275 : nonempty(const msettings *mp, mparm parm)
656 : {
657 676 : return !empty(mp, parm);
658 : }
659 :
660 : static msettings_error
661 680 : validate_certhash(msettings *mp)
662 : {
663 680 : mp->certhash_digits_buffer[0] = '\0';
664 :
665 680 : const char *full_certhash = msetting_string(mp, MP_CERTHASH);
666 680 : const char *certhash = full_certhash;
667 680 : if (*certhash == '\0')
668 : return NULL;
669 :
670 28 : if (strncmp(certhash, "sha256:", 7) == 0) {
671 19 : certhash += 7;
672 : } else {
673 9 : return format_error(mp, "%s: expected to start with 'sha256:'", msetting_parm_name(mp, MP_CERTHASH));
674 : }
675 :
676 19 : size_t i = 0;
677 367 : for (const char *r = certhash; *r != '\0'; r++) {
678 349 : if (*r == ':')
679 21 : continue;
680 328 : if (!isxdigit(*r))
681 1 : return format_error(mp, "%s: invalid hex digit", msetting_parm_name(mp, MP_CERTHASH));
682 327 : if (i < sizeof(mp->certhash_digits_buffer) - 1)
683 287 : mp->certhash_digits_buffer[i++] = tolower(*r);
684 : }
685 18 : mp->certhash_digits_buffer[i] = '\0';
686 18 : if (i == 0)
687 0 : return format_error(mp, "%s: need at least one digit", msetting_parm_name(mp, MP_CERTHASH));
688 :
689 : return NULL;
690 : }
691 :
692 : static bool
693 1973 : validate_identifier(const char *name)
694 : {
695 1973 : int first = name[0];
696 1973 : if (first == '\0')
697 : return true;
698 560 : if (first != '_' && !isalpha(first))
699 : return false;
700 10676 : for (const char *p = name; *p; p++) {
701 10136 : bool ok = (isalnum(*p) || *p == '.' || *p == '-' || *p == '_');
702 10136 : if (!ok)
703 : return false;
704 : }
705 : return true;
706 : }
707 :
708 : bool
709 1866 : msettings_validate(msettings *mp, char **errmsg)
710 : {
711 1866 : if (mp->validated)
712 : return true;
713 :
714 : // 1. The parameters have the types listed in the table in [Section
715 : // Parameters](#parameters).
716 : // (this has already been checked)
717 :
718 : // 2. At least one of **sock** and **host** must be empty.
719 722 : if (nonempty(mp, MP_SOCK) && nonempty(mp, MP_HOST)) {
720 8 : *errmsg = allocprintf(
721 : "With sock='%s', host must be 'localhost', not '%s'",
722 : msetting_string(mp, MP_SOCK),
723 : msetting_string(mp, MP_HOST));
724 8 : return false;
725 : }
726 :
727 : // 3. The string parameter **binary** must either parse as a boolean or as a
728 : // non-negative integer.
729 : // (pretend valid so we can use msettings_connect_binary() to see if it parses)
730 688 : mp->validated = true;
731 688 : long level = msettings_connect_binary(mp);
732 688 : mp->validated = false;
733 688 : if (level < 0) {
734 4 : *errmsg = allocprintf("invalid value '%s' for parameter 'binary'", msetting_string(mp, MP_BINARY));
735 4 : return false;
736 : }
737 :
738 : // 4. If **sock** is not empty, **tls** must be 'off'.
739 684 : if (nonempty(mp, MP_SOCK) && msetting_bool(mp, MP_TLS)) {
740 4 : *errmsg = allocprintf("TLS cannot be used with Unix domain sockets");
741 4 : return false;
742 : }
743 :
744 : // 5. If **certhash** is not empty, it must be of the form `sha256:hexdigits`
745 : // where hexdigits is a non-empty sequence of 0-9, a-f, A-F and colons.
746 680 : const char *certhash_msg = validate_certhash(mp);
747 680 : if (certhash_msg) {
748 10 : *errmsg = strdup(certhash_msg);
749 10 : return false;
750 : }
751 : // 6. If **tls** is 'off', **cert** and **certhash** must be 'off' as well.
752 1320 : if (nonempty(mp, MP_CERT) || nonempty(mp, MP_CERTHASH))
753 32 : if (!msetting_bool(mp, MP_TLS)) {
754 5 : *errmsg = strdup("'cert' and 'certhash' can only be used with monetdbs://");
755 5 : return false;
756 : }
757 :
758 : // 7. Parameters **database**, **tableschema** and **table** must consist only of
759 : // upper- and lowercase letters, digits, dashes and underscores. They must not
760 : // start with a dash.
761 665 : const char *database = msetting_string(mp, MP_DATABASE);
762 665 : if (!validate_identifier(database)) {
763 8 : *errmsg = allocprintf("invalid database name '%s'", database);
764 8 : return false;
765 : }
766 657 : const char *tableschema = msetting_string(mp, MP_TABLESCHEMA);
767 657 : if (!validate_identifier(tableschema)) {
768 6 : *errmsg = allocprintf("invalid schema name '%s'", tableschema);
769 6 : return false;
770 : }
771 651 : const char *table = msetting_string(mp, MP_TABLE);
772 651 : if (!validate_identifier(table)) {
773 6 : *errmsg = allocprintf("invalid table name '%s'", table);
774 6 : return false;
775 : }
776 :
777 : // 8. Parameter **port** must be -1 or in the range 1-65535.
778 645 : long port = msetting_long(mp, MP_PORT);
779 645 : bool port_ok = (port == -1 || (port >= 1 && port <= 65535));
780 645 : if (!port_ok) {
781 3 : *errmsg = allocprintf("invalid port '%ld'", port);
782 3 : return false;
783 : }
784 :
785 : // 9. If **clientcert** is set, **clientkey** must also be set.
786 642 : if (nonempty(mp, MP_CLIENTCERT) && empty(mp, MP_CLIENTKEY)) {
787 1 : *errmsg = allocprintf("clientcert can only be set together with clientkey");
788 1 : return false;
789 : }
790 :
791 : // compute this here so the getter function can take const msettings*
792 641 : const char *sockdir = msetting_string(mp, MP_SOCKDIR);
793 641 : long effective_port = msettings_connect_port(mp);
794 641 : free(mp->unix_sock_name_buffer);
795 641 : mp->unix_sock_name_buffer = allocprintf("%s/.s.monetdb.%ld", sockdir, effective_port);
796 641 : if (mp->unix_sock_name_buffer == NULL)
797 : return false;
798 :
799 641 : mp->validated = true;
800 641 : return true;
801 : }
802 :
803 : bool
804 1486 : msettings_connect_scan(const msettings *mp)
805 : {
806 1486 : if (empty(mp, MP_DATABASE))
807 : return false;
808 456 : if (nonempty(mp, MP_SOCK))
809 : return false;
810 451 : if (nonempty(mp, MP_HOST))
811 : return false;
812 99 : long port = msetting_long(mp, MP_PORT);
813 99 : if (port != -1)
814 : return false;
815 10 : bool tls = msetting_bool(mp, MP_TLS);
816 10 : if (tls)
817 : return false;
818 :
819 : return true;
820 : }
821 :
822 : const char *
823 1572 : msettings_connect_unix(const msettings *mp)
824 : {
825 1572 : assert(mp->validated);
826 1572 : const char *sock = msetting_string(mp, MP_SOCK);
827 1572 : const char *host = msetting_string(mp, MP_HOST);
828 1572 : bool tls = msetting_bool(mp, MP_TLS);
829 :
830 1572 : if (*sock)
831 : return sock;
832 1561 : if (tls)
833 : return "";
834 1537 : if (*host == '\0') {
835 : // This was precomputed in msettings_validate(),
836 : // {sockdir}/.s.monetdb.{port}
837 172 : return mp->unix_sock_name_buffer;
838 : }
839 : return "";
840 : }
841 :
842 :
843 : const char *
844 2870 : msettings_connect_tcp(const msettings *mp)
845 : {
846 2870 : assert(mp->validated);
847 2870 : const char *sock = msetting_string(mp, MP_SOCK);
848 2870 : const char *host = msetting_string(mp, MP_HOST);
849 : // bool tls = msetting_bool(mp, MP_TLS);
850 :
851 2870 : if (*sock)
852 : return "";
853 2860 : if (!*host)
854 137 : return "localhost";
855 : return host;
856 : }
857 :
858 : long
859 2036 : msettings_connect_port(const msettings *mp)
860 : {
861 2036 : long port = msetting_long(mp, MP_PORT);
862 2036 : if (port == -1)
863 : return 50000;
864 : else
865 1871 : return port;
866 : }
867 :
868 : enum msetting_tls_verify
869 36 : msettings_connect_tls_verify(const msettings *mp)
870 : {
871 36 : assert(mp->validated);
872 36 : bool tls = msetting_bool(mp, MP_TLS);
873 36 : const char *cert = msetting_string(mp, MP_CERT);
874 36 : const char *certhash = msetting_string(mp, MP_CERTHASH);
875 :
876 36 : if (!tls)
877 : return verify_none;
878 35 : if (*certhash) // certhash comes before cert
879 : return verify_hash;
880 24 : if (*cert)
881 17 : return verify_cert;
882 : return verify_system;
883 : }
884 :
885 : const char*
886 15 : msettings_connect_clientkey(const msettings *mp)
887 : {
888 15 : return msetting_string(mp, MP_CLIENTKEY);
889 : }
890 :
891 : const char*
892 15 : msettings_connect_clientcert(const msettings *mp)
893 : {
894 15 : const char *cert = msetting_string(mp, MP_CLIENTCERT);
895 15 : if (*cert)
896 : return cert;
897 : else
898 13 : return msetting_string(mp, MP_CLIENTKEY);
899 : }
900 :
901 : const char*
902 4 : msettings_connect_certhash_digits(const msettings *mp)
903 : {
904 4 : return mp->certhash_digits_buffer;
905 : }
906 :
907 : // also used as a validator, returns < 0 on invalid
908 : long
909 699 : msettings_connect_binary(const msettings *mp)
910 : {
911 699 : const long sufficiently_large = 65535;
912 699 : const char *binary = msetting_string(mp, MP_BINARY);
913 :
914 : // may be bool
915 699 : int b = msetting_parse_bool(binary);
916 699 : if (b == 0)
917 : return 0;
918 693 : if (b == 1)
919 : return sufficiently_large;
920 10 : assert(b < 0);
921 :
922 10 : char *end;
923 10 : long level = strtol(binary, &end, 10);
924 10 : if (end != binary && *end == '\0')
925 7 : return level;
926 :
927 : return -1;
928 : }
929 :
930 :
931 : /* automatically incremented each time the corresponding field is updated */
932 : long
933 778 : msettings_user_generation(const msettings *mp)
934 : {
935 778 : return mp->user_generation;
936 : }
937 :
938 : /* automatically incremented each time the corresponding field is updated */
939 : long
940 778 : msettings_password_generation(const msettings *mp)
941 : {
942 778 : return mp->password_generation;
943 : }
944 :
945 :
946 : bool
947 12 : msettings_lang_is_mal(const msettings *mp)
948 : {
949 12 : return mp->lang_is_mal;
950 : }
951 :
952 : bool
953 1440396 : msettings_lang_is_sql(const msettings *mp)
954 : {
955 1440396 : return mp->lang_is_sql;
956 : }
|