LCOV - code coverage report
Current view: top level - clients/mapilib - connect.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 207 375 55.2 %
Date: 2024-04-25 20:03:45 Functions: 7 8 87.5 %

          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             : }

Generated by: LCOV version 1.14