The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
sql_fbapi.c
Go to the documentation of this file.
1 /*
2  * sql_fbapi.c Part of Firebird rlm_sql driver
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * @copyright 2006 The FreeRADIUS server project
19  * @copyright 2006 Vitaly Bodzhgua (vitaly@eastera.net)
20  */
21 
22 RCSID("$Id: 57be163cd0603de51a9c609da01210fb5ec2c30a $")
23 
24 #include "sql_fbapi.h"
25 
26 #include <stdarg.h>
27 
28 static void fb_dpb_add_str(char **dpb, char name, char const *value)
29 {
30  int l;
31 
32  if (!value) return;
33 
34  l = strlen(value);
35 
36  *(*dpb)++= name;
37  *(*dpb)++= (char) l;
38 
39  memmove(*dpb, value, l);
40 
41  *dpb += l;
42 }
43 
44 static void fb_set_sqlda(XSQLDA *sqlda) {
45  int i;
46 
47  for (i = 0; i < sqlda->sqld; i++) {
48  if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_VARYING) {
49  MEM(sqlda->sqlvar[i].sqldata = talloc_array(sqlda, char, sqlda->sqlvar[i].sqllen + sizeof(short)));
50  } else {
51  MEM(sqlda->sqlvar[i].sqldata = talloc_array(sqlda, char, sqlda->sqlvar[i].sqllen));
52  }
53 
54  if (sqlda->sqlvar[i].sqltype & 1) {
55  MEM(sqlda->sqlvar[i].sqlind = talloc(sqlda, short));
56  } else {
57  sqlda->sqlvar[i].sqlind = 0;
58  }
59  }
60 }
61 
62 DIAG_OFF(deprecated-declarations)
64 {
65  ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */
66  ISC_STATUS *pstatus;
67 
68  conn->sql_code = 0;
69 
70  /*
71  * Free any previous errors.
72  */
73  TALLOC_FREE(conn->error);
74 
75  /*
76  * Check if the status array contains an error
77  */
78  if (IS_ISC_ERROR(conn->status)) {
79  conn->sql_code = isc_sqlcode(conn->status);
80 
81  if (conn->sql_code == DUPLICATE_KEY_SQL_CODE) return conn->sql_code;
82 
83  /*
84  * pstatus is a pointer into the status array which is
85  * advanced by isc_interprete. It's initialised to the
86  * first element of the status array.
87  */
88  pstatus = &conn->status[0];
89 
90  /*
91  * It's deprecated because the size of the buffer isn't
92  * passed and this isn't safe. But as were passing a very
93  * large buffer it's unlikely this will be an issue, and
94  * allows us to maintain compatibility with the interbase
95  * API.
96  */
97  isc_interprete(&error[0], &pstatus);
98  conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]);
99 
100  while (isc_interprete(&error[0], &pstatus)) {
101  conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]);
102  }
103 
104  memset(&conn->status, 0, sizeof(conn->status));
105  }
106 
107  return conn->sql_code;
108 }
109 DIAG_ON(deprecated-declarations)
110 
111 void fb_free_sqlda(XSQLDA *sqlda)
112 {
113  int i;
114  for (i = 0; i < sqlda->sqld; i++) {
115  free(sqlda->sqlvar[i].sqldata);
116  free(sqlda->sqlvar[i].sqlind);
117  }
118  sqlda->sqld = 0;
119 }
120 
121 
122 
123 //Macro for NULLs check
124 #define IS_NULL(x) (x->sqltype & 1) && (*x->sqlind < 0)
125 
126 //Structure to manage a SQL_VARYING Firebird's data types
127 typedef struct {
128  short vary_length;
129  char vary_string[1];
130 } VARY;
131 
132 //function fb_store_row based on fiebird's apifull example
134 {
135  int dtype, i, nulls = 0;
136  struct tm times;
137  ISC_QUAD bid;
138  XSQLVAR *var;
139  VARY *vary;
140 
141  conn->row = talloc_zero_array(conn, char *, conn->sqlda_out->sqld + 1);
142 
143  for (i = 0, var = conn->sqlda_out->sqlvar; i < conn->sqlda_out->sqld; var++, i++) {
144  if (IS_NULL(var)) {
145  nulls++;
146  continue;
147  }
148 
149  dtype = var->sqltype & ~1;
150 
151  switch (dtype) {
152  case SQL_TEXT:
153  conn->row[i] = talloc_bstrndup(conn->row, var->sqldata, var->sqllen);
154  break;
155 
156  case SQL_VARYING:
157  vary = (VARY *)var->sqldata;
158  conn->row[i] = talloc_bstrndup(conn->row, vary->vary_string, vary->vary_length);
159  break;
160 
161  case SQL_FLOAT:
162  conn->row[i] = talloc_typed_asprintf(conn->row, "%g", *(double ISC_FAR *) (var->sqldata));
163  break;
164 
165  case SQL_SHORT:
166  case SQL_LONG:
167  case SQL_INT64:
168  {
169  ISC_INT64 value = 0;
170  short dscale = 0;
171 
172  switch (dtype) {
173  case SQL_SHORT:
174  value = (ISC_INT64) *(short *)var->sqldata;
175  break;
176 
177  case SQL_LONG:
178  value = (ISC_INT64) *(int *)var->sqldata;
179  break;
180 
181  case SQL_INT64:
182  value = (ISC_INT64) *(ISC_INT64 *)var->sqldata;
183  break;
184  }
185  dscale = var->sqlscale;
186 
187  if (dscale < 0) {
188  ISC_INT64 tens;
189  short j;
190 
191  tens = 1;
192  for (j = 0; j > dscale; j--) {
193  tens *= 10;
194  }
195 
196  if (value >= 0) {
197  conn->row[i] = talloc_typed_asprintf(conn->row, "%lld.%0*lld",
198  (ISC_INT64) value / tens,
199  -dscale,
200  (ISC_INT64) value % tens);
201  } else if ((value / tens) != 0) {
202  conn->row[i] = talloc_typed_asprintf(conn->row, "%lld.%0*lld",
203  (ISC_INT64) (value / tens),
204  -dscale,
205  (ISC_INT64) -(value % tens));
206  } else {
207  conn->row[i] = talloc_typed_asprintf(conn->row, "%s.%0*lld",
208  "-0", -dscale, (ISC_INT64) - (value % tens));
209  }
210  } else if (dscale) {
211  conn->row[i] = talloc_typed_asprintf(conn->row, "%lld%0*d", (ISC_INT64) value, dscale, 0);
212  } else {
213  conn->row[i] = talloc_typed_asprintf(conn->row, "%lld", (ISC_INT64) value);
214  }
215  }
216  break;
217 
218  case SQL_D_FLOAT:
219  case SQL_DOUBLE:
220  conn->row[i] = talloc_typed_asprintf(conn->row, "%f", *(double ISC_FAR *) (var->sqldata));
221  break;
222 
223  case SQL_TIMESTAMP:
224  isc_decode_timestamp((ISC_TIMESTAMP ISC_FAR *)var->sqldata, &times);
225  conn->row[i] = talloc_typed_asprintf(conn->row, "%04d-%02d-%02d %02d:%02d:%02d.%04d",
226  times.tm_year + 1900,
227  times.tm_mon + 1,
228  times.tm_mday,
229  times.tm_hour,
230  times.tm_min,
231  times.tm_sec,
232  ((ISC_TIMESTAMP *)var->sqldata)->timestamp_time % 10000);
233  break;
234 
235  case SQL_TYPE_DATE:
236  isc_decode_sql_date((ISC_DATE ISC_FAR *)var->sqldata, &times);
237  conn->row[i] = talloc_typed_asprintf(conn->row, "%04d-%02d-%02d",
238  times.tm_year + 1900,
239  times.tm_mon + 1,
240  times.tm_mday);
241  break;
242 
243  case SQL_TYPE_TIME:
244  isc_decode_sql_time((ISC_TIME ISC_FAR *)var->sqldata, &times);
245  conn->row[i] = talloc_typed_asprintf(conn->row, "%02d:%02d:%02d.%04d",
246  times.tm_hour,
247  times.tm_min,
248  times.tm_sec,
249  (*((ISC_TIME *)var->sqldata)) % 10000);
250  break;
251 
252  case SQL_BLOB:
253  case SQL_ARRAY:
254  /* Print the blob id on blobs or arrays */
255  bid = *(ISC_QUAD ISC_FAR *) var->sqldata;
256  conn->row[i] = talloc_typed_asprintf(conn->row, "%08" ISC_LONG_FMT "x:%08" ISC_LONG_FMT "x",
257  bid.gds_quad_high, bid.gds_quad_low);
258  break;
259  }
260  }
261 
262  /*
263  * An "UPDATE ... RETURNING" which updated nothing actually returns a row
264  * with all fields set to NULL. This is effectively no rows.
265  */
266  if (nulls == i) return RLM_SQL_NO_MORE_ROWS;
267 
268  return RLM_SQL_OK;
269 }
270 
272 {
273  char *p, *buff = NULL;
274  char const *database;
276 
277  conn->dpb_len = 7;
278  if (config->sql_login) conn->dpb_len+= strlen(config->sql_login) + 2;
279 
280  if (config->sql_password) conn->dpb_len += strlen(config->sql_password) + 2;
281 
282  MEM(conn->dpb = talloc_array(conn, char, conn->dpb_len));
283  p = conn->dpb;
284 
285  *conn->dpb++= isc_dpb_version1;
286 
287  /*
288  * Except for the version above, all Database Parameter Buffer options
289  * are LTV format, built from:
290  * - 1 byte option code
291  * - 1 byte length of value
292  * - 1 or more bytes of value. Integers are lsb first.
293  */
294  *conn->dpb++= isc_dpb_num_buffers;
295  *conn->dpb++= 1;
296  *conn->dpb++= 90;
297 
298  timeout = fr_time_delta_to_sec(config->trunk_conf.conn_conf->connection_timeout);
299  *conn->dpb++= isc_dpb_connect_timeout;
300  *conn->dpb++= 1;
301  *conn->dpb++= timeout;
302 
303  fb_dpb_add_str(&conn->dpb, isc_dpb_user_name, config->sql_login);
304  fb_dpb_add_str(&conn->dpb, isc_dpb_password, config->sql_password);
305 
306  conn->dpb = p;
307 
308  /*
309  * Check if database and server in the form of server:database.
310  * If config->sql_server contains ':', then config->sql_db
311  * parameter ignored.
312  */
313  if (strchr(config->sql_server, ':')) {
314  database = config->sql_server;
315  } else {
316  /*
317  * Make database and server to be in the form
318  * of server:database
319  */
320  database = buff = talloc_asprintf(NULL, "%s:%s", config->sql_server, config->sql_db);
321  }
322  DEBUG2("rlm_sql_firebird: Connecting to %s", database);
323  isc_attach_database(conn->status, 0, database, &conn->dbh,
324  conn->dpb_len, conn->dpb);
325  talloc_free(buff);
326 
327  return fb_error(conn);
328 }
329 
330 
332 {
333  long fetch_stat;
334  if (conn->statement_type!= isc_info_sql_stmt_select) return 100;
335 
336  fetch_stat = isc_dsql_fetch(conn->status, &conn->stmt,
337  SQL_DIALECT_V6, conn->sqlda_out);
338  if (fetch_stat) {
339  if (fetch_stat!= 100L) {
340  fb_error(conn);
341  } else {
342  conn->sql_code = 0;
343  }
344  }
345 
346  return fetch_stat;
347 }
348 
349 static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
350 {
351  static char stmt_info[] = { isc_info_sql_stmt_type };
352  char info_buffer[128];
353  short l;
354 
355  if (!conn->trh) {
356  isc_start_transaction(conn->status, &conn->trh, 1, &conn->dbh,
357  conn->tpb_len, conn->tpb);
358  if (!conn->trh) return -4;
359  }
360 
361  if (!conn->stmt) {
362  isc_dsql_allocate_statement(conn->status, &conn->dbh,
363  &conn->stmt);
364  if (!conn->stmt) return -1;
365  }
366 
367  isc_dsql_prepare(conn->status, &conn->trh, &conn->stmt, 0, query,
368  conn->sql_dialect, conn->sqlda_out);
369  if (IS_ISC_ERROR(conn->status)) return -2;
370 
371  if (conn->sqlda_out->sqln < conn->sqlda_out->sqld) {
372  conn->sqlda_out = (XSQLDA ISC_FAR *) _talloc_realloc_array(conn, conn->sqlda_out, 1,
373  XSQLDA_LENGTH(conn->sqlda_out->sqld), "XSQLDA");
374  conn->sqlda_out->sqln = conn->sqlda_out->sqld;
375  isc_dsql_describe(conn->status, &conn->stmt, SQL_DIALECT_V6,
376  conn->sqlda_out);
377 
378  if (IS_ISC_ERROR(conn->status)) return -3;
379  }
380  /*
381  * Get statement type
382  */
383  isc_dsql_sql_info(conn->status, &conn->stmt, sizeof(stmt_info),
384  stmt_info, sizeof(info_buffer), info_buffer);
385  if (IS_ISC_ERROR(conn->status)) return -4;
386 
387  l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
388  conn->statement_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
389 
390  if (conn->sqlda_out->sqld) fb_set_sqlda(conn->sqlda_out); //set out sqlda
391 
392  return 0;
393 }
394 
395 
396 int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query) {
397  if (fb_prepare(conn, query)) return fb_error(conn);
398 
399  switch (conn->statement_type) {
400  case isc_info_sql_stmt_exec_procedure:
401  isc_dsql_execute2(conn->status, &conn->trh, &conn->stmt,
402  SQL_DIALECT_V6, 0, conn->sqlda_out);
403  break;
404 
405  default:
406  isc_dsql_execute(conn->status, &conn->trh, &conn->stmt,
407  SQL_DIALECT_V6, 0);
408  break;
409  }
410  return fb_error(conn);
411 }
412 
414  static char count_info[] = {isc_info_sql_records};
415  char info_buffer[128];
416  char *p ;
417  int affected_rows = -1;
418 
419  if (!conn->stmt) return -1;
420 
421  isc_dsql_sql_info(conn->status, &conn->stmt,
422  sizeof (count_info), count_info,
423  sizeof (info_buffer), info_buffer);
424 
425  if (IS_ISC_ERROR(conn->status)) return fb_error(conn);
426 
427  p = info_buffer + 3;
428  while (*p != isc_info_end) {
429  short len;
430  len = (short)isc_vax_integer(++p, 2);
431  p += 2;
432 
433  affected_rows = isc_vax_integer(p, len);
434  if (affected_rows > 0) break;
435  p += len;
436  }
437  return affected_rows;
438 }
439 
441  if (conn->stmt) {
442  isc_dsql_free_statement(conn->status, &conn->stmt, DSQL_drop);
443  conn->stmt = 0;
444  }
445 }
446 
448  conn->sql_code = 0;
449  if (conn->trh) {
450  isc_rollback_transaction(conn->status, &conn->trh);
451  if (IS_ISC_ERROR(conn->status)) return fb_error(conn);
452  }
453  return conn->sql_code;
454 }
455 
457  conn->sql_code = 0;
458  if (conn->trh) {
459  isc_commit_transaction(conn->status, &conn->trh);
460  if (IS_ISC_ERROR(conn->status)) {
461  fb_error(conn);
462  ERROR("Fail to commit. Error: %s. Try to rollback.", conn->error);
463  return fb_rollback(conn);
464  }
465  }
466  return conn->sql_code;
467 }
#define RCSID(id)
Definition: build.h:481
#define DIAG_ON(_x)
Definition: build.h:456
#define DIAG_OFF(_x)
Definition: build.h:455
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
Test enumeration values.
Definition: dict_test.h:92
free(array)
talloc_free(reap)
unsigned char uint8_t
Definition: merged_model.c:30
static const conf_parser_t config[]
Definition: base.c:183
#define DEBUG2(fmt,...)
Definition: radclient.h:43
static char const * name
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:44
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:50
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
short vary_length
Definition: sql_fbapi.c:128
static void fb_dpb_add_str(char **dpb, char name, char const *value)
Definition: sql_fbapi.c:28
int fb_sql_query(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:396
int fb_connect(rlm_sql_firebird_conn_t *conn, rlm_sql_config_t const *config)
Definition: sql_fbapi.c:271
static void fb_set_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:44
char vary_string[1]
Definition: sql_fbapi.c:129
int fb_rollback(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:447
int fb_error(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:63
int fb_fetch(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:331
void fb_free_sqlda(XSQLDA *sqlda)
Definition: sql_fbapi.c:111
static int fb_prepare(rlm_sql_firebird_conn_t *conn, char const *query)
Definition: sql_fbapi.c:349
#define IS_NULL(x)
Definition: sql_fbapi.c:124
sql_rcode_t fb_store_row(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:133
int fb_commit(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:456
int fb_affected_rows(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:413
void fb_free_statement(rlm_sql_firebird_conn_t *conn)
Definition: sql_fbapi.c:440
#define DUPLICATE_KEY_SQL_CODE
Definition: sql_fbapi.h:35
isc_tr_handle trh
Definition: sql_fbapi.h:48
#define IS_ISC_ERROR(status)
Definition: sql_fbapi.h:31
isc_stmt_handle stmt
Definition: sql_fbapi.h:47
isc_db_handle dbh
Definition: sql_fbapi.h:46
ISC_STATUS status[20]
Magic interbase status code array (holds multiple error codes used to construct more detailed error m...
Definition: sql_fbapi.h:50
rlm_sql_row_t row
Definition: sql_fbapi.h:63
#define ISC_LONG_FMT
Definition: sql_fbapi.h:41
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition: time.h:647