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