All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_sql_iodbc.c
Go to the documentation of this file.
1 /*
2  * sql_iodbc.c iODBC support for FreeRadius
3  *
4  * Version: $Id: adeb4a1209bec1d5da9c862806a9e74dbc7bd0ca $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006 The FreeRADIUS server project
21  * Copyright 2000 Jeff Carneal <jeff@apex.net>
22  */
23 
24 RCSID("$Id: adeb4a1209bec1d5da9c862806a9e74dbc7bd0ca $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29 
30 #include <sys/stat.h>
31 
32 #include <isql.h>
33 #include <isqlext.h>
34 #include <sqltypes.h>
35 
36 #include "rlm_sql.h"
37 
38 #define IODBC_MAX_ERROR_LEN 256
39 
40 typedef struct rlm_sql_iodbc_conn {
41  HENV env_handle;
42  HDBC dbc_handle;
43  HSTMT stmt;
44  int id;
45 
47 
48  struct sql_socket *next;
49 
50  void *conn;
52 
53 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
54  rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config);
55 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
56 
58 {
59  DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
60 
61  if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
62 
63  if (conn->dbc_handle) {
64  SQLDisconnect(conn->dbc_handle);
65  SQLFreeConnect(conn->dbc_handle);
66  }
67 
68  if (conn->env_handle) SQLFreeEnv(conn->env_handle);
69 
70  return 0;
71 }
72 
73 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config, struct timeval const *timeout)
74 {
75 
77  SQLRETURN rcode;
78  sql_log_entry_t entry;
79  uint32_t timeout_ms = FR_TIMEVAL_TO_MS(timeout);
80 
81  MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
82  talloc_set_destructor(conn, _sql_socket_destructor);
83 
84  rcode = SQLAllocEnv(&conn->env_handle);
85  if (!SQL_SUCCEEDED(rcode)) {
86  ERROR("rlm_sql_iodbc: SQLAllocEnv failed");
87  if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
88 
89  return RLM_SQL_ERROR;
90  }
91 
92  rcode = SQLAllocConnect(conn->env_handle, &conn->dbc_handle);
93  if (!SQL_SUCCEEDED(rcode)) {
94  ERROR("rlm_sql_iodbc: SQLAllocConnect failed");
95  if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
96 
97  return RLM_SQL_ERROR;
98  }
99 
100  /* Set the connection timeout */
101  SQLSetConnectAttr(conn->dbc_handle, SQL_ATTR_LOGIN_TIMEOUT, &timeout_ms, SQL_IS_UINTEGER);
102 
103  /*
104  * The iodbc API doesn't qualify arguments as const even when they should be.
105  */
106  {
107  SQLCHAR *server, *login, *password;
108 
109  memcpy(&server, &config->sql_server, sizeof(server));
110  memcpy(&login, &config->sql_login, sizeof(login));
111  memcpy(&password, &config->sql_password, sizeof(password));
112 
113  rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
114  }
115  if (!SQL_SUCCEEDED(rcode)) {
116  ERROR("rlm_sql_iodbc: SQLConnectfailed");
117  if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
118 
119  return RLM_SQL_ERROR;
120  }
121 
122  return 0;
123 }
124 
125 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
126 {
127  rlm_sql_iodbc_conn_t *conn = handle->conn;
128  SQLRETURN rcode;
129 
130  rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt);
131  if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
132 
133  if (!conn->dbc_handle) {
134  ERROR("rlm_sql_iodbc: Socket not connected");
135  return RLM_SQL_ERROR;
136  }
137 
138  {
139  SQLCHAR *statement;
140 
141  memcpy(&statement, &query, sizeof(statement));
142  rcode = SQLExecDirect(conn->stmt, statement, SQL_NTS);
143  }
144 
145  if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
146 
147  return 0;
148 }
149 
150 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
151 {
152  int numfields = 0;
153  int i = 0;
154  char **row = NULL;
155  long len = 0;
156  rlm_sql_iodbc_conn_t *conn = handle->conn;
157 
158  if (sql_query(handle, config, query) < 0) return RLM_SQL_ERROR;
159 
160  numfields = sql_num_fields(handle, config);
161 
162  row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
163  memset(row, 0, (sizeof(char *) * (numfields)));
164  row[numfields] = NULL;
165 
166  for(i=1; i<=numfields; i++) {
167  SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
168  len++;
169 
170  /*
171  * Allocate space for each column
172  */
173  row[i - 1] = rad_malloc((size_t) len);
174 
175  /*
176  * This makes me feel dirty, but, according to Microsoft, it works.
177  * Any ODBC datatype can be converted to a 'char *' according to
178  * the following:
179  *
180  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
181  */
182  SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
183  }
184 
185  conn->row = row;
186 
187  return 0;
188 }
189 
191 {
192 
193  SQLSMALLINT count=0;
194  rlm_sql_iodbc_conn_t *conn = handle->conn;
195 
196  SQLNumResultCols(conn->stmt, &count);
197 
198  return (int)count;
199 }
200 
201 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
202 {
203  rlm_sql_iodbc_conn_t *conn = handle->conn;
204 
205  SQLSMALLINT fields, len, i;
206 
207  char const **names;
208  char field[128];
209 
210  SQLNumResultCols(conn->stmt, &fields);
211  if (fields == 0) return RLM_SQL_ERROR;
212 
213  MEM(names = talloc_array(handle, char const *, fields));
214 
215  for (i = 0; i < fields; i++) {
216  char *p;
217 
218  switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
219  field, sizeof(field), &len, NULL)) {
220  case SQL_INVALID_HANDLE:
221  case SQL_ERROR:
222  ERROR("Failed retrieving field name at index %i", i);
223  talloc_free(names);
224  return RLM_SQL_ERROR;
225 
226  default:
227  break;
228  }
229 
230  MEM(p = talloc_array(names, char, (size_t)len + 1));
231  strlcpy(p, field, (size_t)len + 1);
232  names[i] = p;
233  }
234  *out = names;
235 
236  return RLM_SQL_OK;
237 }
238 
240 {
241  SQLRETURN rc;
242  rlm_sql_iodbc_conn_t *conn = handle->conn;
243 
244  *out = handle->row = NULL;
245 
246  if ((rc = SQLFetch(conn->stmt)) == SQL_NO_DATA_FOUND) return 0;
247  /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
248 
249  *out = handle->row = conn->row;
250  return RLM_SQL_OK;
251 }
252 
254 {
255  int i = 0;
256  rlm_sql_iodbc_conn_t *conn = handle->conn;
257 
258  for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]);
259  free(conn->row);
260  conn->row = NULL;
261 
262  SQLFreeStmt(conn->stmt, SQL_DROP);
263 
264  return 0;
265 }
266 
267 /** Retrieves any errors associated with the connection handle
268  *
269  * @note Caller will free any memory allocated in ctx.
270  *
271  * @param ctx to allocate temporary error buffers in.
272  * @param out Array of sql_log_entrys to fill.
273  * @param outlen Length of out array.
274  * @param handle rlm_sql connection handle.
275  * @param config rlm_sql config.
276  * @return number of errors written to the #sql_log_entry_t array.
277  */
278 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
279  rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
280 {
281  rlm_sql_iodbc_conn_t *conn = handle->conn;
282  SQLINTEGER errornum = 0;
283  SQLSMALLINT length = 0;
284  SQLCHAR state[256] = "";
285  SQLCHAR errbuff[IODBC_MAX_ERROR_LEN];
286 
287  rad_assert(outlen > 0);
288 
289  errbuff[0] = '\0';
290  SQLError(conn->env_handle, conn->dbc_handle, conn->stmt,
291  state, &errornum, errbuff, IODBC_MAX_ERROR_LEN, &length);
292  if (errbuff[0] == '\0') return 0;
293 
294  out[0].type = L_ERR;
295  out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff);
296 
297  return 1;
298 }
299 
301 {
302  return sql_free_result(handle, config);
303 }
304 
306 {
307  return sql_free_result(handle, config);
308 }
309 
311 {
312  long count;
313  rlm_sql_iodbc_conn_t *conn = handle->conn;
314 
315  SQLRowCount(conn->stmt, &count);
316  return (int)count;
317 }
318 
319 /* Exported to rlm_sql */
321 rlm_sql_module_t rlm_sql_iodbc = {
322  .name = "rlm_sql_iodbc",
323  .sql_socket_init = sql_socket_init,
324  .sql_query = sql_query,
325  .sql_select_query = sql_select_query,
326  .sql_num_fields = sql_num_fields,
327  .sql_affected_rows = sql_affected_rows,
328  .sql_fields = sql_fields,
329  .sql_fetch_row = sql_fetch_row,
330  .sql_free_result = sql_free_result,
331  .sql_error = sql_error,
332  .sql_finish_query = sql_finish_query,
333  .sql_finish_select_query = sql_finish_select_query
334 };
static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
General connection/server error.
Definition: rlm_sql.h:46
Prototypes and functions for the SQL module.
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
log_type_t type
Type of log entry L_ERR, L_WARN, L_INFO, L_DBG etc..
Definition: rlm_sql.h:62
void * rad_malloc(size_t size)
Definition: util.c:411
#define MEM(x)
Definition: radiusd.h:396
char const * msg
Log message.
Definition: rlm_sql.h:63
#define UNUSED
Definition: libradius.h:134
Error message.
Definition: log.h:36
char const * sql_server
Server to connect to.
Definition: rlm_sql.h:85
rlm_sql_row_t row
Definition: rlm_sql_iodbc.c:46
static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config, struct timeval const *timeout)
Definition: rlm_sql_iodbc.c:73
uint8_t length
Definition: proto_bfd.c:203
static float timeout
Definition: radclient.c:43
static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
rlm_sql_module_t rlm_sql_iodbc
#define rad_assert(expr)
Definition: rad_assert.h:38
char const * name
Definition: rlm_sql.h:191
sql_rcode_t
Definition: rlm_sql.h:44
static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
#define DEBUG2(fmt,...)
Definition: log.h:176
Definition: rlm_sql.h:61
static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Success.
Definition: rlm_sql.h:47
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
char const * sql_password
Login password to use.
Definition: rlm_sql.h:88
void * conn
Database specific connection handle.
Definition: rlm_sql.h:153
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:154
struct rlm_sql_iodbc_conn rlm_sql_iodbc_conn_t
unsigned int state
Definition: proto_bfd.c:200
static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
Definition: rlm_sql_iodbc.c:57
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
Retrieves any errors associated with the connection handle.
char const * sql_login
Login credentials to use.
Definition: rlm_sql.h:87
struct sql_socket * next
Definition: rlm_sql_iodbc.c:48
#define IODBC_MAX_ERROR_LEN
Definition: rlm_sql_iodbc.c:38
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
#define FR_TIMEVAL_TO_MS(_x)
Definition: conffile.h:235
static sql_rcode_t sql_fetch_row(rlm_sql_row_t *out, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
#define RCSID(id)
Definition: build.h:135
#define ERROR(fmt,...)
Definition: log.h:145
#define USES_APPLE_DEPRECATED_API
Definition: build.h:122