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 44464 : 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 44464 : bool insection = false;
516 44464 : bool found = false;
517 :
518 44464 : tdsdump_log(TDS_DBG_INFO1, "Looking for section %s.\n", section);
519 2589534 : while (fgets(line, sizeof(line), in)) {
520 : s = line;
521 :
522 : /* skip leading whitespace */
523 4005728 : while (*s && TDS_ISSPACE(*s))
524 1460658 : s++;
525 :
526 : /* skip it if it's a comment line */
527 2545070 : if (*s == ';' || *s == '#')
528 896868 : continue;
529 :
530 : /* read up to the = ignoring duplicate spaces */
531 : p = 0;
532 : i = 0;
533 13893388 : while (*s && *s != '=') {
534 12245186 : if (!TDS_ISSPACE(*s)) {
535 10508464 : if (TDS_ISSPACE(p))
536 415178 : option[i++] = ' ';
537 10508464 : option[i++] = tolower((unsigned char) *s);
538 : }
539 12245186 : p = *s;
540 12245186 : s++;
541 : }
542 :
543 : /* skip if empty option */
544 1648202 : if (!i)
545 332872 : continue;
546 :
547 : /* skip the = */
548 1315330 : if (*s)
549 1029806 : s++;
550 :
551 : /* terminate the option, must be done after skipping = */
552 1315330 : option[i] = '\0';
553 :
554 : /* skip leading whitespace */
555 3659346 : while (*s && TDS_ISSPACE(*s))
556 1028686 : s++;
557 :
558 : /* read up to a # ; or null ignoring duplicate spaces */
559 : value = s;
560 : p = 0;
561 : i = 0;
562 10967142 : while (*s && *s != ';' && *s != '#') {
563 9651812 : if (!TDS_ISSPACE(*s)) {
564 8541902 : if (TDS_ISSPACE(p))
565 80044 : value[i++] = ' ';
566 8541902 : value[i++] = *s;
567 : }
568 9651812 : p = *s;
569 9651812 : s++;
570 : }
571 1315330 : value[i] = '\0';
572 :
573 1315330 : if (option[0] == '[') {
574 285524 : s = strchr(option, ']');
575 285524 : if (s)
576 285524 : *s = '\0';
577 285524 : tdsdump_log(TDS_DBG_INFO1, "\tFound section %s.\n", &option[1]);
578 :
579 285524 : if (!strcasecmp(section, &option[1])) {
580 40207 : tdsdump_log(TDS_DBG_INFO1, "Got a match.\n");
581 : insection = true;
582 : found = true;
583 : } else {
584 : insection = false;
585 : }
586 1029806 : } else if (insection) {
587 124670 : tds_conf_parse(option, value, param);
588 : }
589 :
590 : }
591 44464 : tdsdump_log(TDS_DBG_INFO1, "\tReached EOF\n");
592 44464 : 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 > INT_MIN && flags < INT_MAX)
635 3774 : login->debug_flags = (int) flags;
636 23506 : } else if (!strcmp(option, TDS_STR_TIMEOUT) || !strcmp(option, TDS_STR_QUERY_TIMEOUT)) {
637 3774 : if (atoi(value) > 0)
638 3774 : login->query_timeout = atoi(value);
639 19732 : } else if (!strcmp(option, TDS_STR_CONNTIMEOUT)) {
640 3774 : if (atoi(value) > 0)
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) > 0)
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) > 0)
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 20 : } else if (!strcmp(option, TDS_STR_ENABLE_TLS_V1_1)) {
714 0 : parse_boolean(option, value, login->enable_tls_v1_1);
715 0 : login->enable_tls_v1_1_specified = 1;
716 : } else {
717 20 : tdsdump_log(TDS_DBG_INFO1, "UNRECOGNIZED option '%s' ... ignoring.\n", option);
718 : }
719 :
720 31084 : if (!s || got_error) {
721 0 : login->valid_configuration = 0;
722 0 : return false;
723 : }
724 : return true;
725 : #undef parse_boolean
726 : }
727 :
728 : static bool
729 2590 : tds_config_login(TDSLOGIN * connection, TDSLOGIN * login)
730 : {
731 2590 : DSTR *res = &login->server_name;
732 :
733 5180 : if (!tds_dstr_isempty(&login->server_name)) {
734 : if (1 || tds_dstr_isempty(&connection->server_name))
735 2590 : res = tds_dstr_dup(&connection->server_name, &login->server_name);
736 : }
737 :
738 2590 : if (login->tds_version)
739 22 : connection->tds_version = login->tds_version;
740 :
741 5180 : if (res && !tds_dstr_isempty(&login->language))
742 326 : res = tds_dstr_dup(&connection->language, &login->language);
743 :
744 5180 : if (res && !tds_dstr_isempty(&login->server_charset))
745 0 : res = tds_dstr_dup(&connection->server_charset, &login->server_charset);
746 :
747 5180 : if (res && !tds_dstr_isempty(&login->client_charset)) {
748 174 : res = tds_dstr_dup(&connection->client_charset, &login->client_charset);
749 174 : tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "client_charset",
750 0 : tds_dstr_cstr(&connection->client_charset));
751 : }
752 :
753 2590 : if (!login->use_utf16)
754 0 : connection->use_utf16 = login->use_utf16;
755 :
756 5180 : if (res && !tds_dstr_isempty(&login->database)) {
757 54 : res = tds_dstr_dup(&connection->database, &login->database);
758 54 : tdsdump_log(TDS_DBG_INFO1, "tds_config_login: %s is %s.\n", "database_name",
759 0 : tds_dstr_cstr(&connection->database));
760 : }
761 :
762 5180 : if (res && !tds_dstr_isempty(&login->client_host_name))
763 494 : res = tds_dstr_dup(&connection->client_host_name, &login->client_host_name);
764 :
765 5180 : if (res && !tds_dstr_isempty(&login->app_name))
766 1180 : res = tds_dstr_dup(&connection->app_name, &login->app_name);
767 :
768 5180 : if (res && !tds_dstr_isempty(&login->user_name))
769 2520 : res = tds_dstr_dup(&connection->user_name, &login->user_name);
770 :
771 5180 : if (res && !tds_dstr_isempty(&login->password)) {
772 : /* for security reason clear memory */
773 2520 : tds_dstr_zero(&connection->password);
774 2520 : res = tds_dstr_dup(&connection->password, &login->password);
775 : }
776 :
777 5180 : if (res && !tds_dstr_isempty(&login->library))
778 2520 : res = tds_dstr_dup(&connection->library, &login->library);
779 :
780 2590 : if (login->encryption_level)
781 0 : connection->encryption_level = login->encryption_level;
782 :
783 2590 : if (login->suppress_language)
784 0 : connection->suppress_language = 1;
785 :
786 2590 : if (!login->bulk_copy)
787 0 : connection->bulk_copy = 0;
788 :
789 2590 : if (login->block_size)
790 0 : connection->block_size = login->block_size;
791 :
792 2590 : if (login->gssapi_use_delegation)
793 0 : connection->gssapi_use_delegation = login->gssapi_use_delegation;
794 :
795 2590 : if (login->mutual_authentication)
796 0 : connection->mutual_authentication = login->mutual_authentication;
797 :
798 2590 : if (login->port)
799 42 : connection->port = login->port;
800 :
801 2590 : if (login->connect_timeout)
802 0 : connection->connect_timeout = login->connect_timeout;
803 :
804 2590 : if (login->query_timeout)
805 0 : connection->query_timeout = login->query_timeout;
806 :
807 2590 : if (!login->check_ssl_hostname)
808 0 : connection->check_ssl_hostname = login->check_ssl_hostname;
809 :
810 5180 : if (res && !tds_dstr_isempty(&login->db_filename))
811 0 : res = tds_dstr_dup(&connection->db_filename, &login->db_filename);
812 :
813 5180 : if (res && !tds_dstr_isempty(&login->openssl_ciphers))
814 0 : res = tds_dstr_dup(&connection->openssl_ciphers, &login->openssl_ciphers);
815 :
816 5180 : if (res && !tds_dstr_isempty(&login->server_spn))
817 0 : res = tds_dstr_dup(&connection->server_spn, &login->server_spn);
818 :
819 : /* copy other info not present in configuration file */
820 2590 : connection->capabilities = login->capabilities;
821 :
822 2590 : if (login->readonly_intent)
823 0 : connection->readonly_intent = login->readonly_intent;
824 :
825 2590 : connection->use_new_password = login->use_new_password;
826 :
827 2590 : if (login->use_ntlmv2_specified) {
828 0 : connection->use_ntlmv2_specified = login->use_ntlmv2_specified;
829 0 : connection->use_ntlmv2 = login->use_ntlmv2;
830 : }
831 :
832 2590 : if (login->enable_tls_v1_specified) {
833 0 : connection->enable_tls_v1_specified = login->enable_tls_v1_specified;
834 0 : connection->enable_tls_v1 = login->enable_tls_v1;
835 : }
836 :
837 2590 : if (login->enable_tls_v1_1_specified) {
838 0 : connection->enable_tls_v1_1_specified = login->enable_tls_v1_1_specified;
839 0 : connection->enable_tls_v1_1 = login->enable_tls_v1_1;
840 : }
841 :
842 2590 : if (res)
843 2590 : res = tds_dstr_dup(&connection->new_password, &login->new_password);
844 :
845 2590 : return res != NULL;
846 : }
847 :
848 : static bool
849 4584 : tds_config_env_tdsdump(TDSLOGIN * login)
850 : {
851 : tds_dir_char path[TDS_VECTOR_SIZE(pid_logpath) + 22];
852 :
853 4584 : tds_dir_char *s = tds_dir_getenv(TDS_DIR("TDSDUMP"));
854 4584 : if (!s)
855 : return true;
856 :
857 4 : if (!tds_dir_len(s)) {
858 0 : pid_t pid = getpid();
859 0 : tds_dir_snprintf(path, TDS_VECTOR_SIZE(path), pid_logpath, (int) pid);
860 0 : s = path;
861 : }
862 :
863 4 : if (!(s = tds_dir_dup(s)))
864 : return false;
865 4 : free(login->dump_file);
866 4 : login->dump_file = s;
867 :
868 4 : tdsdump_log(TDS_DBG_INFO1, "Setting 'dump_file' to '%" tdsPRIdir "' from $TDSDUMP.\n", login->dump_file);
869 : return true;
870 : }
871 :
872 : static void
873 4584 : tds_config_env_tdsport(TDSLOGIN * login)
874 : {
875 : char *s;
876 :
877 4584 : if ((s = getenv("TDSPORT"))) {
878 12 : login->port = tds_lookup_port(s);
879 12 : tds_dstr_empty(&login->instance_name);
880 12 : tdsdump_log(TDS_DBG_INFO1, "Setting 'port' to %s from $TDSPORT.\n", s);
881 : }
882 4584 : return;
883 : }
884 : static void
885 4584 : tds_config_env_tdsver(TDSLOGIN * login)
886 : {
887 : char *tdsver;
888 :
889 4584 : if ((tdsver = getenv("TDSVER"))) {
890 14 : TDS_USMALLINT *pver = tds_config_verstr(tdsver, login);
891 14 : tdsdump_log(TDS_DBG_INFO1, "TDS version %sset to %s from $TDSVER.\n", (pver? "":"not "), tdsver);
892 :
893 : }
894 4584 : return;
895 : }
896 :
897 : /* TDSHOST env var, pkleef@openlinksw.com 01/21/02 */
898 : static bool
899 4584 : tds_config_env_tdshost(TDSLOGIN * login)
900 : {
901 : const char *tdshost;
902 : char tmp[128];
903 : struct addrinfo *addrs;
904 :
905 4584 : if (!(tdshost = getenv("TDSHOST")))
906 : return true;
907 :
908 10 : if (TDS_FAILED(tds_lookup_host_set(tdshost, &login->ip_addrs))) {
909 0 : tdsdump_log(TDS_DBG_WARN, "Name resolution failed for '%s' from $TDSHOST.\n", tdshost);
910 : return false;
911 : }
912 :
913 10 : if (!tds_dstr_copy(&login->server_host_name, tdshost))
914 : return false;
915 20 : for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
916 10 : tdsdump_log(TDS_DBG_INFO1, "Setting IP Address to %s (%s) from $TDSHOST.\n",
917 : tds_addrinfo2str(addrs, tmp, sizeof(tmp)), tdshost);
918 : }
919 : return true;
920 : }
921 : #define TDS_FIND(k,b,c) tds_find(k, b, TDS_VECTOR_SIZE(b), sizeof(b[0]), c)
922 :
923 :
924 : static const void *
925 : tds_find(const void *key, const void *base, size_t nelem, size_t width,
926 : int (*compar)(const void *, const void *))
927 : {
928 : size_t n;
929 : const char *p = (const char*) base;
930 :
931 27434 : for (n = nelem; n != 0; --n) {
932 31252 : if (0 == compar(key, p))
933 : return p;
934 27434 : p += width;
935 : }
936 : return NULL;
937 : }
938 :
939 : struct tdsvername_t
940 : {
941 : const char name[6];
942 : TDS_USMALLINT version;
943 : };
944 :
945 : static int
946 31252 : tds_vername_cmp(const void *key, const void *pelem)
947 : {
948 31252 : return strcmp((const char *)key, ((const struct tdsvername_t *)pelem)->name);
949 : }
950 :
951 : /**
952 : * Set TDS version from given string
953 : * @param tdsver tds string version
954 : * @param login where to store information
955 : * @return as encoded hex value: high byte major, low byte minor.
956 : */
957 : TDS_USMALLINT *
958 3818 : tds_config_verstr(const char *tdsver, TDSLOGIN * login)
959 : {
960 : static const struct tdsvername_t tds_versions[] =
961 : { { "0", 0x000 }
962 : , {"auto", 0x000 }
963 : , { "4.2", 0x402 }
964 : , { "50", 0x500 }
965 : , { "5.0", 0x500 }
966 : , { "70", 0x700 }
967 : , { "7.0", 0x700 }
968 : , { "7.1", 0x701 }
969 : , { "7.2", 0x702 }
970 : , { "7.3", 0x703 }
971 : , { "7.4", 0x704 }
972 : , { "8.0", 0x800 }
973 : };
974 : const struct tdsvername_t *pver;
975 :
976 3818 : if (!login) {
977 0 : assert(login);
978 : return NULL;
979 : }
980 :
981 3818 : if ((pver = (const struct tdsvername_t *) TDS_FIND(tdsver, tds_versions, tds_vername_cmp)) == NULL) {
982 0 : tdsdump_log(TDS_DBG_INFO1, "error: no such version: %s\n", tdsver);
983 : return NULL;
984 : }
985 :
986 3818 : login->tds_version = pver->version;
987 3818 : tdsdump_log(TDS_DBG_INFO1, "Setting tds version to %s (0x%0x).\n", tdsver, pver->version);
988 :
989 3818 : return &login->tds_version;
990 : }
991 :
992 : /**
993 : * Set the full name of interface file
994 : * @param interf file name
995 : */
996 : TDSRET
997 20 : tds_set_interfaces_file_loc(const char *interf)
998 : {
999 20 : tds_dir_char *copy = NULL;
1000 :
1001 : /* If filename passed, copy filename */
1002 20 : if (interf != NULL && interf[0] != '\0') {
1003 10 : copy = tds_dir_from_cstr(interf);
1004 10 : if (copy == NULL)
1005 : return -TDSEMEM;
1006 : }
1007 :
1008 : /* Free it if already set */
1009 20 : free(interf_file);
1010 :
1011 : /* Set */
1012 20 : interf_file = copy;
1013 :
1014 20 : return TDS_SUCCESS;
1015 : }
1016 :
1017 : /**
1018 : * Get the IP address for a hostname. Store server's IP address
1019 : * in the string 'ip' in dotted-decimal notation. (The "hostname" might itself
1020 : * be a dotted-decimal address.
1021 : *
1022 : * If we can't determine the IP address then 'ip' will be set to empty
1023 : * string.
1024 : */
1025 : /* TODO callers seem to set always connection info... change it */
1026 : struct addrinfo *
1027 3830 : tds_lookup_host(const char *servername) /* (I) name of the server */
1028 : {
1029 3830 : struct addrinfo hints, *addr = NULL;
1030 3830 : assert(servername != NULL);
1031 :
1032 3830 : memset(&hints, '\0', sizeof(hints));
1033 : hints.ai_family = AF_UNSPEC;
1034 3830 : hints.ai_socktype = SOCK_STREAM;
1035 3830 : hints.ai_protocol = IPPROTO_TCP;
1036 :
1037 : #ifdef AI_ADDRCONFIG
1038 3830 : hints.ai_flags |= AI_ADDRCONFIG;
1039 3830 : switch (getaddrinfo(servername, NULL, &hints, &addr)) {
1040 3830 : case 0:
1041 3830 : return addr;
1042 0 : case EAI_FAMILY:
1043 : # ifdef EAI_ADDRFAMILY
1044 : case EAI_ADDRFAMILY:
1045 : # endif
1046 0 : hints.ai_flags &= ~AI_ADDRCONFIG;
1047 : break;
1048 : default:
1049 : return NULL;
1050 : }
1051 : #endif
1052 :
1053 0 : if (getaddrinfo(servername, NULL, &hints, &addr))
1054 : return NULL;
1055 0 : return addr;
1056 : }
1057 :
1058 : TDSRET
1059 3830 : tds_lookup_host_set(const char *servername, struct addrinfo **addr)
1060 : {
1061 : struct addrinfo *newaddr;
1062 3830 : assert(servername != NULL && addr != NULL);
1063 :
1064 3830 : if ((newaddr = tds_lookup_host(servername)) != NULL) {
1065 3830 : if (*addr != NULL)
1066 30 : freeaddrinfo(*addr);
1067 3830 : *addr = newaddr;
1068 3830 : return TDS_SUCCESS;
1069 : }
1070 : return TDS_FAIL;
1071 : }
1072 :
1073 : /**
1074 : * Given a portname lookup the port.
1075 : *
1076 : * If we can't determine the port number then return 0.
1077 : */
1078 : static int
1079 12 : tds_lookup_port(const char *portname)
1080 : {
1081 12 : int num = atoi(portname);
1082 12 : if (!num)
1083 2 : num = tds_getservice(portname);
1084 12 : return num;
1085 : }
1086 :
1087 : /* TODO same code in convert.c ?? */
1088 : static int
1089 : hexdigit(int c)
1090 : {
1091 0 : if (c >= '0' && c <= '9')
1092 0 : return c - '0';
1093 : /* ASCII optimization, 'A' -> 'a', 'a' -> 'a' */
1094 0 : c |= 0x20;
1095 0 : if (c >= 'a' && c <= 'f')
1096 0 : return c - 'a' + 10;
1097 : return 0; /* bad hex digit */
1098 : }
1099 :
1100 : static int
1101 0 : hex2num(char *hex)
1102 : {
1103 0 : return hexdigit(hex[0]) * 16 + hexdigit(hex[1]);
1104 : }
1105 :
1106 : /**
1107 : * Open and read the file 'file' searching for a logical server
1108 : * by the name of 'host'. If one is found then lookup
1109 : * the IP address and port number and store them in 'login'
1110 : *
1111 : * \param dir name of base directory for interface file
1112 : * \param file name of the interface file
1113 : * \param host logical host to search for
1114 : * \return false if not fount true if found
1115 : */
1116 : static bool
1117 0 : search_interface_file(TDSLOGIN * login, const tds_dir_char *dir, const tds_dir_char *file, const char *host)
1118 : {
1119 : tds_dir_char *pathname;
1120 : char line[255];
1121 : char tmp_ip[sizeof(line)];
1122 : char tmp_port[sizeof(line)];
1123 : char tmp_ver[sizeof(line)];
1124 : FILE *in;
1125 : char *field;
1126 0 : bool found = false;
1127 0 : bool server_found = false;
1128 : char *lasts;
1129 :
1130 0 : line[0] = '\0';
1131 0 : tmp_ip[0] = '\0';
1132 0 : tmp_port[0] = '\0';
1133 0 : tmp_ver[0] = '\0';
1134 :
1135 0 : tdsdump_log(TDS_DBG_INFO1, "Searching interfaces file %" tdsPRIdir "/%" tdsPRIdir ".\n", dir, file);
1136 :
1137 : /*
1138 : * create the full pathname to the interface file
1139 : */
1140 0 : if (file[0] == '\0') {
1141 0 : pathname = tds_dir_dup(TDS_DIR(""));
1142 : } else {
1143 0 : pathname = tds_join_path(dir, file);
1144 : }
1145 0 : if (!pathname)
1146 : return false;
1147 :
1148 : /*
1149 : * parse the interfaces file and find the server and port
1150 : */
1151 0 : if ((in = tds_dir_open(pathname, TDS_DIR("r"))) == NULL) {
1152 0 : tdsdump_log(TDS_DBG_INFO1, "Couldn't open %" tdsPRIdir ".\n", pathname);
1153 0 : free(pathname);
1154 0 : return false;
1155 : }
1156 0 : tdsdump_log(TDS_DBG_INFO1, "Interfaces file %" tdsPRIdir " opened.\n", pathname);
1157 :
1158 0 : while (fgets(line, sizeof(line) - 1, in)) {
1159 0 : if (line[0] == '#')
1160 0 : continue; /* comment */
1161 :
1162 0 : if (!TDS_ISSPACE(line[0])) {
1163 0 : field = strtok_r(line, "\n\t ", &lasts);
1164 0 : if (!strcmp(field, host)) {
1165 0 : found = true;
1166 0 : tdsdump_log(TDS_DBG_INFO1, "Found matching entry for host %s.\n", host);
1167 : } else
1168 : found = false;
1169 0 : } else if (found && TDS_ISSPACE(line[0])) {
1170 0 : field = strtok_r(line, "\n\t ", &lasts);
1171 0 : if (field != NULL && !strcmp(field, "query")) {
1172 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* tcp or tli */
1173 0 : if (!strcmp(field, "tli")) {
1174 0 : tdsdump_log(TDS_DBG_INFO1, "TLI service.\n");
1175 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* tcp */
1176 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* device */
1177 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* host/port */
1178 0 : if (strlen(field) >= 18) {
1179 0 : sprintf(tmp_port, "%d", hex2num(&field[6]) * 256 + hex2num(&field[8]));
1180 0 : sprintf(tmp_ip, "%d.%d.%d.%d", hex2num(&field[10]),
1181 : hex2num(&field[12]), hex2num(&field[14]), hex2num(&field[16]));
1182 0 : tdsdump_log(TDS_DBG_INFO1, "tmp_port = %s. tmp_ip = %s.\n", tmp_port, tmp_ip);
1183 : }
1184 : } else {
1185 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* ether */
1186 0 : strcpy(tmp_ver, field);
1187 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* host */
1188 0 : strcpy(tmp_ip, field);
1189 0 : tdsdump_log(TDS_DBG_INFO1, "host field %s.\n", tmp_ip);
1190 0 : field = strtok_r(NULL, "\n\t ", &lasts); /* port */
1191 0 : strcpy(tmp_port, field);
1192 : } /* else */
1193 : server_found = true;
1194 : } /* if */
1195 : } /* else if */
1196 : } /* while */
1197 0 : fclose(in);
1198 0 : free(pathname);
1199 :
1200 :
1201 : /*
1202 : * Look up the host and service
1203 : */
1204 0 : if (server_found) {
1205 :
1206 0 : if (TDS_SUCCEED(tds_lookup_host_set(tmp_ip, &login->ip_addrs))) {
1207 : struct addrinfo *addrs;
1208 0 : if (!tds_dstr_copy(&login->server_host_name, tmp_ip))
1209 : return false;
1210 0 : for (addrs = login->ip_addrs; addrs != NULL; addrs = addrs->ai_next) {
1211 0 : tdsdump_log(TDS_DBG_INFO1, "Resolved IP as '%s'.\n",
1212 : tds_addrinfo2str(login->ip_addrs, line, sizeof(line)));
1213 : }
1214 : } else {
1215 0 : tdsdump_log(TDS_DBG_WARN, "Name resolution failed for IP '%s'.\n", tmp_ip);
1216 : }
1217 :
1218 0 : if (tmp_port[0])
1219 0 : login->port = tds_lookup_port(tmp_port);
1220 0 : if (tmp_ver[0])
1221 0 : tds_config_verstr(tmp_ver, login);
1222 : }
1223 : return server_found;
1224 : } /* search_interface_file() */
1225 :
1226 : /**
1227 : * Try to find the IP number and port for a (possibly) logical server name.
1228 : *
1229 : * @note This function uses only the interfaces file and is deprecated.
1230 : */
1231 : static bool
1232 0 : tds_read_interfaces(const char *server, TDSLOGIN * login)
1233 : {
1234 0 : bool found = false;
1235 :
1236 : /* read $SYBASE/interfaces */
1237 :
1238 0 : if (!server || !server[0]) {
1239 0 : server = getenv("TDSQUERY");
1240 0 : if (!server || !server[0])
1241 0 : server = "SYBASE";
1242 0 : tdsdump_log(TDS_DBG_INFO1, "Setting server to %s from $TDSQUERY.\n", server);
1243 :
1244 : }
1245 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server %s....\n", server);
1246 :
1247 : /*
1248 : * Look for the server in the interf_file iff interf_file has been set.
1249 : */
1250 0 : if (interf_file) {
1251 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in file %" tdsPRIdir ".\n", interf_file);
1252 0 : found = search_interface_file(login, TDS_DIR(""), interf_file, server);
1253 : }
1254 :
1255 : /*
1256 : * if we haven't found the server yet then look for a $HOME/.interfaces file
1257 : */
1258 0 : if (!found) {
1259 0 : tds_dir_char *path = tds_get_home_file(TDS_DIR(".interfaces"));
1260 :
1261 0 : if (path) {
1262 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir ".\n", path);
1263 0 : found = search_interface_file(login, TDS_DIR(""), path, server);
1264 0 : free(path);
1265 : }
1266 : }
1267 :
1268 : /*
1269 : * if we haven't found the server yet then look in $SYBBASE/interfaces file
1270 : */
1271 0 : if (!found) {
1272 0 : const tds_dir_char *sybase = tds_dir_getenv(TDS_DIR("SYBASE"));
1273 : #ifdef __VMS
1274 : /* We've got to be in unix syntax for later slash-joined concatenation. */
1275 : #include <unixlib.h>
1276 : const char *unixspec = decc$translate_vms(sybase);
1277 : if ( (int)unixspec != 0 && (int)unixspec != -1 ) sybase = unixspec;
1278 : #endif
1279 0 : if (!sybase || !sybase[0])
1280 0 : sybase = interfaces_path;
1281 :
1282 0 : tdsdump_log(TDS_DBG_INFO1, "Looking for server in %" tdsPRIdir "/interfaces.\n", sybase);
1283 0 : found = search_interface_file(login, sybase, TDS_DIR("interfaces"), server);
1284 : }
1285 :
1286 : /*
1287 : * If we still don't have the server and port then assume the user
1288 : * typed an actual server host name.
1289 : */
1290 0 : if (!found) {
1291 : int ip_port;
1292 : const char *env_port;
1293 :
1294 : /*
1295 : * Make a guess about the port number
1296 : */
1297 :
1298 0 : if (login->port == 0) {
1299 : /*
1300 : * Not set in the [global] section of the
1301 : * configure file, take a guess.
1302 : */
1303 : ip_port = TDS_DEF_PORT;
1304 : } else {
1305 : /*
1306 : * Preserve setting from the [global] section
1307 : * of the configure file.
1308 : */
1309 0 : ip_port = login->port;
1310 : }
1311 0 : if ((env_port = getenv("TDSPORT")) != NULL) {
1312 0 : ip_port = tds_lookup_port(env_port);
1313 0 : tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %s from $TDSPORT.\n", env_port);
1314 : } else
1315 0 : tdsdump_log(TDS_DBG_INFO1, "Setting 'ip_port' to %d as a guess.\n", ip_port);
1316 :
1317 : /*
1318 : * look up the host
1319 : */
1320 :
1321 0 : if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs)))
1322 0 : if (!tds_dstr_copy(&login->server_host_name, server))
1323 : return false;
1324 :
1325 0 : if (ip_port)
1326 0 : login->port = ip_port;
1327 : }
1328 :
1329 : return found;
1330 : }
1331 :
1332 : /**
1333 : * Check the server name to find port info first
1334 : * Warning: connection-> & login-> are all modified when needed
1335 : * \return true when found, else false
1336 : */
1337 : static bool
1338 148 : parse_server_name_for_port(TDSLOGIN * connection, TDSLOGIN * login, bool update_server)
1339 : {
1340 : const char *pSep;
1341 : const char *server;
1342 :
1343 : /* seek the ':' in login server_name */
1344 296 : server = tds_dstr_cstr(&login->server_name);
1345 :
1346 : /* IPv6 address can be quoted */
1347 148 : if (server[0] == '[') {
1348 40 : pSep = strstr(server, "]:");
1349 40 : if (pSep)
1350 20 : ++pSep;
1351 : } else {
1352 108 : pSep = strrchr(server, ':');
1353 : }
1354 :
1355 148 : if (pSep && pSep != server) { /* yes, i found it! */
1356 : /* modify connection-> && login->server_name & ->port */
1357 168 : login->port = connection->port = atoi(pSep + 1);
1358 84 : tds_dstr_empty(&connection->instance_name);
1359 : } else {
1360 : /* handle instance name */
1361 64 : pSep = strrchr(server, '\\');
1362 64 : if (!pSep || pSep == server)
1363 : return false;
1364 :
1365 64 : if (!tds_dstr_copy(&connection->instance_name, pSep + 1))
1366 : return false;
1367 64 : connection->port = 0;
1368 : }
1369 :
1370 148 : if (!update_server)
1371 : return true;
1372 :
1373 74 : if (server[0] == '[' && pSep > server && pSep[-1] == ']') {
1374 20 : server++;
1375 20 : pSep--;
1376 : }
1377 74 : if (!tds_dstr_copyn(&connection->server_name, server, pSep - server))
1378 : return false;
1379 :
1380 74 : return true;
1381 : }
1382 :
1383 : /**
1384 : * Return a structure capturing the compile-time settings provided to the
1385 : * configure script.
1386 : */
1387 :
1388 : const TDS_COMPILETIME_SETTINGS *
1389 0 : tds_get_compiletime_settings(void)
1390 : {
1391 : static const TDS_COMPILETIME_SETTINGS settings = {
1392 : TDS_VERSION_NO
1393 : , FREETDS_SYSCONFDIR
1394 : , "unknown" /* need fancy script in makefile */
1395 : # if TDS50
1396 : , "5.0"
1397 : # elif TDS71
1398 : , "7.1"
1399 : # elif TDS72
1400 : , "7.2"
1401 : # elif TDS73
1402 : , "7.3"
1403 : # elif TDS74
1404 : , "7.4"
1405 : # else
1406 : , "auto"
1407 : # endif
1408 : # ifdef MSDBLIB
1409 : , true
1410 : # else
1411 : , false
1412 : # endif
1413 : # ifdef TDS_SYBASE_COMPAT
1414 : , true
1415 : # else
1416 : , false
1417 : # endif
1418 : # ifdef _REENTRANT
1419 : , true
1420 : # else
1421 : , false
1422 : # endif
1423 : # ifdef HAVE_ICONV
1424 : , true
1425 : # else
1426 : , false
1427 : # endif
1428 : # ifdef IODBC
1429 : , true
1430 : # else
1431 : , false
1432 : # endif
1433 : # ifdef UNIXODBC
1434 : , true
1435 : # else
1436 : , false
1437 : # endif
1438 : # ifdef HAVE_OPENSSL
1439 : , true
1440 : # else
1441 : , false
1442 : # endif
1443 : # ifdef HAVE_GNUTLS
1444 : , true
1445 : # else
1446 : , false
1447 : # endif
1448 : # if ENABLE_ODBC_MARS
1449 : , true
1450 : # else
1451 : , false
1452 : # endif
1453 : # ifdef HAVE_SSPI
1454 : , true
1455 : # else
1456 : , false
1457 : # endif
1458 : # ifdef ENABLE_KRB5
1459 : , true
1460 : # else
1461 : , false
1462 : # endif
1463 : };
1464 :
1465 : assert(settings.tdsver);
1466 :
1467 0 : return &settings;
1468 : }
1469 :
1470 : /**
1471 : * Make sure proper setting are in place for TDS 8.0
1472 : */
1473 : TDSRET
1474 3660 : tds8_adjust_login(TDSLOGIN *login)
1475 : {
1476 : /* TDS 8.0 requires TDS_ENCRYPTION_STRICT (entire-connection encryption) */
1477 3660 : if (IS_TDS80_PLUS(login))
1478 0 : login->encryption_level = TDS_ENCRYPTION_STRICT;
1479 :
1480 3660 : if (login->encryption_level == TDS_ENCRYPTION_STRICT) {
1481 : /* TDS 5.0 (Sybase/SAP ASE) it is optional; but TDS 7.x does not allow it.
1482 : * So, try 8.0 unless they specifically requested 5.0 */
1483 0 : if (!IS_TDS50(login) && !IS_TDS80_PLUS(login))
1484 0 : login->tds_version = 0x800;
1485 :
1486 : /* Validate server certificate signature to reduce risk of MITM attacks */
1487 0 : if (tds_dstr_isempty(&login->cafile))
1488 0 : if (!tds_dstr_copy(&login->cafile, "system"))
1489 : return -TDSEMEM;
1490 : }
1491 :
1492 : return TDS_SUCCESS;
1493 : }
1494 :
1495 : /** @} */
|