#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <sys/stat.h>
#include <ctpublic.h>
#include "rlm_sql.h"
typedef struct rlm_sql_sybase_sock {
CS_CONTEXT *context;
CS_CONNECTION *connection;
CS_COMMAND *command;
char **results;
int id;
int in_use;
struct timeval tv;
} rlm_sql_sybase_sock;
#define MAX_DATASTR_LEN 256
static CS_RETCODE CS_PUBLIC
servermsg_callback(cp, chp, msgp)
CS_CONTEXT *cp;
CS_CONNECTION *chp;
CS_SERVERMSG *msgp;
{
radlog(L_ERR,
"Sybase Server message:\n");
radlog(L_ERR,
"number(%ld) severity(%ld) state(%ld) line(%ld)\n",
(long)msgp->msgnumber, (long)msgp->severity,
(long)msgp->state, (long)msgp->line);
if (msgp->svrnlen > 0 && msgp->proclen > 0)
radlog(L_ERR, "Server name: %s Procedure name: %s", msgp->svrname, msgp->proc);
radlog(L_ERR, "%s\n", msgp->text);
return (CS_SUCCEED);
}
static CS_RETCODE CS_PUBLIC
clientmsg_callback(context, conn, emsgp)
CS_CONTEXT *context;
CS_CONNECTION *conn;
CS_CLIENTMSG *emsgp;
{
radlog(L_ERR,
"Client Library error:\n");
radlog(L_ERR,
"severity(%ld) number(%ld) origin(%ld) layer(%ld)\n",
(long)CS_SEVERITY(emsgp->severity),
(long)CS_NUMBER(emsgp->msgnumber),
(long)CS_ORIGIN(emsgp->msgnumber),
(long)CS_LAYER(emsgp->msgnumber));
radlog(L_ERR, "%s\n", emsgp->msgstring);
if (emsgp->osstringlen > 0)
{
radlog(L_ERR,
"Operating system error number(%ld):\n",
(long)emsgp->osnumber);
radlog(L_ERR, "%s\n", emsgp->osstring);
}
return (CS_SUCCEED);
}
static CS_RETCODE CS_PUBLIC
csmsg_callback(context, emsgp)
CS_CONTEXT *context;
CS_CLIENTMSG *emsgp;
{
radlog(L_ERR,
"CS-Library error:\n");
radlog(L_ERR,
"\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)",
(long)CS_SEVERITY(emsgp->msgnumber),
(long)CS_LAYER(emsgp->msgnumber),
(long)CS_ORIGIN(emsgp->msgnumber),
(long)CS_NUMBER(emsgp->msgnumber));
radlog(L_ERR, "%s\n", emsgp->msgstring);
if (emsgp->osstringlen > 0)
{
radlog(L_ERR, "Operating System Error: %s\n",
emsgp->osstring);
}
return (CS_SUCCEED);
}
static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
rlm_sql_sybase_sock *sybase_sock;
if (!sqlsocket->conn) {
sqlsocket->conn = (rlm_sql_sybase_sock *)rad_malloc(sizeof(rlm_sql_sybase_sock));
if (!sqlsocket->conn) {
return -1;
}
}
sybase_sock = sqlsocket->conn;
memset(sybase_sock, 0, sizeof(*sybase_sock));
sybase_sock->results=NULL;
if (cs_ctx_alloc(CS_VERSION_100, &sybase_sock->context) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate CS context structure (cs_ctx_alloc())");
return -1;
}
if (ct_init(sybase_sock->context, CS_VERSION_100) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize Client-Library (ct_init())");
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (cs_config(sybase_sock->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install CS Library error callback");
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_con_alloc(sybase_sock->context, &sybase_sock->connection) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate connection structure (ct_con_alloc())");
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_con_props(sybase_sock->connection, CS_SET, CS_USERNAME, config->sql_login,
strlen(config->sql_login), NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set username for connection (ct_con_props())\n%s",
sql_error(sqlsocket, config));
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_con_props(sybase_sock->connection, CS_SET, CS_PASSWORD, config->sql_password,
strlen(config->sql_password), NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set password for connection (ct_con_props())\n%s",
sql_error(sqlsocket, config));
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
if (ct_connect(sybase_sock->connection, config->sql_server, strlen(config->sql_server)) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to establish connection to symbolic servername %s\n%s",
config->sql_server, sql_error(sqlsocket, config));
if (sybase_sock->context != (CS_CONTEXT *)NULL) {
ct_exit(sybase_sock->context, CS_FORCE_EXIT);
cs_ctx_drop(sybase_sock->context);
}
return -1;
}
return 0;
}
static int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config)
{
free(sqlsocket->conn);
sqlsocket->conn = NULL;
return 0;
}
static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
CS_RETCODE ret, results_ret;
CS_INT result_type;
if (config->sqltrace)
DEBUG(querystr);
if (sybase_sock->connection == NULL) {
radlog(L_ERR, "Socket not connected");
return -1;
}
if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
sql_error(sqlsocket, config));
return -1;
}
if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to initiate command structure (ct_command())\n%s",
sql_error(sqlsocket, config));
return -1;
}
if (ct_send(sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to send command (ct_send())\n%s",
sql_error(sqlsocket, config));
return -1;
}
if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
if (result_type != CS_CMD_SUCCEED) {
if (result_type == CS_ROW_RESULT) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): sql_query processed a query returning rows. Use sql_select_query instead!");
}
radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
sql_error(sqlsocket, config));
return -1;
}
}
else {
switch ((int) results_ret)
{
case CS_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
, sql_error(sqlsocket, config));
if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
sql_close(sqlsocket, config);
}
return -1;
break;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
sql_error(sqlsocket, config));
return -1;
}
}
if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
if (result_type != CS_CMD_DONE) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
sql_error(sqlsocket, config));
return -1;
}
}
else {
switch ((int) results_ret)
{
case CS_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
, sql_error(sqlsocket, config));
if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
sql_close(sqlsocket, config);
}
return -1;
break;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
sql_error(sqlsocket, config));
return -1;
}
}
results_ret = ct_results(sybase_sock->command, &result_type);
switch ((int) results_ret)
{
case CS_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
, sql_error(sqlsocket, config));
if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
sql_close(sqlsocket, config);
}
return -1;
break;
case CS_END_RESULTS:
break;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
sql_error(sqlsocket, config));
return -1;
break;
}
return 0;
}
static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
CS_RETCODE ret, results_ret;
CS_INT result_type;
CS_DATAFMT descriptor;
int colcount,i;
char **rowdata;
if (config->sqltrace)
DEBUG(querystr);
if (sybase_sock->connection == NULL) {
radlog(L_ERR, "Socket not connected");
return -1;
}
if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
sql_error(sqlsocket, config));
return -1;
}
if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to initiate command structure (ct_command())\n%s",
sql_error(sqlsocket, config));
return -1;
}
if (ct_send(sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to send command (ct_send())\n%s",
sql_error(sqlsocket, config));
return -1;
}
results_ret = ct_results(sybase_sock->command, &result_type);
switch (results_ret) {
case CS_SUCCEED:
switch (result_type) {
case CS_ROW_RESULT:
descriptor.datatype = CS_CHAR_TYPE;
descriptor.format = CS_FMT_NULLTERM;
descriptor.maxlength = MAX_DATASTR_LEN;
descriptor.count = 1;
descriptor.locale = NULL;
colcount = sql_num_fields(sqlsocket, config);
rowdata=(char **)rad_malloc(sizeof(char *) * (colcount+1));
memset(rowdata, 0, (sizeof(char *) * colcount+1));
for (i=0; i < colcount; i++) {
rowdata[i]=rad_malloc((MAX_DATASTR_LEN * sizeof(char))+1);
if (ct_bind(sybase_sock->command, i+1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): ct_bind() failed)\n%s",
sql_error(sqlsocket, config));
return -1;
}
}
rowdata[i]=NULL;
sybase_sock->results=rowdata;
break;
case CS_CMD_SUCCEED:
case CS_CMD_DONE:
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Query returned no data");
break;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected result type from query\n%s",
sql_error(sqlsocket, config));
sql_finish_select_query(sqlsocket, config);
return -1;
break;
}
break;
case CS_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Failure retrieving query results\n%s"
, sql_error(sqlsocket, config));
if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): cleaning up.");
ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
sql_close(sqlsocket, config);
}
return -1;
break;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected return value from ct_results()\n%s",
sql_error(sqlsocket, config));
return -1;
break;
}
return 0;
}
static int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
return 0;
}
static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
int num;
if (ct_res_info(sybase_sock->command, CS_NUMDATA, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_num_fields): error retrieving column count: %s",
sql_error(sqlsocket, config));
return -1;
}
return num;
}
static int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
int num;
if (ct_res_info(sybase_sock->command, CS_ROW_COUNT, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_num_rows): error retrieving row count: %s",
sql_error(sqlsocket, config));
return -1;
}
return num;
}
int sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
CS_INT ret, count;
sqlsocket->row = NULL;
ret = ct_fetch(sybase_sock->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count);
switch (ret) {
case CS_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Failure fething row data\n%s"
, sql_error(sqlsocket, config));
if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): cleaning up.");
ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
sql_close(sqlsocket, config);
}
return SQL_DOWN;
break;
case CS_END_DATA:
return 0;
break;
case CS_SUCCEED:
sqlsocket->row = sybase_sock->results;
return 0;
break;
case CS_ROW_FAIL:
radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Recoverable failure fething row data, try again perhaps?");
return -1;
default:
radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Unexpected returncode from ct_fetch");
return -1;
break;
}
}
static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
return 0;
}
static const char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
static char msg='\0';
return &msg;
}
static int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
return 0;
}
static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config)
{
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_finish_query): Freeing command structure failed.");
return -1;
}
return 0;
}
static int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
int i=0;
ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
radlog(L_ERR,"rlm_sql_sybase(sql_finish_select_query): Freeing command structure failed.");
return -1;
}
if (sybase_sock->results) {
while(sybase_sock->results[i]) free(sybase_sock->results[i++]);
free(sybase_sock->results);
sybase_sock->results=NULL;
}
return 0;
}
static int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
return sql_num_rows(sqlsocket, config);
}
rlm_sql_module_t rlm_sql_sybase = {
"rlm_sql_sybase",
sql_init_socket,
sql_destroy_socket,
sql_query,
sql_select_query,
sql_store_result,
sql_num_fields,
sql_num_rows,
sql_fetch_row,
sql_free_result,
sql_error,
sql_close,
sql_finish_query,
sql_finish_select_query,
sql_affected_rows
};