LCOV - code coverage report
Current view: top level - src/tds - config.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 336 585 57.4 %
Date: 2025-02-21 09:36:06 Functions: 22 28 78.6 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns
       3             :  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011  Frediano Ziglio
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU Library General Public
       7             :  * License as published by the Free Software Foundation; either
       8             :  * version 2 of the License, or (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * Library General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Library General Public
      16             :  * License along with this library; if not, write to the
      17             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18             :  * Boston, MA 02111-1307, USA.
      19             :  */
      20             : 
      21             : #include <config.h>
      22             : 
      23             : #include <stdarg.h>
      24             : #include <stdio.h>
      25             : 
      26             : #if HAVE_ERRNO_H
      27             : #include <errno.h>
      28             : #endif /* HAVE_ERRNO_H */
      29             : 
      30             : #include <assert.h>
      31             : #include <ctype.h>
      32             : 
      33             : #if HAVE_STDLIB_H
      34             : #include <stdlib.h>
      35             : #endif /* HAVE_STDLIB_H */
      36             : 
      37             : #if HAVE_LIMITS_H
      38             : #include <limits.h>
      39             : #endif 
      40             : 
      41             : #if HAVE_STRING_H
      42             : #include <string.h>
      43             : #endif /* HAVE_STRING_H */
      44             : 
      45             : #if HAVE_UNISTD_H
      46             : #include <unistd.h>
      47             : #endif /* HAVE_UNISTD_H */
      48             : 
      49             : #if HAVE_NETDB_H
      50             : #include <netdb.h>
      51             : #endif /* HAVE_NETDB_H */
      52             : 
      53             : #if HAVE_SYS_SOCKET_H
      54             : #include <sys/socket.h>
      55             : #endif /* HAVE_SYS_SOCKET_H */
      56             : 
      57             : #if HAVE_SYS_TYPES_H
      58             : #include <sys/types.h>
      59             : #endif /* HAVE_SYS_TYPES_H */
      60             : 
      61             : #if HAVE_NETINET_IN_H
      62             : #include <netinet/in.h>
      63             : #endif /* HAVE_NETINET_IN_H */
      64             : 
      65             : #if HAVE_ARPA_INET_H
      66             : #include <arpa/inet.h>
      67             : #endif /* HAVE_ARPA_INET_H */
      68             : 
      69             : #ifdef _WIN32
      70             : #include <process.h>
      71             : #endif
      72             : 
      73             : #include <freetds/tds.h>
      74             : #include <freetds/configs.h>
      75             : #include <freetds/utils/string.h>
      76             : #include <freetds/utils.h>
      77             : #include <freetds/replacements.h>
      78             : 
      79             : static bool tds_config_login(TDSLOGIN * connection, TDSLOGIN * login);
      80             : static bool tds_config_env_tdsdump(TDSLOGIN * login);
      81             : static void tds_config_env_tdsver(TDSLOGIN * login);
      82             : static void tds_config_env_tdsport(TDSLOGIN * login);
      83             : static bool tds_config_env_tdshost(TDSLOGIN * login);
      84             : static bool tds_read_conf_sections(FILE * in, const char *server, TDSLOGIN * login);
      85             : static bool tds_read_interfaces(const char *server, TDSLOGIN * login);
      86             : static bool parse_server_name_for_port(TDSLOGIN * connection, TDSLOGIN * login, bool update_server);
      87             : static int tds_lookup_port(const char *portname);
      88             : static bool tds_config_encryption(const char * value, TDSLOGIN * login);
      89             : 
      90             : static tds_dir_char *interf_file = NULL;
      91             : 
      92             : #define TDS_ISSPACE(c) isspace((unsigned char ) (c))
      93             : 
      94             : const char STD_DATETIME_FMT[] = "%b %e %Y %I:%M%p";
      95             : 
      96             : #if !defined(_WIN32) && !defined(DOS32X)
      97             : static const char pid_config_logpath[] = "/tmp/tdsconfig.log.%d";
      98             : static const char freetds_conf[] = "etc/freetds.conf";
      99             : static const char location[] = "(from $FREETDS/etc)";
     100             : static const char pid_logpath[] = "/tmp/freetds.log.%d";
     101             : static const char interfaces_path[] = "/etc/freetds";
     102             : #else
     103             : static const tds_dir_char pid_config_logpath[] = L"c:\\tdsconfig.log.%d";
     104             : static const tds_dir_char freetds_conf[] = L"freetds.conf";
     105             : static const char location[] = "(from $FREETDS)";
     106             : static const tds_dir_char pid_logpath[] = L"c:\\freetds.log.%d";
     107             : static const tds_dir_char interfaces_path[] = L"c:\\";
     108             : #endif
     109             : 
     110             : /**
     111             :  * \ingroup libtds
     112             :  * \defgroup config Configuration
     113             :  * Handle reading of configuration
     114             :  */
     115             : 
     116             : /**
     117             :  * \addtogroup config
     118             :  * @{ 
     119             :  */
     120             : 
     121             : /**
     122             :  * tds_read_config_info() will fill the tds connection structure based on configuration 
     123             :  * information gathered in the following order:
     124             :  * 1) Program specified in TDSLOGIN structure
     125             :  * 2) The environment variables TDSVER, TDSDUMP, TDSPORT, TDSQUERY, TDSHOST
     126             :  * 3) A config file with the following search order:
     127             :  *    a) a readable file specified by environment variable FREETDSCONF
     128             :  *    b) a readable file in ~/.freetds.conf
     129             :  *    c) a readable file in $prefix/etc/freetds.conf
     130             :  * 3) ~/.interfaces if exists
     131             :  * 4) $SYBASE/interfaces if exists
     132             :  * 5) TDS_DEF_* default values
     133             :  *
     134             :  * .tdsrc and freetds.conf have been added to make the package easier to 
     135             :  * integration with various Linux and *BSD distributions.
     136             :  */
     137             : TDSLOGIN *
     138        2590 : tds_read_config_info(TDSSOCKET * tds, TDSLOGIN * login, TDSLOCALE * locale)
     139             : {
     140             :         TDSLOGIN *connection;
     141             :         tds_dir_char *s;
     142        2590 :         int opened = 0;
     143             :         bool found;
     144             : 
     145             :         /* allocate a new structure with hard coded and build-time defaults */
     146        2590 :         connection = tds_alloc_login(false);
     147        2590 :         if (!connection || !tds_init_login(connection, locale)) {
     148           0 :                 tds_free_login(connection);
     149           0 :                 return NULL;
     150             :         }
     151             : 
     152        2590 :         s = tds_dir_getenv(TDS_DIR("TDSDUMPCONFIG"));
     153        2590 :         if (s) {
     154           0 :                 if (*s) {
     155           0 :                         opened = tdsdump_open(s);
     156             :                 } else {
     157             :                         tds_dir_char path[TDS_VECTOR_SIZE(pid_config_logpath) + 22];
     158           0 :                         pid_t pid = getpid();
     159           0 :                         tds_dir_snprintf(path, TDS_VECTOR_SIZE(path), pid_config_logpath, (int) pid);
     160           0 :                         opened = tdsdump_open(path);
     161             :                 }
     162             :         }
     163             : 
     164        2590 :         tdsdump_log(TDS_DBG_INFO1, "Getting connection information for [%s].\n", 
     165           0 :                             tds_dstr_cstr(&login->server_name)); /* (The server name is set in login.c.) */
     166             : 
     167             :         /* Read the config files. */
     168        2590 :         tdsdump_log(TDS_DBG_INFO1, "Attempting to read conf files.\n");
     169        5180 :         found = tds_read_conf_file(connection, tds_dstr_cstr(&login->server_name));
     170        2590 :         if (!found) {
     171          74 :                 if (parse_server_name_for_port(connection, login, true)) {
     172             : 
     173         148 :                         found = tds_read_conf_file(connection, tds_dstr_cstr(&connection->server_name));
     174             :                         /* do it again to really override what found in freetds.conf */
     175          74 :                         parse_server_name_for_port(connection, login, false);
     176         124 :                         if (!found && TDS_SUCCEED(tds_lookup_host_set(tds_dstr_cstr(&connection->server_name), &connection->ip_addrs))) {
     177          50 :                                 if (!tds_dstr_dup(&connection->server_host_name, &connection->server_name)) {
     178           0 :                                         tds_free_login(connection);
     179           0 :                                         return NULL;
     180             :                                 }
     181             :                                 found = true;
     182             :                         }
     183          74 :                         if (!tds_dstr_dup(&login->server_name, &connection->server_name)) {
     184           0 :                                 tds_free_login(connection);
     185           0 :                                 return NULL;
     186             :                         }
     187             :                 }
     188             :         }
     189        2590 :         if (!found) {
     190             :                 /* fallback to interfaces file */
     191           0 :                 tdsdump_log(TDS_DBG_INFO1, "Failed in reading conf file.  Trying interface files.\n");
     192           0 :                 if (!tds_read_interfaces(tds_dstr_cstr(&login->server_name), connection)) {
     193           0 :                         tdsdump_log(TDS_DBG_INFO1, "Failed to find [%s] in configuration files; trying '%s' instead.\n", 
     194           0 :                                                    tds_dstr_cstr(&login->server_name), tds_dstr_cstr(&connection->server_name));
     195           0 :                         if (connection->ip_addrs == NULL)
     196           0 :                                 tdserror(tds_get_ctx(tds), tds, TDSEINTF, 0);
     197             :                 }
     198             :         }
     199             : 
     200             :         /* Override config file settings with environment variables. */
     201        2590 :         tds_fix_login(connection);
     202             : 
     203             :         /* And finally apply anything from the login structure */
     204        2590 :         if (!tds_config_login(connection, login)) {
     205           0 :                 tds_free_login(connection);
     206           0 :                 return NULL;
     207             :         }
     208             :         
     209        2590 :         if (opened) {
     210             :                 struct addrinfo *addrs;
     211             :                 char tmp[128];
     212             : 
     213           0 :                 tdsdump_log(TDS_DBG_INFO1, "Final connection parameters:\n");
     214           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_name", tds_dstr_cstr(&connection->server_name));
     215           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_host_name", tds_dstr_cstr(&connection->server_host_name));
     216             : 
     217           0 :                 for (addrs = connection->ip_addrs; addrs != NULL; addrs = addrs->ai_next)
     218           0 :                         tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "ip_addr", tds_addrinfo2str(addrs, tmp, sizeof(tmp)));
     219             : 
     220           0 :                 if (connection->ip_addrs == NULL)
     221           0 :                         tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "ip_addr", "");
     222             : 
     223           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "instance_name", tds_dstr_cstr(&connection->instance_name));
     224           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "port", connection->port);
     225           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "major_version", TDS_MAJOR(connection));
     226           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "minor_version", TDS_MINOR(connection));
     227           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "block_size", connection->block_size);
     228           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "language", tds_dstr_cstr(&connection->language));
     229           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_charset", tds_dstr_cstr(&connection->server_charset));
     230           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "connect_timeout", connection->connect_timeout);
     231           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "client_host_name", tds_dstr_cstr(&connection->client_host_name));
     232           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "client_charset", tds_dstr_cstr(&connection->client_charset));
     233           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "use_utf16", connection->use_utf16);
     234           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "app_name", tds_dstr_cstr(&connection->app_name));
     235           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "user_name", tds_dstr_cstr(&connection->user_name));
     236             :                 /* tdsdump_log(TDS_DBG_PASSWD, "\t%20s = %s\n", "password", tds_dstr_cstr(&connection->password)); 
     237             :                         (no such flag yet) */
     238           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "library", tds_dstr_cstr(&connection->library));
     239           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "bulk_copy", (int)connection->bulk_copy);
     240           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "suppress_language", (int)connection->suppress_language);
     241           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "encrypt level", (int)connection->encryption_level);
     242           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "query_timeout", connection->query_timeout);
     243             :                 /* tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "capabilities", tds_dstr_cstr(&connection->capabilities)); 
     244             :                         (not null terminated) */
     245           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "database", tds_dstr_cstr(&connection->database));
     246           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %" tdsPRIdir "\n", "dump_file", connection->dump_file);
     247           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %x\n", "debug_flags", connection->debug_flags);
     248           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "text_size", connection->text_size);
     249           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_realm_name", tds_dstr_cstr(&connection->server_realm_name));
     250           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "server_spn", tds_dstr_cstr(&connection->server_spn));
     251           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "cafile", tds_dstr_cstr(&connection->cafile));
     252           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "crlfile", tds_dstr_cstr(&connection->crlfile));
     253           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "check_ssl_hostname", connection->check_ssl_hostname);
     254           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "db_filename", tds_dstr_cstr(&connection->db_filename));
     255           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %d\n", "readonly_intent", connection->readonly_intent);
     256             : #ifdef HAVE_OPENSSL
     257           0 :                 tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "openssl_ciphers", tds_dstr_cstr(&connection->openssl_ciphers));
     258             : #endif
     259             : 
     260           0 :                 tdsdump_close();
     261             :         }
     262             : 
     263             :         /*
     264             :          * If a dump file has been specified, start logging
     265             :          */
     266        2590 :         if (connection->dump_file != NULL && !tdsdump_isopen()) {
     267           4 :                 if (connection->debug_flags)
     268           4 :                         tds_debug_flags = connection->debug_flags;
     269           4 :                 tdsdump_open(connection->dump_file);
     270             :         }
     271             : 
     272             :         return connection;
     273             : }
     274             : 
     275             : /**
     276             :  * Fix configuration after reading it. 
     277             :  * Currently this read some environment variables and replace some options.
     278             :  */
     279             : void
     280        4584 : tds_fix_login(TDSLOGIN * login)
     281             : {
     282             :         /* Now check the environment variables */
     283        4584 :         tds_config_env_tdsver(login);
     284        4584 :         tds_config_env_tdsdump(login);
     285        4584 :         tds_config_env_tdsport(login);
     286        4584 :         tds_config_env_tdshost(login);
     287        4584 : }
     288             : 
     289             : static bool
     290        7932 : tds_try_conf_file(const tds_dir_char *path, const char *how, const char *server, TDSLOGIN * login)
     291             : {
     292        7932 :         bool found = false;
     293             :         FILE *in;
     294             : 
     295        7932 :         if ((in = tds_dir_open(path, TDS_DIR("r"))) == NULL) {
     296        3774 :                 tdsdump_log(TDS_DBG_INFO1, "Could not open '%" tdsPRIdir "' (%s).\n", path, how);
     297             :                 return found;
     298             :         }
     299             : 
     300        4158 :         tdsdump_log(TDS_DBG_INFO1, "Found conf file '%" tdsPRIdir "' %s.\n", path, how);
     301        4158 :         found = tds_read_conf_sections(in, server, login);
     302             : 
     303        4158 :         if (found) {
     304        3670 :                 tdsdump_log(TDS_DBG_INFO1, "Success: [%s] defined in %" tdsPRIdir ".\n", server, path);
     305             :         } else {
     306         488 :                 tdsdump_log(TDS_DBG_INFO2, "[%s] not found.\n", server);
     307             :         }
     308             : 
     309        4158 :         fclose(in);
     310             : 
     311        4158 :         return found;
     312             : }
     313             : 
     314             : /**
     315             :  * Read configuration info for given server
     316             :  * return 0 on error
     317             :  * @param login where to store configuration
     318             :  * @param server       section of file configuration that hold 
     319             :  *                     configuration for a server
     320             :  */
     321             : bool
     322        3794 : tds_read_conf_file(TDSLOGIN * login, const char *server)
     323             : {
     324        3794 :         tds_dir_char *path = NULL;
     325        3794 :         tds_dir_char *eptr = NULL;
     326        3794 :         bool found = false;
     327             : 
     328        3794 :         if (interf_file) {
     329         140 :                 found = tds_try_conf_file(interf_file, "set programmatically", server, login);
     330             :         }
     331             : 
     332             :         /* FREETDSCONF env var, pkleef@openlinksw.com 01/21/02 */
     333         140 :         if (!found) {
     334        3774 :                 path = tds_dir_getenv(TDS_DIR("FREETDSCONF"));
     335        3774 :                 if (path) {
     336         120 :                         found = tds_try_conf_file(path, "(from $FREETDSCONF)", server, login);
     337             :                 } else {
     338        3654 :                         tdsdump_log(TDS_DBG_INFO2, "... $FREETDSCONF not set.  Trying $FREETDS/etc.\n");
     339             :                 }
     340             :         }
     341             : 
     342             :         /* FREETDS env var, Bill Thompson 16/07/03 */
     343        3794 :         if (!found) {
     344        3774 :                 eptr = tds_dir_getenv(TDS_DIR("FREETDS"));
     345        3774 :                 if (eptr) {
     346           0 :                         path = tds_join_path(eptr, freetds_conf);
     347           0 :                         if (path) {
     348           0 :                                 found = tds_try_conf_file(path, location, server, login);
     349           0 :                                 free(path);
     350             :                         }
     351             :                 } else {
     352        3774 :                         tdsdump_log(TDS_DBG_INFO2, "... $FREETDS not set.  Trying $HOME.\n");
     353             :                 }
     354             :         }
     355             : 
     356             : #if !defined(_WIN32) && !defined(DOS32X)
     357        3794 :         if (!found) {
     358        3774 :                 path = tds_get_home_file(TDS_DIR(".config/freetds.conf"));
     359        3774 :                 if (path) {
     360        3774 :                         found = tds_try_conf_file(path, "(.config/freetds.conf)", server, login);
     361        3774 :                         free(path);
     362             :                 }
     363             :         }
     364             : #endif
     365             : 
     366        3794 :         if (!found) {
     367        3774 :                 path = tds_get_home_file(TDS_DIR(".freetds.conf"));
     368        3774 :                 if (path) {
     369        3774 :                         found = tds_try_conf_file(path, "(.freetds.conf)", server, login);
     370        3774 :                         free(path);
     371             :                 } else {
     372           0 :                         tdsdump_log(TDS_DBG_INFO2, "... Error getting ~/.freetds.conf.  Trying %" tdsPRIdir ".\n",
     373             :                                     FREETDS_SYSCONFFILE);
     374             :                 }
     375             :         }
     376             : 
     377        3794 :         if (!found) {
     378         124 :                 found = tds_try_conf_file(FREETDS_SYSCONFFILE, "(default)", server, login);
     379             :         }
     380             : 
     381        3794 :         return found;
     382             : }
     383             : 
     384             : static bool
     385        4158 : tds_read_conf_sections(FILE * in, const char *server, TDSLOGIN * login)
     386             : {
     387        4158 :         DSTR default_instance = DSTR_INITIALIZER;
     388             :         int default_port;
     389             : 
     390             :         bool found;
     391             : 
     392        4158 :         tds_read_conf_section(in, "global", tds_parse_conf_section, login);
     393             : 
     394        4158 :         if (!server[0])
     395             :                 return false;
     396        4158 :         rewind(in);
     397             : 
     398        4158 :         if (!tds_dstr_dup(&default_instance, &login->instance_name))
     399             :                 return false;
     400        4158 :         default_port = login->port;
     401             : 
     402        4158 :         found = tds_read_conf_section(in, server, tds_parse_conf_section, login);
     403        4158 :         if (!login->valid_configuration) {
     404           0 :                 tds_dstr_free(&default_instance);
     405           0 :                 return false;
     406             :         }
     407             : 
     408             :         /* 
     409             :          * If both instance and port are specified and neither one came from the default, it's an error 
     410             :          * TODO: If port/instance is specified in the non-default, it has priority over the default setting. 
     411             :          * TODO: test this. 
     412             :          */
     413        8408 :         if (!tds_dstr_isempty(&login->instance_name) && login->port &&
     414          92 :             !(!tds_dstr_isempty(&default_instance) || default_port)) {
     415           0 :                 tdsdump_log(TDS_DBG_ERROR, "error: cannot specify both port %d and instance %s.\n", 
     416           0 :                                                 login->port, tds_dstr_cstr(&login->instance_name));
     417             :                 /* tdserror(tds_get_ctx(tds), tds, TDSEPORTINSTANCE, 0); */
     418             :         }
     419        4158 :         tds_dstr_free(&default_instance);
     420        4158 :         return found;
     421             : }
     422             : 
     423             : static const struct {
     424             :         char value[7];
     425             :         unsigned char to_return;
     426             : } boolean_values[] = {
     427             :         { "yes",      1 },
     428             :         { "no",               0 },
     429             :         { "on",               1 },
     430             :         { "off",      0 },
     431             :         { "true",     1 },
     432             :         { "false",    0 }
     433             : };
     434             : 
     435             : int
     436           0 : tds_parse_boolean(const char *value, int default_value)
     437             : {
     438             :         int p;
     439             : 
     440       10164 :         for (p = 0; p < TDS_VECTOR_SIZE(boolean_values); ++p) {
     441       10164 :                 if (!strcasecmp(value, boolean_values[p].value))
     442        2898 :                         return boolean_values[p].to_return;
     443             :         }
     444             :         return default_value;
     445             : }
     446             : 
     447             : int
     448           0 : tds_config_boolean(const char *option, const char *value, TDSLOGIN *login)
     449             : {
     450           0 :         int ret = tds_parse_boolean(value, -1);
     451           0 :         if (ret >= 0)
     452             :                 return ret;
     453             : 
     454           0 :         tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for boolean setting '%s'!\n",
     455             :                     value, option);
     456           0 :         login->valid_configuration = 0;
     457           0 :         return 0;
     458             : }
     459             : 
     460             : static int
     461        2898 : tds_parse_boolean_option(const char *option, const char *value, int default_value, bool *p_error)
     462             : {
     463        2898 :         int ret = tds_parse_boolean(value, -1);
     464        2898 :         if (ret >= 0)
     465             :                 return ret;
     466             : 
     467           0 :         tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for boolean setting '%s'!\n",
     468             :                     value, option);
     469           0 :         *p_error = true;
     470           0 :         return default_value;
     471             : }
     472             : 
     473             : static bool
     474        1462 : tds_config_encryption(const char * value, TDSLOGIN * login)
     475             : {
     476        1462 :         TDS_ENCRYPTION_LEVEL lvl = TDS_ENCRYPTION_OFF;
     477             : 
     478        1462 :         if (!strcasecmp(value, TDS_STR_ENCRYPTION_OFF))
     479             :                 ;
     480        1462 :         else if (!strcasecmp(value, TDS_STR_ENCRYPTION_REQUEST))
     481             :                 lvl = TDS_ENCRYPTION_REQUEST;
     482        1442 :         else if (!strcasecmp(value, TDS_STR_ENCRYPTION_REQUIRE))
     483             :                 lvl = TDS_ENCRYPTION_REQUIRE;
     484           0 :         else if (!strcasecmp(value, TDS_STR_ENCRYPTION_STRICT))
     485             :                 lvl = TDS_ENCRYPTION_STRICT;
     486             :         else {
     487           0 :                 tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for '%s' setting!\n",
     488             :                             value, TDS_STR_ENCRYPTION);
     489           0 :                 tdsdump_log(TDS_DBG_ERROR, "Valid settings are: ('%s', '%s', '%s', '%s')\n",
     490             :                         TDS_STR_ENCRYPTION_OFF, TDS_STR_ENCRYPTION_REQUEST, TDS_STR_ENCRYPTION_REQUIRE,
     491             :                         TDS_STR_ENCRYPTION_STRICT);
     492             :                 lvl = TDS_ENCRYPTION_REQUIRE;  /* Assuming "require" is safer than "no" */
     493             :                 return false;
     494             :         }
     495             : 
     496        1462 :         login->encryption_level = lvl;
     497             :         return true;
     498             : }
     499             : 
     500             : /**
     501             :  * Read a section of configuration file (INI style file)
     502             :  * @param in             configuration file
     503             :  * @param section        section to read
     504             :  * @param tds_conf_parse callback that receive every entry in section
     505             :  * @param param          parameter to pass to callback function
     506             :  */
     507             : bool
     508       41114 : tds_read_conf_section(FILE * in, const char *section, TDSCONFPARSE tds_conf_parse, void *param)
     509             : {
     510             :         char line[256], *value;
     511             : #define option line
     512             :         char *s;
     513             :         char p;
     514             :         int i;
     515       41114 :         bool insection = false;
     516       41114 :         bool found = false;
     517             : 
     518       41114 :         tdsdump_log(TDS_DBG_INFO1, "Looking for section %s.\n", section);
     519     2534644 :         while (fgets(line, sizeof(line), in)) {
     520             :                 s = line;
     521             : 
     522             :                 /* skip leading whitespace */
     523     3923616 :                 while (*s && TDS_ISSPACE(*s))
     524     1430086 :                         s++;
     525             : 
     526             :                 /* skip it if it's a comment line */
     527     2493530 :                 if (*s == ';' || *s == '#')
     528      889320 :                         continue;
     529             : 
     530             :                 /* read up to the = ignoring duplicate spaces */
     531             :                 p = 0;
     532             :                 i = 0;
     533    13190088 :                 while (*s && *s != '=') {
     534    11585878 :                         if (!TDS_ISSPACE(*s)) {
     535     9938476 :                                 if (TDS_ISSPACE(p))
     536      369770 :                                         option[i++] = ' ';
     537     9938476 :                                 option[i++] = tolower((unsigned char) *s);
     538             :                         }
     539    11585878 :                         p = *s;
     540    11585878 :                         s++;
     541             :                 }
     542             : 
     543             :                 /* skip if empty option */
     544     1604210 :                 if (!i)
     545      332772 :                         continue;
     546             : 
     547             :                 /* skip the = */
     548     1271438 :                 if (*s)
     549      989344 :                         s++;
     550             : 
     551             :                 /* terminate the option, must be done after skipping = */
     552     1271438 :                 option[i] = '\0';
     553             : 
     554             :                 /* skip leading whitespace */
     555     3531100 :                 while (*s && TDS_ISSPACE(*s))
     556      988224 :                         s++;
     557             : 
     558             :                 /* read up to a # ; or null ignoring duplicate spaces */
     559             :                 value = s;
     560             :                 p = 0;
     561             :                 i = 0;
     562    10252021 :                 while (*s && *s != ';' && *s != '#') {
     563     8980583 :                         if (!TDS_ISSPACE(*s)) {
     564     7911355 :                                 if (TDS_ISSPACE(p))
     565       79824 :                                         value[i++] = ' ';
     566     7911355 :                                 value[i++] = *s;
     567             :                         }
     568     8980583 :                         p = *s;
     569     8980583 :                         s++;
     570             :                 }
     571     1271438 :                 value[i] = '\0';
     572             : 
     573     1271438 :                 if (option[0] == '[') {
     574      282094 :                         s = strchr(option, ']');
     575      282094 :                         if (s)
     576      282094 :                                 *s = '\0';
     577      282094 :                         tdsdump_log(TDS_DBG_INFO1, "\tFound section %s.\n", &option[1]);
     578             : 
     579      282094 :                         if (!strcasecmp(section, &option[1])) {
     580       36867 :                                 tdsdump_log(TDS_DBG_INFO1, "Got a match.\n");
     581             :                                 insection = true;
     582             :                                 found = true;
     583             :                         } else {
     584             :                                 insection = false;
     585             :                         }
     586      989344 :                 } else if (insection) {
     587      114660 :                         tds_conf_parse(option, value, param);
     588             :                 }
     589             : 
     590             :         }
     591       41114 :         tdsdump_log(TDS_DBG_INFO1, "\tReached EOF\n");
     592       41114 :         return found;
     593             : #undef option
     594             : }
     595             : 
     596             : /* Also used to scan ODBC.INI entries */
     597             : bool
     598       31084 : tds_parse_conf_section(const char *option, const char *value, void *param)
     599             : {
     600             : #define parse_boolean(option, value, variable) do { \
     601             :         variable = tds_parse_boolean_option(option, value, variable, &got_error); \
     602             : } while(0)
     603       31084 :         TDSLOGIN *login = (TDSLOGIN *) param;
     604       31084 :         void *s = param;
     605       31084 :         bool got_error = false;
     606             : 
     607       31084 :         tdsdump_log(TDS_DBG_INFO1, "\t%s = '%s'\n", option, value);
     608             : 
     609       31084 :         if (!strcmp(option, TDS_STR_VERSION)) {
     610        3804 :                 tds_config_verstr(value, login);
     611       27280 :         } else if (!strcmp(option, TDS_STR_BLKSZ)) {
     612           0 :                 int val = atoi(value);
     613           0 :                 if (val >= 512 && val < 65536)
     614           0 :                         login->block_size = val;
     615       27280 :         } else if (!strcmp(option, TDS_STR_SWAPDT)) {
     616             :                 /* this option is deprecated, just check value for compatibility */
     617           0 :                 tds_config_boolean(option, value, login);
     618       27280 :         } else if (!strcmp(option, TDS_GSSAPI_DELEGATION)) {
     619             :                 /* gssapi flag addition */
     620           0 :                 parse_boolean(option, value, login->gssapi_use_delegation);
     621       27280 :         } else if (!strcmp(option, TDS_STR_MUTUAL_AUTHENTICATION)) {
     622           0 :                 parse_boolean(option, value, login->mutual_authentication);
     623       27280 :         } else if (!strcmp(option, TDS_STR_DUMPFILE)) {
     624           0 :                 TDS_ZERO_FREE(login->dump_file);
     625           0 :                 if (value[0]) {
     626           0 :                         login->dump_file = tds_dir_from_cstr(value);
     627           0 :                         if (!login->dump_file)
     628             :                                 s = NULL;
     629             :                 }
     630       27280 :         } else if (!strcmp(option, TDS_STR_DEBUGFLAGS)) {
     631             :                 char *end;
     632             :                 long flags;
     633        3774 :                 flags = strtol(value, &end, 0);
     634        3774 :                 if (*value != '\0' && *end == '\0' && flags != LONG_MIN && flags != LONG_MAX)
     635        3774 :                         login->debug_flags = flags;
     636       23506 :         } else if (!strcmp(option, TDS_STR_TIMEOUT) || !strcmp(option, TDS_STR_QUERY_TIMEOUT)) {
     637        3774 :                 if (atoi(value))
     638        3774 :                         login->query_timeout = atoi(value);
     639       19732 :         } else if (!strcmp(option, TDS_STR_CONNTIMEOUT)) {
     640        3774 :                 if (atoi(value))
     641        3774 :                         login->connect_timeout = atoi(value);
     642       15958 :         } else if (!strcmp(option, TDS_STR_HOST)) {
     643             :                 char tmp[128];
     644             :                 struct addrinfo *addrs;
     645             : 
     646        3670 :                 if (TDS_FAILED(tds_lookup_host_set(value, &login->ip_addrs))) {
     647           0 :                         tdsdump_log(TDS_DBG_WARN, "Found host entry %s however name resolution failed. \n", value);
     648           0 :                         return false;
     649             :                 }
     650             : 
     651        3670 :                 tdsdump_log(TDS_DBG_INFO1, "Found host entry %s \n", value);
     652        3670 :                 s = tds_dstr_copy(&login->server_host_name, value);
     653        7340 :                 for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next)
     654        3670 :                         tdsdump_log(TDS_DBG_INFO1, "IP addr is %s.\n", tds_addrinfo2str(addrs, tmp, sizeof(tmp)));
     655             : 
     656       12288 :         } else if (!strcmp(option, TDS_STR_PORT)) {
     657        3890 :                 if (atoi(value))
     658        3890 :                         login->port = atoi(value);
     659        8398 :         } else if (!strcmp(option, TDS_STR_EMUL_LE)) {
     660             :                 /* obsolete, ignore */
     661           0 :                 tds_config_boolean(option, value, login);
     662        8398 :         } else if (!strcmp(option, TDS_STR_TEXTSZ)) {
     663        3898 :                 if (atoi(value))
     664        3898 :                         login->text_size = atoi(value);
     665        4500 :         } else if (!strcmp(option, TDS_STR_CHARSET)) {
     666           0 :                 s = tds_dstr_copy(&login->server_charset, value);
     667           0 :                 tdsdump_log(TDS_DBG_INFO1, "%s is %s.\n", option, tds_dstr_cstr(&login->server_charset));
     668        4500 :         } else if (!strcmp(option, TDS_STR_CLCHARSET)) {
     669         100 :                 s = tds_dstr_copy(&login->client_charset, value);
     670         100 :                 tdsdump_log(TDS_DBG_INFO1, "tds_parse_conf_section: %s is %s.\n", option, tds_dstr_cstr(&login->client_charset));
     671        4400 :         } else if (!strcmp(option, TDS_STR_USE_UTF_16)) {
     672           0 :                 parse_boolean(option, value, login->use_utf16);
     673        4400 :         } else if (!strcmp(option, TDS_STR_LANGUAGE)) {
     674           0 :                 s = tds_dstr_copy(&login->language, value);
     675        4400 :         } else if (!strcmp(option, TDS_STR_APPENDMODE)) {
     676           0 :                 parse_boolean(option, value, tds_append_mode);
     677        4400 :         } else if (!strcmp(option, TDS_STR_INSTANCE)) {
     678           0 :                 s = tds_dstr_copy(&login->instance_name, value);
     679        4400 :         } else if (!strcmp(option, TDS_STR_ENCRYPTION)) {
     680        1462 :                 if (!tds_config_encryption(value, login))
     681             :                         s = NULL;
     682        2938 :         } else if (!strcmp(option, TDS_STR_ASA_DATABASE)) {
     683          20 :                 s = tds_dstr_copy(&login->server_name, value);
     684        2918 :         } else if (!strcmp(option, TDS_STR_USENTLMV2)) {
     685           0 :                 parse_boolean(option, value, login->use_ntlmv2);
     686           0 :                 login->use_ntlmv2_specified = 1;
     687        2918 :         } else if (!strcmp(option, TDS_STR_USELANMAN)) {
     688           0 :                 parse_boolean(option, value, login->use_lanman);
     689        2918 :         } else if (!strcmp(option, TDS_STR_REALM)) {
     690           0 :                 s = tds_dstr_copy(&login->server_realm_name, value);
     691        2918 :         } else if (!strcmp(option, TDS_STR_SPN)) {
     692           0 :                 s = tds_dstr_copy(&login->server_spn, value);
     693        2918 :         } else if (!strcmp(option, TDS_STR_CAFILE)) {
     694           0 :                 s = tds_dstr_copy(&login->cafile, value);
     695        2918 :         } else if (!strcmp(option, TDS_STR_CRLFILE)) {
     696           0 :                 s = tds_dstr_copy(&login->crlfile, value);
     697        2918 :         } else if (!strcmp(option, TDS_STR_CHECKSSLHOSTNAME)) {
     698        1442 :                 parse_boolean(option, value, login->check_ssl_hostname);
     699        1476 :         } else if (!strcmp(option, TDS_STR_SSLHOSTNAME)) {
     700           0 :                 s = tds_dstr_copy(&login->certificate_host_name, value);
     701        1476 :         } else if (!strcmp(option, TDS_STR_DBFILENAME)) {
     702           0 :                 s = tds_dstr_copy(&login->db_filename, value);
     703        1476 :         } else if (!strcmp(option, TDS_STR_DATABASE)) {
     704           0 :                 s = tds_dstr_copy(&login->database, value);
     705        1476 :         } else if (!strcmp(option, TDS_STR_READONLY_INTENT)) {
     706           0 :                 parse_boolean(option, value, login->readonly_intent);
     707           0 :                 tdsdump_log(TDS_DBG_FUNC, "Setting ReadOnly Intent to '%s'.\n", value);
     708        1476 :         } else if (!strcmp(option, TLS_STR_OPENSSL_CIPHERS)) {
     709           0 :                 s = tds_dstr_copy(&login->openssl_ciphers, value);
     710        1476 :         } else if (!strcmp(option, TDS_STR_ENABLE_TLS_V1)) {
     711        1456 :                 parse_boolean(option, value, login->enable_tls_v1);
     712        1456 :                 login->enable_tls_v1_specified = 1;
     713             :         } else {
     714          20 :                 tdsdump_log(TDS_DBG_INFO1, "UNRECOGNIZED option '%s' ... ignoring.\n", option);
     715             :         }
     716             : 
     717       31084 :         if (!s || got_error) {
     718           0 :                 login->valid_configuration = 0;
     719           0 :                 return false;
     720             :         }
     721             :         return true;
     722             : #undef parse_boolean
     723             : }
     724             : 
     725             : static bool
     726        2590 : tds_config_login(TDSLOGIN * connection, TDSLOGIN * login)
     727             : {
     728        2590 :         DSTR *res = &login->server_name;
     729             : 
     730        5180 :         if (!tds_dstr_isempty(&login->server_name)) {
     731             :                 if (1 || tds_dstr_isempty(&connection->server_name))
     732        2590 :                         res = tds_dstr_dup(&connection->server_name, &login->server_name);
     733             :         }
     734             : 
     735        2590 :         if (login->tds_version)
     736          22 :                 connection->tds_version = login->tds_version;
     737             : 
     738        5180 :         if (res && !tds_dstr_isempty(&login->language))
     739         326 :                 res = tds_dstr_dup(&connection->language, &login->language);
     740             : 
     741        5180 :         if (res && !tds_dstr_isempty(&login->server_charset))
     742           0 :                 res = tds_dstr_dup(&connection->server_charset, &login->server_charset);
     743             : 
     744        5180 :         if (res && !tds_dstr_isempty(&login->client_charset)) {
     745         174 :                 res = tds_dstr_dup(&connection->client_charset, &login->client_charset);
     746         174 :                 tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "client_charset",
     747           0 :                             tds_dstr_cstr(&connection->client_charset));
     748             :         }
     749             : 
     750        2590 :         if (!login->use_utf16)
     751           0 :                 connection->use_utf16 = login->use_utf16;
     752             : 
     753        5180 :         if (res && !tds_dstr_isempty(&login->database)) {
     754          54 :                 res = tds_dstr_dup(&connection->database, &login->database);
     755          54 :                 tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "database_name",
     756           0 :                             tds_dstr_cstr(&connection->database));
     757             :         }
     758             : 
     759        5180 :         if (res && !tds_dstr_isempty(&login->client_host_name))
     760         494 :                 res = tds_dstr_dup(&connection->client_host_name, &login->client_host_name);
     761             : 
     762        5180 :         if (res && !tds_dstr_isempty(&login->app_name))
     763        1180 :                 res = tds_dstr_dup(&connection->app_name, &login->app_name);
     764             : 
     765        5180 :         if (res && !tds_dstr_isempty(&login->user_name))
     766        2520 :                 res = tds_dstr_dup(&connection->user_name, &login->user_name);
     767             : 
     768        5180 :         if (res && !tds_dstr_isempty(&login->password)) {
     769             :                 /* for security reason clear memory */
     770        2520 :                 tds_dstr_zero(&connection->password);
     771        2520 :                 res = tds_dstr_dup(&connection->password, &login->password);
     772             :         }
     773             : 
     774        5180 :         if (res && !tds_dstr_isempty(&login->library))
     775        2520 :                 res = tds_dstr_dup(&connection->library, &login->library);
     776             : 
     777        2590 :         if (login->encryption_level)
     778           0 :                 connection->encryption_level = login->encryption_level;
     779             : 
     780        2590 :         if (login->suppress_language)
     781           0 :                 connection->suppress_language = 1;
     782             : 
     783        2590 :         if (!login->bulk_copy)
     784           0 :                 connection->bulk_copy = 0;
     785             : 
     786        2590 :         if (login->block_size)
     787           0 :                 connection->block_size = login->block_size;
     788             : 
     789        2590 :         if (login->gssapi_use_delegation)
     790           0 :                 connection->gssapi_use_delegation = login->gssapi_use_delegation;
     791             : 
     792        2590 :         if (login->mutual_authentication)
     793           0 :                 connection->mutual_authentication = login->mutual_authentication;
     794             : 
     795        2590 :         if (login->port)
     796          42 :                 connection->port = login->port;
     797             : 
     798        2590 :         if (login->connect_timeout)
     799           0 :                 connection->connect_timeout = login->connect_timeout;
     800             : 
     801        2590 :         if (login->query_timeout)
     802           0 :                 connection->query_timeout = login->query_timeout;
     803             : 
     804        2590 :         if (!login->check_ssl_hostname)
     805           0 :                 connection->check_ssl_hostname = login->check_ssl_hostname;
     806             : 
     807        5180 :         if (res && !tds_dstr_isempty(&login->db_filename))
     808           0 :                 res = tds_dstr_dup(&connection->db_filename, &login->db_filename);
     809             : 
     810        5180 :         if (res && !tds_dstr_isempty(&login->openssl_ciphers))
     811           0 :                 res = tds_dstr_dup(&connection->openssl_ciphers, &login->openssl_ciphers);
     812             : 
     813        5180 :         if (res && !tds_dstr_isempty(&login->server_spn))
     814           0 :                 res = tds_dstr_dup(&connection->server_spn, &login->server_spn);
     815             : 
     816             :         /* copy other info not present in configuration file */
     817        2590 :         connection->capabilities = login->capabilities;
     818             : 
     819        2590 :         if (login->readonly_intent)
     820           0 :                 connection->readonly_intent = login->readonly_intent;
     821             : 
     822        2590 :         connection->use_new_password = login->use_new_password;
     823             : 
     824        2590 :         if (login->use_ntlmv2_specified) {
     825           0 :                 connection->use_ntlmv2_specified = login->use_ntlmv2_specified;
     826           0 :                 connection->use_ntlmv2 = login->use_ntlmv2;
     827             :         }
     828             : 
     829        2590 :         if (login->enable_tls_v1_specified) {
     830           0 :                 connection->enable_tls_v1_specified = login->enable_tls_v1_specified;
     831           0 :                 connection->enable_tls_v1 = login->enable_tls_v1;
     832             :         }
     833             : 
     834        2590 :         if (res)
     835        2590 :                 res = tds_dstr_dup(&connection->new_password, &login->new_password);
     836             : 
     837        2590 :         return res != NULL;
     838             : }
     839             : 
     840             : static bool
     841        4584 : tds_config_env_tdsdump(TDSLOGIN * login)
     842             : {
     843             :         tds_dir_char path[TDS_VECTOR_SIZE(pid_logpath) + 22];
     844             : 
     845        4584 :         tds_dir_char *s = tds_dir_getenv(TDS_DIR("TDSDUMP"));
     846        4584 :         if (!s)
     847             :                 return true;
     848             : 
     849           4 :         if (!tds_dir_len(s)) {
     850           0 :                 pid_t pid = getpid();
     851           0 :                 tds_dir_snprintf(path, TDS_VECTOR_SIZE(path), pid_logpath, (int) pid);
     852           0 :                 s = path;
     853             :         }
     854             : 
     855           4 :         if (!(s = tds_dir_dup(s)))
     856             :                 return false;
     857           4 :         free(login->dump_file);
     858           4 :         login->dump_file = s;
     859             : 
     860           4 :         tdsdump_log(TDS_DBG_INFO1, "Setting 'dump_file' to '%" tdsPRIdir "' from $TDSDUMP.\n", login->dump_file);
     861             :         return true;
     862             : }
     863             : 
     864             : static void
     865        4584 : tds_config_env_tdsport(TDSLOGIN * login)
     866             : {
     867             :         char *s;
     868             : 
     869        4584 :         if ((s = getenv("TDSPORT"))) {
     870          12 :                 login->port = tds_lookup_port(s);
     871          12 :                 tds_dstr_empty(&login->instance_name);
     872          12 :                 tdsdump_log(TDS_DBG_INFO1, "Setting 'port' to %s from $TDSPORT.\n", s);
     873             :         }
     874        4584 :         return;
     875             : }
     876             : static void
     877        4584 : tds_config_env_tdsver(TDSLOGIN * login)
     878             : {
     879             :         char *tdsver;
     880             : 
     881        4584 :         if ((tdsver = getenv("TDSVER"))) {
     882          14 :                 TDS_USMALLINT *pver = tds_config_verstr(tdsver, login);
     883          14 :                 tdsdump_log(TDS_DBG_INFO1, "TDS version %sset to %s from $TDSVER.\n", (pver? "":"not "), tdsver);
     884             : 
     885             :         }
     886        4584 :         return;
     887             : }
     888             : 
     889             : /* TDSHOST env var, pkleef@openlinksw.com 01/21/02 */
     890             : static bool
     891        4584 : tds_config_env_tdshost(TDSLOGIN * login)
     892             : {
     893             :         const char *tdshost;
     894             :         char tmp[128];
     895             :         struct addrinfo *addrs;
     896             : 
     897        4584 :         if (!(tdshost = getenv("TDSHOST")))
     898             :                 return true;
     899             : 
     900          10 :         if (TDS_FAILED(tds_lookup_host_set(tdshost, &login->ip_addrs))) {
     901           0 :                 tdsdump_log(TDS_DBG_WARN, "Name resolution failed for '%s' from $TDSHOST.\n", tdshost);
     902             :                 return false;
     903             :         }
     904             : 
     905          10 :         if (!tds_dstr_copy(&login->server_host_name, tdshost))
     906             :                 return false;
     907          20 :         for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
     908          10 :                 tdsdump_log(TDS_DBG_INFO1, "Setting IP Address to %s (%s) from $TDSHOST.\n",
     909             :                             tds_addrinfo2str(addrs, tmp, sizeof(tmp)), tdshost);
     910             :         }
     911             :         return true;
     912             : }
     913             : #define TDS_FIND(k,b,c) tds_find(k, b, TDS_VECTOR_SIZE(b), sizeof(b[0]), c)
     914             : 
     915             : 
     916             : static const void *
     917             : tds_find(const void *key, const void *base, size_t nelem, size_t width,
     918             :          int (*compar)(const void *, const void *))
     919             : {
     920             :         size_t n;
     921             :         const char *p = (const char*) base;
     922             : 
     923       27434 :         for (n = nelem; n != 0; --n) {
     924       31252 :                 if (0 == compar(key, p))
     925             :                         return p;
     926       27434 :                 p += width;
     927             :         }
     928             :         return NULL;
     929             : }
     930             : 
     931             : struct tdsvername_t 
     932             : {
     933             :         const char name[6];
     934             :         TDS_USMALLINT version;
     935             : };
     936             : 
     937             : static int
     938       31252 : tds_vername_cmp(const void *key, const void *pelem)
     939             : {
     940       31252 :         return strcmp((const char *)key, ((const struct tdsvername_t *)pelem)->name);
     941             : }
     942             : 
     943             : /**
     944             :  * Set TDS version from given string
     945             :  * @param tdsver tds string version
     946             :  * @param login where to store information
     947             :  * @return as encoded hex value: high byte major, low byte minor.
     948             :  */
     949             : TDS_USMALLINT *
     950        3818 : tds_config_verstr(const char *tdsver, TDSLOGIN * login)
     951             : {
     952             :         static const struct tdsvername_t tds_versions[] = 
     953             :                 { {   "0", 0x000 }
     954             :                 , {"auto", 0x000 }
     955             :                 , { "4.2", 0x402 }
     956             :                 , {  "50", 0x500 }
     957             :                 , { "5.0", 0x500 }
     958             :                 , {  "70", 0x700 }
     959             :                 , { "7.0", 0x700 }
     960             :                 , { "7.1", 0x701 }
     961             :                 , { "7.2", 0x702 }
     962             :                 , { "7.3", 0x703 }
     963             :                 , { "7.4", 0x704 }
     964             :                 , { "8.0", 0x800 }
     965             :                 };
     966             :         const struct tdsvername_t *pver;
     967             : 
     968        3818 :         if (!login) {
     969           0 :                 assert(login);
     970             :                 return NULL;
     971             :         }
     972             : 
     973        3818 :         if ((pver = (const struct tdsvername_t *) TDS_FIND(tdsver, tds_versions, tds_vername_cmp)) == NULL) {
     974           0 :                 tdsdump_log(TDS_DBG_INFO1, "error: no such version: %s\n", tdsver);
     975             :                 return NULL;
     976             :         }
     977             :         
     978        3818 :         login->tds_version = pver->version;
     979        3818 :         tdsdump_log(TDS_DBG_INFO1, "Setting tds version to %s (0x%0x).\n", tdsver, pver->version);
     980             : 
     981        3818 :         return &login->tds_version;
     982             : }
     983             : 
     984             : /**
     985             :  * Set the full name of interface file
     986             :  * @param interf file name
     987             :  */
     988             : TDSRET
     989          20 : tds_set_interfaces_file_loc(const char *interf)
     990             : {
     991             :         /* Free it if already set */
     992          20 :         if (interf_file != NULL)
     993          10 :                 TDS_ZERO_FREE(interf_file);
     994             :         /* If no filename passed, leave it NULL */
     995          20 :         if ((interf == NULL) || (interf[0] == '\0')) {
     996             :                 return TDS_SUCCESS;
     997             :         }
     998             :         /* Set to new value */
     999          10 :         if ((interf_file = tds_dir_from_cstr(interf)) == NULL) {
    1000             :                 return TDS_FAIL;
    1001             :         }
    1002          10 :         return TDS_SUCCESS;
    1003             : }
    1004             : 
    1005             : /**
    1006             :  * Get the IP address for a hostname. Store server's IP address 
    1007             :  * in the string 'ip' in dotted-decimal notation.  (The "hostname" might itself
    1008             :  * be a dotted-decimal address.  
    1009             :  *
    1010             :  * If we can't determine the IP address then 'ip' will be set to empty
    1011             :  * string.
    1012             :  */
    1013             : /* TODO callers seem to set always connection info... change it */
    1014             : struct addrinfo *
    1015        3830 : tds_lookup_host(const char *servername) /* (I) name of the server                  */
    1016             : {
    1017        3830 :         struct addrinfo hints, *addr = NULL;
    1018        3830 :         assert(servername != NULL);
    1019             : 
    1020        3830 :         memset(&hints, '\0', sizeof(hints));
    1021             :         hints.ai_family = AF_UNSPEC;
    1022        3830 :         hints.ai_socktype = SOCK_STREAM;
    1023        3830 :         hints.ai_protocol = IPPROTO_TCP;
    1024             : 
    1025             : #ifdef AI_ADDRCONFIG
    1026        3830 :         hints.ai_flags |= AI_ADDRCONFIG;
    1027        3830 :         switch (getaddrinfo(servername, NULL, &hints, &addr)) {
    1028        3830 :         case 0:
    1029        3830 :                 return addr;
    1030           0 :         case EAI_FAMILY:
    1031             : #  ifdef EAI_ADDRFAMILY
    1032             :         case EAI_ADDRFAMILY:
    1033             : #  endif
    1034           0 :                 hints.ai_flags &= ~AI_ADDRCONFIG;
    1035             :                 break;
    1036             :         default:
    1037             :                 return NULL;
    1038             :         }
    1039             : #endif
    1040             : 
    1041           0 :         if (getaddrinfo(servername, NULL, &hints, &addr))
    1042             :                 return NULL;
    1043           0 :         return addr;
    1044             : }
    1045             : 
    1046             : TDSRET
    1047        3830 : tds_lookup_host_set(const char *servername, struct addrinfo **addr)
    1048             : {
    1049             :         struct addrinfo *newaddr;
    1050        3830 :         assert(servername != NULL && addr != NULL);
    1051             : 
    1052        3830 :         if ((newaddr = tds_lookup_host(servername)) != NULL) {
    1053        3830 :                 if (*addr != NULL)
    1054          30 :                         freeaddrinfo(*addr);
    1055        3830 :                 *addr = newaddr;
    1056        3830 :                 return TDS_SUCCESS;
    1057             :         }
    1058             :         return TDS_FAIL;
    1059             : }
    1060             : 
    1061             : /**
    1062             :  * Given a portname lookup the port.
    1063             :  *
    1064             :  * If we can't determine the port number then return 0.
    1065             :  */
    1066             : static int
    1067          12 : tds_lookup_port(const char *portname)
    1068             : {
    1069          12 :         int num = atoi(portname);
    1070          12 :         if (!num)
    1071           2 :                 num = tds_getservice(portname);
    1072          12 :         return num;
    1073             : }
    1074             : 
    1075             : /* TODO same code in convert.c ?? */
    1076             : static int
    1077             : hexdigit(int c)
    1078             : {
    1079           0 :         if (c >= '0' && c <= '9')
    1080           0 :                 return c - '0';
    1081             :         /* ASCII optimization, 'A' -> 'a', 'a' -> 'a' */
    1082           0 :         c |= 0x20;
    1083           0 :         if (c >= 'a' && c <= 'f')
    1084           0 :                 return c - 'a' + 10;
    1085             :         return 0;       /* bad hex digit */
    1086             : }
    1087             : 
    1088             : static int
    1089           0 : hex2num(char *hex)
    1090             : {
    1091           0 :         return hexdigit(hex[0]) * 16 + hexdigit(hex[1]);
    1092             : }
    1093             : 
    1094             : /**
    1095             :  * Open and read the file 'file' searching for a logical server
    1096             :  * by the name of 'host'.  If one is found then lookup
    1097             :  * the IP address and port number and store them in 'login'
    1098             :  *
    1099             :  * \param dir name of base directory for interface file
    1100             :  * \param file name of the interface file
    1101             :  * \param host logical host to search for
    1102             :  * \return false if not fount true if found
    1103             :  */
    1104             : static bool
    1105           0 : search_interface_file(TDSLOGIN * login, const tds_dir_char *dir, const tds_dir_char *file, const char *host)
    1106             : {
    1107             :         tds_dir_char *pathname;
    1108             :         char line[255];
    1109             :         char tmp_ip[sizeof(line)];
    1110             :         char tmp_port[sizeof(line)];
    1111             :         char tmp_ver[sizeof(line)];
    1112             :         FILE *in;
    1113             :         char *field;
    1114           0 :         bool found = false;
    1115           0 :         bool server_found = false;
    1116             :         char *lasts;
    1117             : 
    1118           0 :         line[0] = '\0';
    1119           0 :         tmp_ip[0] = '\0';
    1120           0 :         tmp_port[0] = '\0';
    1121           0 :         tmp_ver[0] = '\0';
    1122             : 
    1123           0 :         tdsdump_log(TDS_DBG_INFO1, "Searching interfaces file %" tdsPRIdir "/%" tdsPRIdir ".\n", dir, file);
    1124             : 
    1125             :         /*
    1126             :          * create the full pathname to the interface file
    1127             :          */
    1128           0 :         if (file[0] == '\0') {
    1129           0 :                 pathname = tds_dir_dup(TDS_DIR(""));
    1130             :         } else {
    1131           0 :                 pathname = tds_join_path(dir, file);
    1132             :         }
    1133           0 :         if (!pathname)
    1134             :                 return false;
    1135             : 
    1136             :         /*
    1137             :          * parse the interfaces file and find the server and port
    1138             :          */
    1139           0 :         if ((in = tds_dir_open(pathname, TDS_DIR("r"))) == NULL) {
    1140           0 :                 tdsdump_log(TDS_DBG_INFO1, "Couldn't open %" tdsPRIdir ".\n", pathname);
    1141           0 :                 free(pathname);
    1142           0 :                 return false;
    1143             :         }
    1144           0 :         tdsdump_log(TDS_DBG_INFO1, "Interfaces file %" tdsPRIdir " opened.\n", pathname);
    1145             : 
    1146           0 :         while (fgets(line, sizeof(line) - 1, in)) {
    1147           0 :                 if (line[0] == '#')
    1148           0 :                         continue;       /* comment */
    1149             : 
    1150           0 :                 if (!TDS_ISSPACE(line[0])) {
    1151           0 :                         field = strtok_r(line, "\n\t ", &lasts);
    1152           0 :                         if (!strcmp(field, host)) {
    1153           0 :                                 found = true;
    1154           0 :                                 tdsdump_log(TDS_DBG_INFO1, "Found matching entry for host %s.\n", host);
    1155             :                         } else
    1156             :                                 found = false;
    1157           0 :                 } else if (found && TDS_ISSPACE(line[0])) {
    1158           0 :                         field = strtok_r(line, "\n\t ", &lasts);
    1159           0 :                         if (field != NULL && !strcmp(field, "query")) {
    1160           0 :                                 field = strtok_r(NULL, "\n\t ", &lasts);  /* tcp or tli */
    1161           0 :                                 if (!strcmp(field, "tli")) {
    1162           0 :                                         tdsdump_log(TDS_DBG_INFO1, "TLI service.\n");
    1163           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* tcp */
    1164           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* device */
    1165           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* host/port */
    1166           0 :                                         if (strlen(field) >= 18) {
    1167           0 :                                                 sprintf(tmp_port, "%d", hex2num(&field[6]) * 256 + hex2num(&field[8]));
    1168           0 :                                                 sprintf(tmp_ip, "%d.%d.%d.%d", hex2num(&field[10]),
    1169             :                                                         hex2num(&field[12]), hex2num(&field[14]), hex2num(&field[16]));
    1170           0 :                                                 tdsdump_log(TDS_DBG_INFO1, "tmp_port = %s. tmp_ip = %s.\n", tmp_port, tmp_ip);
    1171             :                                         }
    1172             :                                 } else {
    1173           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* ether */
    1174           0 :                                         strcpy(tmp_ver, field);
    1175           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* host */
    1176           0 :                                         strcpy(tmp_ip, field);
    1177           0 :                                         tdsdump_log(TDS_DBG_INFO1, "host field %s.\n", tmp_ip);
    1178           0 :                                         field = strtok_r(NULL, "\n\t ", &lasts);  /* port */
    1179           0 :                                         strcpy(tmp_port, field);
    1180             :                                 }       /* else */
    1181             :                                 server_found = true;
    1182             :                         }       /* if */
    1183             :                 }               /* else if */
    1184             :         }                       /* while */
    1185           0 :         fclose(in);
    1186           0 :         free(pathname);
    1187             : 
    1188             : 
    1189             :         /*
    1190             :          * Look up the host and service
    1191             :          */
    1192           0 :         if (server_found) {
    1193             : 
    1194           0 :                 if (TDS_SUCCEED(tds_lookup_host_set(tmp_ip, &login->ip_addrs))) {
    1195             :                         struct addrinfo *addrs;
    1196           0 :                         if (!tds_dstr_copy(&login->server_host_name, tmp_ip))
    1197             :                                 return false;
    1198           0 :                         for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
    1199           0 :                                 tdsdump_log(TDS_DBG_INFO1, "Resolved IP as '%s'.\n",
    1200             :                                             tds_addrinfo2str(login->ip_addrs, line, sizeof(line)));
    1201             :                         }
    1202             :                 } else {
    1203           0 :                         tdsdump_log(TDS_DBG_WARN, "Name resolution failed for IP '%s'.\n", tmp_ip);
    1204             :                 }
    1205             : 
    1206           0 :                 if (tmp_port[0])
    1207           0 :                         login->port = tds_lookup_port(tmp_port);
    1208           0 :                 if (tmp_ver[0])
    1209           0 :                         tds_config_verstr(tmp_ver, login);
    1210             :         }
    1211             :         return server_found;
    1212             : }                               /* search_interface_file()  */
    1213             : 
    1214             : /**
    1215             :  * Try to find the IP number and port for a (possibly) logical server name.
    1216             :  *
    1217             :  * @note This function uses only the interfaces file and is deprecated.
    1218             :  */
    1219             : static bool
    1220           0 : tds_read_interfaces(const char *server, TDSLOGIN * login)
    1221             : {
    1222           0 :         bool found = false;
    1223             : 
    1224             :         /* read $SYBASE/interfaces */
    1225             : 
    1226           0 :         if (!server || !server[0]) {
    1227           0 :                 server = getenv("TDSQUERY");
    1228           0 :                 if (!server || !server[0])
    1229           0 :                         server = "SYBASE";
    1230           0 :                 tdsdump_log(TDS_DBG_INFO1, "Setting server to %s from $TDSQUERY.\n", server);
    1231             : 
    1232             :         }
    1233           0 :         tdsdump_log(TDS_DBG_INFO1, "Looking for server %s....\n", server);
    1234             : 
    1235             :         /*
    1236             :          * Look for the server in the interf_file iff interf_file has been set.
    1237             :          */
    1238           0 :         if (interf_file) {
    1239           0 :                 tdsdump_log(TDS_DBG_INFO1, "Looking for server in file %" tdsPRIdir ".\n", interf_file);
    1240           0 :                 found = search_interface_file(login, TDS_DIR(""), interf_file, server);
    1241             :         }
    1242             : 
    1243             :         /*
    1244             :          * if we haven't found the server yet then look for a $HOME/.interfaces file
    1245             :          */
    1246           0 :         if (!found) {
    1247           0 :                 tds_dir_char *path = tds_get_home_file(TDS_DIR(".interfaces"));
    1248             : 
    1249           0 :                 if (path) {
    1250           0 :                         tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir ".\n", path);
    1251           0 :                         found = search_interface_file(login, TDS_DIR(""), path, server);
    1252           0 :                         free(path);
    1253             :                 }
    1254             :         }
    1255             : 
    1256             :         /*
    1257             :          * if we haven't found the server yet then look in $SYBBASE/interfaces file
    1258             :          */
    1259           0 :         if (!found) {
    1260           0 :                 const tds_dir_char *sybase = tds_dir_getenv(TDS_DIR("SYBASE"));
    1261             : #ifdef __VMS
    1262             :                 /* We've got to be in unix syntax for later slash-joined concatenation. */
    1263             :                 #include <unixlib.h>
    1264             :                 const char *unixspec = decc$translate_vms(sybase);
    1265             :                 if ( (int)unixspec != 0 && (int)unixspec != -1 ) sybase = unixspec;
    1266             : #endif
    1267           0 :                 if (!sybase || !sybase[0])
    1268           0 :                         sybase = interfaces_path;
    1269             : 
    1270           0 :                 tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir "/interfaces.\n", sybase);
    1271           0 :                 found = search_interface_file(login, sybase, TDS_DIR("interfaces"), server);
    1272             :         }
    1273             : 
    1274             :         /*
    1275             :          * If we still don't have the server and port then assume the user
    1276             :          * typed an actual server host name.
    1277             :          */
    1278           0 :         if (!found) {
    1279             :                 int ip_port;
    1280             :                 const char *env_port;
    1281             : 
    1282             :                 /*
    1283             :                  * Make a guess about the port number
    1284             :                  */
    1285             : 
    1286           0 :                 if (login->port == 0) {
    1287             :                         /*
    1288             :                          * Not set in the [global] section of the
    1289             :                          * configure file, take a guess.
    1290             :                          */
    1291             :                         ip_port = TDS_DEF_PORT;
    1292             :                 } else {
    1293             :                         /*
    1294             :                          * Preserve setting from the [global] section
    1295             :                          * of the configure file.
    1296             :                          */
    1297           0 :                         ip_port = login->port;
    1298             :                 }
    1299           0 :                 if ((env_port = getenv("TDSPORT")) != NULL) {
    1300           0 :                         ip_port = tds_lookup_port(env_port);
    1301           0 :                         tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %s from $TDSPORT.\n", env_port);
    1302             :                 } else
    1303           0 :                         tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %d as a guess.\n", ip_port);
    1304             : 
    1305             :                 /*
    1306             :                  * look up the host
    1307             :                  */
    1308             : 
    1309           0 :                 if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs)))
    1310           0 :                         if (!tds_dstr_copy(&login->server_host_name, server))
    1311             :                                 return false;
    1312             : 
    1313           0 :                 if (ip_port)
    1314           0 :                         login->port = ip_port;
    1315             :         }
    1316             : 
    1317             :         return found;
    1318             : }
    1319             : 
    1320             : /**
    1321             :  * Check the server name to find port info first
    1322             :  * Warning: connection-> & login-> are all modified when needed
    1323             :  * \return true when found, else false
    1324             :  */
    1325             : static bool
    1326         148 : parse_server_name_for_port(TDSLOGIN * connection, TDSLOGIN * login, bool update_server)
    1327             : {
    1328             :         const char *pSep;
    1329             :         const char *server;
    1330             : 
    1331             :         /* seek the ':' in login server_name */
    1332         296 :         server = tds_dstr_cstr(&login->server_name);
    1333             : 
    1334             :         /* IPv6 address can be quoted */
    1335         148 :         if (server[0] == '[') {
    1336          40 :                 pSep = strstr(server, "]:");
    1337          40 :                 if (pSep)
    1338          20 :                         ++pSep;
    1339             :         } else {
    1340         108 :                 pSep = strrchr(server, ':');
    1341             :         }
    1342             : 
    1343         148 :         if (pSep && pSep != server) {   /* yes, i found it! */
    1344             :                 /* modify connection-> && login->server_name & ->port */
    1345         168 :                 login->port = connection->port = atoi(pSep + 1);
    1346          84 :                 tds_dstr_empty(&connection->instance_name);
    1347             :         } else {
    1348             :                 /* handle instance name */
    1349          64 :                 pSep = strrchr(server, '\\');
    1350          64 :                 if (!pSep || pSep == server)
    1351             :                         return false;
    1352             : 
    1353          64 :                 if (!tds_dstr_copy(&connection->instance_name, pSep + 1))
    1354             :                         return false;
    1355          64 :                 connection->port = 0;
    1356             :         }
    1357             : 
    1358         148 :         if (!update_server)
    1359             :                 return true;
    1360             : 
    1361          74 :         if (server[0] == '[' && pSep > server && pSep[-1] == ']') {
    1362          20 :                 server++;
    1363          20 :                 pSep--;
    1364             :         }
    1365          74 :         if (!tds_dstr_copyn(&connection->server_name, server, pSep - server))
    1366             :                 return false;
    1367             : 
    1368          74 :         return true;
    1369             : }
    1370             : 
    1371             : /**
    1372             :  * Return a structure capturing the compile-time settings provided to the
    1373             :  * configure script.  
    1374             :  */
    1375             : 
    1376             : const TDS_COMPILETIME_SETTINGS *
    1377           0 : tds_get_compiletime_settings(void)
    1378             : {
    1379             :         static const TDS_COMPILETIME_SETTINGS settings = {
    1380             :                   TDS_VERSION_NO
    1381             :                 , FREETDS_SYSCONFDIR
    1382             :                 , "unknown"   /* need fancy script in makefile */
    1383             : #               if TDS50
    1384             :                         , "5.0"
    1385             : #               elif TDS71
    1386             :                         , "7.1"
    1387             : #               elif TDS72
    1388             :                         , "7.2"
    1389             : #               elif TDS73
    1390             :                         , "7.3"
    1391             : #               elif TDS74
    1392             :                         , "7.4"
    1393             : #               else
    1394             :                         , "auto"
    1395             : #               endif
    1396             : #               ifdef MSDBLIB
    1397             :                         , true
    1398             : #               else
    1399             :                         , false
    1400             : #               endif
    1401             : #               ifdef TDS_SYBASE_COMPAT
    1402             :                         , true
    1403             : #               else
    1404             :                         , false
    1405             : #               endif
    1406             : #               ifdef _REENTRANT
    1407             :                         , true
    1408             : #               else
    1409             :                         , false
    1410             : #               endif
    1411             : #               ifdef HAVE_ICONV
    1412             :                         , true
    1413             : #               else
    1414             :                         , false
    1415             : #               endif
    1416             : #               ifdef IODBC
    1417             :                         , true
    1418             : #               else
    1419             :                         , false
    1420             : #               endif
    1421             : #               ifdef UNIXODBC
    1422             :                         , true
    1423             : #               else
    1424             :                         , false
    1425             : #               endif
    1426             : #               ifdef HAVE_OPENSSL
    1427             :                         , true
    1428             : #               else
    1429             :                         , false
    1430             : #               endif
    1431             : #               ifdef HAVE_GNUTLS
    1432             :                         , true
    1433             : #               else
    1434             :                         , false
    1435             : #               endif
    1436             : #               if ENABLE_ODBC_MARS
    1437             :                         , true
    1438             : #               else
    1439             :                         , false
    1440             : #               endif
    1441             : #               ifdef HAVE_SSPI
    1442             :                         , true
    1443             : #               else
    1444             :                         , false
    1445             : #               endif
    1446             : #               ifdef ENABLE_KRB5
    1447             :                         , true
    1448             : #               else
    1449             :                         , false
    1450             : #               endif
    1451             :         };
    1452             : 
    1453             :         assert(settings.tdsver);
    1454             : 
    1455           0 :         return &settings;
    1456             : }
    1457             : 
    1458             : /**
    1459             :  * Make sure proper setting are in place for TDS 8.0
    1460             :  */
    1461             : TDSRET
    1462        3660 : tds8_adjust_login(TDSLOGIN *login)
    1463             : {
    1464        3660 :         if (!IS_TDS80_PLUS(login) && login->encryption_level != TDS_ENCRYPTION_STRICT)
    1465             :                 return TDS_SUCCESS;
    1466             : 
    1467           0 :         login->tds_version = 0x800;
    1468           0 :         login->encryption_level = TDS_ENCRYPTION_STRICT;
    1469             : 
    1470             :         /* we must have certificates */
    1471           0 :         if (tds_dstr_isempty(&login->cafile)) {
    1472           0 :                 if (!tds_dstr_copy(&login->cafile, "system"))
    1473             :                         return -TDSEMEM;
    1474             :         }
    1475             : 
    1476             :         return TDS_SUCCESS;
    1477             : }
    1478             : 
    1479             : /** @} */

Generated by: LCOV version 1.13