Skip to content

Commit a3b4957

Browse files
committed
WL#13905: libmysql support for DNS SRV
RB#24157
1 parent 8935dd0 commit a3b4957

File tree

12 files changed

+555
-9
lines changed

12 files changed

+555
-9
lines changed

client/mysql.cc

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ static int connect_flag = CLIENT_INTERACTIVE;
179179
static bool opt_binary_mode = false;
180180
static bool opt_connect_expired_password = false;
181181
static char *current_host;
182+
static char *dns_srv_host;
182183
static char *current_db;
183184
static char *current_user = nullptr;
184185
static char *opt_password = nullptr;
@@ -1479,6 +1480,7 @@ void mysql_end(int sig) {
14791480
my_free(opt_mysql_unix_port);
14801481
my_free(current_db);
14811482
my_free(current_host);
1483+
my_free(dns_srv_host);
14821484
my_free(current_user);
14831485
my_free(full_username);
14841486
my_free(part_username);
@@ -1567,8 +1569,15 @@ static void kill_query(const char *reason) {
15671569
}
15681570
#endif
15691571

1570-
if (!mysql_real_connect(kill_mysql, current_host, current_user, opt_password,
1571-
"", opt_mysql_port, opt_mysql_unix_port, 0)) {
1572+
MYSQL *ret;
1573+
if (dns_srv_host)
1574+
ret = mysql_real_connect_dns_srv(kill_mysql, dns_srv_host, current_user,
1575+
opt_password, "", 0);
1576+
else
1577+
ret =
1578+
mysql_real_connect(kill_mysql, current_host, current_user, opt_password,
1579+
"", opt_mysql_port, opt_mysql_unix_port, 0);
1580+
if (!ret) {
15721581
#ifdef HAVE_SETNS
15731582
if (opt_network_namespace) (void)restore_original_network_namespace();
15741583
#endif
@@ -1732,6 +1741,9 @@ static struct my_option my_long_options[] = {
17321741
nullptr, GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
17331742
{"host", 'h', "Connect to host.", &current_host, &current_host, nullptr,
17341743
GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, nullptr, 0, nullptr},
1744+
{"dns_srv_host", -1, "Connect to a DNS SRV resource", &dns_srv_host,
1745+
&dns_srv_host, nullptr, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, nullptr, 0,
1746+
nullptr},
17351747
{"html", 'H', "Produce HTML output.", &opt_html, &opt_html, nullptr,
17361748
GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
17371749
{"xml", 'X', "Produce XML output.", &opt_xml, &opt_xml, nullptr, GET_BOOL,
@@ -4115,6 +4127,8 @@ static int com_connect(String *buffer, char *line) {
41154127
if (tmp) {
41164128
my_free(current_host);
41174129
current_host = my_strdup(PSI_NOT_INSTRUMENTED, tmp, MYF(MY_WME));
4130+
my_free(dns_srv_host);
4131+
dns_srv_host = nullptr;
41184132
}
41194133
} else {
41204134
/* Quick re-connect */
@@ -4458,10 +4472,16 @@ static int sql_real_connect(char *host, char *database, char *user,
44584472
return ignore_errors ? -1 : 1; // Abort
44594473
}
44604474
#endif
4461-
4462-
if (!mysql_real_connect(&mysql, host, user, password, database,
4463-
opt_mysql_port, opt_mysql_unix_port,
4464-
connect_flag | CLIENT_MULTI_STATEMENTS)) {
4475+
MYSQL *ret;
4476+
if (dns_srv_host)
4477+
ret = mysql_real_connect_dns_srv(&mysql, dns_srv_host, user, password,
4478+
database,
4479+
connect_flag | CLIENT_MULTI_STATEMENTS);
4480+
else
4481+
ret = mysql_real_connect(&mysql, host, user, password, database,
4482+
opt_mysql_port, opt_mysql_unix_port,
4483+
connect_flag | CLIENT_MULTI_STATEMENTS);
4484+
if (!ret) {
44654485
#ifdef HAVE_SETNS
44664486
if (opt_network_namespace) (void)restore_original_network_namespace();
44674487
#endif

config.h.cmake

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,4 +339,9 @@
339339

340340
#define SO_EXT "@CMAKE_SHARED_MODULE_SUFFIX@"
341341

342+
343+
/* From libmysql/CMakeLists.txt */
344+
#cmakedefine HAVE_UNIX_DNS_SRV @HAVE_UNIX_DNS_SRV@
345+
#cmakedefine HAVE_WIN32_DNS_SRV @HAVE_WIN32_DNS_SRV@
346+
342347
#endif

include/errmsg.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ extern const char *client_errors[]; /* Error messages */
128128
#define CR_KERBEROS_USER_NOT_FOUND 2067
129129
#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
130130
#define CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL 2069
131-
#define CR_ERROR_LAST /*Copy last error nr:*/ 2069
131+
#define CR_DNS_SRV_LOOKUP_FAILED 2070
132+
#define CR_ERROR_LAST /*Copy last error nr:*/ 2070
132133
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
133134

134135
/* Visual Studio requires '__inline' for C code */

include/mysql.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,12 @@ void STDCALL mysql_reset_server_public_key(void);
786786

787787
#define HAVE_MYSQL_REAL_CONNECT
788788

789+
MYSQL *STDCALL mysql_real_connect_dns_srv(MYSQL *mysql,
790+
const char *dns_srv_name,
791+
const char *user, const char *passwd,
792+
const char *db,
793+
unsigned long client_flag);
794+
789795
#ifdef __cplusplus
790796
}
791797
#endif

include/mysql.h.pp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@
359359
void finish_client_errs(void);
360360
extern const char *client_errors[];
361361
static inline const char *ER_CLIENT(int client_errno) {
362-
if (client_errno >= 2000 && client_errno <= 2069)
362+
if (client_errno >= 2000 && client_errno <= 2070)
363363
return client_errors[client_errno - 2000];
364364
return client_errors[2000];
365365
}
@@ -805,3 +805,8 @@
805805
int mysql_stmt_next_result(MYSQL_STMT *stmt);
806806
void mysql_close(MYSQL *sock);
807807
void mysql_reset_server_public_key(void);
808+
MYSQL * mysql_real_connect_dns_srv(MYSQL *mysql,
809+
const char *dns_srv_name,
810+
const char *user, const char *passwd,
811+
const char *db,
812+
unsigned long client_flag);

libmysql/CMakeLists.txt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ SET(CLIENT_API_FUNCTIONS
139139
mysql_thread_safe
140140
mysql_use_result
141141
mysql_warning_count
142-
142+
mysql_real_connect_dns_srv
143143
CACHE INTERNAL "Functions exported by client API"
144144
)
145145

@@ -177,6 +177,7 @@ SET(CLIENT_API_NONBLOCKING_FUNCTIONS
177177
SET(CLIENT_SOURCES
178178
libmysql.cc
179179
errmsg.cc
180+
dns_srv.cc
180181
../sql-common/client.cc
181182
../sql-common/client_plugin.cc
182183
../sql-common/client_authentication.cc
@@ -232,6 +233,31 @@ ENDIF()
232233

233234
LIST(APPEND LIBS_TO_LINK ${SSL_LIBRARIES})
234235

236+
UNSET(HAVE_WIN32_DNS_SRV)
237+
UNSET(HAVE_UNIX_DNS_SRV)
238+
SET(HAVE_DNS_SRV 0)
239+
IF(WIN32)
240+
LIST(APPEND LIBS_TO_LINK dnsapi)
241+
SET(HAVE_WIN32_DNS_SRV 1 PARENT_SCOPE)
242+
SET(HAVE_DNS_SRV 1)
243+
MESSAGE(STATUS "Found Win32 DNS SRV APIs")
244+
ELSEIF(FREEBSD)
245+
SET(HAVE_DNS_SRV 1)
246+
SET(HAVE_UNIX_DNS_SRV 1 PARENT_SCOPE)
247+
MESSAGE(STATUS "BSD built in DNS SRV APIs")
248+
ELSE()
249+
FIND_LIBRARY(RESOLV_LIBRARY NAMES resolv)
250+
IF (RESOLV_LIBRARY)
251+
LIST(APPEND LIBS_TO_LINK ${RESOLV_LIBRARY})
252+
SET(HAVE_UNIX_DNS_SRV 1 PARENT_SCOPE)
253+
SET(HAVE_DNS_SRV 1)
254+
MESSAGE(STATUS "Found Unix DNS SRV APIs")
255+
ENDIF()
256+
ENDIF()
257+
258+
IF(HAVE_DNS_SRV EQUAL 0)
259+
MESSAGE(FATAL_ERROR "Can't find neither Win32 nor Unix DNS SRV APIs")
260+
ENDIF()
235261
#
236262
# On Windows platform client library includes the client-side
237263
# Windows Native Authentication plugin.

libmysql/dns_srv.cc

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
3+
This program is free software; you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License, version 2.0,
5+
as published by the Free Software Foundation.
6+
7+
This program is also distributed with certain software (including
8+
but not limited to OpenSSL) that is licensed under separate terms,
9+
as designated in a particular file or component or in included license
10+
documentation. The authors of MySQL hereby grant you an additional
11+
permission to link the program and your derivative works with the
12+
separately licensed software that they have included with MySQL.
13+
14+
Without limiting anything contained in the foregoing, this file,
15+
which is part of C Driver for MySQL (Connector/C), is also subject to the
16+
Universal FOSS Exception, version 1.0, a copy of which can be found at
17+
http://oss.oracle.com/licenses/universal-foss-exception.
18+
19+
This program is distributed in the hope that it will be useful,
20+
but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
GNU General Public License, version 2.0, for more details.
23+
24+
You should have received a copy of the GNU General Public License
25+
along with this program; if not, write to the Free Software
26+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27+
28+
#include <my_config.h>
29+
#include <sql_common.h>
30+
#include "dns_srv_data.h"
31+
32+
#ifdef HAVE_UNIX_DNS_SRV
33+
34+
#ifdef __FreeBSD__
35+
#include <netinet/in.h>
36+
#endif
37+
#include <netdb.h>
38+
#include <resolv.h>
39+
40+
// POSIX version
41+
42+
static bool get_dns_srv(Dns_srv_data &data, const char *dnsname, int &error) {
43+
struct __res_state state {};
44+
res_ninit(&state);
45+
unsigned char query_buffer[NS_PACKETSZ];
46+
bool ret = true;
47+
48+
data.clear();
49+
50+
int res = res_nsearch(&state, dnsname, ns_c_in, ns_t_srv, query_buffer,
51+
sizeof(query_buffer));
52+
53+
if (res >= 0) {
54+
ns_msg msg;
55+
ns_initparse(query_buffer, res, &msg);
56+
57+
for (int x = 0; x < ns_msg_count(msg, ns_s_an); x++) {
58+
// Get next DNS SRV record and its data
59+
60+
ns_rr rr;
61+
ns_parserr(&msg, ns_s_an, x, &rr);
62+
const unsigned char *srv_data = ns_rr_rdata(rr);
63+
64+
// Read port, priority and weight.
65+
// Note: Each NS_GET16 call moves srv_data to next value
66+
67+
uint16_t port, prio, weight;
68+
69+
NS_GET16(prio, srv_data);
70+
NS_GET16(weight, srv_data);
71+
NS_GET16(port, srv_data);
72+
73+
// Read host name
74+
75+
char name_buffer[NS_MAXDNAME];
76+
77+
dn_expand(ns_msg_base(msg), ns_msg_end(msg), srv_data, name_buffer,
78+
sizeof(name_buffer));
79+
data.add(name_buffer, port, prio, weight);
80+
}
81+
ret = false;
82+
} else {
83+
ret = true;
84+
error = h_errno;
85+
}
86+
87+
res_nclose(&state);
88+
return ret;
89+
}
90+
91+
#elif defined(HAVE_WIN32_DNS_SRV)
92+
#include <windns.h>
93+
#include <winsock2.h>
94+
95+
// Windows version
96+
97+
static bool get_dns_srv(Dns_srv_data &data, const char *dnsname, int &error) {
98+
DNS_STATUS status; // Return value of DnsQuery_A() function.
99+
PDNS_RECORD pDnsRecord = nullptr; // Pointer to DNS_RECORD structure.
100+
101+
data.clear();
102+
status = DnsQuery(dnsname, DNS_TYPE_SRV, DNS_QUERY_STANDARD, nullptr,
103+
&pDnsRecord, nullptr);
104+
105+
if (status == ERROR_SUCCESS) {
106+
// Iterate over linked list of DNS records
107+
108+
PDNS_RECORD pRecord = pDnsRecord;
109+
while (pRecord) {
110+
if (pRecord->wType == DNS_TYPE_SRV) {
111+
data.add(pRecord->Data.Srv.pNameTarget, pRecord->Data.Srv.wPort,
112+
pRecord->Data.Srv.wPriority, pRecord->Data.Srv.wWeight);
113+
}
114+
pRecord = pRecord->pNext;
115+
}
116+
117+
DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep);
118+
} else
119+
error = status;
120+
return status != ERROR_SUCCESS;
121+
}
122+
#else
123+
124+
#error "No DNS SRV Support detected for your OS. Consider adjusting Cmake."
125+
126+
#if 0
127+
// dummy function returning an error in case it's not supported by the OS
128+
129+
static bool get_dns_srv(Dns_srv_data &data, const char *dnsname, int &error) {
130+
error = -1; // set a special error code for not supported
131+
return true;
132+
}
133+
#endif
134+
#endif
135+
136+
/**
137+
Connect to a server using a DNS SRV name
138+
139+
See rfc2782 for what a DNS SRV is and how is one read
140+
141+
@param mysql a MySQL handle to use
142+
@param dns_srv_name the name of the DNS SRV resource to query. ANSI
143+
@param user the user name to pass to @ref mysql_real_connect
144+
@param passwd the password to pass to @ref mysql_real_connect
145+
@param db the database to pass to @ref mysql_real_connect
146+
@param client_flag the client flag to pass to @ref mysql_real_connect
147+
148+
@retval NULL an error has occurred
149+
@retval non-NULL the connected MySQL handle to use
150+
151+
If the OS doesn't support it the function returns OS error -1.
152+
153+
SRV FORMAT:
154+
_service._proto.name. TTL class SRV priority weight port target.
155+
156+
Example:
157+
_sip._tcp.example.com. 86400 IN SRV 0 5 5060 sipserver.example.com.
158+
159+
@sa mysql_real_connect
160+
*/
161+
MYSQL *STDCALL mysql_real_connect_dns_srv(MYSQL *mysql,
162+
const char *dns_srv_name,
163+
const char *user, const char *passwd,
164+
const char *db,
165+
unsigned long client_flag) {
166+
Dns_srv_data data;
167+
int err = 0;
168+
169+
if (get_dns_srv(data, dns_srv_name, err)) {
170+
set_mysql_extended_error(mysql, CR_DNS_SRV_LOOKUP_FAILED, unknown_sqlstate,
171+
ER_CLIENT(CR_DNS_SRV_LOOKUP_FAILED), err);
172+
return nullptr;
173+
}
174+
175+
std::string host;
176+
uint port;
177+
while (!data.pop_next(host, port)) {
178+
MYSQL *ret =
179+
mysql_real_connect(mysql, host.c_str(), user, passwd, db, port, NULL,
180+
client_flag | CLIENT_REMEMBER_OPTIONS);
181+
if (ret) return ret;
182+
}
183+
return nullptr;
184+
}

0 commit comments

Comments
 (0)