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 : #include "stream.h" /* include before mapi.h */
15 : #include "stream_socket.h"
16 : #include "mapi.h"
17 : #include "mapi_prompt.h"
18 : #include "mcrypt.h"
19 : #include "matomic.h"
20 : #include "mstring.h"
21 : #include "mutils.h"
22 :
23 : #include "mapi_intern.h"
24 :
25 : #ifdef HAVE_SYS_SOCKET_H
26 : # include <arpa/inet.h> /* addr_in */
27 : #else /* UNIX specific */
28 : #ifdef HAVE_WINSOCK_H /* Windows specific */
29 : # include <winsock.h>
30 : #endif
31 : #endif
32 :
33 :
34 : #ifdef HAVE_SYS_UN_H
35 : #define DO_UNIX_DOMAIN (1)
36 : #else
37 : #define DO_UNIX_DOMAIN (0)
38 : #endif
39 :
40 : #ifdef _MSC_VER
41 : #define SOCKET_STRERROR() wsaerror(WSAGetLastError())
42 : #else
43 : #define SOCKET_STRERROR() strerror(errno)
44 : #endif
45 :
46 :
47 : static MapiMsg scan_sockets(Mapi mid);
48 : static MapiMsg connect_socket(Mapi mid);
49 : static MapiMsg connect_socket_tcp(Mapi mid);
50 : static SOCKET connect_socket_tcp_addr(Mapi mid, struct addrinfo *addr);
51 : static MapiMsg mapi_handshake(Mapi mid);
52 :
53 : #ifndef HAVE_OPENSSL
54 : // The real implementation is in connect_openssl.c.
55 : MapiMsg
56 : wrap_tls(Mapi mid, SOCKET sock)
57 : {
58 : closesocket(sock);
59 : return mapi_setError(mid, "Cannot connect to monetdbs://, not built with OpenSSL support", __func__, MERROR);
60 : }
61 : #endif // HAVE_OPENSSL
62 :
63 : #ifndef HAVE_SYS_UN_H
64 :
65 : MapiMsg
66 : connect_socket_unix(Mapi mid)
67 : {
68 : return mapi_setError(mid, "Unix domain sockets not supported", __func__, MERROR);
69 : }
70 :
71 : static MapiMsg
72 : scan_unix_sockets(Mapi mid)
73 : {
74 : return mapi_setError(mid, "Unix domain sockets not supported", __func__, MERROR);
75 : }
76 :
77 : #endif
78 :
79 :
80 :
81 :
82 : /* (Re-)establish a connection with the server. */
83 : MapiMsg
84 1394 : mapi_reconnect(Mapi mid)
85 : {
86 1394 : char *err = NULL;
87 1394 : if (!msettings_validate(mid->settings, &err)) {
88 0 : mapi_setError(mid, err, __func__, MERROR);
89 0 : free(err);
90 0 : return MERROR;
91 : }
92 :
93 : // If neither host nor port are given, scan the Unix domain sockets in
94 : // /tmp and see if any of them serve this database.
95 : // Otherwise, just try to connect to what was given.
96 1394 : if (msettings_connect_scan(mid->settings))
97 0 : return scan_sockets(mid);
98 : else
99 1394 : return establish_connection(mid);
100 : }
101 :
102 : static MapiMsg
103 0 : scan_sockets(Mapi mid)
104 : {
105 0 : if (scan_unix_sockets(mid) == MOK)
106 : return MOK;
107 :
108 : // When the Unix sockets have been scanned we can freely modify 'original'.
109 0 : msettings_error errmsg = msetting_set_string(mid->settings, MP_HOST, "localhost");
110 0 : char *allocated_errmsg = NULL;
111 0 : if (!errmsg && !msettings_validate(mid->settings, &allocated_errmsg)) {
112 0 : errmsg = allocated_errmsg;
113 : }
114 0 : if (errmsg) {
115 0 : MapiMsg err = mapi_setError(mid, errmsg, __func__, MERROR);
116 0 : free(allocated_errmsg);
117 0 : return err;
118 : }
119 0 : return establish_connection(mid);
120 : }
121 :
122 : /* (Re-)establish a connection with the server. */
123 : MapiMsg
124 1394 : establish_connection(Mapi mid)
125 : {
126 1394 : if (mid->connected) {
127 1 : mapi_log_record(mid, "CONN", "Found leftover open connection");
128 1 : close_connection(mid);
129 : }
130 :
131 : MapiMsg msg = MREDIRECT;
132 2778 : while (msg == MREDIRECT) {
133 : // Generally at this point we need to set up a new TCP or Unix
134 : // domain connection.
135 : //
136 : // The only exception is if mapi_handshake() below has decided
137 : // that the handshake must be restarted on the existing
138 : // connection.
139 1394 : if (!mid->connected) {
140 1394 : msg = connect_socket(mid);
141 1394 : if (msg != MOK)
142 10 : return msg;
143 : }
144 1384 : msg = mapi_handshake(mid);
145 : }
146 :
147 : return msg;
148 : }
149 :
150 : static MapiMsg
151 1394 : connect_socket(Mapi mid)
152 : {
153 1394 : assert(!mid->connected);
154 1394 : const char *sockname = msettings_connect_unix(mid->settings);
155 1394 : const char *tcp_host = msettings_connect_tcp(mid->settings);
156 :
157 1394 : assert(*sockname || *tcp_host);
158 2778 : do {
159 1394 : if (*sockname && connect_socket_unix(mid) == MOK)
160 : break;
161 1331 : if (*tcp_host && connect_socket_tcp(mid) == MOK)
162 : break;
163 10 : assert(mid->error == MERROR);
164 10 : mid->error = MERROR; // in case assert above was not enabled
165 10 : return mid->error;
166 : } while (0);
167 :
168 1384 : mid->connected = true;
169 1384 : return MOK;
170 : }
171 :
172 : MapiMsg
173 1380 : wrap_socket(Mapi mid, SOCKET sock)
174 : {
175 : // do not use check_stream here yet because the socket is not yet in 'mid'
176 1380 : stream *broken_stream = NULL;
177 1380 : MapiMsg msg;
178 1380 : stream *rstream = NULL;
179 1380 : stream *wstream = NULL;
180 :
181 1380 : wstream = socket_wstream(sock, "Mapi client write");
182 1380 : if (wstream == NULL || mnstr_errnr(wstream) != MNSTR_NO__ERROR) {
183 0 : broken_stream = wstream;
184 0 : goto bailout;
185 : }
186 :
187 1380 : rstream = socket_rstream(sock, "Mapi client write");
188 1380 : if (rstream == NULL || mnstr_errnr(rstream) != MNSTR_NO__ERROR) {
189 0 : broken_stream = rstream;
190 0 : goto bailout;
191 : }
192 :
193 1380 : msg = mapi_wrap_streams(mid, rstream, wstream);
194 1380 : if (msg != MOK)
195 0 : goto bailout;
196 : return MOK;
197 :
198 0 : bailout:
199 0 : if (rstream)
200 0 : mnstr_destroy(rstream);
201 0 : if (wstream)
202 0 : mnstr_destroy(wstream);
203 0 : closesocket(sock);
204 0 : if (broken_stream) {
205 0 : char *error_message = "create stream from socket";
206 : // malloc failure is the only way these calls could have failed
207 0 : return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(broken_stream));
208 : } else {
209 : return MERROR;
210 : }
211 : }
212 :
213 : static MapiMsg
214 1331 : connect_socket_tcp(Mapi mid)
215 : {
216 1331 : int ret;
217 :
218 1331 : bool use_tls = msetting_bool(mid->settings, MP_TLS);
219 1331 : const char *host = msettings_connect_tcp(mid->settings);
220 1331 : int port = msettings_connect_port(mid->settings);
221 :
222 1331 : assert(host);
223 1331 : char portbuf[10];
224 1331 : snprintf(portbuf, sizeof(portbuf), "%d", port);
225 :
226 1331 : mapi_log_record(mid, "CONN", "Connecting to %s:%d", host, port);
227 :
228 1331 : struct addrinfo hints = (struct addrinfo) {
229 : .ai_family = AF_UNSPEC,
230 : .ai_socktype = SOCK_STREAM,
231 : .ai_protocol = IPPROTO_TCP,
232 : };
233 1331 : struct addrinfo *addresses;
234 1331 : ret = getaddrinfo(host, portbuf, &hints, &addresses);
235 1331 : if (ret != 0) {
236 0 : return mapi_printError(
237 : mid, __func__, MERROR,
238 : "getaddrinfo %s:%s failed: %s", host, portbuf, gai_strerror(ret));
239 : }
240 1331 : if (addresses == NULL) {
241 0 : return mapi_printError(
242 : mid, __func__, MERROR,
243 : "getaddrinfo return 0 addresses");
244 : }
245 :
246 : assert(addresses);
247 : SOCKET s;
248 1335 : for (struct addrinfo *addr = addresses; addr; addr = addr->ai_next) {
249 1333 : s = connect_socket_tcp_addr(mid, addr);
250 1333 : if (s != INVALID_SOCKET)
251 : break;
252 : }
253 1331 : freeaddrinfo(addresses);
254 1331 : if (s == INVALID_SOCKET) {
255 : // connect_socket_tcp_addr has already set an error message
256 : return MERROR;
257 : }
258 :
259 : /* compare our own address with that of our peer and
260 : * if they are the same, we were connected to our own
261 : * socket, so then we can't use this connection */
262 1329 : union {
263 : struct sockaddr_storage ss;
264 : struct sockaddr_in i4;
265 : struct sockaddr_in6 i6;
266 : } myaddr, praddr;
267 1329 : socklen_t myaddrlen, praddrlen;
268 1329 : myaddrlen = (socklen_t) sizeof(myaddr.ss);
269 1329 : praddrlen = (socklen_t) sizeof(praddr.ss);
270 2658 : if (getsockname(s, (struct sockaddr *) &myaddr.ss, &myaddrlen) == 0 &&
271 1329 : getpeername(s, (struct sockaddr *) &praddr.ss, &praddrlen) == 0 &&
272 2658 : myaddr.ss.ss_family == praddr.ss.ss_family &&
273 : (myaddr.ss.ss_family == AF_INET
274 1329 : ? myaddr.i4.sin_port == praddr.i4.sin_port
275 0 : : myaddr.i6.sin6_port == praddr.i6.sin6_port) &&
276 : (myaddr.ss.ss_family == AF_INET
277 0 : ? myaddr.i4.sin_addr.s_addr == praddr.i4.sin_addr.s_addr
278 0 : : memcmp(myaddr.i6.sin6_addr.s6_addr,
279 : praddr.i6.sin6_addr.s6_addr,
280 : sizeof(praddr.i6.sin6_addr.s6_addr)) == 0)) {
281 0 : closesocket(s);
282 0 : return mapi_setError(mid, "connected to self", __func__, MERROR);
283 : }
284 :
285 1329 : mapi_log_record(mid, "CONN", "Network connection established");
286 1329 : MapiMsg msg = use_tls ? wrap_tls(mid, s) : wrap_socket(mid, s);
287 1329 : if (msg != MOK)
288 : return msg;
289 :
290 : return msg;
291 : }
292 :
293 : static SOCKET
294 1333 : connect_socket_tcp_addr(Mapi mid, struct addrinfo *info)
295 : {
296 1333 : if (mid->tracelog) {
297 0 : char addrbuf[100] = {0};
298 0 : const char *addrtext;
299 0 : int port;
300 0 : if (info->ai_family == AF_INET) {
301 0 : struct sockaddr_in *addr4 = (struct sockaddr_in*)info->ai_addr;
302 0 : port = ntohs(addr4->sin_port);
303 0 : void *addr = &addr4->sin_addr;
304 0 : addrtext = inet_ntop(info->ai_family, addr, addrbuf, sizeof(addrbuf));
305 0 : } else if (info->ai_family == AF_INET6) {
306 0 : struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)info->ai_addr;
307 0 : port = ntohs(addr6->sin6_port);
308 0 : void *addr = &addr6->sin6_addr;
309 0 : addrtext = inet_ntop(info->ai_family, addr, addrbuf, sizeof(addrbuf));
310 : } else {
311 : port = -1;
312 : addrtext = NULL;
313 : }
314 0 : mapi_log_record(mid, "CONN", "Trying IP %s port %d", addrtext ? addrtext : "<UNKNOWN>", port);
315 : }
316 :
317 :
318 1333 : int socktype = info->ai_socktype;
319 : #ifdef SOCK_CLOEXEC
320 1333 : socktype |= SOCK_CLOEXEC;
321 : #endif
322 :
323 1333 : SOCKET s = socket(info->ai_family, socktype, info->ai_protocol);
324 1333 : if (s == INVALID_SOCKET) {
325 4 : mapi_printError(
326 : mid, __func__, MERROR,
327 2 : "could not create TCP socket: %s", SOCKET_STRERROR());
328 2 : return INVALID_SOCKET;
329 : }
330 :
331 : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
332 : (void) fcntl(s, F_SETFD, FD_CLOEXEC);
333 : #endif
334 :
335 : // cast addrlen to int to satisfy Windows.
336 1331 : if (connect(s, info->ai_addr, (int)info->ai_addrlen) == SOCKET_ERROR) {
337 2 : mapi_printError(
338 : mid, __func__, MERROR,
339 2 : "could not connect: %s", SOCKET_STRERROR());
340 2 : closesocket(s);
341 2 : return INVALID_SOCKET;
342 : }
343 :
344 : return s;
345 : }
346 :
347 : static MapiMsg
348 1384 : mapi_handshake(Mapi mid)
349 : {
350 1384 : char buf[BLOCK];
351 1384 : size_t len;
352 1384 : MapiHdl hdl;
353 :
354 1384 : const char *username = msetting_string(mid->settings, MP_USER);
355 1384 : const char *password = msetting_string(mid->settings, MP_PASSWORD);
356 :
357 : /* consume server challenge */
358 1384 : len = mnstr_read_block(mid->from, buf, 1, sizeof(buf));
359 1384 : check_stream(mid, mid->from, "Connection terminated while starting handshake", (mid->blk.eos = true, mid->error));
360 :
361 1383 : mapi_log_data(mid, "RECV HANDSHAKE", buf, len);
362 :
363 1383 : assert(len < sizeof(buf));
364 1383 : buf[len] = 0;
365 :
366 1383 : if (len == 0) {
367 0 : mapi_setError(mid, "Challenge string is not valid, it is empty", __func__, MERROR);
368 0 : return mid->error;
369 : }
370 : /* buf at this point looks like "challenge:servertype:protover[:.*]" */
371 :
372 1383 : char *strtok_state = NULL;
373 1383 : char *chal = strtok_r(buf, ":", &strtok_state);
374 1383 : if (chal == NULL) {
375 0 : mapi_setError(mid, "Challenge string is not valid, challenge not found", __func__, MERROR);
376 0 : close_connection(mid);
377 0 : return mid->error;
378 : }
379 :
380 1383 : char *server = strtok_r(NULL, ":", &strtok_state);
381 1383 : if (server == NULL) {
382 0 : mapi_setError(mid, "Challenge string is not valid, server not found", __func__, MERROR);
383 0 : close_connection(mid);
384 0 : return mid->error;
385 : }
386 :
387 1383 : char *protover = strtok_r(NULL, ":", &strtok_state);
388 1383 : if (protover == NULL) {
389 0 : mapi_setError(mid, "Challenge string is not valid, protocol not found", __func__, MERROR);
390 0 : close_connection(mid);
391 0 : return mid->error;
392 : }
393 1383 : int pversion = atoi(protover);
394 1383 : if (pversion != 9) {
395 : /* because the headers changed, and because it makes no sense to
396 : * try and be backwards (or forwards) compatible, we bail out
397 : * with a friendly message saying so */
398 0 : snprintf(buf, sizeof(buf), "unsupported protocol version: %d, "
399 : "this client only supports version 9", pversion);
400 0 : mapi_setError(mid, buf, __func__, MERROR);
401 0 : close_connection(mid);
402 0 : return mid->error;
403 : }
404 :
405 1383 : char *hashes = strtok_r(NULL, ":", &strtok_state);
406 1383 : if (hashes == NULL) {
407 : /* protocol violation, not enough fields */
408 0 : mapi_setError(mid, "Not enough fields in challenge string", __func__, MERROR);
409 0 : close_connection(mid);
410 0 : return mid->error;
411 : }
412 1383 : char *algsv[] = {
413 : "RIPEMD160",
414 : "SHA512",
415 : "SHA384",
416 : "SHA256",
417 : "SHA224",
418 : "SHA1",
419 : NULL
420 : };
421 1383 : char **algs = algsv;
422 :
423 : /* rBuCQ9WTn3:mserver:9:RIPEMD160,SHA256,SHA1,MD5:LIT:SHA1: */
424 :
425 1383 : if (!*username || !*password) {
426 0 : mapi_setError(mid, "username and password must be set",
427 : __func__, MERROR);
428 0 : close_connection(mid);
429 0 : return mid->error;
430 : }
431 :
432 : /* the database has sent a list of supported hashes to us, it's
433 : * in the form of a comma separated list and in the variable
434 : * rest. We try to use the strongest algorithm. */
435 :
436 :
437 : /* in rest now should be the byte order of the server */
438 1383 : char *byteo = strtok_r(NULL, ":", &strtok_state);
439 :
440 : /* Proto v9 is like v8, but mandates that the password is a
441 : * hash, that is salted like in v8. The hash algorithm is
442 : * specified in the 6th field. If we don't support it, we
443 : * can't login. */
444 1383 : char *serverhash = strtok_r(NULL, ":", &strtok_state);
445 :
446 1383 : char *handshake_options = strtok_r(NULL, ":", &strtok_state);
447 1383 : if (handshake_options) {
448 1378 : if (sscanf(handshake_options, "sql=%d", &mid->handshake_options) != 1) {
449 0 : mapi_setError(mid, "invalid handshake options",
450 : __func__, MERROR);
451 0 : close_connection(mid);
452 0 : return mid->error;
453 : }
454 : }
455 :
456 : /* hash password, if not already */
457 1383 : if (password[0] != '\1') {
458 196 : char *pwdhash = NULL;
459 196 : if (strcmp(serverhash, "RIPEMD160") == 0) {
460 0 : pwdhash = mcrypt_RIPEMD160Sum(password,
461 : strlen(password));
462 196 : } else if (strcmp(serverhash, "SHA512") == 0) {
463 196 : pwdhash = mcrypt_SHA512Sum(password,
464 : strlen(password));
465 0 : } else if (strcmp(serverhash, "SHA384") == 0) {
466 0 : pwdhash = mcrypt_SHA384Sum(password,
467 : strlen(password));
468 0 : } else if (strcmp(serverhash, "SHA256") == 0) {
469 0 : pwdhash = mcrypt_SHA256Sum(password,
470 : strlen(password));
471 0 : } else if (strcmp(serverhash, "SHA224") == 0) {
472 0 : pwdhash = mcrypt_SHA224Sum(password,
473 : strlen(password));
474 0 : } else if (strcmp(serverhash, "SHA1") == 0) {
475 0 : pwdhash = mcrypt_SHA1Sum(password,
476 : strlen(password));
477 : } else {
478 0 : (void)pwdhash;
479 0 : snprintf(buf, sizeof(buf), "server requires unknown hash '%.100s'",
480 : serverhash);
481 0 : close_connection(mid);
482 0 : return mapi_setError(mid, buf, __func__, MERROR);
483 : }
484 :
485 196 : if (pwdhash == NULL) {
486 0 : snprintf(buf, sizeof(buf), "allocation failure or unknown hash '%.100s'",
487 : serverhash);
488 0 : close_connection(mid);
489 0 : return mapi_setError(mid, buf, __func__, MERROR);
490 : }
491 :
492 196 : char *replacement_password = malloc(1 + strlen(pwdhash) + 1);
493 196 : if (replacement_password == NULL) {
494 0 : close_connection(mid);
495 0 : return mapi_setError(mid, "malloc failed", __func__, MERROR);
496 : }
497 196 : sprintf(replacement_password, "\1%s", pwdhash);
498 196 : free(pwdhash);
499 196 : msettings_error errmsg = msetting_set_string(mid->settings, MP_PASSWORD, replacement_password);
500 196 : free(replacement_password);
501 196 : if (errmsg != NULL) {
502 0 : close_connection(mid);
503 0 : return mapi_setError(mid, "could not stow hashed password", __func__, MERROR);
504 : }
505 : }
506 :
507 :
508 1383 : const char *pw = msetting_string(mid->settings, MP_PASSWORD);
509 1383 : assert(*pw == '\1');
510 1383 : pw++;
511 :
512 1383 : char *hash = NULL;
513 1383 : for (; *algs != NULL; algs++) {
514 : /* TODO: make this actually obey the separation by
515 : * commas, and only allow full matches */
516 1383 : if (strstr(hashes, *algs) != NULL) {
517 1383 : char *pwh = mcrypt_hashPassword(*algs, pw, chal);
518 1383 : size_t len;
519 1383 : if (pwh == NULL)
520 0 : continue;
521 1383 : len = strlen(pwh) + strlen(*algs) + 3 /* {}\0 */;
522 1383 : hash = malloc(len);
523 1383 : if (hash == NULL) {
524 0 : close_connection(mid);
525 0 : free(pwh);
526 0 : return mapi_setError(mid, "malloc failure", __func__, MERROR);
527 : }
528 1383 : snprintf(hash, len, "{%s}%s", *algs, pwh);
529 1383 : free(pwh);
530 1383 : break;
531 : }
532 : }
533 1383 : if (hash == NULL) {
534 : /* the server doesn't support what we can */
535 0 : snprintf(buf, sizeof(buf), "unsupported hash algorithms: %.100s", hashes);
536 0 : close_connection(mid);
537 0 : return mapi_setError(mid, buf, __func__, MERROR);
538 : }
539 :
540 1383 : mnstr_set_bigendian(mid->from, strcmp(byteo, "BIG") == 0);
541 :
542 1383 : char *p = buf;
543 1383 : int remaining = sizeof(buf);
544 1383 : int n;
545 : #define CHECK_SNPRINTF(...) \
546 : do { \
547 : n = snprintf(p, remaining, __VA_ARGS__); \
548 : if (n < remaining) { \
549 : remaining -= n; \
550 : p += n; \
551 : } else { \
552 : mapi_setError(mid, "combination of database name and user name too long", __func__, MERROR); \
553 : free(hash); \
554 : close_connection(mid); \
555 : return mid->error; \
556 : } \
557 : } while (0)
558 :
559 : #ifdef WORDS_BIGENDIAN
560 : char *our_endian = "BIG";
561 : #else
562 1383 : char *our_endian = "LIT";
563 : #endif
564 : /* note: if we make the database field an empty string, it
565 : * means we want the default. However, it *should* be there. */
566 1383 : const char *language = msetting_string(mid->settings, MP_LANGUAGE);
567 1383 : const char *database = msetting_string(mid->settings, MP_DATABASE);
568 1383 : CHECK_SNPRINTF("%s:%s:%s:%s:%s:FILETRANS:",
569 : our_endian,
570 : username, hash,
571 : language, database);
572 :
573 1383 : if (mid->handshake_options > MAPI_HANDSHAKE_AUTOCOMMIT) {
574 1378 : CHECK_SNPRINTF("auto_commit=%d", msetting_bool(mid->settings, MP_AUTOCOMMIT));
575 : }
576 1383 : if (mid->handshake_options > MAPI_HANDSHAKE_REPLY_SIZE) {
577 1378 : CHECK_SNPRINTF(",reply_size=%ld", msetting_long(mid->settings, MP_REPLYSIZE));
578 : }
579 1383 : if (mid->handshake_options > MAPI_HANDSHAKE_SIZE_HEADER) {
580 1378 : CHECK_SNPRINTF(",size_header=%d", mid->sizeheader); // with underscore, despite X command without
581 : }
582 1383 : if (mid->handshake_options > MAPI_HANDSHAKE_COLUMNAR_PROTOCOL) {
583 1378 : CHECK_SNPRINTF(",columnar_protocol=%d", mid->columnar_protocol);
584 : }
585 1383 : if (mid->handshake_options > MAPI_HANDSHAKE_TIME_ZONE) {
586 1378 : CHECK_SNPRINTF(",time_zone=%ld", msetting_long(mid->settings, MP_TIMEZONE));
587 : }
588 1383 : if (mid->handshake_options > 0) {
589 1378 : CHECK_SNPRINTF(":");
590 : }
591 1383 : CHECK_SNPRINTF("\n");
592 :
593 1383 : free(hash);
594 :
595 1383 : len = strlen(buf);
596 1383 : mapi_log_data(mid, "HANDSHAKE SEND", buf, len);
597 1383 : mnstr_write(mid->to, buf, 1, len);
598 1383 : check_stream(mid, mid->to, "Could not send initial byte sequence", mid->error);
599 1383 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
600 1383 : check_stream(mid, mid->to, "Could not send initial byte sequence", mid->error);
601 :
602 : // Clear the redirects before we receive new ones
603 1383 : for (char **r = mid->redirects; *r != NULL; r++) {
604 0 : free(*r);
605 0 : *r = NULL;
606 : }
607 :
608 : /* consume the welcome message from the server */
609 1383 : hdl = mapi_new_handle(mid);
610 1383 : if (hdl == NULL) {
611 0 : close_connection(mid);
612 0 : return MERROR;
613 : }
614 1383 : mid->active = hdl;
615 1383 : read_into_cache(hdl, 0);
616 1383 : if (mid->error) {
617 8 : char *errorstr = NULL;
618 8 : MapiMsg error;
619 8 : struct MapiResultSet *result;
620 : /* propagate error from result to mid, the error probably is in
621 : * the last produced result, not the first
622 : * mapi_close_handle clears the errors, so save them first */
623 16 : for (result = hdl->result; result; result = result->next) {
624 8 : errorstr = result->errorstr;
625 8 : result->errorstr = NULL; /* clear these so errorstr doesn't get freed */
626 : }
627 8 : if (!errorstr)
628 0 : errorstr = mid->errorstr;
629 8 : error = mid->error;
630 :
631 8 : if (hdl->result)
632 8 : hdl->result->errorstr = NULL; /* clear these so errorstr doesn't get freed */
633 8 : mid->errorstr = NULL;
634 8 : mapi_close_handle(hdl);
635 8 : mapi_setError(mid, errorstr, __func__, error);
636 8 : if (errorstr != mapi_nomem)
637 8 : free(errorstr); /* now free it after a copy has been made */
638 8 : close_connection(mid);
639 8 : return mid->error;
640 : }
641 1375 : if (hdl->result && hdl->result->cache.line) {
642 : int i;
643 : size_t motdlen = 0;
644 : struct MapiResultSet *result = hdl->result;
645 :
646 0 : for (i = 0; i < result->cache.writer; i++) {
647 0 : if (result->cache.line[i].rows) {
648 0 : char **r;
649 0 : int m;
650 0 : switch (result->cache.line[i].rows[0]) {
651 0 : case '#':
652 0 : motdlen += strlen(result->cache.line[i].rows) + 1;
653 0 : break;
654 : case '^':
655 : r = mid->redirects;
656 : m = NELEM(mid->redirects) - 1;
657 0 : while (*r != NULL && m > 0) {
658 0 : m--;
659 0 : r++;
660 : }
661 0 : if (m == 0)
662 : break;
663 0 : *r++ = strdup(result->cache.line[i].rows + 1);
664 0 : *r = NULL;
665 0 : break;
666 : }
667 : }
668 : }
669 0 : if (motdlen > 0) {
670 0 : mid->motd = malloc(motdlen + 1);
671 0 : *mid->motd = 0;
672 0 : for (i = 0; i < result->cache.writer; i++)
673 0 : if (result->cache.line[i].rows && result->cache.line[i].rows[0] == '#') {
674 0 : strcat(mid->motd, result->cache.line[i].rows);
675 0 : strcat(mid->motd, "\n");
676 : }
677 : }
678 :
679 0 : if (*mid->redirects != NULL) {
680 : /* redirect, looks like:
681 : * ^mapi:monetdb://localhost:50001/test?lang=sql&user=monetdb
682 : * or
683 : * ^mapi:merovingian://proxy?database=test */
684 :
685 : /* first see if we reached our redirection limit */
686 0 : if (mid->redircnt >= mid->redirmax) {
687 0 : mapi_close_handle(hdl);
688 0 : mapi_setError(mid, "too many redirects", __func__, MERROR);
689 0 : close_connection(mid);
690 0 : return mid->error;
691 : }
692 0 : mid->redircnt++;
693 :
694 : /* we only implement following the first */
695 0 : char *red = mid->redirects[0];
696 :
697 0 : char *error_message = NULL;
698 0 : if (!msettings_parse_url(mid->settings, red, &error_message)
699 0 : || !msettings_validate(mid->settings, &error_message)
700 : ) {
701 0 : mapi_close_handle(hdl);
702 0 : close_connection(mid);
703 0 : MapiMsg err = mapi_printError(
704 : mid, __func__, MERROR,
705 : "%s: %s",
706 0 : error_message ? error_message : "invalid redirect",
707 : red);
708 0 : free(error_message);
709 0 : return err;
710 : }
711 :
712 0 : if (strncmp("mapi:merovingian", red, 16) == 0) {
713 : // do not close the connection so caller knows to restart handshake
714 0 : mapi_log_record(mid, "HANDSHAKE", "Restarting handshake on current socket");
715 0 : assert(mid->connected);
716 : } else {
717 0 : mapi_log_record(mid, "HANDSHAKE", "Redirected elsewhere, closing socket");
718 0 : close_connection(mid);
719 : }
720 0 : return MREDIRECT;
721 : }
722 : }
723 1375 : mapi_close_handle(hdl);
724 :
725 1375 : if (mid->trace)
726 0 : printf("connection established\n");
727 :
728 : // I don't understand this assert.
729 1375 : if (!msettings_lang_is_sql(mid->settings))
730 192 : return mid->error;
731 :
732 1183 : if (mid->error != MOK)
733 : return mid->error;
734 :
735 : /* use X commands to send options that couldn't be sent in the handshake */
736 : /* tell server about auto_complete and cache limit if handshake options weren't used */
737 1183 : bool autocommit = msetting_bool(mid->settings, MP_AUTOCOMMIT);
738 1183 : if (mid->handshake_options <= MAPI_HANDSHAKE_AUTOCOMMIT && autocommit != msetting_bool(msettings_default, MP_AUTOCOMMIT)) {
739 0 : char buf[50];
740 0 : sprintf(buf, "%d", !!autocommit);
741 0 : MapiMsg result = mapi_Xcommand(mid, "auto_commit", buf);
742 0 : if (result != MOK)
743 0 : return mid->error;
744 : }
745 1183 : long replysize = msetting_long(mid->settings, MP_REPLYSIZE);
746 1183 : if (mid->handshake_options <= MAPI_HANDSHAKE_REPLY_SIZE && replysize != msetting_long(msettings_default, MP_REPLYSIZE)) {
747 0 : char buf[50];
748 0 : sprintf(buf, "%ld", replysize);
749 0 : MapiMsg result = mapi_Xcommand(mid, "reply_size", buf);
750 0 : if (result != MOK)
751 0 : return mid->error;
752 : }
753 1183 : if (mid->handshake_options <= MAPI_HANDSHAKE_SIZE_HEADER && mid->sizeheader != MapiStructDefaults.sizeheader) {
754 0 : char buf[50];
755 0 : sprintf(buf, "%d", !!mid->sizeheader);
756 0 : MapiMsg result = mapi_Xcommand(mid, "sizeheader", buf); // no underscore!
757 0 : if (result != MOK)
758 0 : return mid->error;
759 : }
760 : // There is no if (mid->handshake_options <= MAPI_HANDSHAKE_COLUMNAR_PROTOCOL && mid->columnar_protocol != MapiStructDefaults.columnar_protocol)
761 : // The reason is that columnar_protocol is very new. If it isn't supported in the handshake it isn't supported at
762 : // all so sending the Xcommand would just give an error.
763 1183 : if (mid->handshake_options <= MAPI_HANDSHAKE_TIME_ZONE) {
764 0 : mapi_set_time_zone(mid, msetting_long(mid->settings, MP_TIMEZONE));
765 : }
766 :
767 1183 : return mid->error;
768 :
769 : }
|