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