The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_mysql.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 4043b4070f030956dbd9e6236ae01db34b3d3ba2 $
19  * @file rlm_sql_mysql.c
20  * @brief MySQL driver.
21  *
22  * @copyright 2014-2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @copyright 2000-2007,2015 The FreeRADIUS server project
24  * @copyright 2000 Mike Machado (mike@innercite.com)
25  * @copyright 2000 Alan DeKok (aland@freeradius.org)
26  */
27 RCSID("$Id: 4043b4070f030956dbd9e6236ae01db34b3d3ba2 $")
28 
29 #define LOG_PREFIX "sql - mysql"
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/util/debug.h>
33 
34 #include <sys/stat.h>
35 
36 #include "config.h"
37 
38 #ifdef HAVE_MYSQL_MYSQL_H
39 # include <mysql/errmsg.h>
40 DIAG_OFF(strict-prototypes) /* Seen with homebrew mysql client 5.7.13 */
41 # include <mysql/mysql.h>
42 DIAG_ON(strict-prototypes)
43 # include <mysql/mysqld_error.h>
44 #elif defined(HAVE_MYSQL_H)
45 # include <errmsg.h>
46 DIAG_OFF(strict-prototypes) /* Seen with homebrew mysql client 5.7.13 */
47 # include <mysql.h>
48 DIAG_ON(strict-prototypes)
49 # include <mysqld_error.h>
50 #endif
51 
52 #include "rlm_sql.h"
53 
54 typedef enum {
59 
61  { L("auto"), SERVER_WARNINGS_AUTO },
62  { L("no"), SERVER_WARNINGS_NO },
63  { L("yes"), SERVER_WARNINGS_YES }
64 };
66 
67 typedef struct {
68  MYSQL db; //!< Structure representing connection details.
69  MYSQL *sock; //!< Connection details as returned by connection init functions.
70  MYSQL_RES *result; //!< Result from most recent query.
71  connection_t *conn; //!< Generic connection structure for this connection.
72  int fd; //!< fd for this connection's I/O events.
73  fr_sql_query_t *query_ctx; //!< Current query running on this connection.
74  int status; //!< returned by the most recent non-blocking function call.
76 
77 typedef struct {
78  char const *tls_ca_file; //!< Path to the CA used to validate the server's certificate.
79  char const *tls_ca_path; //!< Directory containing CAs that may be used to validate the
80  //!< servers certificate.
81  char const *tls_certificate_file; //!< Public certificate we present to the server.
82  char const *tls_private_key_file; //!< Private key for the certificate we present to the server.
83 
84  char const *tls_crl_file; //!< Public certificate we present to the server.
85  char const *tls_crl_path; //!< Private key for the certificate we present to the server.
86 
87  char const *tls_cipher; //!< Colon separated list of TLS ciphers for TLS <= 1.2.
88 
89  bool tls_required; //!< Require that the connection is encrypted.
90  bool tls_check_cert; //!< Verify there's a trust relationship between the server's
91  ///< cert and one of the CAs we have configured.
92  bool tls_check_cert_cn; //!< Verify that the CN in the server cert matches the host
93  ///< we passed to mysql_real_connect().
94 
95  char const *warnings_str; //!< Whether we always query the server for additional warnings.
96  rlm_sql_mysql_warnings warnings; //!< mysql_warning_count() doesn't
97  //!< appear to work with NDB cluster
99 
101  { FR_CONF_OFFSET_FLAGS("ca_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_ca_file) },
102  { FR_CONF_OFFSET_FLAGS("ca_path", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_ca_path) },
103  { FR_CONF_OFFSET_FLAGS("certificate_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_certificate_file) },
104  { FR_CONF_OFFSET_FLAGS("private_key_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_private_key_file) },
105  { FR_CONF_OFFSET_FLAGS("crl_file", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_crl_file) },
106  { FR_CONF_OFFSET_FLAGS("crl_path", CONF_FLAG_FILE_INPUT, rlm_sql_mysql_t, tls_crl_path) },
107  /*
108  * MySQL Specific TLS attributes
109  */
110  { FR_CONF_OFFSET("cipher", rlm_sql_mysql_t, tls_cipher) },
111 
112  /*
113  * The closest thing we have to these options in other modules is
114  * in rlm_rest. rlm_ldap has its own bizarre option set.
115  *
116  * There, the options can be toggled independently, here they can't
117  * but for consistency we break them out anyway, and warn if the user
118  * has provided an invalid list of flags.
119  */
120  { FR_CONF_OFFSET("tls_required", rlm_sql_mysql_t, tls_required) },
121  { FR_CONF_OFFSET("check_cert", rlm_sql_mysql_t, tls_check_cert) },
122  { FR_CONF_OFFSET("check_cert_cn", rlm_sql_mysql_t, tls_check_cert_cn) },
124 };
125 
126 static const conf_parser_t driver_config[] = {
127  { FR_CONF_POINTER("tls", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) tls_config },
128 
129  { FR_CONF_OFFSET("warnings", rlm_sql_mysql_t, warnings_str), .dflt = "auto" },
131 };
132 
133 /* Prototypes */
135 
136 static int mod_instantiate(module_inst_ctx_t const *mctx)
137 {
138  rlm_sql_mysql_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sql_mysql_t);
139  int warnings;
140 
141  warnings = fr_table_value_by_str(server_warnings_table, inst->warnings_str, -1);
142  if (warnings < 0) {
143  ERROR("Invalid warnings value \"%s\", must be yes, no, or auto", inst->warnings_str);
144  return -1;
145  }
146  inst->warnings = (rlm_sql_mysql_warnings)warnings;
147 
148  if (inst->tls_check_cert && !inst->tls_required) {
149  WARN("Implicitly setting tls_required = yes, as tls_check_cert = yes");
150  inst->tls_required = true;
151  }
152  if (inst->tls_check_cert_cn) {
153  if (!inst->tls_required) {
154  WARN("Implicitly setting tls_required = yes, as check_cert_cn = yes");
155  inst->tls_required = true;
156  }
157 
158  if (!inst->tls_check_cert) {
159  WARN("Implicitly setting check_cert = yes, as check_cert_cn = yes");
160  inst->tls_check_cert = true;
161  }
162  }
163  return 0;
164 }
165 
166 static void mod_unload(void)
167 {
168  mysql_library_end();
169 }
170 
171 static int mod_load(void)
172 {
173  if (mysql_library_init(0, NULL, NULL)) {
174  ERROR("libmysql initialisation failed");
175 
176  return -1;
177  }
178 
179  INFO("libmysql version: %s", mysql_get_client_info());
180 
181  return 0;
182 }
183 
184 /** Callback for I/O events in response to mysql_real_connect_start()
185  */
186 static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
187 {
188  rlm_sql_mysql_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_mysql_conn_t);
189 
191 
192  if (c->status == 0) goto connected;
193  c->status = mysql_real_connect_cont(&c->sock, &c->db, c->status);
194 
195  /*
196  * If status is not zero, we're still waiting for something.
197  * The event will be fired again when that happens.
198  */
199  if (c->status != 0) {
200  (void) fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
201  c->status & MYSQL_WAIT_READ ? _sql_connect_io_notify : NULL,
202  c->status & MYSQL_WAIT_WRITE ? _sql_connect_io_notify : NULL, NULL, c);
203  return;
204  }
205 
206 connected:
207  if (!c->sock) {
208  ERROR("MySQL error: %s", mysql_error(&c->db));
210  return;
211  }
212 
213  DEBUG2("Connected to database on %s, server version %s, protocol version %i",
214  mysql_get_host_info(c->sock),
215  mysql_get_server_info(c->sock), mysql_get_proto_info(c->sock));
216 
218 }
219 
221  UNUSED connection_state_t state, void *uctx)
222 {
223  rlm_sql_t const *sql = talloc_get_type_abort_const(uctx, rlm_sql_t);
224  rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
225  int ret;
226  MYSQL_RES *result;
227 
228  DEBUG2("Executing \"%s\" on connection %s", sql->config.connect_query, conn->name);
229 
230  ret = mysql_real_query(sql_conn->sock, sql->config.connect_query, strlen(sql->config.connect_query));
231  if (ret != 0) {
232  char const *info;
233  ERROR("Failed running \"open_query\"");
234  info = mysql_info(sql_conn->sock);
235  if (info) ERROR("%s", info);
237  return;
238  }
239 
240  /*
241  * These queries should not return any results - but let's be safe
242  */
243  result = mysql_store_result(sql_conn->sock);
244  if (result) mysql_free_result(result);
245  while ((mysql_next_result(sql_conn->sock) == 0) &&
246  (result = mysql_store_result(sql_conn->sock))) {
247  mysql_free_result(result);
248  }
249 }
250 
251 static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
252 {
253  rlm_sql_t const *sql = talloc_get_type_abort_const(uctx, rlm_sql_t);
254  rlm_sql_mysql_t const *inst = talloc_get_type_abort(sql->driver_submodule->data, rlm_sql_mysql_t);
256  rlm_sql_config_t const *config = &sql->config;
257 
258  unsigned long sql_flags;
259  enum mysql_option ssl_mysql_opt;
260  unsigned int ssl_mode = 0;
261  bool ssl_mode_isset = false;
262 
263  MEM(c = talloc_zero(conn, rlm_sql_mysql_conn_t));
264  c->conn = conn;
265  c->fd = -1;
266 
267  DEBUG("Starting connect to MySQL server");
268 
269  mysql_init(&c->db);
270 
271  /*
272  * If any of the TLS options are set, configure TLS
273  *
274  * According to MySQL docs this function always returns 0, so we won't
275  * know if ssl setup succeeded until mysql_real_connect is called below.
276  */
277  if (inst->tls_ca_file || inst->tls_ca_path ||
278  inst->tls_certificate_file || inst->tls_private_key_file) {
279  mysql_ssl_set(&(c->db), inst->tls_private_key_file, inst->tls_certificate_file,
280  inst->tls_ca_file, inst->tls_ca_path, inst->tls_cipher);
281  }
282 
283 #ifdef MARIADB_BASE_VERSION
284  if (inst->tls_required || inst->tls_check_cert || inst->tls_check_cert_cn) {
285  ssl_mode_isset = true;
286  /**
287  * For MariaDB, It should be true as can be seen in
288  * https://github.com/MariaDB/server/blob/mariadb-5.5.68/sql-common/client.c#L4338
289  */
290  ssl_mode = true;
291  ssl_mysql_opt = MYSQL_OPT_SSL_VERIFY_SERVER_CERT;
292  }
293 #else
294  ssl_mysql_opt = MYSQL_OPT_SSL_MODE;
295  if (inst->tls_required) {
296  ssl_mode = SSL_MODE_REQUIRED;
297  ssl_mode_isset = true;
298  }
299  if (inst->tls_check_cert) {
300  ssl_mode = SSL_MODE_VERIFY_CA;
301  ssl_mode_isset = true;
302  }
303  if (inst->tls_check_cert_cn) {
304  ssl_mode = SSL_MODE_VERIFY_IDENTITY;
305  ssl_mode_isset = true;
306  }
307 #endif
308  if (ssl_mode_isset) mysql_options(&(c->db), ssl_mysql_opt, &ssl_mode);
309 
310  if (inst->tls_crl_file) mysql_options(&(c->db), MYSQL_OPT_SSL_CRL, inst->tls_crl_file);
311  if (inst->tls_crl_path) mysql_options(&(c->db), MYSQL_OPT_SSL_CRLPATH, inst->tls_crl_path);
312 
313  mysql_options(&(c->db), MYSQL_READ_DEFAULT_GROUP, "freeradius");
314 
315  /*
316  * We need to know about connection errors, and are capable
317  * of reconnecting automatically.
318  */
319  {
320  bool reconnect = 0;
321  mysql_options(&(c->db), MYSQL_OPT_RECONNECT, &reconnect);
322  }
323 
324  sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS;
325 
326 #ifdef CLIENT_MULTI_STATEMENTS
327  sql_flags |= CLIENT_MULTI_STATEMENTS;
328 #endif
329 
330  mysql_options(&c->db, MYSQL_OPT_NONBLOCK, 0);
331 
332  c->status = mysql_real_connect_start(&c->sock, &c->db,
333  config->sql_server,
334  config->sql_login,
335  config->sql_password,
336  config->sql_db,
337  config->sql_port, NULL, sql_flags);
338 
339  c->fd = mysql_get_socket(&c->db);
340 
341  if (c->fd <= 0) {
342  ERROR("Could't connect to MySQL server %s@%s:%s", config->sql_login,
343  config->sql_server, config->sql_db);
344  ERROR("MySQL error: %s", mysql_error(&c->db));
345  error:
346  talloc_free(c);
348  }
349 
350  if (c->status == 0) {
351  DEBUG2("Connected to database '%s' on %s, server version %s, protocol version %i",
352  config->sql_db, mysql_get_host_info(c->sock),
353  mysql_get_server_info(c->sock), mysql_get_proto_info(c->sock));
356  }
357 
358  if (fr_event_fd_insert(c, NULL, c->conn->el, c->fd,
359  c->status & MYSQL_WAIT_READ ? _sql_connect_io_notify : NULL,
360  c->status & MYSQL_WAIT_WRITE ? _sql_connect_io_notify : NULL, NULL, c) != 0) goto error;
361 
362  DEBUG2("Connecting to database '%s' on %s:%d, fd %d",
363  config->sql_db, config->sql_server, config->sql_port, c->fd);
364 
365  *h = c;
366 
368  _sql_connect_query_run, true, sql);
369 
371 }
372 
373 static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
374 {
375  rlm_sql_mysql_conn_t *c = talloc_get_type_abort(h, rlm_sql_mysql_conn_t);
376 
377  if (c->fd >= 0) {
379  c->fd = -1;
380  }
381  mysql_close(&c->db);
382  c->query_ctx = NULL;
383  talloc_free(h);
384 }
385 
386 /** Analyse the last error that occurred on the socket, and determine an action
387  *
388  * @param server Socket from which to extract the server error. May be NULL.
389  * @param client_errno Error from the client.
390  * @return an action for #rlm_sql_t to take.
391  */
392 static sql_rcode_t sql_check_error(MYSQL *server, int client_errno)
393 {
394  int sql_errno = 0;
395 
396  /*
397  * The client and server error numbers are in the
398  * same numberspace.
399  */
400  if (server) sql_errno = mysql_errno(server);
401  if ((sql_errno == 0) && (client_errno != 0)) sql_errno = client_errno;
402 
403  if (sql_errno > 0) switch (sql_errno) {
404  case CR_SERVER_GONE_ERROR:
405  case CR_SERVER_LOST:
406  return RLM_SQL_RECONNECT;
407 
408  case CR_OUT_OF_MEMORY:
409  case CR_COMMANDS_OUT_OF_SYNC:
410  case CR_UNKNOWN_ERROR:
411  default:
412  return RLM_SQL_ERROR;
413 
414  /*
415  * Constraints errors that signify a duplicate, or that we might
416  * want to try an alternative query.
417  *
418  * Error constants not found in the 3.23/4.0/4.1 manual page
419  * are checked for.
420  * Other error constants should always be available.
421  */
422  case ER_DUP_UNIQUE: /* Can't write, because of unique constraint, to table '%s'. */
423  case ER_DUP_KEY: /* Can't write; duplicate key in table '%s' */
424 
425  case ER_DUP_ENTRY: /* Duplicate entry '%s' for key %d. */
426  case ER_NO_REFERENCED_ROW: /* Cannot add or update a child row: a foreign key constraint fails */
427  case ER_ROW_IS_REFERENCED: /* Cannot delete or update a parent row: a foreign key constraint fails */
428 #ifdef ER_FOREIGN_DUPLICATE_KEY
429  case ER_FOREIGN_DUPLICATE_KEY: /* Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry. */
430 #endif
431 #ifdef ER_DUP_ENTRY_WITH_KEY_NAME
432  case ER_DUP_ENTRY_WITH_KEY_NAME: /* Duplicate entry '%s' for key '%s' */
433 #endif
434 #ifdef ER_NO_REFERENCED_ROW_2
435  case ER_NO_REFERENCED_ROW_2:
436 #endif
437 #ifdef ER_ROW_IS_REFERENCED_2
438  case ER_ROW_IS_REFERENCED_2:
439 #endif
440  return RLM_SQL_ALT_QUERY;
441 
442  /*
443  * Constraints errors that signify an invalid query
444  * that can never succeed.
445  */
446  case ER_BAD_NULL_ERROR: /* Column '%s' cannot be null */
447  case ER_NON_UNIQ_ERROR: /* Column '%s' in %s is ambiguous */
448  return RLM_SQL_QUERY_INVALID;
449 
450  }
451 
452  return RLM_SQL_OK;
453 }
454 
456 {
457  sql_rcode_t rcode;
458  int ret;
459 
460 retry_store_result:
461  conn->result = mysql_store_result(conn->sock);
462  if (!conn->result) {
463  rcode = sql_check_error(conn->sock, 0);
464  if (rcode != RLM_SQL_OK) return rcode;
465  ret = mysql_next_result(conn->sock);
466  if (ret == 0) {
467  /* there are more results */
468  goto retry_store_result;
469  } else if (ret > 0) return sql_check_error(NULL, ret);
470  /* ret == -1 signals no more results */
471  }
472  return RLM_SQL_OK;
473 }
474 
476 {
477  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
478 
479  if (conn->result) return mysql_num_rows(conn->result);
480 
481  return 0;
482 }
483 
484 static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
485 {
486  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
487 
488  unsigned int fields, i;
489  MYSQL_FIELD *field_info;
490  char const **names;
491 
492  /*
493  * Use our internal function to abstract out the API call.
494  * Different versions of SQL use different functions,
495  * and some don't like NULL pointers.
496  */
497  fields = mysql_field_count(conn->sock);
498  if (fields == 0) return RLM_SQL_ERROR;
499 
500  /*
501  * https://bugs.mysql.com/bug.php?id=32318
502  * Hints that we don't have to free field_info.
503  */
504  field_info = mysql_fetch_fields(conn->result);
505  if (!field_info) return RLM_SQL_ERROR;
506 
507  MEM(names = talloc_array(query_ctx, char const *, fields));
508 
509  for (i = 0; i < fields; i++) names[i] = field_info[i].name;
510  *out = names;
511 
512  return RLM_SQL_OK;
513 }
514 
515 static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
516 {
517  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
518  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
519  MYSQL_ROW row;
520  int ret;
521  unsigned int num_fields, i;
522  unsigned long *field_lens;
523 
524  /*
525  * Check pointer before de-referencing it.
526  */
527  if (!conn->result) {
528  query_ctx->rcode = RLM_SQL_RECONNECT;
530  }
531 
532  TALLOC_FREE(query_ctx->row); /* Clear previous row set */
533 
534 retry_fetch_row:
535  row = mysql_fetch_row(conn->result);
536  if (!row) {
537  query_ctx->rcode = sql_check_error(conn->sock, 0);
538  if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
539 
540  mysql_free_result(conn->result);
541  conn->result = NULL;
542 
543  ret = mysql_next_result(conn->sock);
544  if (ret == 0) {
545  /* there are more results */
546  if ((sql_store_result(conn, &query_ctx->inst->config) == 0) && (conn->result != NULL)) {
547  goto retry_fetch_row;
548  }
549  } else if (ret > 0) {
550  query_ctx->rcode = sql_check_error(NULL, ret);
551  if (query_ctx->rcode == RLM_SQL_OK) RETURN_MODULE_OK;
553  }
554  /* If ret is -1 then there are no more rows */
555 
556  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
558  }
559 
560  num_fields = mysql_field_count(conn->sock);
561  if (!num_fields) {
562  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
564  }
565 
566  field_lens = mysql_fetch_lengths(conn->result);
567 
568  MEM(query_ctx->row = talloc_zero_array(query_ctx, char *, num_fields + 1));
569  for (i = 0; i < num_fields; i++) {
570  MEM(query_ctx->row[i] = talloc_bstrndup(query_ctx->row, row[i], field_lens[i]));
571  }
572 
573  query_ctx->rcode = RLM_SQL_OK;
575 }
576 
578 {
579  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
580 
581  if (conn->result) {
582  mysql_free_result(conn->result);
583  conn->result = NULL;
584  }
585  TALLOC_FREE(query_ctx->row);
586 
587  return RLM_SQL_OK;
588 }
589 
590 /** Retrieves any warnings associated with the last query
591  *
592  * MySQL stores a limited number of warnings associated with the last query
593  * executed. These can be very useful in diagnosing issues, or in some cases
594  * working around bugs in MySQL which causes it to return the wrong error.
595  *
596  * @note Caller should free any memory allocated in ctx (talloc_free_children()).
597  *
598  * @param ctx to allocate temporary error buffers in.
599  * @param out Array of sql_log_entrys to fill.
600  * @param outlen Length of out array.
601  * @param conn MySQL connection the query was run on.
602  * @param config rlm_sql config.
603  * @return
604  * - Number of errors written to the #sql_log_entry_t array.
605  * - -1 on failure.
606  */
607 static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
609 {
610  MYSQL_RES *result;
611  MYSQL_ROW row;
612  unsigned int num_fields;
613  size_t i = 0;
614 
615  if (outlen == 0) return 0;
616 
617  /*
618  * Retrieve any warnings associated with the previous query
619  * that were left lingering on the server.
620  */
621  if (mysql_query(conn->sock, "SHOW WARNINGS") != 0) return -1;
622  result = mysql_store_result(conn->sock);
623  if (!result) return -1;
624 
625  /*
626  * Fields should be [0] = Level, [1] = Code, [2] = Message
627  */
628  num_fields = mysql_field_count(conn->sock);
629  if (num_fields < 3) {
630  WARN("Failed retrieving warnings, expected 3 fields got %u", num_fields);
631  mysql_free_result(result);
632 
633  return -1;
634  }
635 
636  while ((row = mysql_fetch_row(result))) {
637  char *msg = NULL;
639 
640  /*
641  * Translate the MySQL log level into our internal
642  * log levels, so they get colourised correctly.
643  */
644  if (strcasecmp(row[0], "warning") == 0) type = L_WARN;
645  else if (strcasecmp(row[0], "note") == 0) type = L_DBG;
646  else type = L_ERR;
647 
648  msg = talloc_typed_asprintf(ctx, "%s: %s", row[1], row[2]);
649  out[i].type = type;
650  out[i].msg = msg;
651  if (++i == outlen) break;
652  }
653 
654  mysql_free_result(result);
655 
656  return i;
657 }
658 
659 /** Retrieves any errors associated with the query context
660  *
661  * @note Caller should free any memory allocated in ctx (talloc_free_children()).
662  *
663  * @param ctx to allocate temporary error buffers in.
664  * @param out Array of sql_log_entrys to fill.
665  * @param outlen Length of out array.
666  * @param query_ctx Query context to retrieve error for.
667  * @param config rlm_sql config.
668  * @return number of errors written to the #sql_log_entry_t array.
669  */
670 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
671  fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
672 {
674  rlm_sql_mysql_conn_t *conn;
675  char const *error;
676  size_t i = 0;
677 
678  if (!query_ctx->tconn) return 0;
679  conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
680 
681  fr_assert(outlen > 0);
682 
683  error = mysql_error(conn->sock);
684 
685  /*
686  * Grab the error now in case it gets cleared on the next operation.
687  */
688  if (error && (error[0] != '\0')) {
689  error = talloc_typed_asprintf(ctx, "ERROR %u (%s): %s", mysql_errno(conn->sock), error,
690  mysql_sqlstate(conn->sock));
691  }
692 
693  /*
694  * Don't attempt to get errors from the server, if the last error
695  * was that the server was unavailable.
696  */
697  if ((outlen > 1) && (sql_check_error(conn->sock, 0) != RLM_SQL_RECONNECT)) {
698  size_t ret;
699  unsigned int msgs;
700 
701  switch (inst->warnings) {
703  /*
704  * Check to see if any warnings can be retrieved from the server.
705  */
706  msgs = mysql_warning_count(conn->sock);
707  if (msgs == 0) {
708  DEBUG3("No additional diagnostic info on server");
709  break;
710  }
711 
712  FALL_THROUGH;
713  case SERVER_WARNINGS_YES:
714  ret = sql_warnings(ctx, out, outlen - 1, conn, config);
715  if (ret > 0) i += ret;
716  break;
717 
718  case SERVER_WARNINGS_NO:
719  break;
720 
721  default:
722  fr_assert(0);
723  }
724  }
725 
726  if (error) {
727  out[i].type = L_ERR;
728  out[i].msg = error;
729  }
730  i++;
731 
732  return i;
733 }
734 
735 /** Finish query
736  *
737  * As a single SQL statement may return multiple results
738  * sets, (for example stored procedures) it is necessary to check
739  * whether more results exist and process them in turn if so.
740  *
741  */
743 {
744  rlm_sql_mysql_conn_t *conn;
745  int ret;
746  MYSQL_RES *result;
747 
748  /*
749  * If the query is not in a state which would return results, then do nothing.
750  */
751  if (query_ctx->treq && !(query_ctx->treq->state &
753 
754  /*
755  * If the connection doesn't exist there's nothing to do
756  */
757  if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
758 
759  conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
760 
761  /*
762  * If the connection is not active, then all that we can do is free any stored results
763  */
764  if (query_ctx->tconn->conn->state != CONNECTION_STATE_CONNECTED) {
765  sql_free_result(query_ctx, config);
766  return RLM_SQL_OK;
767  }
768 
769  /*
770  * If there's no result associated with the
771  * connection handle, assume the first result in the
772  * result set hasn't been retrieved.
773  *
774  * MySQL docs says there's no performance penalty for
775  * calling mysql_store_result for queries which don't
776  * return results.
777  */
778  if (conn->result == NULL) {
779  result = mysql_store_result(conn->sock);
780  if (result) mysql_free_result(result);
781  /*
782  * ...otherwise call sql_free_result to free an
783  * already stored result.
784  */
785  } else {
786  sql_free_result(query_ctx, config); /* sql_free_result sets conn->result to NULL */
787  }
788 
789  /*
790  * Drain any other results associated with the handle
791  *
792  * mysql_next_result advances the result cursor so that
793  * the next call to mysql_store_result will retrieve
794  * the next result from the server.
795  *
796  * Unfortunately this really does appear to be the
797  * only way to return the handle to a consistent state.
798  */
799  while (((ret = mysql_next_result(conn->sock)) == 0) &&
800  (result = mysql_store_result(conn->sock))) {
801  mysql_free_result(result);
802  }
803  if (ret > 0) return sql_check_error(NULL, ret);
804 
805  return RLM_SQL_OK;
806 }
807 
809 {
810  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_mysql_conn_t);
811 
812  return mysql_affected_rows(conn->sock);
813 }
814 
815 static size_t sql_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, void *arg)
816 {
817  size_t inlen;
818  connection_t *c = talloc_get_type_abort(arg, connection_t);
819  rlm_sql_mysql_conn_t *conn = talloc_get_type_abort(c->h, rlm_sql_mysql_conn_t);
820 
821  /* Check for potential buffer overflow */
822  inlen = strlen(in);
823  if ((inlen * 2 + 1) > outlen) return 0;
824  /* Prevent integer overflow */
825  if ((inlen * 2 + 1) <= inlen) return 0;
826 
827  return mysql_real_escape_string(&conn->db, out, in, inlen);
828 }
829 
830 static void sql_conn_writable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
831 {
832  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
834 }
835 
836 static void sql_conn_readable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
837 {
838  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
840 }
841 
842 static void sql_conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
843 {
844  trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
845  ERROR("%s - Connection failed: %s", tconn->conn->name, fr_syserror(fd_errno));
847 }
848 
849 /** Allocate an SQL trunk connection
850  *
851  * @param[in] tconn Trunk handle.
852  * @param[in] el Event list which will be used for I/O and timer events.
853  * @param[in] conn_conf Configuration of the connection.
854  * @param[in] log_prefix What to prefix log messages with.
855  * @param[in] uctx User context passed to trunk_alloc.
856  */
858  connection_conf_t const *conn_conf,
859  char const *log_prefix, void *uctx)
860 {
861  connection_t *conn;
862  rlm_sql_thread_t *thread = talloc_get_type_abort(uctx, rlm_sql_thread_t);
863 
864  conn = connection_alloc(tconn, el,
866  .init = _sql_connection_init,
867  .close = _sql_connection_close
868  },
869  conn_conf, log_prefix, thread->inst);
870  if (!conn) {
871  PERROR("Failed allocating state handler for new SQL connection");
872  return NULL;
873  }
874 
875  return conn;
876 }
877 
880  trunk_connection_event_t notify_on, UNUSED void *uctx)
881 {
882  rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
883  fr_event_fd_cb_t read_fn = NULL, write_fn = NULL;
884 
885  switch (notify_on) {
888  return;
889 
891  read_fn = sql_conn_readable;
892  break;
893 
895  write_fn = sql_conn_writable;
896  break;
897 
899  read_fn = sql_conn_readable;
900  write_fn = sql_conn_writable;
901  break;
902  }
903 
904  if (fr_event_fd_insert(sql_conn, NULL, el, sql_conn->fd, read_fn, write_fn, sql_conn_error, tconn) < 0) {
905  PERROR("Failed inserting FD event");
907  }
908 }
909 
911  connection_t *conn, UNUSED void *uctx)
912 {
913  rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
914  request_t *request;
915  trunk_request_t *treq;
916  fr_sql_query_t *query_ctx;
917  char const *info;
918  int err;
919 
920  if (trunk_connection_pop_request(&treq, tconn) != 0) return;
921  if (!treq) return;
922 
923  query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
924  request = query_ctx->request;
925 
926  /*
927  * Each of the MariaDB async "start" calls returns a non-zero value
928  * if they are waiting on I/O.
929  * A return value of zero means that the operation completed.
930  */
931 
932  switch (query_ctx->status) {
933  case SQL_QUERY_PREPARED:
934  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
935  sql_conn->status = mysql_real_query_start(&err, sql_conn->sock, query_ctx->query_str, strlen(query_ctx->query_str));
936  query_ctx->tconn = tconn;
937 
938  if (sql_conn->status) {
939  ROPTIONAL(RDEBUG3, DEBUG3, "Waiting for IO");
940  query_ctx->status = SQL_QUERY_SUBMITTED;
941  sql_conn->query_ctx = query_ctx;
943  return;
944  }
945 
946  if (err) {
947  /*
948  * Need to check what kind of error this is - it may
949  * be a unique key conflict, we run the next query.
950  */
951  info = mysql_info(sql_conn->sock);
952  query_ctx->rcode = sql_check_error(sql_conn->sock, 0);
953  if (info) ERROR("%s", info);
954  switch (query_ctx->rcode) {
955  case RLM_SQL_OK:
956  case RLM_SQL_ALT_QUERY:
957  break;
958 
959  default:
960  query_ctx->status = SQL_QUERY_FAILED;
962  return;
963  }
964  }
965  query_ctx->status = SQL_QUERY_RETURNED;
966 
967  break;
968 
969  case SQL_QUERY_RETURNED:
970  ROPTIONAL(RDEBUG2, DEBUG2, "Fetching results");
971  fr_assert(query_ctx->tconn == tconn);
972  sql_conn->status = mysql_store_result_start(&sql_conn->result, sql_conn->sock);
973 
974  if (sql_conn->status) {
975  ROPTIONAL(RDEBUG3, DEBUG3, "Waiting for IO");
976  query_ctx->status = SQL_QUERY_FETCHING_RESULTS;
977  sql_conn->query_ctx = query_ctx;
979  return;
980  }
981  query_ctx->status = SQL_QUERY_RESULTS_FETCHED;
982 
983  break;
984 
985  default:
986  /*
987  * The request outstanding on this connection returned
988  * immediately, so we are not actually waiting for I/O.
989  */
990  return;
991  }
992 
993  /*
994  * The current request is not waiting for I/O so the request can run
995  */
996  ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
998  if (request) unlang_interpret_mark_runnable(request);
999 }
1000 
1002  connection_t *conn, UNUSED void *uctx)
1003 {
1004  rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
1005  fr_sql_query_t *query_ctx;
1006  char const *info;
1007  int err = 0;
1008  request_t *request;
1009 
1010  /*
1011  * Lookup the outstanding SQL query for this connection.
1012  * There will only ever be one per tconn.
1013  */
1014  query_ctx = sql_conn->query_ctx;
1015 
1016  /*
1017  * No outstanding query on this connection.
1018  * Should not happen, but added for safety.
1019  */
1020  if (unlikely(!query_ctx)) return;
1021 
1022  switch (query_ctx->status) {
1023  case SQL_QUERY_SUBMITTED:
1024  sql_conn->status = mysql_real_query_cont(&err, sql_conn->sock, sql_conn->status);
1025  break;
1026 
1028  sql_conn->status = mysql_store_result_cont(&sql_conn->result, sql_conn->sock, sql_conn->status);
1029  break;
1030 
1031  default:
1032  /*
1033  * The request outstanding on this connection returned
1034  * immediately, so we are not actually waiting for I/O.
1035  */
1036  return;
1037  }
1038 
1039  /*
1040  * Are we still waiting for any further I/O?
1041  */
1042  if (sql_conn->status != 0) return;
1043 
1044  sql_conn->query_ctx = NULL;
1045 
1046  switch (query_ctx->status) {
1047  case SQL_QUERY_SUBMITTED:
1048  query_ctx->status = SQL_QUERY_RETURNED;
1049  break;
1050 
1052  query_ctx->status = SQL_QUERY_RESULTS_FETCHED;
1053  break;
1054 
1055  default:
1056  fr_assert(0);
1057  }
1058 
1059  request = query_ctx->request;
1060  if (request) unlang_interpret_mark_runnable(request);
1061 
1062  if (err) {
1063  info = mysql_info(sql_conn->sock);
1064  query_ctx->rcode = sql_check_error(sql_conn->sock, 0);
1065  if (info) ROPTIONAL(RERROR, ERROR, "%s", info);
1066  return;
1067  }
1068 
1069  query_ctx->rcode = RLM_SQL_OK;
1070 }
1071 
1072 static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
1073  UNUSED void *uctx)
1074 {
1075  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
1076  rlm_sql_mysql_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_mysql_conn_t);
1077 
1078  if (!query_ctx->treq) return;
1079  if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
1080  if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
1081 }
1082 
1084  connection_t *conn, UNUSED void *uctx)
1085 {
1086  trunk_request_t *treq;
1087 
1088  /*
1089  * The MariaDB non-blocking API doesn't have any cancellation functions -
1090  * rather you are expected to close the connection.
1091  */
1092  if ((trunk_connection_pop_cancellation(&treq, tconn)) == 0) {
1095  }
1096 }
1097 
1098 static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx,
1099  UNUSED trunk_request_state_t state, UNUSED void *uctx)
1100 {
1101  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
1102 
1103  query_ctx->treq = NULL;
1104  query_ctx->rcode = RLM_SQL_ERROR;
1105 
1106  if (request) unlang_interpret_mark_runnable(request);
1107 }
1108 
1109 static unlang_action_t sql_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
1110 {
1111  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
1112 
1113  if (query_ctx->rcode == RLM_SQL_OK) RETURN_MODULE_OK;
1115 }
1116 
1117 static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
1118 {
1119  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
1120 
1121  if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
1122 
1123  if (query_ctx->status == SQL_QUERY_RETURNED) {
1124  trunk_request_requeue(query_ctx->treq);
1125 
1127  query_ctx->rcode = RLM_SQL_ERROR;
1129  }
1130 
1131  return UNLANG_ACTION_YIELD;
1132  }
1133 
1135 }
1136 
1137 /** Allocate the argument used for the SQL escape function
1138  *
1139  * In this case, a dedicated connection to allow the escape
1140  * function to have access to server side parameters, though
1141  * no packets ever flow after the connection is made.
1142  */
1143 static void *sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
1144 {
1145  rlm_sql_t const *inst = talloc_get_type_abort(uctx, rlm_sql_t);
1146  connection_t *conn;
1147 
1148  conn = connection_alloc(ctx, el,
1149  &(connection_funcs_t){
1150  .init = _sql_connection_init,
1151  .close = _sql_connection_close,
1152  },
1153  inst->config.trunk_conf.conn_conf,
1154  inst->name, inst);
1155 
1156  if (!conn) {
1157  PERROR("Failed allocating state handler for SQL escape connection");
1158  return NULL;
1159  }
1160 
1161  connection_signal_init(conn);
1162  return conn;
1163 }
1164 
1165 static void sql_escape_arg_free(void *uctx)
1166 {
1167  connection_t *conn = talloc_get_type_abort(uctx, connection_t);
1168  connection_signal_halt(conn);
1169 }
1170 
1171 /* Exported to rlm_sql */
1174  .common = {
1175  .name = "sql_mysql",
1176  .magic = MODULE_MAGIC_INIT,
1177  .inst_size = sizeof(rlm_sql_mysql_t),
1178  .onload = mod_load,
1179  .unload = mod_unload,
1180  .config = driver_config,
1182  },
1184  .sql_query_resume = sql_query_resume,
1185  .sql_select_query_resume = sql_select_query_resume,
1186  .sql_num_rows = sql_num_rows,
1187  .sql_affected_rows = sql_affected_rows,
1188  .sql_fields = sql_fields,
1189  .sql_fetch_row = sql_fetch_row,
1190  .sql_free_result = sql_free_result,
1191  .sql_error = sql_error,
1192  .sql_finish_query = sql_finish_query,
1193  .sql_finish_select_query = sql_finish_query,
1194  .sql_escape_func = sql_escape_func,
1195  .sql_escape_arg_alloc = sql_escape_arg_alloc,
1196  .sql_escape_arg_free = sql_escape_arg_free,
1197  .uses_trunks = true,
1198  .trunk_io_funcs = {
1199  .connection_alloc = sql_trunk_connection_alloc,
1200  .connection_notify = sql_trunk_connection_notify,
1201  .request_mux = sql_trunk_request_mux,
1202  .request_demux = sql_trunk_request_demux,
1203  .request_cancel_mux = sql_request_cancel_mux,
1204  .request_cancel = sql_request_cancel,
1205  .request_fail = sql_request_fail,
1206  }
1207 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition: action.h:42
log_entry msg
Definition: acutest.h:794
#define RCSID(id)
Definition: build.h:446
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define DIAG_ON(_x)
Definition: build.h:421
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define DIAG_OFF(_x)
Definition: build.h:420
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition: cf_parse.h:310
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:256
@ CONF_FLAG_FILE_INPUT
File matching value must exist, and must be readable.
Definition: cf_parse.h:412
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:400
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition: connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition: connection.h:84
Holds a complete set of functions for a connection.
Definition: connection.h:186
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_slen_t err
Definition: dict.h:645
static fr_slen_t in
Definition: dict.h:645
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define fr_event_fd_insert(...)
Definition: event.h:232
void(* fr_event_fd_cb_t)(fr_event_list_t *el, int fd, int flags, void *uctx)
Called when an IO event occurs on a file descriptor.
Definition: event.h:137
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition: event.h:62
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition: function.h:89
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
#define PERROR(_fmt,...)
Definition: log.h:228
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
talloc_free(reap)
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition: event.c:1253
Stores all information relating to an event list.
Definition: event.c:411
fr_log_type_t
Definition: log.h:54
@ L_WARN
Warning.
Definition: log.h:57
@ L_ERR
Error message.
Definition: log.h:56
@ L_DBG
Only displayed when debugging is enabled.
Definition: log.h:59
int strcasecmp(char *s1, char *s2)
Definition: missing.c:66
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
static const conf_parser_t config[]
Definition: base.c:188
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define WARN(fmt,...)
Definition: radclient.h:47
#define INFO(fmt,...)
Definition: radict.c:54
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1329
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition: rlm_sql.h:145
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition: rlm_sql.h:141
rlm_sql_t const * inst
Module instance data.
Definition: rlm_sql.h:109
rlm_sql_t const * inst
Module instance for this query.
Definition: rlm_sql.h:137
char const * query_str
Query string to run.
Definition: rlm_sql.h:143
request_t * request
Request this query relates to.
Definition: rlm_sql.h:138
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:44
@ RLM_SQL_QUERY_INVALID
Query syntax error.
Definition: rlm_sql.h:45
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition: rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:50
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:170
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:147
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:146
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:142
char const * connect_query
Query executed after establishing new connection.
Definition: rlm_sql.h:96
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
@ SQL_QUERY_FETCHING_RESULTS
Fetching results from server.
Definition: rlm_sql.h:132
@ SQL_QUERY_FAILED
Failed to submit.
Definition: rlm_sql.h:128
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition: rlm_sql.h:130
@ SQL_QUERY_PREPARED
Ready to submit.
Definition: rlm_sql.h:129
@ SQL_QUERY_RESULTS_FETCHED
Results fetched from the server.
Definition: rlm_sql.h:133
Definition: rlm_sql.h:61
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.
static int mod_load(void)
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
bool tls_check_cert
Verify there's a trust relationship between the server's cert and one of the CAs we have configured.
Definition: rlm_sql_mysql.c:90
char const * tls_certificate_file
Public certificate we present to the server.
Definition: rlm_sql_mysql.c:81
static void * sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
Allocate the argument used for the SQL escape function.
fr_sql_query_t * query_ctx
Current query running on this connection.
Definition: rlm_sql_mysql.c:73
static sql_rcode_t sql_free_result(fr_sql_query_t *, rlm_sql_config_t const *)
static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
static fr_table_num_sorted_t const server_warnings_table[]
Definition: rlm_sql_mysql.c:60
char const * tls_private_key_file
Private key for the certificate we present to the server.
Definition: rlm_sql_mysql.c:82
char const * tls_crl_path
Private key for the certificate we present to the server.
Definition: rlm_sql_mysql.c:85
char const * tls_crl_file
Public certificate we present to the server.
Definition: rlm_sql_mysql.c:84
int status
returned by the most recent non-blocking function call.
Definition: rlm_sql_mysql.c:74
char const * tls_cipher
Colon separated list of TLS ciphers for TLS <= 1.2.
Definition: rlm_sql_mysql.c:87
static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Callback for I/O events in response to mysql_real_connect_start()
static connection_t * sql_trunk_connection_alloc(trunk_connection_t *tconn, fr_event_list_t *el, connection_conf_t const *conn_conf, char const *log_prefix, void *uctx)
Allocate an SQL trunk connection.
static void sql_conn_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
char const * tls_ca_path
Directory containing CAs that may be used to validate the servers certificate.
Definition: rlm_sql_mysql.c:79
static void sql_trunk_request_demux(UNUSED fr_event_list_t *el, UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
static size_t sql_escape_func(UNUSED request_t *request, char *out, size_t outlen, char const *in, void *arg)
static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
rlm_sql_mysql_warnings
Definition: rlm_sql_mysql.c:54
@ SERVER_WARNINGS_AUTO
Definition: rlm_sql_mysql.c:55
@ SERVER_WARNINGS_NO
Definition: rlm_sql_mysql.c:57
@ SERVER_WARNINGS_YES
Definition: rlm_sql_mysql.c:56
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static void sql_escape_arg_free(void *uctx)
static void sql_conn_readable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
int fd
fd for this connection's I/O events.
Definition: rlm_sql_mysql.c:72
static conf_parser_t tls_config[]
static void sql_conn_writable(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
rlm_sql_driver_t rlm_sql_mysql
MYSQL * sock
Connection details as returned by connection init functions.
Definition: rlm_sql_mysql.c:69
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
MYSQL db
Structure representing connection details.
Definition: rlm_sql_mysql.c:68
static void mod_unload(void)
static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
char const * warnings_str
Whether we always query the server for additional warnings.
Definition: rlm_sql_mysql.c:95
static const conf_parser_t driver_config[]
static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static sql_rcode_t sql_store_result(rlm_sql_mysql_conn_t *conn, UNUSED rlm_sql_config_t const *config)
static size_t server_warnings_table_len
Definition: rlm_sql_mysql.c:65
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Finish query.
static void _sql_connect_query_run(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, fr_sql_query_t *query_ctx, rlm_sql_config_t const *config)
Retrieves any errors associated with the query context.
static unlang_action_t sql_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
connection_t * conn
Generic connection structure for this connection.
Definition: rlm_sql_mysql.c:71
bool tls_check_cert_cn
Verify that the CN in the server cert matches the host we passed to mysql_real_connect().
Definition: rlm_sql_mysql.c:92
static void sql_trunk_connection_notify(trunk_connection_t *tconn, connection_t *conn, fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
static size_t sql_warnings(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_mysql_conn_t *conn, UNUSED rlm_sql_config_t const *config)
Retrieves any warnings associated with the last query.
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
char const * tls_ca_file
Path to the CA used to validate the server's certificate.
Definition: rlm_sql_mysql.c:78
bool tls_required
Require that the connection is encrypted.
Definition: rlm_sql_mysql.c:89
MYSQL_RES * result
Result from most recent query.
Definition: rlm_sql_mysql.c:70
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
rlm_sql_mysql_warnings warnings
mysql_warning_count() doesn't appear to work with NDB cluster
Definition: rlm_sql_mysql.c:96
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
Definition: connection.c:1290
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
Definition: connection.c:1166
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
Definition: connection.c:1106
connection_watch_entry_t * connection_add_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed after a state function has been called.
Definition: connection.c:531
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
Definition: connection.c:1507
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1136
void * data
Module's instance data.
Definition: module.h:271
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:202
module_instance_t * driver_submodule
Driver's submodule.
Definition: rlm_sql.h:243
rlm_sql_config_t config
Definition: rlm_sql.h:236
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition: table.h:134
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition: talloc.c:564
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:492
#define talloc_get_type_abort_const
Definition: talloc.h:271
static const char * names[8]
Definition: time.c:617
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition: trunk.c:3809
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
void trunk_connection_signal_readable(trunk_connection_t *tconn)
Signal that a trunk connection is readable.
Definition: trunk.c:3895
trunk_enqueue_t trunk_request_requeue(trunk_request_t *treq)
Re-enqueue a request on the same connection.
Definition: trunk.c:2664
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2272
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3857
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
void trunk_connection_signal_reconnect(trunk_connection_t *tconn, connection_reason_t reason)
Signal a trunk connection is no longer viable.
Definition: trunk.c:3973
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition: trunk.c:3877
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition: trunk.c:2065
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition: trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition: trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition: trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition: trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition: trunk.h:75
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition: trunk.h:55
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition: trunk.h:57
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
Definition: trunk.h:173
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
Definition: trunk.h:182
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition: trunk.h:172
static fr_event_list_t * el
static size_t char fr_sbuff_t size_t inlen
Definition: value.h:984
static size_t char ** out
Definition: value.h:984