27 RCSID(
"$Id: 340d22351adbdd015eebfe1c32548ce3854d8c62 $")
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
36 #ifdef HAVE_MYSQL_MYSQL_H
37 # include <mysql/mysql_version.h>
38 # include <mysql/errmsg.h>
39 # include <mysql/mysql.h>
40 # include <mysql/mysqld_error.h>
41 #elif defined(HAVE_MYSQL_H)
42 # include <mysql_version.h>
45 # include <mysqld_error.h>
110 DEBUG2(
"rlm_sql_mysql: Socket destructor called, closing socket");
113 mysql_close(conn->
sock);
121 if (--mysql_instance_count == 0) mysql_library_end();
131 static bool version_done =
false;
136 INFO(
"rlm_sql_mysql: libmysql version: %s", mysql_get_client_info());
139 if (mysql_instance_count == 0) {
140 if (mysql_library_init(0, NULL, NULL)) {
141 ERROR(
"rlm_sql_mysql: libmysql initialisation failed");
146 mysql_instance_count++;
157 ERROR(
"rlm_sql_mysql: Invalid warnings value \"%s\", must be yes, no, or auto", driver->
warnings_str);
169 unsigned int connect_timeout = timeout->tv_usec;
170 unsigned long sql_flags;
175 DEBUG(
"rlm_sql_mysql: Starting connect to MySQL server");
177 mysql_init(&(conn->
db));
191 mysql_options(&(conn->
db), MYSQL_READ_DEFAULT_GROUP,
"freeradius");
197 #ifdef MYSQL_OPT_RECONNECT
199 my_bool reconnect = 0;
200 mysql_options(&(conn->
db), MYSQL_OPT_RECONNECT, &reconnect);
204 #if (MYSQL_VERSION_ID >= 50000)
205 mysql_options(&(conn->
db), MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
229 mysql_options(&(conn->
db), MYSQL_OPT_READ_TIMEOUT, &read_timeout);
230 mysql_options(&(conn->
db), MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
234 #if (MYSQL_VERSION_ID >= 40100)
235 sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
237 sql_flags = CLIENT_FOUND_ROWS;
240 #ifdef CLIENT_MULTI_STATEMENTS
241 sql_flags |= CLIENT_MULTI_STATEMENTS;
243 conn->
sock = mysql_real_connect(&(conn->
db),
252 ERROR(
"rlm_sql_mysql: Couldn't connect to MySQL server %s@%s:%s", config->
sql_login,
254 ERROR(
"rlm_sql_mysql: MySQL error: %s", mysql_error(&conn->
db));
260 DEBUG2(
"rlm_sql_mysql: Connected to database '%s' on %s, server version %s, protocol version %i",
261 config->
sql_db, mysql_get_host_info(conn->
sock),
262 mysql_get_server_info(conn->
sock), mysql_get_proto_info(conn->
sock));
281 if (server) sql_errno = mysql_errno(server);
282 if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
284 if (sql_errno > 0)
switch (sql_errno) {
285 case CR_SERVER_GONE_ERROR:
290 case CR_OUT_OF_MEMORY:
291 case CR_COMMANDS_OUT_OF_SYNC:
292 case CR_UNKNOWN_ERROR:
308 case ER_NO_REFERENCED_ROW:
309 case ER_ROW_IS_REFERENCED:
310 #ifdef ER_FOREIGN_DUPLICATE_KEY
311 case ER_FOREIGN_DUPLICATE_KEY:
313 #ifdef ER_DUP_ENTRY_WITH_KEY_NAME
314 case ER_DUP_ENTRY_WITH_KEY_NAME:
316 #ifdef ER_NO_REFERENCED_ROW_2
317 case ER_NO_REFERENCED_ROW_2:
319 #ifdef ER_ROW_IS_REFERENCED_2
320 case ER_ROW_IS_REFERENCED_2:
328 case ER_BAD_NULL_ERROR:
329 case ER_NON_UNIQ_ERROR:
344 ERROR(
"rlm_sql_mysql: Socket not connected");
348 mysql_query(conn->
sock, query);
355 info = mysql_info(conn->
sock);
356 if (info)
DEBUG2(
"rlm_sql_mysql: %s", info);
368 ERROR(
"rlm_sql_mysql: Socket not connected");
373 if (!(conn->
result = mysql_store_result(conn->
sock))) {
376 #if (MYSQL_VERSION_ID >= 40100)
377 ret = mysql_next_result(conn->
sock);
380 goto retry_store_result;
392 #if MYSQL_VERSION_ID >= 32224
393 if (!(num = mysql_field_count(conn->
sock))) {
395 if (!(num = mysql_num_fields(conn->
sock))) {
406 rcode =
sql_query(handle, config, query);
430 return mysql_num_rows(conn->
result);
440 unsigned int fields, i;
441 MYSQL_FIELD *field_info;
444 fields = mysql_num_fields(conn->
result);
451 field_info = mysql_fetch_fields(conn->
result);
454 MEM(names = talloc_array(handle,
char const *, fields));
456 for (i = 0; i < fields; i++) names[i] = field_info[i].
name;
476 *out = handle->
row = mysql_fetch_row(conn->
result);
481 #if (MYSQL_VERSION_ID >= 40100)
484 ret = mysql_next_result(conn->
sock);
488 goto retry_fetch_row;
501 mysql_free_result(conn->
result);
532 unsigned int num_fields;
535 if (outlen == 0)
return 0;
541 if (mysql_query(conn->
sock,
"SHOW WARNINGS") != 0)
return -1;
542 result = mysql_store_result(conn->
sock);
543 if (!result)
return -1;
548 num_fields = mysql_field_count(conn->
sock);
549 if (num_fields < 3) {
550 WARN(
"rlm_sql_mysql: Failed retrieving warnings, expected 3 fields got %u", num_fields);
551 mysql_free_result(result);
556 while ((row = mysql_fetch_row(result))) {
568 msg = talloc_asprintf(ctx,
"%s: %s", row[1], row[2]);
571 if (++i == outlen)
break;
574 mysql_free_result(result);
601 error = mysql_error(conn->
sock);
606 if (error && (error[0] !=
'\0')) {
607 error = talloc_asprintf(ctx,
"ERROR %u (%s): %s", mysql_errno(conn->
sock), error,
608 mysql_sqlstate(conn->
sock));
624 msgs = mysql_warning_count(conn->
sock);
626 DEBUG3(
"rlm_sql_mysql: No additional diagnostic info on server");
632 ret =
sql_warnings(ctx, out, outlen - 1, handle, config);
633 if (ret > 0) i += ret;
662 #if (MYSQL_VERSION_ID >= 40100)
676 if (conn->
result == NULL) {
677 result = mysql_store_result(conn->
sock);
678 if (result) mysql_free_result(result);
697 while (((ret = mysql_next_result(conn->
sock)) == 0) &&
698 (result = mysql_store_result(conn->
sock))) {
699 mysql_free_result(result);
710 return mysql_affected_rows(conn->
sock);
721 if ((inlen * 2 + 1) > outlen)
return 0;
723 if ((inlen * 2 + 1) <= inlen)
return 0;
725 return mysql_real_escape_string(conn->
sock, out, in, inlen);
732 .
name =
"rlm_sql_mysql",
static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
#define PW_TYPE_FILE_INPUT
File matching value must exist, and must be readable.
rlm_sql_mysql_warnings warnings
mysql_warning_count() doesn't appear to work with NDB cluster
General connection/server error.
static int _sql_socket_destructor(rlm_sql_mysql_conn_t *conn)
Only displayed when debugging is enabled.
Prototypes and functions for the SQL module.
log_type_t type
Type of log entry L_ERR, L_WARN, L_INFO, L_DBG etc..
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Finish query.
char const * tls_private_key_file
Private key for the certificate we present to the server.
static int _mod_destructor(UNUSED rlm_sql_mysql_config_t *driver)
char const * msg
Log message.
#define CONF_PARSER_TERMINATOR
char const * sql_server
Server to connect to.
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
static sql_rcode_t sql_free_result(rlm_sql_handle_t *, rlm_sql_config_t *)
char const * warnings_str
Whether we always query the server for additional warnings.
#define PW_TYPE_SUBSECTION
Defines a CONF_PAIR to C data type mapping.
Key constraint violation.
static sql_rcode_t sql_store_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
Stale connection, should reconnect.
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config, struct timeval const *timeout)
uint32_t sql_port
Port to connect to.
int cf_section_parse(CONF_SECTION *, void *base, CONF_PARSER const *variables)
Parse a configuration section into user-supplied variables.
static CONF_PARSER tls_config[]
static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
char const * tls_ca_path
Directory containing CAs that may be used to validate the servers certificate.
char const * tls_ca_file
Path to the CA used to validate the server's certificate.
static sql_rcode_t sql_fetch_row(rlm_sql_row_t *out, rlm_sql_handle_t *handle, rlm_sql_config_t *config)
char const * sql_password
Login password to use.
void * driver
Where drivers should write a pointer to their configurations.
static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
void * conn
Database specific connection handle.
int strcasecmp(char *s1, char *s2)
static sql_rcode_t sql_check_error(MYSQL *server, int client_errno)
Analyse the last error that occurred on the socket, and determine an action.
rlm_sql_row_t row
Row data from the last query.
static int mysql_instance_count
rlm_sql_module_t rlm_sql_mysql
static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
static const FR_NAME_NUMBER server_warnings_table[]
static const CONF_PARSER driver_config[]
#define FR_CONF_OFFSET(_n, _t, _s, _f)
char const * sql_login
Login credentials to use.
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Retrieves any errors associated with the connection handle.
uint32_t query_timeout
How long to allow queries to run for.
char const * sql_db
Database to run queries against.
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
String of printable characters.
#define FR_CONF_POINTER(_n, _t, _p)
static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
Retrieves any warnings associated with the last query.
struct rlm_sql_mysql_conn rlm_sql_mysql_conn_t
struct rlm_sql_mysql_config rlm_sql_mysql_config_t
static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, void *arg)
char const * tls_certificate_file
Public certificate we present to the server.