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