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_topen(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_topen(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 : #ifdef HAVE_GNUTLS
260 0 : tdsdump_log(TDS_DBG_INFO1, "\t%20s = %s\n", "gnutls_ciphers", tds_dstr_cstr(&connection->gnutls_ciphers));
261 : #endif
262 :
263 0 : tdsdump_close();
264 : }
265 :
266 : /*
267 : * If a dump file has been specified, start logging
268 : */
269 2590 : if (connection->dump_file != NULL && !tdsdump_isopen()) {
270 4 : if (connection->debug_flags)
271 4 : tds_debug_flags = connection->debug_flags;
272 4 : tdsdump_topen(connection->dump_file);
273 : }
274 :
275 : return connection;
276 : }
277 :
278 : /**
279 : * Fix configuration after reading it.
280 : * Currently this read some environment variables and replace some options.
281 : */
282 : void
283 4610 : tds_fix_login(TDSLOGIN * login)
284 : {
285 : /* Now check the environment variables */
286 4610 : tds_config_env_tdsver(login);
287 4610 : tds_config_env_tdsdump(login);
288 4610 : tds_config_env_tdsport(login);
289 4610 : tds_config_env_tdshost(login);
290 4610 : }
291 :
292 : static bool
293 7984 : tds_try_conf_file(const tds_dir_char *path, const char *how, const char *server, TDSLOGIN * login)
294 : {
295 7984 : bool found = false;
296 : FILE *in;
297 :
298 7984 : if ((in = tds_dir_open(path, TDS_DIR("r"))) == NULL) {
299 3800 : tdsdump_log(TDS_DBG_INFO1, "Could not open '%" tdsPRIdir "' (%s).\n", path, how);
300 : return found;
301 : }
302 :
303 4184 : tdsdump_log(TDS_DBG_INFO1, "Found conf file '%" tdsPRIdir "' %s.\n", path, how);
304 4184 : found = tds_read_conf_sections(in, server, login);
305 :
306 4184 : if (found) {
307 3696 : tdsdump_log(TDS_DBG_INFO1, "Success: [%s] defined in %" tdsPRIdir ".\n", server, path);
308 : } else {
309 488 : tdsdump_log(TDS_DBG_INFO2, "[%s] not found.\n", server);
310 : }
311 :
312 4184 : fclose(in);
313 :
314 4184 : return found;
315 : }
316 :
317 : /**
318 : * Read configuration info for given server
319 : * return 0 on error
320 : * @param login where to store configuration
321 : * @param server section of file configuration that hold
322 : * configuration for a server
323 : */
324 : bool
325 3820 : tds_read_conf_file(TDSLOGIN * login, const char *server)
326 : {
327 3820 : tds_dir_char *path = NULL;
328 3820 : tds_dir_char *eptr = NULL;
329 3820 : bool found = false;
330 :
331 3820 : if (interf_file) {
332 140 : found = tds_try_conf_file(interf_file, "set programmatically", server, login);
333 : }
334 :
335 : /* FREETDSCONF env var, pkleef@openlinksw.com 01/21/02 */
336 140 : if (!found) {
337 3800 : path = tds_dir_getenv(TDS_DIR("FREETDSCONF"));
338 3800 : if (path) {
339 120 : found = tds_try_conf_file(path, "(from $FREETDSCONF)", server, login);
340 : } else {
341 3680 : tdsdump_log(TDS_DBG_INFO2, "... $FREETDSCONF not set. Trying $FREETDS/etc.\n");
342 : }
343 : }
344 :
345 : /* FREETDS env var, Bill Thompson 16/07/03 */
346 3820 : if (!found) {
347 3800 : eptr = tds_dir_getenv(TDS_DIR("FREETDS"));
348 3800 : if (eptr) {
349 0 : path = tds_join_path(eptr, freetds_conf);
350 0 : if (path) {
351 0 : found = tds_try_conf_file(path, location, server, login);
352 0 : free(path);
353 : }
354 : } else {
355 3800 : tdsdump_log(TDS_DBG_INFO2, "... $FREETDS not set. Trying $HOME.\n");
356 : }
357 : }
358 :
359 : #if !defined(_WIN32) && !defined(DOS32X)
360 3820 : if (!found) {
361 3800 : path = tds_get_home_file(TDS_DIR(".config/freetds.conf"));
362 3800 : if (path) {
363 3800 : found = tds_try_conf_file(path, "(.config/freetds.conf)", server, login);
364 3800 : free(path);
365 : }
366 : }
367 : #endif
368 :
369 3820 : if (!found) {
370 3800 : path = tds_get_home_file(TDS_DIR(".freetds.conf"));
371 3800 : if (path) {
372 3800 : found = tds_try_conf_file(path, "(.freetds.conf)", server, login);
373 3800 : free(path);
374 : } else {
375 0 : tdsdump_log(TDS_DBG_INFO2, "... Error getting ~/.freetds.conf. Trying %" tdsPRIdir ".\n",
376 : FREETDS_SYSCONFFILE);
377 : }
378 : }
379 :
380 3820 : if (!found) {
381 124 : found = tds_try_conf_file(FREETDS_SYSCONFFILE, "(default)", server, login);
382 : }
383 :
384 3820 : return found;
385 : }
386 :
387 : static bool
388 4184 : tds_read_conf_sections(FILE * in, const char *server, TDSLOGIN * login)
389 : {
390 4184 : DSTR default_instance = DSTR_INITIALIZER;
391 : int default_port;
392 :
393 : bool found;
394 :
395 4184 : tds_read_conf_section(in, "global", tds_parse_conf_section, login);
396 :
397 4184 : if (!server[0])
398 : return false;
399 4184 : rewind(in);
400 :
401 4184 : if (!tds_dstr_dup(&default_instance, &login->instance_name))
402 : return false;
403 4184 : default_port = login->port;
404 :
405 4184 : found = tds_read_conf_section(in, server, tds_parse_conf_section, login);
406 4184 : if (!login->valid_configuration) {
407 0 : tds_dstr_free(&default_instance);
408 0 : return false;
409 : }
410 :
411 : /*
412 : * If both instance and port are specified and neither one came from the default, it's an error
413 : * TODO: If port/instance is specified in the non-default, it has priority over the default setting.
414 : * TODO: test this.
415 : */
416 8460 : if (!tds_dstr_isempty(&login->instance_name) && login->port &&
417 92 : !(!tds_dstr_isempty(&default_instance) || default_port)) {
418 0 : tdsdump_log(TDS_DBG_ERROR, "error: cannot specify both port %d and instance %s.\n",
419 0 : login->port, tds_dstr_cstr(&login->instance_name));
420 : /* tdserror(tds_get_ctx(tds), tds, TDSEPORTINSTANCE, 0); */
421 : }
422 4184 : tds_dstr_free(&default_instance);
423 4184 : return found;
424 : }
425 :
426 : static const struct {
427 : char value[7];
428 : unsigned char to_return;
429 : } boolean_values[] = {
430 : { "yes", 1 },
431 : { "no", 0 },
432 : { "on", 1 },
433 : { "off", 0 },
434 : { "true", 1 },
435 : { "false", 0 }
436 : };
437 :
438 : int
439 0 : tds_parse_boolean(const char *value, int default_value)
440 : {
441 : int p;
442 :
443 10232 : for (p = 0; p < TDS_VECTOR_SIZE(boolean_values); ++p) {
444 10232 : if (!strcasecmp(value, boolean_values[p].value))
445 2920 : return boolean_values[p].to_return;
446 : }
447 : return default_value;
448 : }
449 :
450 : int
451 0 : tds_config_boolean(const char *option, const char *value, TDSLOGIN *login)
452 : {
453 0 : int ret = tds_parse_boolean(value, -1);
454 0 : if (ret >= 0)
455 : return ret;
456 :
457 0 : tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for boolean setting '%s'!\n",
458 : value, option);
459 0 : login->valid_configuration = 0;
460 0 : return 0;
461 : }
462 :
463 : static int
464 2920 : tds_parse_boolean_option(const char *option, const char *value, int default_value, bool *p_error)
465 : {
466 2920 : int ret = tds_parse_boolean(value, -1);
467 2920 : if (ret >= 0)
468 : return ret;
469 :
470 0 : tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for boolean setting '%s'!\n",
471 : value, option);
472 0 : *p_error = true;
473 0 : return default_value;
474 : }
475 :
476 : static bool
477 1476 : tds_config_encryption(const char * value, TDSLOGIN * login)
478 : {
479 1476 : TDS_ENCRYPTION_LEVEL lvl = TDS_ENCRYPTION_OFF;
480 :
481 1476 : if (!strcasecmp(value, TDS_STR_ENCRYPTION_OFF))
482 : ;
483 1476 : else if (!strcasecmp(value, TDS_STR_ENCRYPTION_REQUEST))
484 : lvl = TDS_ENCRYPTION_REQUEST;
485 1456 : else if (!strcasecmp(value, TDS_STR_ENCRYPTION_REQUIRE))
486 : lvl = TDS_ENCRYPTION_REQUIRE;
487 0 : else if (!strcasecmp(value, TDS_STR_ENCRYPTION_STRICT))
488 : lvl = TDS_ENCRYPTION_STRICT;
489 : else {
490 0 : tdsdump_log(TDS_DBG_ERROR, "UNRECOGNIZED option value '%s' for '%s' setting!\n",
491 : value, TDS_STR_ENCRYPTION);
492 0 : tdsdump_log(TDS_DBG_ERROR, "Valid settings are: ('%s', '%s', '%s', '%s')\n",
493 : TDS_STR_ENCRYPTION_OFF, TDS_STR_ENCRYPTION_REQUEST, TDS_STR_ENCRYPTION_REQUIRE,
494 : TDS_STR_ENCRYPTION_STRICT);
495 : lvl = TDS_ENCRYPTION_REQUIRE; /* Assuming "require" is safer than "no" */
496 : return false;
497 : }
498 :
499 1476 : login->encryption_level = lvl;
500 : return true;
501 : }
502 :
503 : /**
504 : * Read a section of configuration file (INI style file)
505 : * @param in configuration file
506 : * @param section section to read
507 : * @param tds_conf_parse callback that receive every entry in section
508 : * @param param parameter to pass to callback function
509 : */
510 : bool
511 45264 : tds_read_conf_section(FILE * in, const char *section, TDSCONFPARSE tds_conf_parse, void *param)
512 : {
513 : char line[256], *value;
514 : #define option line
515 : char *s;
516 : char p;
517 : int i;
518 45264 : bool insection = false;
519 45264 : bool found = false;
520 :
521 45264 : tdsdump_log(TDS_DBG_INFO1, "Looking for section %s.\n", section);
522 2610210 : while (fgets(line, sizeof(line), in)) {
523 : s = line;
524 :
525 : /* skip leading whitespace */
526 4036020 : while (*s && TDS_ISSPACE(*s))
527 1471074 : s++;
528 :
529 : /* skip it if it's a comment line */
530 2564946 : if (*s == ';' || *s == '#')
531 903004 : continue;
532 :
533 : /* read up to the = ignoring duplicate spaces */
534 : p = 0;
535 : i = 0;
536 14013328 : while (*s && *s != '=') {
537 12351386 : if (!TDS_ISSPACE(*s)) {
538 10600260 : if (TDS_ISSPACE(p))
539 418158 : option[i++] = ' ';
540 10600260 : option[i++] = tolower((unsigned char) *s);
541 : }
542 12351386 : p = *s;
543 12351386 : s++;
544 : }
545 :
546 : /* skip if empty option */
547 1661942 : if (!i)
548 335260 : continue;
549 :
550 : /* skip the = */
551 1326682 : if (*s)
552 1038614 : s++;
553 :
554 : /* terminate the option, must be done after skipping = */
555 1326682 : option[i] = '\0';
556 :
557 : /* skip leading whitespace */
558 3690858 : while (*s && TDS_ISSPACE(*s))
559 1037494 : s++;
560 :
561 : /* read up to a # ; or null ignoring duplicate spaces */
562 : value = s;
563 : p = 0;
564 : i = 0;
565 11110552 : while (*s && *s != ';' && *s != '#') {
566 9783870 : if (!TDS_ISSPACE(*s)) {
567 8664360 : if (TDS_ISSPACE(p))
568 80836 : value[i++] = ' ';
569 8664360 : value[i++] = *s;
570 : }
571 9783870 : p = *s;
572 9783870 : s++;
573 : }
574 1326682 : value[i] = '\0';
575 :
576 1326682 : if (option[0] == '[') {
577 288068 : s = strchr(option, ']');
578 288068 : if (s)
579 288068 : *s = '\0';
580 288068 : tdsdump_log(TDS_DBG_INFO1, "\tFound section %s.\n", &option[1]);
581 :
582 288068 : if (!strcasecmp(section, &option[1])) {
583 40971 : tdsdump_log(TDS_DBG_INFO1, "Got a match.\n");
584 : insection = true;
585 : found = true;
586 : } else {
587 : insection = false;
588 : }
589 1038614 : } else if (insection) {
590 126988 : tds_conf_parse(option, value, param);
591 : }
592 :
593 : }
594 45264 : tdsdump_log(TDS_DBG_INFO1, "\tReached EOF\n");
595 45264 : return found;
596 : #undef option
597 : }
598 :
599 : /* Also used to scan ODBC.INI entries */
600 : bool
601 31302 : tds_parse_conf_section(const char *option, const char *value, void *param)
602 : {
603 : #define parse_boolean(option, value, variable) do { \
604 : variable = tds_parse_boolean_option(option, value, variable, &got_error); \
605 : } while(0)
606 31302 : TDSLOGIN *login = (TDSLOGIN *) param;
607 31302 : void *s = param;
608 31302 : bool got_error = false;
609 :
610 31302 : tdsdump_log(TDS_DBG_INFO1, "\t%s = '%s'\n", option, value);
611 :
612 31302 : if (!strcmp(option, TDS_STR_VERSION)) {
613 3830 : tds_config_verstr(value, login);
614 27472 : } else if (!strcmp(option, TDS_STR_BLKSZ)) {
615 0 : int val = atoi(value);
616 0 : if (val >= 512 && val < 65536)
617 0 : login->block_size = val;
618 27472 : } else if (!strcmp(option, TDS_STR_SWAPDT)) {
619 : /* this option is deprecated, just check value for compatibility */
620 0 : tds_config_boolean(option, value, login);
621 27472 : } else if (!strcmp(option, TDS_GSSAPI_DELEGATION)) {
622 : /* gssapi flag addition */
623 0 : parse_boolean(option, value, login->gssapi_use_delegation);
624 27472 : } else if (!strcmp(option, TDS_STR_MUTUAL_AUTHENTICATION)) {
625 0 : parse_boolean(option, value, login->mutual_authentication);
626 27472 : } else if (!strcmp(option, TDS_STR_DUMPFILE)) {
627 0 : TDS_ZERO_FREE(login->dump_file);
628 0 : if (value[0]) {
629 0 : login->dump_file = tds_dir_from_cstr(value);
630 0 : if (!login->dump_file)
631 : s = NULL;
632 : }
633 27472 : } else if (!strcmp(option, TDS_STR_DEBUGFLAGS)) {
634 : char *end;
635 : long flags;
636 3800 : flags = strtol(value, &end, 0);
637 3800 : if (*value != '\0' && *end == '\0' && flags > INT_MIN && flags < INT_MAX)
638 3800 : login->debug_flags = (int) flags;
639 23672 : } else if (!strcmp(option, TDS_STR_TIMEOUT) || !strcmp(option, TDS_STR_QUERY_TIMEOUT)) {
640 3800 : if (atoi(value) > 0)
641 3800 : login->query_timeout = atoi(value);
642 19872 : } else if (!strcmp(option, TDS_STR_CONNTIMEOUT)) {
643 3800 : if (atoi(value) > 0)
644 3800 : login->connect_timeout = atoi(value);
645 16072 : } else if (!strcmp(option, TDS_STR_HOST)) {
646 : char tmp[128];
647 : struct addrinfo *addrs;
648 :
649 3696 : if (TDS_FAILED(tds_lookup_host_set(value, &login->ip_addrs))) {
650 0 : tdsdump_log(TDS_DBG_WARN, "Found host entry %s however name resolution failed. \n", value);
651 0 : return false;
652 : }
653 :
654 3696 : tdsdump_log(TDS_DBG_INFO1, "Found host entry %s \n", value);
655 3696 : s = tds_dstr_copy(&login->server_host_name, value);
656 7392 : for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next)
657 3696 : tdsdump_log(TDS_DBG_INFO1, "IP addr is %s.\n", tds_addrinfo2str(addrs, tmp, sizeof(tmp)));
658 :
659 12376 : } else if (!strcmp(option, TDS_STR_PORT)) {
660 3916 : if (atoi(value) > 0)
661 3916 : login->port = atoi(value);
662 8460 : } else if (!strcmp(option, TDS_STR_EMUL_LE)) {
663 : /* obsolete, ignore */
664 0 : tds_config_boolean(option, value, login);
665 8460 : } else if (!strcmp(option, TDS_STR_TEXTSZ)) {
666 3924 : if (atoi(value) > 0)
667 3924 : login->text_size = atoi(value);
668 4536 : } else if (!strcmp(option, TDS_STR_CHARSET)) {
669 0 : s = tds_dstr_copy(&login->server_charset, value);
670 0 : tdsdump_log(TDS_DBG_INFO1, "%s is %s.\n", option, tds_dstr_cstr(&login->server_charset));
671 4536 : } else if (!strcmp(option, TDS_STR_CLCHARSET)) {
672 100 : s = tds_dstr_copy(&login->client_charset, value);
673 100 : tdsdump_log(TDS_DBG_INFO1, "tds_parse_conf_section: %s is %s.\n", option, tds_dstr_cstr(&login->client_charset));
674 4436 : } else if (!strcmp(option, TDS_STR_USE_UTF_16)) {
675 0 : parse_boolean(option, value, login->use_utf16);
676 4436 : } else if (!strcmp(option, TDS_STR_LANGUAGE)) {
677 0 : s = tds_dstr_copy(&login->language, value);
678 4436 : } else if (!strcmp(option, TDS_STR_APPENDMODE)) {
679 0 : parse_boolean(option, value, tds_append_mode);
680 4436 : } else if (!strcmp(option, TDS_STR_INSTANCE)) {
681 0 : s = tds_dstr_copy(&login->instance_name, value);
682 4436 : } else if (!strcmp(option, TDS_STR_ENCRYPTION)) {
683 1476 : if (!tds_config_encryption(value, login))
684 : s = NULL;
685 2960 : } else if (!strcmp(option, TDS_STR_ASA_DATABASE)) {
686 20 : s = tds_dstr_copy(&login->server_name, value);
687 2940 : } else if (!strcmp(option, TDS_STR_USENTLMV2)) {
688 0 : parse_boolean(option, value, login->use_ntlmv2);
689 0 : login->use_ntlmv2_specified = 1;
690 2940 : } else if (!strcmp(option, TDS_STR_USELANMAN)) {
691 0 : parse_boolean(option, value, login->use_lanman);
692 2940 : } else if (!strcmp(option, TDS_STR_REALM)) {
693 0 : s = tds_dstr_copy(&login->server_realm_name, value);
694 2940 : } else if (!strcmp(option, TDS_STR_SPN)) {
695 0 : s = tds_dstr_copy(&login->server_spn, value);
696 2940 : } else if (!strcmp(option, TDS_STR_CAFILE)) {
697 0 : s = tds_dstr_copy(&login->cafile, value);
698 2940 : } else if (!strcmp(option, TDS_STR_CRLFILE)) {
699 0 : s = tds_dstr_copy(&login->crlfile, value);
700 2940 : } else if (!strcmp(option, TDS_STR_CHECKSSLHOSTNAME)) {
701 1456 : parse_boolean(option, value, login->check_ssl_hostname);
702 1484 : } else if (!strcmp(option, TDS_STR_SSLHOSTNAME)) {
703 0 : s = tds_dstr_copy(&login->certificate_host_name, value);
704 1484 : } else if (!strcmp(option, TDS_STR_DBFILENAME)) {
705 0 : s = tds_dstr_copy(&login->db_filename, value);
706 1484 : } else if (!strcmp(option, TDS_STR_DATABASE)) {
707 0 : s = tds_dstr_copy(&login->database, value);
708 1484 : } else if (!strcmp(option, TDS_STR_READONLY_INTENT)) {
709 0 : parse_boolean(option, value, login->readonly_intent);
710 0 : tdsdump_log(TDS_DBG_FUNC, "Setting ReadOnly Intent to '%s'.\n", value);
711 1484 : } else if (!strcmp(option, TLS_STR_OPENSSL_CIPHERS)) {
712 0 : s = tds_dstr_copy(&login->openssl_ciphers, value);
713 1484 : } else if (!strcmp(option, TLS_STR_GNUTLS_CIPHERS)) {
714 0 : s = tds_dstr_copy(&login->gnutls_ciphers, value);
715 1484 : } else if (!strcmp(option, TDS_STR_ENABLE_TLS_V1)) {
716 1464 : parse_boolean(option, value, login->enable_tls_v1);
717 1464 : login->enable_tls_v1_specified = 1;
718 20 : } else if (!strcmp(option, TDS_STR_ENABLE_TLS_V1_1)) {
719 0 : parse_boolean(option, value, login->enable_tls_v1_1);
720 0 : login->enable_tls_v1_1_specified = 1;
721 : } else {
722 20 : tdsdump_log(TDS_DBG_INFO1, "UNRECOGNIZED option '%s' ... ignoring.\n", option);
723 : }
724 :
725 31302 : if (!s || got_error) {
726 0 : login->valid_configuration = 0;
727 0 : return false;
728 : }
729 : return true;
730 : #undef parse_boolean
731 : }
732 :
733 : static bool
734 2590 : tds_config_login(TDSLOGIN * connection, TDSLOGIN * login)
735 : {
736 2590 : DSTR *res = &login->server_name;
737 :
738 5180 : if (!tds_dstr_isempty(&login->server_name)) {
739 : if (1 || tds_dstr_isempty(&connection->server_name))
740 2590 : res = tds_dstr_dup(&connection->server_name, &login->server_name);
741 : }
742 :
743 2590 : if (login->tds_version)
744 22 : connection->tds_version = login->tds_version;
745 :
746 5180 : if (res && !tds_dstr_isempty(&login->language))
747 326 : res = tds_dstr_dup(&connection->language, &login->language);
748 :
749 5180 : if (res && !tds_dstr_isempty(&login->server_charset))
750 0 : res = tds_dstr_dup(&connection->server_charset, &login->server_charset);
751 :
752 5180 : if (res && !tds_dstr_isempty(&login->client_charset)) {
753 174 : res = tds_dstr_dup(&connection->client_charset, &login->client_charset);
754 174 : tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "client_charset",
755 0 : tds_dstr_cstr(&connection->client_charset));
756 : }
757 :
758 2590 : if (!login->use_utf16)
759 0 : connection->use_utf16 = login->use_utf16;
760 :
761 5180 : if (res && !tds_dstr_isempty(&login->database)) {
762 54 : res = tds_dstr_dup(&connection->database, &login->database);
763 54 : tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "database_name",
764 0 : tds_dstr_cstr(&connection->database));
765 : }
766 :
767 5180 : if (res && !tds_dstr_isempty(&login->client_host_name))
768 494 : res = tds_dstr_dup(&connection->client_host_name, &login->client_host_name);
769 :
770 5180 : if (res && !tds_dstr_isempty(&login->app_name))
771 1180 : res = tds_dstr_dup(&connection->app_name, &login->app_name);
772 :
773 5180 : if (res && !tds_dstr_isempty(&login->user_name))
774 2520 : res = tds_dstr_dup(&connection->user_name, &login->user_name);
775 :
776 5180 : if (res && !tds_dstr_isempty(&login->password)) {
777 : /* for security reason clear memory */
778 2520 : tds_dstr_zero(&connection->password);
779 2520 : res = tds_dstr_dup(&connection->password, &login->password);
780 : }
781 :
782 5180 : if (res && !tds_dstr_isempty(&login->library))
783 2520 : res = tds_dstr_dup(&connection->library, &login->library);
784 :
785 2590 : if (login->encryption_level)
786 0 : connection->encryption_level = login->encryption_level;
787 :
788 2590 : if (login->suppress_language)
789 0 : connection->suppress_language = 1;
790 :
791 2590 : if (!login->bulk_copy)
792 0 : connection->bulk_copy = 0;
793 :
794 2590 : if (login->block_size)
795 0 : connection->block_size = login->block_size;
796 :
797 2590 : if (login->gssapi_use_delegation)
798 0 : connection->gssapi_use_delegation = login->gssapi_use_delegation;
799 :
800 2590 : if (login->mutual_authentication)
801 0 : connection->mutual_authentication = login->mutual_authentication;
802 :
803 2590 : if (login->port)
804 42 : connection->port = login->port;
805 :
806 2590 : if (login->connect_timeout)
807 0 : connection->connect_timeout = login->connect_timeout;
808 :
809 2590 : if (login->query_timeout)
810 0 : connection->query_timeout = login->query_timeout;
811 :
812 2590 : if (!login->check_ssl_hostname)
813 0 : connection->check_ssl_hostname = login->check_ssl_hostname;
814 :
815 5180 : if (res && !tds_dstr_isempty(&login->db_filename))
816 0 : res = tds_dstr_dup(&connection->db_filename, &login->db_filename);
817 :
818 5180 : if (res && !tds_dstr_isempty(&login->openssl_ciphers))
819 0 : res = tds_dstr_dup(&connection->openssl_ciphers, &login->openssl_ciphers);
820 :
821 5180 : if (res && !tds_dstr_isempty(&login->gnutls_ciphers))
822 0 : res = tds_dstr_dup(&connection->gnutls_ciphers, &login->gnutls_ciphers);
823 :
824 5180 : if (res && !tds_dstr_isempty(&login->server_spn))
825 0 : res = tds_dstr_dup(&connection->server_spn, &login->server_spn);
826 :
827 : /* copy other info not present in configuration file */
828 2590 : connection->capabilities = login->capabilities;
829 :
830 2590 : if (login->readonly_intent)
831 0 : connection->readonly_intent = login->readonly_intent;
832 :
833 2590 : connection->use_new_password = login->use_new_password;
834 :
835 2590 : if (login->use_ntlmv2_specified) {
836 0 : connection->use_ntlmv2_specified = login->use_ntlmv2_specified;
837 0 : connection->use_ntlmv2 = login->use_ntlmv2;
838 : }
839 :
840 2590 : if (login->enable_tls_v1_specified) {
841 0 : connection->enable_tls_v1_specified = login->enable_tls_v1_specified;
842 0 : connection->enable_tls_v1 = login->enable_tls_v1;
843 : }
844 :
845 2590 : if (login->enable_tls_v1_1_specified) {
846 0 : connection->enable_tls_v1_1_specified = login->enable_tls_v1_1_specified;
847 0 : connection->enable_tls_v1_1 = login->enable_tls_v1_1;
848 : }
849 :
850 2590 : if (res)
851 2590 : res = tds_dstr_dup(&connection->new_password, &login->new_password);
852 :
853 2590 : return res != NULL;
854 : }
855 :
856 : static bool
857 4610 : tds_config_env_tdsdump(TDSLOGIN * login)
858 : {
859 : tds_dir_char path[TDS_VECTOR_SIZE(pid_logpath) + 22];
860 :
861 4610 : tds_dir_char *s = tds_dir_getenv(TDS_DIR("TDSDUMP"));
862 4610 : if (!s)
863 : return true;
864 :
865 4 : if (!tds_dir_len(s)) {
866 0 : pid_t pid = getpid();
867 0 : tds_dir_snprintf(path, TDS_VECTOR_SIZE(path), pid_logpath, (int) pid);
868 0 : s = path;
869 : }
870 :
871 4 : if (!(s = tds_dir_dup(s)))
872 : return false;
873 4 : free(login->dump_file);
874 4 : login->dump_file = s;
875 :
876 4 : tdsdump_log(TDS_DBG_INFO1, "Setting 'dump_file' to '%" tdsPRIdir "' from $TDSDUMP.\n", login->dump_file);
877 : return true;
878 : }
879 :
880 : static void
881 4610 : tds_config_env_tdsport(TDSLOGIN * login)
882 : {
883 : char *s;
884 :
885 4610 : if ((s = getenv("TDSPORT"))) {
886 12 : login->port = tds_lookup_port(s);
887 12 : tds_dstr_empty(&login->instance_name);
888 12 : tdsdump_log(TDS_DBG_INFO1, "Setting 'port' to %s from $TDSPORT.\n", s);
889 : }
890 4610 : return;
891 : }
892 : static void
893 4610 : tds_config_env_tdsver(TDSLOGIN * login)
894 : {
895 : char *tdsver;
896 :
897 4610 : if ((tdsver = getenv("TDSVER"))) {
898 14 : TDS_USMALLINT *pver = tds_config_verstr(tdsver, login);
899 14 : tdsdump_log(TDS_DBG_INFO1, "TDS version %sset to %s from $TDSVER.\n", (pver? "":"not "), tdsver);
900 :
901 : }
902 4610 : return;
903 : }
904 :
905 : /* TDSHOST env var, pkleef@openlinksw.com 01/21/02 */
906 : static bool
907 4610 : tds_config_env_tdshost(TDSLOGIN * login)
908 : {
909 : const char *tdshost;
910 : char tmp[128];
911 : struct addrinfo *addrs;
912 :
913 4610 : if (!(tdshost = getenv("TDSHOST")))
914 : return true;
915 :
916 10 : if (TDS_FAILED(tds_lookup_host_set(tdshost, &login->ip_addrs))) {
917 0 : tdsdump_log(TDS_DBG_WARN, "Name resolution failed for '%s' from $TDSHOST.\n", tdshost);
918 : return false;
919 : }
920 :
921 10 : if (!tds_dstr_copy(&login->server_host_name, tdshost))
922 : return false;
923 20 : for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
924 10 : tdsdump_log(TDS_DBG_INFO1, "Setting IP Address to %s (%s) from $TDSHOST.\n",
925 : tds_addrinfo2str(addrs, tmp, sizeof(tmp)), tdshost);
926 : }
927 : return true;
928 : }
929 : #define TDS_FIND(k,b,c) tds_find(k, b, TDS_VECTOR_SIZE(b), sizeof(b[0]), c)
930 :
931 :
932 : static const void *
933 : tds_find(const void *key, const void *base, size_t nelem, size_t width,
934 : int (*compar)(const void *, const void *))
935 : {
936 : size_t n;
937 : const char *p = (const char*) base;
938 :
939 27642 : for (n = nelem; n != 0; --n) {
940 31486 : if (0 == compar(key, p))
941 : return p;
942 27642 : p += width;
943 : }
944 : return NULL;
945 : }
946 :
947 : struct tdsvername_t
948 : {
949 : const char name[6];
950 : TDS_USMALLINT version;
951 : };
952 :
953 : static int
954 31486 : tds_vername_cmp(const void *key, const void *pelem)
955 : {
956 31486 : return strcmp((const char *)key, ((const struct tdsvername_t *)pelem)->name);
957 : }
958 :
959 : /**
960 : * Set TDS version from given string
961 : * @param tdsver tds string version
962 : * @param login where to store information
963 : * @return as encoded hex value: high byte major, low byte minor.
964 : */
965 : TDS_USMALLINT *
966 3844 : tds_config_verstr(const char *tdsver, TDSLOGIN * login)
967 : {
968 : static const struct tdsvername_t tds_versions[] =
969 : { { "0", 0x000 }
970 : , {"auto", 0x000 }
971 : , { "4.2", 0x402 }
972 : , { "50", 0x500 }
973 : , { "5.0", 0x500 }
974 : , { "70", 0x700 }
975 : , { "7.0", 0x700 }
976 : , { "7.1", 0x701 }
977 : , { "7.2", 0x702 }
978 : , { "7.3", 0x703 }
979 : , { "7.4", 0x704 }
980 : , { "8.0", 0x800 }
981 : };
982 : const struct tdsvername_t *pver;
983 :
984 3844 : if (!login) {
985 0 : assert(login);
986 : return NULL;
987 : }
988 :
989 3844 : if ((pver = (const struct tdsvername_t *) TDS_FIND(tdsver, tds_versions, tds_vername_cmp)) == NULL) {
990 0 : tdsdump_log(TDS_DBG_INFO1, "error: no such version: %s\n", tdsver);
991 : return NULL;
992 : }
993 :
994 3844 : login->tds_version = pver->version;
995 3844 : tdsdump_log(TDS_DBG_INFO1, "Setting tds version to %s (0x%0x).\n", tdsver, pver->version);
996 :
997 3844 : return &login->tds_version;
998 : }
999 :
1000 : /**
1001 : * Set the full name of interface file
1002 : * @param interf file name
1003 : */
1004 : TDSRET
1005 20 : tds_set_interfaces_file_loc(const char *interf)
1006 : {
1007 20 : tds_dir_char *copy = NULL;
1008 :
1009 : /* If filename passed, copy filename */
1010 20 : if (interf != NULL && interf[0] != '\0') {
1011 10 : copy = tds_dir_from_cstr(interf);
1012 10 : if (copy == NULL)
1013 : return -TDSEMEM;
1014 : }
1015 :
1016 : /* Free it if already set */
1017 20 : free(interf_file);
1018 :
1019 : /* Set */
1020 20 : interf_file = copy;
1021 :
1022 20 : return TDS_SUCCESS;
1023 : }
1024 :
1025 : /**
1026 : * Get the IP address for a hostname. Store server's IP address
1027 : * in the string 'ip' in dotted-decimal notation. (The "hostname" might itself
1028 : * be a dotted-decimal address.
1029 : *
1030 : * If we can't determine the IP address then 'ip' will be set to empty
1031 : * string.
1032 : */
1033 : /* TODO callers seem to set always connection info... change it */
1034 : struct addrinfo *
1035 3856 : tds_lookup_host(const char *servername) /* (I) name of the server */
1036 : {
1037 3856 : struct addrinfo hints, *addr = NULL;
1038 3856 : assert(servername != NULL);
1039 :
1040 3856 : memset(&hints, '\0', sizeof(hints));
1041 : hints.ai_family = AF_UNSPEC;
1042 3856 : hints.ai_socktype = SOCK_STREAM;
1043 3856 : hints.ai_protocol = IPPROTO_TCP;
1044 :
1045 : #ifdef AI_ADDRCONFIG
1046 3856 : hints.ai_flags |= AI_ADDRCONFIG;
1047 3856 : switch (getaddrinfo(servername, NULL, &hints, &addr)) {
1048 3856 : case 0:
1049 3856 : return addr;
1050 0 : case EAI_FAMILY:
1051 : # ifdef EAI_ADDRFAMILY
1052 : case EAI_ADDRFAMILY:
1053 : # endif
1054 0 : hints.ai_flags &= ~AI_ADDRCONFIG;
1055 : break;
1056 : default:
1057 : return NULL;
1058 : }
1059 : #endif
1060 :
1061 0 : if (getaddrinfo(servername, NULL, &hints, &addr))
1062 : return NULL;
1063 0 : return addr;
1064 : }
1065 :
1066 : TDSRET
1067 3856 : tds_lookup_host_set(const char *servername, struct addrinfo **addr)
1068 : {
1069 : struct addrinfo *newaddr;
1070 3856 : assert(servername != NULL && addr != NULL);
1071 :
1072 3856 : if ((newaddr = tds_lookup_host(servername)) != NULL) {
1073 3856 : if (*addr != NULL)
1074 30 : freeaddrinfo(*addr);
1075 3856 : *addr = newaddr;
1076 3856 : return TDS_SUCCESS;
1077 : }
1078 : return TDS_FAIL;
1079 : }
1080 :
1081 : /**
1082 : * Given a portname lookup the port.
1083 : *
1084 : * If we can't determine the port number then return 0.
1085 : */
1086 : static int
1087 12 : tds_lookup_port(const char *portname)
1088 : {
1089 12 : int num = atoi(portname);
1090 12 : if (!num)
1091 2 : num = tds_getservice(portname);
1092 12 : return num;
1093 : }
1094 :
1095 : /* TODO same code in convert.c ?? */
1096 : static int
1097 : hexdigit(int c)
1098 : {
1099 0 : if (c >= '0' && c <= '9')
1100 0 : return c - '0';
1101 : /* ASCII optimization, 'A' -> 'a', 'a' -> 'a' */
1102 0 : c |= 0x20;
1103 0 : if (c >= 'a' && c <= 'f')
1104 0 : return c - 'a' + 10;
1105 : return 0; /* bad hex digit */
1106 : }
1107 :
1108 : static int
1109 0 : hex2num(char *hex)
1110 : {
1111 0 : return hexdigit(hex[0]) * 16 + hexdigit(hex[1]);
1112 : }
1113 :
1114 : /**
1115 : * Open and read the file 'file' searching for a logical server
1116 : * by the name of 'host'. If one is found then lookup
1117 : * the IP address and port number and store them in 'login'
1118 : *
1119 : * \param dir name of base directory for interface file
1120 : * \param file name of the interface file
1121 : * \param host logical host to search for
1122 : * \return false if not fount true if found
1123 : */
1124 : static bool
1125 0 : search_interface_file(TDSLOGIN * login, const tds_dir_char *dir, const tds_dir_char *file, const char *host)
1126 : {
1127 : tds_dir_char *pathname;
1128 : char line[255];
1129 : char tmp_ip[sizeof(line)];
1130 : char tmp_port[sizeof(line)];
1131 : char tmp_ver[sizeof(line)];
1132 : FILE *in;
1133 : char *field;
1134 0 : bool found = false;
1135 0 : bool server_found = false;
1136 : char *lasts;
1137 :
1138 0 : line[0] = '\0';
1139 0 : tmp_ip[0] = '\0';
1140 0 : tmp_port[0] = '\0';
1141 0 : tmp_ver[0] = '\0';
1142 :
1143 0 : tdsdump_log(TDS_DBG_INFO1, "Searching interfaces file %" tdsPRIdir "/%" tdsPRIdir ".\n", dir, file);
1144 :
1145 : /*
1146 : * create the full pathname to the interface file
1147 : */
1148 0 : if (file[0] == '\0') {
1149 0 : pathname = tds_dir_dup(TDS_DIR(""));
1150 : } else {
1151 0 : pathname = tds_join_path(dir, file);
1152 : }
1153 0 : if (!pathname)
1154 : return false;
1155 :
1156 : /*
1157 : * parse the interfaces file and find the server and port
1158 : */
1159 0 : if ((in = tds_dir_open(pathname, TDS_DIR("r"))) == NULL) {
1160 0 : tdsdump_log(TDS_DBG_INFO1, "Couldn't open %" tdsPRIdir ".\n", pathname);
1161 0 : free(pathname);
1162 0 : return false;
1163 : }
1164 0 : tdsdump_log(TDS_DBG_INFO1, "Interfaces file %" tdsPRIdir " opened.\n", pathname);
1165 :
1166 0 : while (fgets(line, sizeof(line) - 1, in)) {
1167 0 : if (line[0] == '#')
1168 0 : continue; /* comment */
1169 :
1170 0 : if (!TDS_ISSPACE(line[0])) {
1171 0 : field = strtok_r(line, "\n\t ", &lasts);
1172 0 : if (!strcmp(field, host)) {
1173 0 : found = true;
1174 0 : tdsdump_log(TDS_DBG_INFO1, "Found matching entry for host %s.\n", host);
1175 : } else
1176 : found = false;
1177 0 : } else if (found && TDS_ISSPACE(line[0])) {
1178 0 : field = strtok_r(line, "\n\t ", &lasts);
1179 0 : if (field != NULL && !strcmp(field, "query")) {
1180 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* tcp or tli */
1181 0 : if (!strcmp(field, "tli")) {
1182 0 : tdsdump_log(TDS_DBG_INFO1, "TLI service.\n");
1183 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* tcp */
1184 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* device */
1185 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* host/port */
1186 0 : if (strlen(field) >= 18) {
1187 0 : sprintf(tmp_port, "%d", hex2num(&field[6]) * 256 + hex2num(&field[8]));
1188 0 : sprintf(tmp_ip, "%d.%d.%d.%d", hex2num(&field[10]),
1189 : hex2num(&field[12]), hex2num(&field[14]), hex2num(&field[16]));
1190 0 : tdsdump_log(TDS_DBG_INFO1, "tmp_port = %s. tmp_ip = %s.\n", tmp_port, tmp_ip);
1191 : }
1192 : } else {
1193 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* ether */
1194 0 : strcpy(tmp_ver, field);
1195 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* host */
1196 0 : strcpy(tmp_ip, field);
1197 0 : tdsdump_log(TDS_DBG_INFO1, "host field %s.\n", tmp_ip);
1198 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* port */
1199 0 : strcpy(tmp_port, field);
1200 : } /* else */
1201 : server_found = true;
1202 : } /* if */
1203 : } /* else if */
1204 : } /* while */
1205 0 : fclose(in);
1206 0 : free(pathname);
1207 :
1208 :
1209 : /*
1210 : * Look up the host and service
1211 : */
1212 0 : if (server_found) {
1213 :
1214 0 : if (TDS_SUCCEED(tds_lookup_host_set(tmp_ip, &login->ip_addrs))) {
1215 : struct addrinfo *addrs;
1216 0 : if (!tds_dstr_copy(&login->server_host_name, tmp_ip))
1217 : return false;
1218 0 : for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
1219 0 : tdsdump_log(TDS_DBG_INFO1, "Resolved IP as '%s'.\n",
1220 : tds_addrinfo2str(login->ip_addrs, line, sizeof(line)));
1221 : }
1222 : } else {
1223 0 : tdsdump_log(TDS_DBG_WARN, "Name resolution failed for IP '%s'.\n", tmp_ip);
1224 : }
1225 :
1226 0 : if (tmp_port[0])
1227 0 : login->port = tds_lookup_port(tmp_port);
1228 0 : if (tmp_ver[0])
1229 0 : tds_config_verstr(tmp_ver, login);
1230 : }
1231 : return server_found;
1232 : } /* search_interface_file() */
1233 :
1234 : /**
1235 : * Try to find the IP number and port for a (possibly) logical server name.
1236 : *
1237 : * @note This function uses only the interfaces file and is deprecated.
1238 : */
1239 : static bool
1240 0 : tds_read_interfaces(const char *server, TDSLOGIN * login)
1241 : {
1242 0 : bool found = false;
1243 :
1244 : /* read $SYBASE/interfaces */
1245 :
1246 0 : if (!server || !server[0]) {
1247 0 : server = getenv("TDSQUERY");
1248 0 : if (!server || !server[0])
1249 0 : server = "SYBASE";
1250 0 : tdsdump_log(TDS_DBG_INFO1, "Setting server to %s from $TDSQUERY.\n", server);
1251 :
1252 : }
1253 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server %s....\n", server);
1254 :
1255 : /*
1256 : * Look for the server in the interf_file iff interf_file has been set.
1257 : */
1258 0 : if (interf_file) {
1259 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in file %" tdsPRIdir ".\n", interf_file);
1260 0 : found = search_interface_file(login, TDS_DIR(""), interf_file, server);
1261 : }
1262 :
1263 : /*
1264 : * if we haven't found the server yet then look for a $HOME/.interfaces file
1265 : */
1266 0 : if (!found) {
1267 0 : tds_dir_char *path = tds_get_home_file(TDS_DIR(".interfaces"));
1268 :
1269 0 : if (path) {
1270 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir ".\n", path);
1271 0 : found = search_interface_file(login, TDS_DIR(""), path, server);
1272 0 : free(path);
1273 : }
1274 : }
1275 :
1276 : /*
1277 : * if we haven't found the server yet then look in $SYBBASE/interfaces file
1278 : */
1279 0 : if (!found) {
1280 0 : const tds_dir_char *sybase = tds_dir_getenv(TDS_DIR("SYBASE"));
1281 : #ifdef __VMS
1282 : /* We've got to be in unix syntax for later slash-joined concatenation. */
1283 : #include <unixlib.h>
1284 : const char *unixspec = decc$translate_vms(sybase);
1285 : if ( (int)unixspec != 0 && (int)unixspec != -1 ) sybase = unixspec;
1286 : #endif
1287 0 : if (!sybase || !sybase[0])
1288 0 : sybase = interfaces_path;
1289 :
1290 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir "/interfaces.\n", sybase);
1291 0 : found = search_interface_file(login, sybase, TDS_DIR("interfaces"), server);
1292 : }
1293 :
1294 : /*
1295 : * If we still don't have the server and port then assume the user
1296 : * typed an actual server host name.
1297 : */
1298 0 : if (!found) {
1299 : int ip_port;
1300 : const char *env_port;
1301 :
1302 : /*
1303 : * Make a guess about the port number
1304 : */
1305 :
1306 0 : if (login->port == 0) {
1307 : /*
1308 : * Not set in the [global] section of the
1309 : * configure file, take a guess.
1310 : */
1311 : ip_port = TDS_DEF_PORT;
1312 : } else {
1313 : /*
1314 : * Preserve setting from the [global] section
1315 : * of the configure file.
1316 : */
1317 0 : ip_port = login->port;
1318 : }
1319 0 : if ((env_port = getenv("TDSPORT")) != NULL) {
1320 0 : ip_port = tds_lookup_port(env_port);
1321 0 : tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %s from $TDSPORT.\n", env_port);
1322 : } else
1323 0 : tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %d as a guess.\n", ip_port);
1324 :
1325 : /*
1326 : * look up the host
1327 : */
1328 :
1329 0 : if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs)))
1330 0 : if (!tds_dstr_copy(&login->server_host_name, server))
1331 : return false;
1332 :
1333 0 : if (ip_port)
1334 0 : login->port = ip_port;
1335 : }
1336 :
1337 : return found;
1338 : }
1339 :
1340 : /**
1341 : * Check the server name to find port info first
1342 : * Warning: connection-> & login-> are all modified when needed
1343 : * \return true when found, else false
1344 : */
1345 : static bool
1346 148 : parse_server_name_for_port(TDSLOGIN * connection, TDSLOGIN * login, bool update_server)
1347 : {
1348 : const char *pSep;
1349 : const char *server;
1350 :
1351 : /* seek the ':' in login server_name */
1352 296 : server = tds_dstr_cstr(&login->server_name);
1353 :
1354 : /* IPv6 address can be quoted */
1355 148 : if (server[0] == '[') {
1356 40 : pSep = strstr(server, "]:");
1357 40 : if (pSep)
1358 20 : ++pSep;
1359 : } else {
1360 108 : pSep = strrchr(server, ':');
1361 : }
1362 :
1363 148 : if (pSep && pSep != server) { /* yes, i found it! */
1364 : /* modify connection-> && login->server_name & ->port */
1365 168 : login->port = connection->port = atoi(pSep + 1);
1366 84 : tds_dstr_empty(&connection->instance_name);
1367 : } else {
1368 : /* handle instance name */
1369 64 : pSep = strrchr(server, '\\');
1370 64 : if (!pSep || pSep == server)
1371 : return false;
1372 :
1373 64 : if (!tds_dstr_copy(&connection->instance_name, pSep + 1))
1374 : return false;
1375 64 : connection->port = 0;
1376 : }
1377 :
1378 148 : if (!update_server)
1379 : return true;
1380 :
1381 74 : if (server[0] == '[' && pSep > server && pSep[-1] == ']') {
1382 20 : server++;
1383 20 : pSep--;
1384 : }
1385 74 : if (!tds_dstr_copyn(&connection->server_name, server, pSep - server))
1386 : return false;
1387 :
1388 74 : return true;
1389 : }
1390 :
1391 : /**
1392 : * Return a structure capturing the compile-time settings provided to the
1393 : * configure script.
1394 : */
1395 :
1396 : const TDS_COMPILETIME_SETTINGS *
1397 0 : tds_get_compiletime_settings(void)
1398 : {
1399 : static const TDS_COMPILETIME_SETTINGS settings = {
1400 : TDS_VERSION_NO
1401 : , FREETDS_SYSCONFDIR
1402 : , "unknown" /* need fancy script in makefile */
1403 : # if TDS50
1404 : , "5.0"
1405 : # elif TDS71
1406 : , "7.1"
1407 : # elif TDS72
1408 : , "7.2"
1409 : # elif TDS73
1410 : , "7.3"
1411 : # elif TDS74
1412 : , "7.4"
1413 : # else
1414 : , "auto"
1415 : # endif
1416 : # ifdef MSDBLIB
1417 : , true
1418 : # else
1419 : , false
1420 : # endif
1421 : # ifdef TDS_SYBASE_COMPAT
1422 : , true
1423 : # else
1424 : , false
1425 : # endif
1426 : # ifdef _REENTRANT
1427 : , true
1428 : # else
1429 : , false
1430 : # endif
1431 : # ifdef HAVE_ICONV
1432 : , true
1433 : # else
1434 : , false
1435 : # endif
1436 : # ifdef IODBC
1437 : , true
1438 : # else
1439 : , false
1440 : # endif
1441 : # ifdef UNIXODBC
1442 : , true
1443 : # else
1444 : , false
1445 : # endif
1446 : # ifdef HAVE_OPENSSL
1447 : , true
1448 : # else
1449 : , false
1450 : # endif
1451 : # ifdef HAVE_GNUTLS
1452 : , true
1453 : # else
1454 : , false
1455 : # endif
1456 : # if ENABLE_ODBC_MARS
1457 : , true
1458 : # else
1459 : , false
1460 : # endif
1461 : # ifdef HAVE_SSPI
1462 : , true
1463 : # else
1464 : , false
1465 : # endif
1466 : # ifdef ENABLE_KRB5
1467 : , true
1468 : # else
1469 : , false
1470 : # endif
1471 : };
1472 :
1473 : assert(settings.tdsver);
1474 :
1475 0 : return &settings;
1476 : }
1477 :
1478 : /**
1479 : * Make sure proper setting are in place for TDS 8.0
1480 : */
1481 : TDSRET
1482 3686 : tds8_adjust_login(TDSLOGIN *login)
1483 : {
1484 : /* TDS 8.0 requires TDS_ENCRYPTION_STRICT (entire-connection encryption) */
1485 3686 : if (IS_TDS80_PLUS(login))
1486 0 : login->encryption_level = TDS_ENCRYPTION_STRICT;
1487 :
1488 3686 : if (login->encryption_level == TDS_ENCRYPTION_STRICT) {
1489 : /* TDS 5.0 (Sybase/SAP ASE) it is optional; but TDS 7.x does not allow it.
1490 : * So, try 8.0 unless they specifically requested 5.0 */
1491 0 : if (!IS_TDS50(login) && !IS_TDS80_PLUS(login))
1492 0 : login->tds_version = 0x800;
1493 :
1494 : /* Validate server certificate signature to reduce risk of MITM attacks */
1495 0 : if (tds_dstr_isempty(&login->cafile))
1496 0 : if (!tds_dstr_copy(&login->cafile, "system"))
1497 : return -TDSEMEM;
1498 : }
1499 :
1500 : return TDS_SUCCESS;
1501 : }
1502 :
1503 : /** @} */
|