The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_freetds.c
Go to the documentation of this file.
1  /*
2  * This program is 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: 79bb8e20596127214fdbefce24aed84675523aef $
19  * @file rlm_sql.c
20  * @brief Implements FreeTDS rlm_sql driver.
21  *
22  * @copyright 2013 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  * @copyright 2000,2006 The FreeRADIUS server project
24  * @copyright 2000 Mattias Sjostrom (mattias@nogui.se)
25  */
26 
27 RCSID("$Id: 79bb8e20596127214fdbefce24aed84675523aef $")
28 
29 #define LOG_PREFIX "sql - freetds"
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/util/debug.h>
33 
34 #include <sys/stat.h>
35 
36 #include <ctpublic.h>
37 
38 #include "rlm_sql.h"
39 
40 typedef struct {
41  CS_CONTEXT *context; //!< Structure FreeTDS uses to avoid creating globals.
42  CS_CONNECTION *db; //!< Handle specifying a single connection to the database.
43  CS_COMMAND *command; //!< A prepared statement.
44  char **results; //!< Result strings from statement execution.
45  char *error; //!< The last error string created by one of the call backs.
46  bool established; //!< Set to false once the connection has been properly established.
47  CS_INT rows_affected; //!< Rows affected by last INSERT / UPDATE / DELETE.
49 
50 #define MAX_DATASTR_LEN 256
51 
52 /** Client-Library error handler
53  *
54  * Callback for any errors raised by the Client-Library. Will overwrite any previous errors associated
55  * with a connection.
56  *
57  * @param context The FreeTDS library context.
58  * @param conn DB connection handle.
59  * @param emsgp Pointer to the error structure.
60  * @return CS_SUCCEED
61  */
62 static CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_CLIENTMSG *emsgp)
63 {
64  rlm_sql_freetds_conn_t *this = NULL;
65  int len = 0;
66 
67  /*
68  * Not actually an error, but the client wanted to tell us something...
69  */
70  if (emsgp->severity == CS_SV_INFORM) {
71  INFO("%s", emsgp->msgstring);
72 
73  return CS_SUCCEED;
74  }
75 
76  if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) {
77  ERROR("failed retrieving context userdata");
78 
79  return CS_SUCCEED;
80  }
81 
82  if (this->error) TALLOC_FREE(this->error);
83 
84  this->error = talloc_typed_asprintf(this, "client error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
85  (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber),
86  (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber),
87  emsgp->msgstring);
88 
89  if (emsgp->osstringlen > 0) {
90  this->error = talloc_asprintf_append(this->error, ". os error: number(%ld): %s",
91  (long)emsgp->osnumber, emsgp->osstring);
92  }
93 
94  return CS_SUCCEED;
95 }
96 
97 /** Client error handler
98  *
99  * Callback for any errors raised by the client. Will overwrite any previous errors associated
100  * with a connection.
101  *
102  * @param context The FreeTDS library context.
103  * @param emsgp Pointer to the error structure.
104  * @return CS_SUCCEED
105  */
106 static CS_RETCODE CS_PUBLIC csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp)
107 {
108  rlm_sql_freetds_conn_t *this = NULL;
109  int len = 0;
110 
111  /*
112  * Not actually an error, but the client wanted to tell us something...
113  */
114  if (emsgp->severity == CS_SV_INFORM) {
115  INFO("%s", emsgp->msgstring);
116 
117  return CS_SUCCEED;
118  }
119 
120  if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) {
121  ERROR("failed retrieving context userdata");
122 
123  return CS_SUCCEED;
124  }
125 
126  if (this->error) TALLOC_FREE(this->error);
127 
128  this->error = talloc_typed_asprintf(this, "cs error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
129  (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber),
130  (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber),
131  emsgp->msgstring);
132 
133  if (emsgp->osstringlen > 0) {
134  this->error = talloc_asprintf_append(this->error, ". os error: number(%ld): %s",
135  (long)emsgp->osnumber, emsgp->osstring);
136  }
137 
138  return CS_SUCCEED;
139 }
140 
141 /** Server error handler
142  *
143  * Callback for any messages sent back from the server.
144  *
145  * There's no standard categorisation of messages sent back from the server, so we don't know they're errors,
146  * the only thing we can do is write them to the long as informational messages.
147  *
148  * @param context The FreeTDS library context.
149  * @param conn DB connection handle.
150  * @param msgp Pointer to the error structure.
151  * @return CS_SUCCEED
152  */
153 static CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_SERVERMSG *msgp)
154 {
155  rlm_sql_freetds_conn_t *this = NULL;
156  int len = 0;
157 
158  if ((cs_config(context, CS_GET, CS_USERDATA, &this, sizeof(this), &len) != CS_SUCCEED) || !this) {
159  ERROR("failed retrieving context userdata");
160 
161  return CS_SUCCEED;
162  }
163 
164  /*
165  * Because apparently there are no standard severity levels *brilliant*
166  */
167  if (this->established) {
168  INFO("server msg from \"%s\": severity(%ld), number(%ld), origin(%ld), "
169  "layer(%ld), procedure \"%s\": %s",
170  (msgp->svrnlen > 0) ? msgp->svrname : "unknown",
171  (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state, (long)msgp->line,
172  (msgp->proclen > 0) ? msgp->proc : "none", msgp->text);
173  } else {
174  if (this->error) TALLOC_FREE(this->error);
175 
176  this->error = talloc_typed_asprintf(this, "Server msg from \"%s\": severity(%ld), number(%ld), "
177  "origin(%ld), layer(%ld), procedure \"%s\": %s",
178  (msgp->svrnlen > 0) ? msgp->svrname : "unknown",
179  (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state,
180  (long)msgp->line,
181  (msgp->proclen > 0) ? msgp->proc : "none", msgp->text);
182  }
183 
184  return CS_SUCCEED;
185 }
186 
187 /*************************************************************************
188  *
189  * Function: sql_query
190  *
191  * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
192  * the database.
193  *
194  *************************************************************************/
195 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config, char const *query)
196 {
197  rlm_sql_freetds_conn_t *conn = handle->conn;
198 
199  CS_RETCODE results_ret;
200  CS_INT result_type;
201 
202  /*
203  * Reset rows_affected in case the query fails.
204  * Prevents accidentally returning the rows_affected from a previous query.
205  */
206  conn->rows_affected = -1;
207 
208  if (ct_cmd_alloc(conn->db, &conn->command) != CS_SUCCEED) {
209  ERROR("Unable to allocate command structure (ct_cmd_alloc())");
210 
211  return RLM_SQL_ERROR;
212  }
213 
214  if (ct_command(conn->command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
215  ERROR("Unable to initialise command structure (ct_command())");
216 
217  return RLM_SQL_ERROR;
218  }
219 
220  if (ct_send(conn->command) != CS_SUCCEED) {
221  ERROR("Unable to send command (ct_send())");
222 
223  return RLM_SQL_ERROR;
224  }
225 
226  /*
227  * We'll make three calls to ct_results, first to get a success indicator, secondly to get a
228  * done indicator, and thirdly to get a "nothing left to handle" status.
229  */
230 
231  /*
232  * First call to ct_results, we need returncode CS_SUCCEED and result_type CS_CMD_SUCCEED.
233  */
234  if ((results_ret = ct_results(conn->command, &result_type)) == CS_SUCCEED) {
235  if (result_type != CS_CMD_SUCCEED) {
236  if (result_type == CS_ROW_RESULT) {
237  ERROR("sql_query processed a query returning rows. "
238  "Use sql_select_query instead!");
239  }
240  ERROR("Result failure or unexpected result type from query");
241 
242  return RLM_SQL_ERROR;
243  }
244  } else {
245  switch (results_ret) {
246  case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */
247  ERROR("Failure retrieving query results");
248 
249  if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) {
250  INFO("Cleaning up");
251 
252  return RLM_SQL_RECONNECT;
253  }
254  conn->command = NULL;
255 
256  return RLM_SQL_ERROR;
257  default:
258  ERROR("Unexpected return value from ct_results()");
259 
260  return RLM_SQL_ERROR;
261  }
262  }
263 
264  /*
265  * Retrieve the number of rows affected - the later calls
266  * to ct_results end up resetting the underlying counter so we
267  * no longer have access to this.
268  */
269  if (ct_res_info(conn->command, CS_ROW_COUNT, &conn->rows_affected, CS_UNUSED, NULL) != CS_SUCCEED) {
270  ERROR("rlm_sql_freetds: error retrieving row count");
271 
272  return RLM_SQL_ERROR;
273  }
274 
275  /*
276  * Second call to ct_results, we need returncode CS_SUCCEED
277  * and result_type CS_CMD_DONE.
278  */
279  if ((results_ret = ct_results(conn->command, &result_type)) == CS_SUCCEED) {
280  if (result_type != CS_CMD_DONE) {
281  ERROR("Result failure or unexpected result type from query");
282 
283  return RLM_SQL_ERROR;
284  }
285  } else {
286  switch (results_ret) {
287  case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */
288  ERROR("Failure retrieving query results");
289  if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) return RLM_SQL_RECONNECT;
290 
291  conn->command = NULL;
292  return RLM_SQL_ERROR;
293 
294  default:
295  ERROR("Unexpected return value from ct_results()");
296 
297  return RLM_SQL_ERROR;
298  }
299  }
300 
301  /*
302  * Third call to ct_results, we need returncode CS_END_RESULTS result_type will be ignored.
303  */
304  results_ret = ct_results(conn->command, &result_type);
305  switch (results_ret) {
306  case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */
307  ERROR("Failure retrieving query results");
308  if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) return RLM_SQL_RECONNECT;
309  conn->command = NULL;
310 
311  return RLM_SQL_ERROR;
312 
313  case CS_END_RESULTS: /* This is where we want to end up */
314  break;
315 
316  default:
317  ERROR("Unexpected return value from ct_results()");
318 
319  return RLM_SQL_ERROR;
320  }
321 
322  return RLM_SQL_OK;
323 }
324 
325 /*************************************************************************
326  *
327  * Function: sql_num_fields
328  *
329  * Purpose: database specific num_fields function. Returns number
330  * of columns from query
331  *
332  *************************************************************************/
334 {
335  rlm_sql_freetds_conn_t *conn = handle->conn;
336  CS_INT num = 0;
337 
338  if (ct_res_info(conn->command, CS_NUMDATA, &num, CS_UNUSED, NULL) != CS_SUCCEED) {
339  ERROR("Error retrieving column count");
340 
341  return RLM_SQL_ERROR;
342  }
343 
344  return num;
345 }
346 
347 /*************************************************************************
348  *
349  * Function: sql_fields
350  *
351  * Purpose: Return name of regular result columns.
352  *
353  *************************************************************************/
354 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
355 {
356  rlm_sql_freetds_conn_t *conn = handle->conn;
357  CS_DATAFMT datafmt;
358  int fields, i;
359  char const **names;
360 
361  /* Get number of elements in row result */
362  if (ct_res_info(conn->command, CS_NUMDATA, (CS_INT *)&fields, CS_UNUSED, NULL) != CS_SUCCEED) {
363  ERROR("sql_fields() Error retrieving column count");
364 
365  return RLM_SQL_ERROR;
366  }
367 
368  if (fields <= 0) return RLM_SQL_ERROR;
369 
370  MEM(names = talloc_array(handle, char const *, fields));
371 
372  for (i = 0; i < fields; i++) {
373  int col = i + 1;
374  char *p;
375 
376  /*
377  ** Get the column description. ct_describe() fills the
378  ** datafmt parameter with a description of the column.
379  */
380  if (ct_describe(conn->command, col, &datafmt) != CS_SUCCEED) {
381  ERROR("sql_fields() Problems with ct_describe(), column %d", col);
383  return RLM_SQL_ERROR;
384  }
385 
386  if (datafmt.namelen > 0) {
387  MEM(p = talloc_array(names, char, (size_t)datafmt.namelen + 1));
388  strlcpy(p, datafmt.name, (size_t)datafmt.namelen + 1);
389  names[i] = p;
390  }
391  }
392 
393  *out = names;
394 
395  return RLM_SQL_OK;
396 }
397 
398 /** Retrieves any errors associated with the connection handle
399  *
400  * @note Caller will free any memory allocated in ctx.
401  *
402  * @param ctx to allocate temporary error buffers in.
403  * @param out Array of sql_log_entrys to fill.
404  * @param outlen Length of out array.
405  * @param handle rlm_sql connection handle.
406  * @param config rlm_sql config.
407  * @return number of errors written to the #sql_log_entry_t array.
408  */
409 static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
411 {
412  rlm_sql_freetds_conn_t *conn = handle->conn;
413 
414  fr_assert(conn && conn->db);
415  fr_assert(outlen > 0);
416 
417  if (!conn->error) return 0;
418 
419  out[0].type = L_ERR;
420  out[0].msg = conn->error;
421 
422  return 1;
423 }
424 
426 {
427  rlm_sql_freetds_conn_t *conn = handle->conn;
428 
429  ct_cancel(NULL, conn->command, CS_CANCEL_ALL);
430  if (ct_cmd_drop(conn->command) != CS_SUCCEED) {
431  ERROR("freeing command structure failed");
432 
433  return RLM_SQL_ERROR;
434  }
435  conn->command = NULL;
436 
437  TALLOC_FREE(conn->results);
438 
439  return RLM_SQL_OK;
440 
441 }
442 
443 /** Execute a query when we expected a result set
444  *
445  * @note Only the first row from queries returning several rows will be returned by this function,
446  * consecutive rows will be discarded.
447  *
448  */
449 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
450 {
451  rlm_sql_freetds_conn_t *conn = handle->conn;
452 
453  CS_RETCODE results_ret;
454  CS_INT result_type;
455  CS_DATAFMT descriptor;
456 
457  int colcount,i;
458  char **rowdata;
459 
460  if (!conn->db) {
461  ERROR("socket not connected");
462 
463  return RLM_SQL_ERROR;
464  }
465 
466  if (ct_cmd_alloc(conn->db, &conn->command) != CS_SUCCEED) {
467  ERROR("unable to allocate command structure (ct_cmd_alloc())");
468 
469  return RLM_SQL_ERROR;
470  }
471 
472  if (ct_command(conn->command, CS_LANG_CMD, query, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
473  ERROR("unable to initiate command structure (ct_command()");
474 
475  return RLM_SQL_ERROR;
476  }
477 
478  if (ct_send(conn->command) != CS_SUCCEED) {
479  ERROR("unable to send command (ct_send())");
480  return RLM_SQL_ERROR;
481  }
482 
483  results_ret = ct_results(conn->command, &result_type);
484  switch (results_ret) {
485  case CS_SUCCEED:
486  switch (result_type) {
487  case CS_ROW_RESULT:
488 
489  /*
490  * Set up a target buffer for the results data, and associate the buffer with the results,
491  * but the actual fetching takes place in sql_fetch_row.
492  * The layer above MUST call sql_fetch_row and/or sql_finish_select_query
493  * or this socket will be unusable and may cause segfaults
494  * if reused later on.
495  */
496 
497  /*
498  * Set up the DATAFMT structure that describes our target array
499  * and tells freetds what we want future ct_fetch calls to do.
500  */
501  descriptor.datatype = CS_CHAR_TYPE; /* The target buffer is a string */
502  descriptor.format = CS_FMT_NULLTERM; /* Null termination please */
503  descriptor.maxlength = MAX_DATASTR_LEN; /* The string arrays are this large */
504  descriptor.count = 1; /* Fetch one row of data */
505  descriptor.locale = NULL; /* Don't do NLS stuff */
506 
507  colcount = sql_num_fields(handle, config); /* Get number of elements in row result */
508 
509  rowdata = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */
510  rowdata[colcount] = NULL;
511 
512  for (i = 0; i < colcount; i++) {
513  /* Space to hold the result data */
514  rowdata[i] = talloc_zero_array(rowdata, char, MAX_DATASTR_LEN + 1);
515 
516  /* Associate the target buffer with the data */
517  if (ct_bind(conn->command, i + 1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) {
518  talloc_free(rowdata);
519 
520  ERROR("ct_bind() failed)");
521 
522  return RLM_SQL_ERROR;
523  }
524 
525  }
526 
527  rowdata[i] = NULL; /* Terminate the array */
528  conn->results = rowdata;
529  break;
530 
531  case CS_CMD_SUCCEED:
532  case CS_CMD_DONE:
533  ERROR("query returned no data");
534  break;
535 
536  default:
537 
538  ERROR("unexpected result type from query");
540 
541  return RLM_SQL_ERROR;
542  }
543  break;
544 
545  case CS_FAIL:
546 
547  /*
548  * Serious failure, freetds requires us to cancel the results and maybe even close the db.
549  */
550 
551  ERROR("failure retrieving query results");
552 
553  if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) {
554  ERROR("cleaning up");
555 
556  return RLM_SQL_RECONNECT;
557  }
558  conn->command = NULL;
559 
560  return RLM_SQL_ERROR;
561 
562  default:
563  ERROR("unexpected return value from ct_results()");
564 
565  return RLM_SQL_ERROR;
566  }
567 
568  return RLM_SQL_OK;
569 }
570 
572 {
573  rlm_sql_freetds_conn_t *conn = handle->conn;
574 
575  return (conn->rows_affected);
576 }
577 
579 {
580  rlm_sql_freetds_conn_t *conn = handle->conn;
581  CS_INT ret, count;
582 
583  *out = NULL;
584  handle->row = NULL;
585 
586  ret = ct_fetch(conn->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count);
587  switch (ret) {
588  case CS_FAIL:
589  /*
590  * Serious failure, freetds requires us to cancel the results and maybe even close the db.
591  */
592  ERROR("failure fetching row data");
593  if (ct_cancel(NULL, conn->command, CS_CANCEL_ALL) == CS_FAIL) {
594  ERROR("cleaning up");
595  } else {
596  conn->command = NULL;
597  }
598 
599  return RLM_SQL_RECONNECT;
600 
601  case CS_END_DATA:
602  return RLM_SQL_NO_MORE_ROWS;
603 
604  case CS_SUCCEED:
605  *out = handle->row = conn->results;
606 
607  return RLM_SQL_OK;
608 
609  case CS_ROW_FAIL:
610  ERROR("recoverable failure fetching row data");
611 
612  return RLM_SQL_RECONNECT;
613 
614  default:
615  ERROR("unexpected returncode from ct_fetch");
616 
617  return RLM_SQL_ERROR;
618  }
619 }
620 
622 {
623 
624  /*
625  * Not implemented, never called from rlm_sql anyway result buffer is freed in the
626  * finish_query functions.
627  */
628  return RLM_SQL_OK;
629 
630 }
631 
633 {
634  rlm_sql_freetds_conn_t *conn = handle->conn;
635 
636  ct_cancel(NULL, conn->command, CS_CANCEL_ALL);
637  if (ct_cmd_drop(conn->command) != CS_SUCCEED) {
638  ERROR("freeing command structure failed");
639 
640  return RLM_SQL_ERROR;
641  }
642  conn->command = NULL;
643  conn->rows_affected = -1;
644 
645  return RLM_SQL_OK;
646 }
647 
649 {
650  return sql_num_rows(handle, config);
651 }
652 
653 
655 {
656  DEBUG2("socket destructor called, closing socket");
657 
658  if (conn->command) {
659  ct_cancel(NULL, conn->command, CS_CANCEL_ALL);
660  if (ct_cmd_drop(conn->command) != CS_SUCCEED) {
661  ERROR("freeing command structure failed");
662 
663  return RLM_SQL_ERROR;
664  }
665  }
666 
667  if (conn->db) {
668  /*
669  * We first try gracefully closing the connection (which informs the server)
670  * Then if that fails we force the connection closure.
671  *
672  * Sybase docs says this may fail because of pending results, but we
673  * should not have any pending results at this point, so something else must
674  * of gone wrong.
675  */
676  if (ct_close(conn->db, CS_UNUSED) != CS_SUCCEED) {
677  ct_close(conn->db, CS_FORCE_CLOSE);
678  }
679 
680  ct_con_drop(conn->db);
681  }
682 
683  if (conn->context) {
684  ct_exit(conn->context, CS_UNUSED);
685  cs_ctx_drop(conn->context);
686  }
687 
688  return RLM_SQL_OK;
689 }
690 
692 {
694  unsigned int timeout_ms = fr_time_delta_to_msec(timeout);
695 
696  MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_freetds_conn_t));
697  talloc_set_destructor(conn, _sql_socket_destructor);
698 
699  /*
700  * Allocate a CS context structure. This should really only be done once, but because of
701  * the db pooling design of rlm_sql, we'll have to go with one context per db
702  */
703  if (cs_ctx_alloc(CS_VERSION_100, &conn->context) != CS_SUCCEED) {
704  ERROR("unable to allocate CS context structure (cs_ctx_alloc())");
705 
706  goto error;
707  }
708 
709  /*
710  * Initialize ctlib
711  */
712  if (ct_init(conn->context, CS_VERSION_100) != CS_SUCCEED) {
713  ERROR("unable to initialize Client-Library");
714 
715  goto error;
716  }
717 
718  if (ct_config(conn->context, CS_GET, CS_LOGIN_TIMEOUT, (CS_VOID *)&timeout_ms, CS_UNUSED, NULL) != CS_SUCCEED) {
719  ERROR("Setting connection timeout failed");
720 
721  goto error;
722  }
723 
724  /*
725  * Install callback functions for error-handling
726  */
727  if (cs_config(conn->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) {
728  ERROR("unable to install CS Library error callback");
729 
730  goto error;
731  }
732 
733  if (cs_config(conn->context, CS_SET, CS_USERDATA,
734  (CS_VOID *)&handle->conn, sizeof(handle->conn), NULL) != CS_SUCCEED) {
735  ERROR("unable to set userdata pointer");
736 
737  goto error;
738  }
739 
740  if (ct_callback(conn->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) {
741  ERROR("unable to install client message callback");
742 
743  goto error;
744  }
745 
746  if (ct_callback(conn->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) {
747  ERROR("unable to install server message callback");
748 
749  goto error;
750  }
751 
752  /*
753  * Allocate a ctlib db structure
754  */
755  if (ct_con_alloc(conn->context, &conn->db) != CS_SUCCEED) {
756  ERROR("unable to allocate db structure");
757 
758  goto error;
759  }
760 
761  /*
762  * Set User and Password properties for the db
763  */
764  {
765  char database[128];
766 
767  if (ct_con_props(conn->db, CS_SET, CS_USERNAME,
768  UNCONST(CS_VOID *, config->sql_login), strlen(config->sql_login), NULL) != CS_SUCCEED) {
769  ERROR("unable to set username for db");
770 
771  goto error;
772  }
773 
774  if (ct_con_props(conn->db, CS_SET, CS_PASSWORD,
775  UNCONST(CS_VOID *, config->sql_password), strlen(config->sql_password), NULL) != CS_SUCCEED) {
776  ERROR("unable to set password for db");
777 
778  goto error;
779  }
780 
781  /*
782  * Connect to the database
783  */
784  if (ct_connect(conn->db, UNCONST(CS_CHAR *, config->sql_server), strlen(config->sql_server)) != CS_SUCCEED) {
785  ERROR("unable to establish db to symbolic servername %s",
786  config->sql_server);
787 
788  goto error;
789  }
790 
791  /*
792  * There doesn't appear to be a way to set the database with the API, so use an
793  * sql statement when we first open the connection.
794  */
795  snprintf(database, sizeof(database), "USE %s;", config->sql_db);
796  if (sql_query(handle, config, database) != RLM_SQL_OK) {
797  goto error;
798  }
799 
800  sql_finish_query(handle, config);
801  }
802 
803  return RLM_SQL_OK;
804 
805 error:
806  if (conn->context) {
807  sql_log_entry_t error;
808 
809  if (sql_error(NULL, &error, 1, handle, config) > 0) ERROR("%s", error.msg);
810  }
811 
812  return RLM_SQL_ERROR;
813 }
814 
815 /* Exported to rlm_sql */
818  .common = {
819  .magic = MODULE_MAGIC_INIT,
820  .name = "sql_freetds"
821  },
822  .number = 10,
823  .sql_socket_init = sql_socket_init,
824  .sql_query = sql_query,
825  .sql_select_query = sql_select_query,
826  .sql_num_fields = sql_num_fields,
827  .sql_num_rows = sql_num_rows,
828  .sql_fields = sql_fields,
829  .sql_affected_rows = sql_affected_rows,
830  .sql_fetch_row = sql_fetch_row,
831  .sql_free_result = sql_free_result,
832  .sql_error = sql_error,
833  .sql_finish_query = sql_finish_query,
834  .sql_finish_select_query = sql_finish_select_query
835 };
static int context
Definition: radmin.c:71
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define RCSID(id)
Definition: build.h:444
#define NDEBUG_UNUSED
Definition: build.h:324
#define UNUSED
Definition: build.h:313
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
talloc_free(reap)
@ L_ERR
Error message.
Definition: log.h:56
static const conf_parser_t config[]
Definition: base.c:188
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define INFO(fmt,...)
Definition: radict.c:54
Prototypes and functions for the SQL module.
char const * msg
Log message.
Definition: rlm_sql.h:62
void * conn
Database specific connection handle.
Definition: rlm_sql.h:136
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:42
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:46
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:44
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:45
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:48
char ** rlm_sql_row_t
Definition: rlm_sql.h:57
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:137
Definition: rlm_sql.h:59
static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, fr_time_delta_t timeout)
char ** results
Result strings from statement execution.
static CS_RETCODE CS_PUBLIC servermsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_SERVERMSG *msgp)
Server error handler.
static CS_RETCODE CS_PUBLIC csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *emsgp)
Client error handler.
static sql_rcode_t sql_fetch_row(rlm_sql_row_t *out, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
CS_CONNECTION * db
Handle specifying a single connection to the database.
static int _sql_socket_destructor(rlm_sql_freetds_conn_t *conn)
#define MAX_DATASTR_LEN
static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
CS_COMMAND * command
A prepared statement.
static CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, UNUSED CS_CONNECTION *conn, CS_CLIENTMSG *emsgp)
Client-Library error handler.
static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
CS_INT rows_affected
Rows affected by last INSERT / UPDATE / DELETE.
bool established
Set to false once the connection has been properly established.
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config, char const *query)
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
Execute a query when we expected a result set.
CS_CONTEXT * context
Structure FreeTDS uses to avoid creating globals.
static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
char * error
The last error string created by one of the call backs.
rlm_sql_driver_t rlm_sql_freetds
static sql_rcode_t sql_free_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
static size_t sql_error(UNUSED TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
Retrieves any errors associated with the connection handle.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
return count
Definition: module.c:175
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:185
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
static const char * names[8]
Definition: time.c:617
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition: time.h:635
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
static size_t char ** out
Definition: value.h:984