LCOV - code coverage report
Current view: top level - clients/mapilib - connect.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 213 381 55.9 %
Date: 2024-04-26 00:35:57 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        1393 : mapi_reconnect(Mapi mid)
      85             : {
      86        1393 :         char *err = NULL;
      87        1393 :         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        1393 :         if (msettings_connect_scan(mid->settings))
      97           0 :                 return scan_sockets(mid);
      98             :         else
      99        1393 :                 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        1393 : establish_connection(Mapi mid)
     125             : {
     126        1393 :         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        2776 :         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        1393 :                 if (!mid->connected) {
     140        1393 :                         msg = connect_socket(mid);
     141        1393 :                         if (msg != MOK)
     142          10 :                                 return msg;
     143             :                 }
     144        1383 :                 msg = mapi_handshake(mid);
     145             :         }
     146             : 
     147             :         return msg;
     148             : }
     149             : 
     150             : static MapiMsg
     151        1393 : connect_socket(Mapi mid)
     152             : {
     153        1393 :         assert(!mid->connected);
     154        1393 :         const char *sockname = msettings_connect_unix(mid->settings);
     155        1393 :         const char *tcp_host = msettings_connect_tcp(mid->settings);
     156             : 
     157        1393 :         assert(*sockname || *tcp_host);
     158        2776 :         do {
     159        1393 :                 if (*sockname && connect_socket_unix(mid) == MOK)
     160             :                         break;
     161        1327 :                 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        1383 :         mid->connected = true;
     169        1383 :         return MOK;
     170             : }
     171             : 
     172             : MapiMsg
     173        1379 : wrap_socket(Mapi mid, SOCKET sock)
     174             : {
     175             :         // do not use check_stream here yet because the socket is not yet in 'mid'
     176        1379 :         stream *broken_stream = NULL;
     177        1379 :         MapiMsg msg;
     178        1379 :         stream *rstream = NULL;
     179        1379 :         stream *wstream = NULL;
     180             : 
     181        1379 :         wstream = socket_wstream(sock, "Mapi client write");
     182        1379 :         if (wstream == NULL || mnstr_errnr(wstream) != MNSTR_NO__ERROR) {
     183           0 :                 broken_stream = wstream;
     184           0 :                 goto bailout;
     185             :         }
     186             : 
     187        1379 :         rstream = socket_rstream(sock, "Mapi client write");
     188        1379 :         if (rstream == NULL || mnstr_errnr(rstream) != MNSTR_NO__ERROR) {
     189           0 :                 broken_stream = rstream;
     190           0 :                 goto bailout;
     191             :         }
     192             : 
     193        1379 :         msg = mapi_wrap_streams(mid, rstream, wstream);
     194        1379 :         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        1327 : connect_socket_tcp(Mapi mid)
     215             : {
     216        1327 :         int ret;
     217             : 
     218        1327 :         bool use_tls = msetting_bool(mid->settings, MP_TLS);
     219        1327 :         const char *host = msettings_connect_tcp(mid->settings);
     220        1327 :         int port = msettings_connect_port(mid->settings);
     221             : 
     222        1327 :         assert(host);
     223        1327 :         char portbuf[10];
     224        1327 :         snprintf(portbuf, sizeof(portbuf), "%d", port);
     225             : 
     226        1327 :         mapi_log_record(mid, "CONN", "Connecting to %s:%d", host, port);
     227             : 
     228        1327 :         struct addrinfo hints = (struct addrinfo) {
     229             :                 .ai_family = AF_UNSPEC,
     230             :                 .ai_socktype = SOCK_STREAM,
     231             :                 .ai_protocol = IPPROTO_TCP,
     232             :         };
     233        1327 :         struct addrinfo *addresses;
     234        1327 :         ret = getaddrinfo(host, portbuf, &hints, &addresses);
     235        1327 :         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        1327 :         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        1331 :         for (struct addrinfo *addr = addresses; addr; addr = addr->ai_next) {
     249        1329 :                 s = connect_socket_tcp_addr(mid, addr);
     250        1329 :                 if (s != INVALID_SOCKET)
     251             :                         break;
     252             :         }
     253        1327 :         freeaddrinfo(addresses);
     254        1327 :         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        1325 :         union {
     263             :                 struct sockaddr_storage ss;
     264             :                 struct sockaddr_in i4;
     265             :                 struct sockaddr_in6 i6;
     266             :         } myaddr, praddr;
     267        1325 :         socklen_t myaddrlen, praddrlen;
     268        1325 :         myaddrlen = (socklen_t) sizeof(myaddr.ss);
     269        1325 :         praddrlen = (socklen_t) sizeof(praddr.ss);
     270        2650 :         if (getsockname(s, (struct sockaddr *) &myaddr.ss, &myaddrlen) == 0 &&
     271        1325 :                 getpeername(s, (struct sockaddr *) &praddr.ss, &praddrlen) == 0 &&
     272        2650 :                 myaddr.ss.ss_family == praddr.ss.ss_family &&
     273             :                 (myaddr.ss.ss_family == AF_INET
     274        1325 :                 ? 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        1325 :         mapi_log_record(mid, "CONN", "Network connection established");
     286        1325 :         MapiMsg msg = use_tls ? wrap_tls(mid, s) : wrap_socket(mid, s);
     287        1325 :         if (msg != MOK)
     288             :                 return msg;
     289             : 
     290             :         return msg;
     291             : }
     292             : 
     293             : static SOCKET
     294        1329 : connect_socket_tcp_addr(Mapi mid, struct addrinfo *info)
     295             : {
     296        1329 :         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        1329 :         int socktype = info->ai_socktype;
     319             : #ifdef SOCK_CLOEXEC
     320        1329 :         socktype |= SOCK_CLOEXEC;
     321             : #endif
     322             : 
     323        1329 :         SOCKET s =  socket(info->ai_family, socktype, info->ai_protocol);
     324        1329 :         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        1327 :         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        1383 : mapi_handshake(Mapi mid)
     349             : {
     350        1383 :         char buf[BLOCK];
     351        1383 :         size_t len;
     352        1383 :         MapiHdl hdl;
     353             : 
     354        1383 :         const char *username = msetting_string(mid->settings, MP_USER);
     355        1383 :         const char *password = msetting_string(mid->settings, MP_PASSWORD);
     356             : 
     357             :         /* consume server challenge */
     358        1383 :         len = mnstr_read_block(mid->from, buf, 1, sizeof(buf));
     359        1383 :         check_stream(mid, mid->from, "Connection terminated while starting handshake", (mid->blk.eos = true, mid->error));
     360             : 
     361        1382 :         mapi_log_data(mid, "RECV HANDSHAKE", buf, len);
     362             : 
     363        1382 :         assert(len < sizeof(buf));
     364        1382 :         buf[len] = 0;
     365             : 
     366        1382 :         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        1382 :         char *strtok_state = NULL;
     373        1382 :         char *chal = strtok_r(buf, ":", &strtok_state);
     374        1382 :         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        1382 :         char *server = strtok_r(NULL, ":", &strtok_state);
     381        1382 :         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        1382 :         char *protover = strtok_r(NULL, ":", &strtok_state);
     388        1382 :         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        1382 :         int pversion = atoi(protover);
     394        1382 :         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        1382 :         char *hashes = strtok_r(NULL, ":", &strtok_state);
     406        1382 :         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        1382 :         char *algsv[] = {
     413             :                 "RIPEMD160",
     414             :                 "SHA512",
     415             :                 "SHA384",
     416             :                 "SHA256",
     417             :                 "SHA224",
     418             :                 "SHA1",
     419             :                 NULL
     420             :         };
     421        1382 :         char **algs = algsv;
     422             : 
     423             :         /* rBuCQ9WTn3:mserver:9:RIPEMD160,SHA256,SHA1,MD5:LIT:SHA1: */
     424             : 
     425        1382 :         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        1382 :         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        1382 :         char *serverhash = strtok_r(NULL, ":", &strtok_state);
     445             : 
     446        1382 :         char *handshake_options = strtok_r(NULL, ":", &strtok_state);
     447        1382 :         if (handshake_options) {
     448        1377 :                 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             :         /* search for OOBINTR option,
     457             :          * NOTE this consumes the rest of the challenge */
     458        1382 :         char *rest = strtok_r(NULL, ":", &strtok_state);
     459        2759 :         while (rest != NULL) {
     460        2754 :                 if (strcmp(rest, "OOBINTR=1") == 0) {
     461        1377 :                         mid->oobintr = true;
     462        1377 :                         break;
     463             :                 }
     464        1377 :                 rest = strtok_r(NULL, ":", &strtok_state);
     465             :         }
     466             : 
     467             :         /* hash password, if not already */
     468        1382 :         if (password[0] != '\1') {
     469         199 :                 char *pwdhash = NULL;
     470         199 :                 if (strcmp(serverhash, "RIPEMD160") == 0) {
     471           0 :                         pwdhash = mcrypt_RIPEMD160Sum(password,
     472             :                                                         strlen(password));
     473         199 :                 } else if (strcmp(serverhash, "SHA512") == 0) {
     474         199 :                         pwdhash = mcrypt_SHA512Sum(password,
     475             :                                                         strlen(password));
     476           0 :                 } else if (strcmp(serverhash, "SHA384") == 0) {
     477           0 :                         pwdhash = mcrypt_SHA384Sum(password,
     478             :                                                         strlen(password));
     479           0 :                 } else if (strcmp(serverhash, "SHA256") == 0) {
     480           0 :                         pwdhash = mcrypt_SHA256Sum(password,
     481             :                                                         strlen(password));
     482           0 :                 } else if (strcmp(serverhash, "SHA224") == 0) {
     483           0 :                         pwdhash = mcrypt_SHA224Sum(password,
     484             :                                                         strlen(password));
     485           0 :                 } else if (strcmp(serverhash, "SHA1") == 0) {
     486           0 :                         pwdhash = mcrypt_SHA1Sum(password,
     487             :                                                         strlen(password));
     488             :                 } else {
     489           0 :                         (void)pwdhash;
     490           0 :                         snprintf(buf, sizeof(buf), "server requires unknown hash '%.100s'",
     491             :                                         serverhash);
     492           0 :                         close_connection(mid);
     493           0 :                         return mapi_setError(mid, buf, __func__, MERROR);
     494             :                 }
     495             : 
     496         199 :                 if (pwdhash == NULL) {
     497           0 :                         snprintf(buf, sizeof(buf), "allocation failure or unknown hash '%.100s'",
     498             :                                         serverhash);
     499           0 :                         close_connection(mid);
     500           0 :                         return mapi_setError(mid, buf, __func__, MERROR);
     501             :                 }
     502             : 
     503         199 :                 char *replacement_password = malloc(1 + strlen(pwdhash) + 1);
     504         199 :                 if (replacement_password == NULL) {
     505           0 :                         close_connection(mid);
     506           0 :                         return mapi_setError(mid, "malloc failed", __func__, MERROR);
     507             :                 }
     508         199 :                 sprintf(replacement_password, "\1%s", pwdhash);
     509         199 :                 free(pwdhash);
     510         199 :                 msettings_error errmsg = msetting_set_string(mid->settings, MP_PASSWORD, replacement_password);
     511         199 :                 free(replacement_password);
     512         199 :                 if (errmsg != NULL) {
     513           0 :                         close_connection(mid);
     514           0 :                         return mapi_setError(mid, "could not stow hashed password", __func__, MERROR);
     515             :                 }
     516             :         }
     517             : 
     518             : 
     519        1382 :         const char *pw = msetting_string(mid->settings, MP_PASSWORD);
     520        1382 :         assert(*pw == '\1');
     521        1382 :         pw++;
     522             : 
     523        1382 :         char *hash = NULL;
     524        1382 :         for (; *algs != NULL; algs++) {
     525             :                 /* TODO: make this actually obey the separation by
     526             :                         * commas, and only allow full matches */
     527        1382 :                 if (strstr(hashes, *algs) != NULL) {
     528        1382 :                         char *pwh = mcrypt_hashPassword(*algs, pw, chal);
     529        1382 :                         size_t len;
     530        1382 :                         if (pwh == NULL)
     531           0 :                                 continue;
     532        1382 :                         len = strlen(pwh) + strlen(*algs) + 3 /* {}\0 */;
     533        1382 :                         hash = malloc(len);
     534        1382 :                         if (hash == NULL) {
     535           0 :                                 close_connection(mid);
     536           0 :                                 free(pwh);
     537           0 :                                 return mapi_setError(mid, "malloc failure", __func__, MERROR);
     538             :                         }
     539        1382 :                         snprintf(hash, len, "{%s}%s", *algs, pwh);
     540        1382 :                         free(pwh);
     541        1382 :                         break;
     542             :                 }
     543             :         }
     544        1382 :         if (hash == NULL) {
     545             :                 /* the server doesn't support what we can */
     546           0 :                 snprintf(buf, sizeof(buf), "unsupported hash algorithms: %.100s", hashes);
     547           0 :                 close_connection(mid);
     548           0 :                 return mapi_setError(mid, buf, __func__, MERROR);
     549             :         }
     550             : 
     551        1382 :         mnstr_set_bigendian(mid->from, strcmp(byteo, "BIG") == 0);
     552             : 
     553        1382 :         char *p = buf;
     554        1382 :         int remaining = sizeof(buf);
     555        1382 :         int n;
     556             : #define CHECK_SNPRINTF(...) \
     557             :         do { \
     558             :                 n = snprintf(p, remaining, __VA_ARGS__); \
     559             :                 if (n < remaining) { \
     560             :                         remaining -= n; \
     561             :                         p += n; \
     562             :                 } else { \
     563             :                         mapi_setError(mid, "combination of database name and user name too long", __func__, MERROR); \
     564             :                         free(hash); \
     565             :                         close_connection(mid); \
     566             :                         return mid->error; \
     567             :                 } \
     568             :         } while (0)
     569             : 
     570             : #ifdef WORDS_BIGENDIAN
     571             :         char *our_endian = "BIG";
     572             : #else
     573        1382 :         char *our_endian = "LIT";
     574             : #endif
     575             :         /* note: if we make the database field an empty string, it
     576             :                 * means we want the default.  However, it *should* be there. */
     577        1382 :         const char *language = msetting_string(mid->settings, MP_LANGUAGE);
     578        1382 :         const char *database = msetting_string(mid->settings, MP_DATABASE);
     579        1382 :         CHECK_SNPRINTF("%s:%s:%s:%s:%s:FILETRANS:",
     580             :                         our_endian,
     581             :                         username, hash,
     582             :                         language, database);
     583             : 
     584        1382 :         if (mid->handshake_options > MAPI_HANDSHAKE_AUTOCOMMIT) {
     585        1377 :                 CHECK_SNPRINTF("auto_commit=%d", msetting_bool(mid->settings, MP_AUTOCOMMIT));
     586             :         }
     587        1382 :         if (mid->handshake_options > MAPI_HANDSHAKE_REPLY_SIZE) {
     588        1377 :                 CHECK_SNPRINTF(",reply_size=%ld", msetting_long(mid->settings, MP_REPLYSIZE));
     589             :         }
     590        1382 :         if (mid->handshake_options > MAPI_HANDSHAKE_SIZE_HEADER) {
     591        1377 :                 CHECK_SNPRINTF(",size_header=%d", mid->sizeheader); // with underscore, despite X command without
     592             :         }
     593        1382 :         if (mid->handshake_options > MAPI_HANDSHAKE_COLUMNAR_PROTOCOL) {
     594        1377 :                 CHECK_SNPRINTF(",columnar_protocol=%d", mid->columnar_protocol);
     595             :         }
     596        1382 :         if (mid->handshake_options > MAPI_HANDSHAKE_TIME_ZONE) {
     597        1377 :                 CHECK_SNPRINTF(",time_zone=%ld", msetting_long(mid->settings, MP_TIMEZONE));
     598             :         }
     599        1382 :         if (mid->handshake_options > 0) {
     600        1377 :                 CHECK_SNPRINTF(":");
     601             :         }
     602        1382 :         CHECK_SNPRINTF("\n");
     603             : 
     604        1382 :         free(hash);
     605             : 
     606        1382 :         len = strlen(buf);
     607        1382 :         mapi_log_data(mid, "HANDSHAKE SEND", buf, len);
     608        1382 :         mnstr_write(mid->to, buf, 1, len);
     609        1382 :         check_stream(mid, mid->to, "Could not send initial byte sequence", mid->error);
     610        1382 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
     611        1382 :         check_stream(mid, mid->to, "Could not send initial byte sequence", mid->error);
     612             : 
     613             :         // Clear the redirects before we receive new ones
     614        1382 :         for (char **r = mid->redirects; *r != NULL; r++) {
     615           0 :                 free(*r);
     616           0 :                 *r = NULL;
     617             :         }
     618             : 
     619             :         /* consume the welcome message from the server */
     620        1382 :         hdl = mapi_new_handle(mid);
     621        1382 :         if (hdl == NULL) {
     622           0 :                 close_connection(mid);
     623           0 :                 return MERROR;
     624             :         }
     625        1382 :         mid->active = hdl;
     626        1382 :         read_into_cache(hdl, 0);
     627        1382 :         if (mid->error) {
     628           8 :                 char *errorstr = NULL;
     629           8 :                 MapiMsg error;
     630           8 :                 struct MapiResultSet *result;
     631             :                 /* propagate error from result to mid, the error probably is in
     632             :                  * the last produced result, not the first
     633             :                  * mapi_close_handle clears the errors, so save them first */
     634          16 :                 for (result = hdl->result; result; result = result->next) {
     635           8 :                         errorstr = result->errorstr;
     636           8 :                         result->errorstr = NULL;     /* clear these so errorstr doesn't get freed */
     637             :                 }
     638           8 :                 if (!errorstr)
     639           0 :                         errorstr = mid->errorstr;
     640           8 :                 error = mid->error;
     641             : 
     642           8 :                 if (hdl->result)
     643           8 :                         hdl->result->errorstr = NULL;     /* clear these so errorstr doesn't get freed */
     644           8 :                 mid->errorstr = NULL;
     645           8 :                 mapi_close_handle(hdl);
     646           8 :                 mapi_setError(mid, errorstr, __func__, error);
     647           8 :                 if (errorstr != mapi_nomem)
     648           8 :                         free(errorstr); /* now free it after a copy has been made */
     649           8 :                 close_connection(mid);
     650           8 :                 return mid->error;
     651             :         }
     652        1374 :         if (hdl->result && hdl->result->cache.line) {
     653             :                 int i;
     654             :                 size_t motdlen = 0;
     655             :                 struct MapiResultSet *result = hdl->result;
     656             : 
     657           0 :                 for (i = 0; i < result->cache.writer; i++) {
     658           0 :                         if (result->cache.line[i].rows) {
     659           0 :                                 char **r;
     660           0 :                                 int m;
     661           0 :                                 switch (result->cache.line[i].rows[0]) {
     662           0 :                                 case '#':
     663           0 :                                         motdlen += strlen(result->cache.line[i].rows) + 1;
     664           0 :                                         break;
     665             :                                 case '^':
     666             :                                         r = mid->redirects;
     667             :                                         m = NELEM(mid->redirects) - 1;
     668           0 :                                         while (*r != NULL && m > 0) {
     669           0 :                                                 m--;
     670           0 :                                                 r++;
     671             :                                         }
     672           0 :                                         if (m == 0)
     673             :                                                 break;
     674           0 :                                         *r++ = strdup(result->cache.line[i].rows + 1);
     675           0 :                                         *r = NULL;
     676           0 :                                         break;
     677             :                                 }
     678             :                         }
     679             :                 }
     680           0 :                 if (motdlen > 0) {
     681           0 :                         mid->motd = malloc(motdlen + 1);
     682           0 :                         *mid->motd = 0;
     683           0 :                         for (i = 0; i < result->cache.writer; i++)
     684           0 :                                 if (result->cache.line[i].rows && result->cache.line[i].rows[0] == '#') {
     685           0 :                                         strcat(mid->motd, result->cache.line[i].rows);
     686           0 :                                         strcat(mid->motd, "\n");
     687             :                                 }
     688             :                 }
     689             : 
     690           0 :                 if (*mid->redirects != NULL) {
     691             :                         /* redirect, looks like:
     692             :                          * ^mapi:monetdb://localhost:50001/test?lang=sql&user=monetdb
     693             :                          * or
     694             :                          * ^mapi:merovingian://proxy?database=test */
     695             : 
     696             :                         /* first see if we reached our redirection limit */
     697           0 :                         if (mid->redircnt >= mid->redirmax) {
     698           0 :                                 mapi_close_handle(hdl);
     699           0 :                                 mapi_setError(mid, "too many redirects", __func__, MERROR);
     700           0 :                                 close_connection(mid);
     701           0 :                                 return mid->error;
     702             :                         }
     703           0 :                         mid->redircnt++;
     704             : 
     705             :                         /* we only implement following the first */
     706           0 :                         char *red = mid->redirects[0];
     707             : 
     708           0 :                         char *error_message = NULL;
     709           0 :                         if (!msettings_parse_url(mid->settings, red, &error_message)
     710           0 :                             || !msettings_validate(mid->settings, &error_message)
     711             :                         ) {
     712           0 :                                 mapi_close_handle(hdl);
     713           0 :                                 close_connection(mid);
     714           0 :                                 MapiMsg err = mapi_printError(
     715             :                                         mid, __func__, MERROR,
     716             :                                         "%s: %s",
     717           0 :                                         error_message ? error_message : "invalid redirect",
     718             :                                         red);
     719           0 :                                 free(error_message);
     720           0 :                                 return err;
     721             :                         }
     722             : 
     723           0 :                         if (strncmp("mapi:merovingian", red, 16) == 0) {
     724             :                                 // do not close the connection so caller knows to restart handshake
     725           0 :                                 mapi_log_record(mid, "HANDSHAKE", "Restarting handshake on current socket");
     726           0 :                                 assert(mid->connected);
     727             :                         } else {
     728           0 :                                 mapi_log_record(mid, "HANDSHAKE", "Redirected elsewhere, closing socket");
     729           0 :                                 close_connection(mid);
     730             :                         }
     731           0 :                         return MREDIRECT;
     732             :                 }
     733             :         }
     734        1374 :         mapi_close_handle(hdl);
     735             : 
     736        1374 :         if (mid->trace)
     737           0 :                 printf("connection established\n");
     738             : 
     739             :         // I don't understand this assert.
     740        1374 :         if (!msettings_lang_is_sql(mid->settings))
     741         188 :                 return mid->error;
     742             : 
     743        1186 :         if (mid->error != MOK)
     744             :                 return mid->error;
     745             : 
     746             :         /* use X commands to send options that couldn't be sent in the handshake */
     747             :         /* tell server about auto_complete and cache limit if handshake options weren't used */
     748        1186 :         bool autocommit = msetting_bool(mid->settings, MP_AUTOCOMMIT);
     749        1186 :         if (mid->handshake_options <= MAPI_HANDSHAKE_AUTOCOMMIT && autocommit != msetting_bool(msettings_default, MP_AUTOCOMMIT)) {
     750           0 :                 char buf[50];
     751           0 :                 sprintf(buf, "%d", !!autocommit);
     752           0 :                 MapiMsg result = mapi_Xcommand(mid, "auto_commit", buf);
     753           0 :                 if (result != MOK)
     754           0 :                         return mid->error;
     755             :         }
     756        1186 :         long replysize = msetting_long(mid->settings, MP_REPLYSIZE);
     757        1186 :         if (mid->handshake_options <= MAPI_HANDSHAKE_REPLY_SIZE && replysize != msetting_long(msettings_default, MP_REPLYSIZE)) {
     758           0 :                 char buf[50];
     759           0 :                 sprintf(buf, "%ld", replysize);
     760           0 :                 MapiMsg result = mapi_Xcommand(mid, "reply_size", buf);
     761           0 :                 if (result != MOK)
     762           0 :                         return mid->error;
     763             :         }
     764        1186 :         if (mid->handshake_options <= MAPI_HANDSHAKE_SIZE_HEADER && mid->sizeheader != MapiStructDefaults.sizeheader) {
     765           0 :                 char buf[50];
     766           0 :                 sprintf(buf, "%d", !!mid->sizeheader);
     767           0 :                 MapiMsg result = mapi_Xcommand(mid, "sizeheader", buf); // no underscore!
     768           0 :                 if (result != MOK)
     769           0 :                         return mid->error;
     770             :         }
     771             :         // There is no if  (mid->handshake_options <= MAPI_HANDSHAKE_COLUMNAR_PROTOCOL && mid->columnar_protocol != MapiStructDefaults.columnar_protocol)
     772             :         // The reason is that columnar_protocol is very new. If it isn't supported in the handshake it isn't supported at
     773             :         // all so sending the Xcommand would just give an error.
     774        1186 :         if (mid->handshake_options <= MAPI_HANDSHAKE_TIME_ZONE) {
     775           0 :                 mapi_set_time_zone(mid, msetting_long(mid->settings, MP_TIMEZONE));
     776             :         }
     777             : 
     778        1186 :         return mid->error;
     779             : 
     780             : }

Generated by: LCOV version 1.14