1 /* This file is part of GNU Radius.
2    Copyright (C) 2001 Vlad Lungu
3    Copyright (C) 2000,2001,2004,2006,2007,2008 Sergey Pozniakoff
4 
5    GNU Radius 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 3 of the License, or
8    (at your option) any later version.
9 
10    GNU Radius 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 GNU Radius; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 
29 #include <common.h>
30 #include <radsql.h>
31 
32 #include <sql.h>
33 #include <sqlext.h>
34 #include <sqltypes.h>
35 
36 typedef struct {
37         SQLHENV         env;
38         SQLHDBC         dbc;
39 } ODBCconn;
40 
41 static void
rad_odbc_diag(SQLSMALLINT handle_type,SQLHANDLE handle,char * what)42 rad_odbc_diag(SQLSMALLINT handle_type, SQLHANDLE handle, char *what)
43 {
44         char state[16];
45         SQLINTEGER nerror;
46         SQLCHAR message[1024];
47         SQLSMALLINT msglen;
48 
49         SQLGetDiagRec(handle_type,
50                       handle,
51                       1,
52                       state,
53                       &nerror,
54                       message, sizeof message, &msglen);
55         grad_log(GRAD_LOG_ERR,
56                  "%s: %s %d %s",
57                  what, state, nerror, message);
58 }
59 
60 /* ************************************************************************* */
61 /* Interface routines */
62 static int
rad_odbc_reconnect(int type,struct sql_connection * conn)63 rad_odbc_reconnect(int type, struct sql_connection *conn)
64 {
65 
66         ODBCconn        *oconn;
67         long            result;
68         char            *dbname;
69         char            portbuf[16];
70         char            *portstr;
71 
72         switch (type) {
73         case SQL_AUTH:
74                 dbname = conn->cfg->auth_db;
75                 break;
76         case SQL_ACCT:
77                 dbname = conn->cfg->acct_db;
78                 break;
79         }
80 
81         if (conn->cfg->port == 0)
82                 portstr = NULL;
83         else {
84                 portstr = portbuf;
85                 snprintf(portbuf, sizeof(portbuf), "%d", conn->cfg->port);
86         }
87 
88         oconn = grad_emalloc(sizeof(ODBCconn));
89         result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &oconn->env);
90         if (result != SQL_SUCCESS) {
91                 rad_odbc_diag(SQL_HANDLE_ENV, oconn->env,
92                               "SQLAllocHandle failed");
93                 return -1;
94         }
95         result = SQLSetEnvAttr(oconn->env,
96                                SQL_ATTR_ODBC_VERSION,
97                                (void*)SQL_OV_ODBC3, 0);
98         if (result != SQL_SUCCESS) {
99                 rad_odbc_diag(SQL_HANDLE_ENV, oconn->dbc,
100                               "SQLSetEnvAttr failed");
101                 return -1;
102         }
103         result = SQLAllocHandle(SQL_HANDLE_DBC, oconn->env, &oconn->dbc);
104         if (result != SQL_SUCCESS) {
105                 rad_odbc_diag(SQL_HANDLE_DBC, oconn->dbc,
106                               "SQLAllocHandle failed");
107                 return -1;
108         }
109         result = SQLConnect(oconn->dbc,
110                             (SQLCHAR*)dbname, SQL_NTS,
111                             (SQLCHAR*)conn->cfg->login, SQL_NTS,
112                             (SQLCHAR*)conn->cfg->password, SQL_NTS);
113         if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
114                 rad_odbc_diag(SQL_HANDLE_DBC, oconn->dbc,
115                               "SQLConnect failed");
116                 return -1;
117         }
118 
119         conn->data = oconn;
120         conn->connected = 1;
121         return 0;
122 }
123 
124 static void
rad_odbc_disconnect(struct sql_connection * conn,int drop ARG_UNUSED)125 rad_odbc_disconnect(struct sql_connection *conn, int drop ARG_UNUSED)
126 {
127         ODBCconn *odata;
128         if (!conn->data)
129                 return ;
130         odata = (ODBCconn*)(conn->data);
131         SQLDisconnect(odata->dbc);
132         SQLFreeHandle(SQL_HANDLE_ENV, odata->env);
133         grad_free(conn->data);
134         conn->data = NULL;
135         conn->connected = 0;
136 }
137 
138 static int
rad_odbc_query(struct sql_connection * conn,const char * query,int * return_count)139 rad_odbc_query(struct sql_connection *conn, const char *query,
140 	       int *return_count)
141 {
142         ODBCconn        *odata;
143         long            result;
144         SQLHSTMT        stmt;
145         SQLINTEGER      count;
146 
147         if (!conn || !conn->data)
148                 return -1;
149 
150         GRAD_DEBUG1(1, "query: %s", query);
151         odata = (ODBCconn*)(conn->data);
152         result = SQLAllocHandle(SQL_HANDLE_STMT,odata->dbc,&stmt);
153         if (result != SQL_SUCCESS) {
154                 rad_odbc_diag(SQL_HANDLE_DBC, odata->dbc,
155                               "SQLAllocHandle");
156                 return -1;
157         }
158 
159         result = SQLExecDirect(stmt, query, SQL_NTS);
160         if (result != SQL_SUCCESS) {
161                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
162                               "SQLExecDirect: %s %d %s");
163                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
164                 return -1;
165         }
166         result = SQLRowCount(stmt, &count);
167         if (result != SQL_SUCCESS) {
168                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
169                               "SQLRowCount");
170                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
171                 return -1;
172         }
173         if (return_count)
174                 *return_count = count;
175         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
176         return 0;
177 }
178 
179 static char *
rad_odbc_getpwd(struct sql_connection * conn,const char * query)180 rad_odbc_getpwd(struct sql_connection *conn, const char *query)
181 {
182         ODBCconn        *odata;
183         long            result;
184         SQLHSTMT        stmt;
185         SQLINTEGER      size;
186         SQLCHAR         passwd[128];
187         char           *return_passwd = NULL;
188 
189         if (!conn || !conn->data)
190                 return NULL;
191 
192         GRAD_DEBUG1(1, "query: %s", query);
193 
194         odata = (ODBCconn*)(conn->data);
195         result = SQLAllocHandle(SQL_HANDLE_STMT, odata->dbc, &stmt);
196         if (result != SQL_SUCCESS) {
197                 rad_odbc_diag(SQL_HANDLE_DBC, odata->dbc,
198                               "SQLAllocHandle");
199                 return NULL;
200         }
201 
202         result = SQLExecDirect(stmt, query, SQL_NTS);
203         if (result != SQL_SUCCESS) {
204                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
205                               "SQLExecDirect");
206                 return NULL;
207         }
208 
209 
210         result = SQLFetch(stmt);
211         if (result != SQL_SUCCESS) {
212                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
213                               "SQLFetch");
214                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
215                 return NULL;
216         }
217 
218         SQLGetData(stmt, 1, SQL_C_CHAR, passwd, 128, &size);
219         if (result != SQL_SUCCESS) {
220                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
221                               "SQLGetData");
222                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
223                 return NULL;
224         }
225 
226         result = SQLFetch(stmt);
227 
228         if (result == SQL_SUCCESS) {
229                 grad_log(GRAD_LOG_NOTICE,
230                          _("query returned more tuples: %s"),
231                          query);
232                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
233                 return NULL;
234         }
235 
236         if (result != SQL_NO_DATA) {
237                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
238                               "SQLFetch");
239                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
240                 return NULL;
241         }
242 
243         return_passwd = grad_estrdup(passwd);
244 
245         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
246         return return_passwd;
247 }
248 
249 typedef struct {
250         SQLHSTMT stmt;
251         int   nfields;
252 	int   ntuples;
253 } EXEC_DATA;
254 
255 static int
rad_odbc_n_columns(struct sql_connection * conn,void * data,size_t * np)256 rad_odbc_n_columns(struct sql_connection *conn, void *data, size_t *np)
257 {
258         EXEC_DATA *edata = (EXEC_DATA*)data;
259 
260         if (!data)
261 		return -1;
262 	*np = edata->nfields;
263 	return 0;
264 }
265 
266 static int
rad_odbc_n_tuples(struct sql_connection * conn,void * data,size_t * np)267 rad_odbc_n_tuples(struct sql_connection *conn, void *data, size_t *np)
268 {
269         EXEC_DATA *edata = (EXEC_DATA*)data;
270 
271         if (!data)
272 		return -1;
273 	*np = edata->ntuples;
274 	return 0;
275 }
276 
277 static void *
rad_odbc_exec(struct sql_connection * conn,const char * query)278 rad_odbc_exec(struct sql_connection *conn, const char *query)
279 {
280 
281         ODBCconn        *odata;
282         long            result;
283         SQLHSTMT        stmt;
284         SQLSMALLINT     ccount;
285 	SQLINTEGER      rcount;
286         EXEC_DATA      *data;
287 
288         if (!conn || !conn->data)
289                 return NULL;
290 
291         GRAD_DEBUG1(1, "query: %s", query);
292 
293         odata = (ODBCconn*)conn->data;
294         result = SQLAllocHandle(SQL_HANDLE_STMT,odata->dbc, &stmt);
295         if (result != SQL_SUCCESS) {
296                 rad_odbc_diag(SQL_HANDLE_DBC, odata->dbc,
297                               "SQLAllocHandle");
298                 return NULL;
299         }
300 
301         result = SQLExecDirect(stmt, query, SQL_NTS);
302         if (result != SQL_SUCCESS) {
303                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
304                               "SQLExecDirect");
305                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
306                 return NULL;
307         }
308 
309         if (SQLNumResultCols(stmt, &ccount) != SQL_SUCCESS
310 	    || SQLRowCount(stmt, &rcount) != SQL_SUCCESS) {
311                 rad_odbc_diag(SQL_HANDLE_STMT, stmt,
312                               "SQLNumResultCount");
313                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
314                 return NULL;
315         }
316 
317 	if (rcount == 0) {
318 		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
319                 return NULL;
320         }
321 
322 
323         data = grad_emalloc(sizeof(*data));
324         data->stmt = stmt;
325         data->nfields = ccount;
326 	data->ntuples = rcount;
327         return (void*)data;
328 }
329 
330 static char *
rad_odbc_column(void * data,size_t ncol)331 rad_odbc_column(void *data, size_t ncol)
332 {
333         SQLCHAR buffer[1024];
334         long result;
335         SQLINTEGER      size;
336         EXEC_DATA *edata = (EXEC_DATA*)data;
337 
338         if (!data)
339                 return NULL;
340         if (ncol >= edata->nfields) {
341                 grad_log(GRAD_LOG_ERR,
342                          _("too few columns returned (%d req'd)"), ncol);
343                 return NULL;
344         }
345 
346         result = SQLGetData(edata->stmt,ncol+1,SQL_C_CHAR,
347                             buffer, sizeof buffer, &size);
348         if (result != SQL_SUCCESS) {
349                 rad_odbc_diag(SQL_HANDLE_STMT, edata->stmt,
350                               "SQLGetData");
351                 return NULL;
352         }
353         return grad_estrdup(buffer);
354 }
355 
356 /*ARGSUSED*/
357 static int
rad_odbc_next_tuple(struct sql_connection * conn,void * data)358 rad_odbc_next_tuple(struct sql_connection *conn, void *data)
359 {
360         long result;
361         EXEC_DATA *edata = (EXEC_DATA*)data;
362 
363         if (!data)
364                 return 1;
365 
366         result = SQLFetch(edata->stmt);
367 
368         if (result == SQL_SUCCESS)
369                 return 0;
370 
371         if (result == SQL_NO_DATA)
372                 return 1;
373 
374         rad_odbc_diag(SQL_HANDLE_STMT, edata->stmt,
375                       "SQLFetch");
376         return 1;
377 }
378 
379 /*ARGSUSED*/
380 static void
rad_odbc_free(struct sql_connection * conn,void * data)381 rad_odbc_free(struct sql_connection *conn, void *data)
382 {
383         EXEC_DATA *edata = (EXEC_DATA*)data;
384 
385         if (!data)
386                 return;
387 
388         SQLFreeHandle(SQL_HANDLE_STMT, edata->stmt);
389         grad_free(edata);
390 }
391 
392 DECL_SQL_DISPATCH_TAB(odbc) = {
393         "odbc",
394         0,
395         rad_odbc_reconnect,
396         rad_odbc_disconnect,
397         rad_odbc_query,
398         rad_odbc_getpwd,
399         rad_odbc_exec,
400         rad_odbc_column,
401         rad_odbc_next_tuple,
402         rad_odbc_free,
403 	rad_odbc_n_tuples,
404 	rad_odbc_n_columns,
405 };
406 
407