Permit EAP-Message and State from the home server, so that
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_sybase / sql_sybase.c
1 /*
2  * sql_sybase.c Sybase (ctlibrary) routines for rlm_sql
3  *              Error handling stolen from Sybase example code "firstapp.c"
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Copyright 2000  The FreeRADIUS server project
20  * Copyright 2000  Mattias Sjostrom <mattias@nogui.se>
21  */
22
23 #include <stdio.h>
24 #include <sys/stat.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include        "radiusd.h"
29
30 #include <ctpublic.h>
31 #include "rlm_sql.h"
32
33
34 typedef struct rlm_sql_sybase_sock {
35         CS_CONTEXT      *context;
36         CS_CONNECTION   *connection;
37         CS_COMMAND      *command;
38         char            **results;
39         int             id;
40         int             in_use;
41         struct timeval  tv;
42 } rlm_sql_sybase_sock;
43
44
45 #define MAX_DATASTR_LEN 256
46
47 /************************************************************************
48 * Handler for server messages. Client-Library will call this
49 * routine when it receives a message from the server.
50 ************************************************************************/
51
52 static CS_RETCODE CS_PUBLIC
53 servermsg_callback(cp, chp, msgp)
54 CS_CONTEXT         *cp;
55 CS_CONNECTION      *chp;
56 CS_SERVERMSG       *msgp;
57 {
58
59         /*
60         ** Print the message info.
61         */
62         radlog(L_ERR,
63                 "Sybase Server message:\n");
64         radlog(L_ERR,
65                 "number(%ld) severity(%ld) state(%ld) line(%ld)\n",
66                 (long)msgp->msgnumber, (long)msgp->severity,
67                 (long)msgp->state, (long)msgp->line);
68
69         /*
70         ** Print the server and procedure names if supplied.
71         */
72         if (msgp->svrnlen > 0 && msgp->proclen > 0)
73                 radlog(L_ERR, "Server name: %s   Procedure name: %s", msgp->svrname, msgp->proc);
74
75         /*
76         ** Print the null terminated message.
77         */
78         radlog(L_ERR, "%s\n", msgp->text);
79
80         /*
81         ** Server message callbacks must return CS_SUCCEED.
82         */
83         return (CS_SUCCEED);
84 }
85
86 /************************************************************************
87 *  Client-Library error handler.
88 ************************************************************************/
89
90 static CS_RETCODE CS_PUBLIC
91 clientmsg_callback(context, conn, emsgp)
92 CS_CONTEXT         *context;
93 CS_CONNECTION      *conn;
94 CS_CLIENTMSG       *emsgp;
95 {
96
97         /*
98         ** Error number: Print the error's severity, number, origin, and
99         ** layer. These four numbers uniquely identify the error.
100         */
101         radlog(L_ERR,
102                 "Client Library error:\n");
103         radlog(L_ERR,
104                 "severity(%ld) number(%ld) origin(%ld) layer(%ld)\n",
105                 (long)CS_SEVERITY(emsgp->severity),
106                 (long)CS_NUMBER(emsgp->msgnumber),
107                 (long)CS_ORIGIN(emsgp->msgnumber),
108                 (long)CS_LAYER(emsgp->msgnumber));
109
110         /*
111         ** Error text: Print the error text.
112         */
113         radlog(L_ERR, "%s\n", emsgp->msgstring);
114
115         if (emsgp->osstringlen > 0)
116         {
117                 radlog(L_ERR,
118                         "Operating system error number(%ld):\n",
119                         (long)emsgp->osnumber);
120                 radlog(L_ERR, "%s\n", emsgp->osstring);
121         }
122
123         return (CS_SUCCEED);
124 }
125
126 /************************************************************************
127 *  CS-Library error handler. This function will be invoked
128 *  when CS-Library has detected an error.
129 ************************************************************************/
130
131 static CS_RETCODE CS_PUBLIC
132 csmsg_callback(context, emsgp)
133 CS_CONTEXT         *context;
134 CS_CLIENTMSG       *emsgp;
135 {
136
137         /*
138         ** Print the error number and message.
139         */
140         radlog(L_ERR,
141                 "CS-Library error:\n");
142         radlog(L_ERR,
143                 "\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)",
144                 (long)CS_SEVERITY(emsgp->msgnumber),
145                 (long)CS_LAYER(emsgp->msgnumber),
146                 (long)CS_ORIGIN(emsgp->msgnumber),
147                 (long)CS_NUMBER(emsgp->msgnumber));
148
149         radlog(L_ERR, "%s\n", emsgp->msgstring);
150
151         /*
152         ** Print any operating system error information.
153         */
154         if (emsgp->osstringlen > 0)
155         {
156                 radlog(L_ERR, "Operating System Error: %s\n",
157                         emsgp->osstring);
158         }
159
160         return (CS_SUCCEED);
161 }
162
163 /*************************************************************************
164  *
165  *      Function: sql_init_socket
166  *
167  *      Purpose: Establish connection to the db
168  *
169  *************************************************************************/
170 static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
171
172         rlm_sql_sybase_sock *sybase_sock;
173
174
175         if (!sqlsocket->conn) {
176                 sqlsocket->conn = (rlm_sql_sybase_sock *)rad_malloc(sizeof(rlm_sql_sybase_sock));
177                 if (!sqlsocket->conn) {
178                         return -1;
179                 }
180         }
181         sybase_sock = sqlsocket->conn;
182         memset(sybase_sock, 0, sizeof(*sybase_sock));
183
184         sybase_sock->results=NULL;
185
186         /* Allocate a CS context structure. This should really only be done once, but because of
187            the connection pooling design of rlm_sql, we'll have to go with one context per connection */
188
189         if (cs_ctx_alloc(CS_VERSION_100, &sybase_sock->context) != CS_SUCCEED) {
190                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate CS context structure (cs_ctx_alloc())");
191                 return -1;
192         }
193
194         /* Initialize ctlib */
195
196         if (ct_init(sybase_sock->context, CS_VERSION_100) != CS_SUCCEED) {
197                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize Client-Library (ct_init())");
198                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
199                         cs_ctx_drop(sybase_sock->context);
200                 }
201                 return -1;
202         }
203
204         /* Install callback functions for error-handling */
205
206         if (cs_config(sybase_sock->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) {
207                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install CS Library error callback");
208                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
209                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
210                         cs_ctx_drop(sybase_sock->context);
211                 }
212                 return -1;
213         }
214
215         if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) {
216                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
217                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
218                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
219                         cs_ctx_drop(sybase_sock->context);
220                 }
221                 return -1;
222         }
223
224         if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) {
225                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
226                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
227                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
228                         cs_ctx_drop(sybase_sock->context);
229                 }
230                 return -1;
231         }
232
233         /* Allocate a ctlib connection structure */
234
235         if (ct_con_alloc(sybase_sock->context, &sybase_sock->connection) != CS_SUCCEED) {
236                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate connection structure (ct_con_alloc())");
237                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
238                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
239                         cs_ctx_drop(sybase_sock->context);
240                 }
241                 return -1;
242         }
243
244         /* Initialize inline error handling for the connection */
245
246 /*      if (ct_diag(sybase_sock->connection, CS_INIT, CS_UNUSED, CS_UNUSED, NULL) != CS_SUCCEED) {
247                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize error handling (ct_diag())");
248                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
249                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
250                         cs_ctx_drop(sybase_sock->context);
251                 }
252                 return -1;
253         } */
254
255
256
257         /* Set User and Password properties for the connection */
258
259         if (ct_con_props(sybase_sock->connection, CS_SET, CS_USERNAME, config->sql_login,
260                                          strlen(config->sql_login), NULL) != CS_SUCCEED) {
261                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set username for connection (ct_con_props())\n%s",
262                 sql_error(sqlsocket, config));
263                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
264                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
265                         cs_ctx_drop(sybase_sock->context);
266                 }
267                 return -1;
268         }
269
270         if (ct_con_props(sybase_sock->connection, CS_SET, CS_PASSWORD, config->sql_password,
271                                         strlen(config->sql_password), NULL) != CS_SUCCEED) {
272                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set password for connection (ct_con_props())\n%s",
273                 sql_error(sqlsocket, config));
274                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
275                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
276                         cs_ctx_drop(sybase_sock->context);
277                 }
278                 return -1;
279         }
280
281         /* Establish the connection */
282
283         if (ct_connect(sybase_sock->connection, config->sql_server, strlen(config->sql_server)) != CS_SUCCEED) {
284                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to establish connection to symbolic servername %s\n%s",
285                                 config->sql_server, sql_error(sqlsocket, config));
286                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
287                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
288                         cs_ctx_drop(sybase_sock->context);
289                 }
290                 return -1;
291         }
292         return 0;
293 }
294
295
296 /*************************************************************************
297  *
298  *      Function: sql_destroy_socket
299  *
300  *      Purpose: Free socket and private connection data
301  *
302  *************************************************************************/
303 static int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config)
304 {
305         free(sqlsocket->conn);
306         sqlsocket->conn = NULL;
307         return 0;
308 }
309
310 /*************************************************************************
311  *
312  *      Function: sql_query
313  *
314  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
315  *               the database.
316  *
317  *************************************************************************/
318 static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
319
320         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
321
322         CS_RETCODE      ret, results_ret;
323         CS_INT          result_type;
324
325         if (config->sqltrace)
326                 DEBUG(querystr);
327          if (sybase_sock->connection == NULL) {
328                 radlog(L_ERR, "Socket not connected");
329                 return -1;
330         }
331
332         if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
333                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
334                                 sql_error(sqlsocket, config));
335                 return -1;
336         }
337
338         if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
339                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to initiate command structure (ct_command())\n%s",
340                                 sql_error(sqlsocket, config));
341                 return -1;
342         }
343
344         if (ct_send(sybase_sock->command) != CS_SUCCEED) {
345                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to send command (ct_send())\n%s",
346                                 sql_error(sqlsocket, config));
347                 return -1;
348         }
349
350         /*
351         ** We'll make three calls to ct_results, first to get a success indicator, secondly to get a done indicator, and
352         ** thirdly to get a "nothing left to handle" status.
353         */
354
355         /*
356         ** First call to ct_results,
357         ** we need returncode CS_SUCCEED
358         ** and result_type CS_CMD_SUCCEED.
359         */
360
361         if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
362                 if (result_type != CS_CMD_SUCCEED) {
363                         if  (result_type == CS_ROW_RESULT) {
364                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): sql_query processed a query returning rows. Use sql_select_query instead!");
365                         }
366                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
367                                          sql_error(sqlsocket, config));
368                         return -1;
369                 }
370         }
371         else {
372                 switch ((int) results_ret)
373                 {
374
375                 case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
376                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
377                                         , sql_error(sqlsocket, config));
378                         if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
379                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
380                                 ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
381                                 sql_close(sqlsocket, config);
382                         }
383                         return -1;
384                         break;
385
386                 default:
387                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
388                                         sql_error(sqlsocket, config));
389                         return -1;
390                 }
391         }
392
393
394         /*
395         ** Second call to ct_results,
396         ** we need returncode CS_SUCCEED
397         ** and result_type CS_CMD_DONE.
398         */
399
400         if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
401                 if (result_type != CS_CMD_DONE) {
402                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
403                                          sql_error(sqlsocket, config));
404                         return -1;
405                 }
406         }
407         else {
408                 switch ((int) results_ret)
409                 {
410
411                 case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
412                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
413                                         , sql_error(sqlsocket, config));
414                         if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
415                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
416                                 ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
417                                 sql_close(sqlsocket, config);
418                         }
419                         return -1;
420                         break;
421
422                 default:
423                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
424                                         sql_error(sqlsocket, config));
425                         return -1;
426                 }
427         }
428
429
430         /*
431         ** Third call to ct_results,
432         ** we need returncode CS_END_RESULTS
433         ** result_type will be ignored.
434         */
435
436         results_ret = ct_results(sybase_sock->command, &result_type);
437
438         switch ((int) results_ret)
439         {
440
441         case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
442                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
443                                 , sql_error(sqlsocket, config));
444                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
445                         radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
446                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
447                         sql_close(sqlsocket, config);
448                 }
449                 return -1;
450                 break;
451
452         case CS_END_RESULTS:  /* This is where we want to end up */
453                 break;
454
455         default:
456                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
457                                 sql_error(sqlsocket, config));
458                 return -1;
459                 break;
460         }
461         return 0;
462 }
463
464
465
466
467 /*************************************************************************
468  *
469  *      Function: sql_select_query
470  *
471  *      Purpose: Issue a select query to the database
472  *
473  *      Note: Only the first row from queries returning several rows
474  *            will be returned by this function, consequitive rows will
475  *            be discarded.
476  *
477  *************************************************************************/
478 static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
479
480         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
481
482         CS_RETCODE      ret, results_ret;
483         CS_INT          result_type;
484         CS_DATAFMT      descriptor;
485
486         int             colcount,i;
487         char            **rowdata;
488
489         if (config->sqltrace)
490                 DEBUG(querystr);
491          if (sybase_sock->connection == NULL) {
492                 radlog(L_ERR, "Socket not connected");
493                 return -1;
494         }
495
496
497         if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
498                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
499                                 sql_error(sqlsocket, config));
500                 return -1;
501         }
502
503         if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
504                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to initiate command structure (ct_command())\n%s",
505                                 sql_error(sqlsocket, config));
506                 return -1;
507         }
508
509         if (ct_send(sybase_sock->command) != CS_SUCCEED) {
510                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to send command (ct_send())\n%s",
511                                 sql_error(sqlsocket, config));
512                 return -1;
513         }
514
515         results_ret = ct_results(sybase_sock->command, &result_type);
516
517         switch (results_ret) {
518
519         case CS_SUCCEED:
520
521                 switch (result_type) {
522
523                 case CS_ROW_RESULT:
524
525                 /*
526                 ** Houston, we have a row.
527                 **
528                 ** We set up a target buffer for the results data, and
529                 ** associate the buffer with the results, but the actual
530                 ** fetching takes place in sql_fetch_row. The layer above
531                 ** MUST call sql_fetch_row and/or sql_finish_select_query
532                 ** or this socket will be unusable and may cause segfaults
533                 ** if reused later on.
534                 */
535
536                         /*
537                         ** Set up the DATAFMT structure that describes our target array
538                         ** and tells sybase what we want future ct_fetch calls to do.
539                         */
540                         descriptor.datatype = CS_CHAR_TYPE;     /* The target buffer is a string */
541                         descriptor.format = CS_FMT_NULLTERM;    /* Null termination please */
542                         descriptor.maxlength = MAX_DATASTR_LEN; /* The string arrays are this large */
543                         descriptor.count = 1;                   /* Fetch one row of data */
544                         descriptor.locale = NULL;               /* Don't do NLS stuff */
545
546
547                         colcount = sql_num_fields(sqlsocket, config); /* Get number of elements in row result */
548
549
550                         rowdata=(char **)rad_malloc(sizeof(char *) * (colcount+1));     /* Space for pointers */
551                         memset(rowdata, 0, (sizeof(char *) * colcount+1));  /* NULL-pad the pointers */
552
553                         for (i=0; i < colcount; i++) {
554
555                                 rowdata[i]=rad_malloc((MAX_DATASTR_LEN * sizeof(char))+1); /* Space to hold the result data */
556
557                                 /* Associate the target buffer with the data */
558                                 if (ct_bind(sybase_sock->command, i+1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) {
559                                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): ct_bind() failed)\n%s",
560                                                         sql_error(sqlsocket, config));
561                                         return -1;
562                                 }
563
564                         }
565                         rowdata[i]=NULL; /* Terminate the array */
566                         sybase_sock->results=rowdata;
567                         break;
568
569                 case CS_CMD_SUCCEED:
570                 case CS_CMD_DONE:
571
572                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Query returned no data");
573                         break;
574
575                 default:
576
577                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected result type from query\n%s",
578                                          sql_error(sqlsocket, config));
579                         sql_finish_select_query(sqlsocket, config);
580                         return -1;
581                         break;
582                 }
583                 break;
584
585         case CS_FAIL:
586
587                 /*
588                 ** Serious failure, sybase requires us to cancel
589                 ** the results and maybe even close the connection.
590                 */
591
592                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Failure retrieving query results\n%s"
593                                 , sql_error(sqlsocket, config));
594                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
595                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): cleaning up.");
596                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
597                         sql_close(sqlsocket, config);
598                 }
599                 return -1;
600                 break;
601
602         default:
603
604                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected return value from ct_results()\n%s",
605                                 sql_error(sqlsocket, config));
606                 return -1;
607                 break;
608         }
609         return 0;
610 }
611
612
613 /*************************************************************************
614  *
615  *      Function: sql_store_result
616  *
617  *      Purpose: database specific store_result function. Returns a result
618  *               set for the query.
619  *
620  *************************************************************************/
621 static int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
622         /*
623         ** Not needed for Sybase, code that may have gone here is
624         ** in sql_select_query and sql_fetch_row
625         */
626         return 0;
627 }
628
629
630 /*************************************************************************
631  *
632  *      Function: sql_num_fields
633  *
634  *      Purpose: database specific num_fields function. Returns number
635  *               of columns from query
636  *
637  *************************************************************************/
638 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
639
640         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
641         int     num;
642
643         if (ct_res_info(sybase_sock->command, CS_NUMDATA, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
644                 radlog(L_ERR,"rlm_sql_sybase(sql_num_fields): error retrieving column count: %s",
645                         sql_error(sqlsocket, config));
646                 return -1;
647         }
648         return num;
649 }
650
651 /*************************************************************************
652  *
653  *      Function: sql_num_rows
654  *
655  *      Purpose: database specific num_rows. Returns number of rows in
656  *               query
657  *
658  *************************************************************************/
659 static int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
660
661         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
662         int     num;
663
664         if (ct_res_info(sybase_sock->command, CS_ROW_COUNT, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
665                 radlog(L_ERR,"rlm_sql_sybase(sql_num_rows): error retrieving row count: %s",
666                         sql_error(sqlsocket, config));
667                 return -1;
668         }
669         return num;
670 }
671
672
673 /*************************************************************************
674  *
675  *      Function: sql_fetch_row
676  *
677  *      Purpose: database specific fetch_row. Returns a SQL_ROW struct
678  *               with all the data for the query in 'sqlsocket->row'. Returns
679  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'.
680  *
681  *************************************************************************/
682 int sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
683
684         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
685         CS_INT          ret, count;
686
687         sqlsocket->row = NULL;
688
689
690         ret = ct_fetch(sybase_sock->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count);
691
692         switch (ret) {
693
694         case CS_FAIL:
695
696                 /*
697                 ** Serious failure, sybase requires us to cancel
698                 ** the results and maybe even close the connection.
699                 */
700
701                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Failure fething row data\n%s"
702                                 , sql_error(sqlsocket, config));
703                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
704                         radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): cleaning up.");
705                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
706                         sql_close(sqlsocket, config);
707                 }
708                 return SQL_DOWN;
709                 break;
710
711         case CS_END_DATA:
712
713                 return 0;
714                 break;
715
716         case CS_SUCCEED:
717
718                 sqlsocket->row = sybase_sock->results;
719                 return 0;
720                 break;
721
722         case CS_ROW_FAIL:
723
724                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Recoverable failure fething row data, try again perhaps?");
725                 return -1;
726
727         default:
728
729                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Unexpected returncode from ct_fetch");
730                 return -1;
731                 break;
732         }
733
734 }
735
736
737
738 /*************************************************************************
739  *
740  *      Function: sql_free_result
741  *
742  *      Purpose: database specific free_result. Frees memory allocated
743  *               for a result set
744  *
745  *************************************************************************/
746 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
747
748         /*
749         ** Not implemented, never called from rlm_sql anyway
750         ** result buffer is freed in the finish_query functions.
751         */
752
753         return 0;
754
755 }
756
757
758
759 /*************************************************************************
760  *
761  *      Function: sql_error
762  *
763  *      Purpose: database specific error. Returns error associated with
764  *               connection
765  *
766  *************************************************************************/
767 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
768         static char     msg='\0';
769 /*
770         static char     msgbuf[2048];
771
772         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
773         CS_INT          msgcount;
774         CS_CLIENTMSG    cmsg;
775         CS_SERVERMSG    smsg;
776
777         int             i;
778         char            ctempbuf[2][512];
779         char            stempbuf[2][512];
780
781         msgbuf[0]=(char)NULL;
782         ctempbuf[0][0]=(char)NULL;
783         ctempbuf[1][0]=(char)NULL;
784         stempbuf[0][0]=(char)NULL;
785         stempbuf[1][0]=(char)NULL;
786
787         if (ct_diag(sybase_sock->connection, CS_STATUS, CS_CLIENTMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) {
788                 radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Client messages");
789                 return msgbuf;
790         }
791         radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Client messages: %d", (int)msgcount);
792
793         for (i=1; i<=msgcount; i++) {
794                 if (ct_diag(sybase_sock->connection, CS_GET, CS_CLIENTMSG_TYPE, (CS_INT)i, &cmsg) != CS_SUCCEED) {
795                         radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Client message");
796                         return msgbuf;
797                 }
798                 sprintf(ctempbuf[i-1],"rlm_sql_sybase: Client Library Error: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s",
799                                 (long)CS_SEVERITY(cmsg.severity),
800                                 (long)CS_NUMBER(cmsg.msgnumber),
801                                 (long)CS_ORIGIN(cmsg.msgnumber),
802                                 (long)CS_LAYER(cmsg.msgnumber),
803                                 cmsg.msgstring);
804         }
805
806
807         if (ct_diag(sybase_sock->connection, CS_STATUS, CS_SERVERMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) {
808                 radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Server messages");
809                 return msgbuf;
810         }
811         radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Server messages: %d", (int)msgcount);
812
813         for (i=1; i<=msgcount; i++) {
814                 if (ct_diag(sybase_sock->connection, CS_GET, CS_SERVERMSG_TYPE, (CS_INT)i, &smsg) != CS_SUCCEED) {
815                         radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Server message");
816                         return msgbuf;
817                 }
818                 sprintf(stempbuf[i-1],"rlm_sql_sybase: Server message: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s",
819                                 (long)CS_SEVERITY(cmsg.severity),
820                                 (long)CS_NUMBER(cmsg.msgnumber),
821                                 (long)CS_ORIGIN(cmsg.msgnumber),
822                                 (long)CS_LAYER(cmsg.msgnumber),
823                                 cmsg.msgstring);
824         }
825         sprintf(msgbuf,"%s || %s || %s || %s", ctempbuf[1], ctempbuf[2], stempbuf[1], stempbuf[2]);
826
827         return msgbuf;
828 */
829         return &msg;
830 }
831
832
833 /*************************************************************************
834  *
835  *      Function: sql_close
836  *
837  *      Purpose: database specific close. Closes an open database
838  *               connection and cleans up any open handles.
839  *
840  *************************************************************************/
841 static int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
842 /*
843         rlm_sql_oracle_sock *oracle_sock = sqlsocket->conn;
844
845         if (oracle_sock->conn) {
846                 OCILogoff (oracle_sock->conn, oracle_sock->errHandle);
847         }
848
849         if (oracle_sock->queryHandle) {
850                 OCIHandleFree((dvoid *)oracle_sock->queryHandle, (ub4) OCI_HTYPE_STMT);
851         }
852         if (oracle_sock->errHandle) {
853                 OCIHandleFree((dvoid *)oracle_sock->errHandle, (ub4) OCI_HTYPE_ERROR);
854         }
855         if (oracle_sock->env) {
856                 OCIHandleFree((dvoid *)oracle_sock->env, (ub4) OCI_HTYPE_ENV);
857         }
858
859         oracle_sock->conn = NULL;
860 */
861         return 0;
862 }
863
864
865 /*************************************************************************
866  *
867  *      Function: sql_finish_query
868  *
869  *      Purpose: End the query, such as freeing memory
870  *
871  *************************************************************************/
872 static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config)
873 {
874         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
875
876         ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
877
878         if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
879                 radlog(L_ERR,"rlm_sql_sybase(sql_finish_query): Freeing command structure failed.");
880                 return -1;
881         }
882
883         return 0;
884 }
885
886
887
888 /*************************************************************************
889  *
890  *      Function: sql_finish_select_query
891  *
892  *      Purpose: End the select query, such as freeing memory or result
893  *
894  *************************************************************************/
895 static int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
896
897         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
898         int     i=0;
899
900         ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
901
902         if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
903                 radlog(L_ERR,"rlm_sql_sybase(sql_finish_select_query): Freeing command structure failed.");
904                 return -1;
905         }
906
907         if (sybase_sock->results) {
908                 while(sybase_sock->results[i]) free(sybase_sock->results[i++]);
909                 free(sybase_sock->results);
910                 sybase_sock->results=NULL;
911         }
912
913         return 0;
914
915 }
916
917
918 /*************************************************************************
919  *
920  *      Function: sql_affected_rows
921  *
922  *      Purpose: Return the number of rows affected by the query (update,
923  *               or insert)
924  *
925  *************************************************************************/
926 static int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
927
928         return sql_num_rows(sqlsocket, config);
929
930 }
931
932
933
934
935 /* Exported to rlm_sql */
936 rlm_sql_module_t rlm_sql_sybase = {
937         "rlm_sql_sybase",
938         sql_init_socket,
939         sql_destroy_socket,
940         sql_query,
941         sql_select_query,
942         sql_store_result,
943         sql_num_fields,
944         sql_num_rows,
945         sql_fetch_row,
946         sql_free_result,
947         sql_error,
948         sql_close,
949         sql_finish_query,
950         sql_finish_select_query,
951         sql_affected_rows
952 };