modules/qi/query_instructions.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- qi_kill_body
- sql_execute_watched
- create_name_query
- create_asblock_query
- add_filter
- create_query
- QI_fast_output
- filter
- write_results
- report_sql_error
- __report_sql_error
- write_objects
- insert_radix_serials
- write_radix_immediate
- map_qc2rx
- run_referral
- qi_prep_run_refer
- qi_collect_domain
- add_ref_name
- qi_collect_ids
- qi_fetch_references
- QI_execute
- instruction_free
- QI_free
- valid_query
- QI_new
- QI_queries_to_string
1 /***************************************
2 $Revision: 1.83 $
3
4 Query instructions (qi). This is where the queries are executed.
5
6 Status: NOT REVUED, TESTED
7
8 ******************/ /******************
9 Filename : query_instructions.c
10 Authors : ottrey@ripe.net - framework and draft implementation
11 marek@ripe.net - cleaned and extended, added referral,
12 accounting support and watchdog cancellation.
13 OSs Tested : Solaris
14 ******************/ /******************
15 Copyright (c) 1999 RIPE NCC
16
17 All Rights Reserved
18
19 Permission to use, copy, modify, and distribute this software and its
20 documentation for any purpose and without fee is hereby granted,
21 provided that the above copyright notice appear in all copies and that
22 both that copyright notice and this permission notice appear in
23 supporting documentation, and that the name of the author not be
24 used in advertising or publicity pertaining to distribution of the
25 software without specific, written prior permission.
26
27 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 ***************************************/
34 #include <stdio.h>
35 #include <string.h>
36 #include <glib.h>
37
38 #include "which_keytypes.h"
39 #include "query_instructions.h"
40 #include "mysql_driver.h"
41 #include "rp.h"
42 #include "stubs.h"
43 #include "constants.h"
44 #include "memwrap.h"
45 #include "wh_queries.h"
46 #include "ud.h"
47
48 #include "defs.h"
49
50 /*+ String sizes +*/
51 #define STR_S 63
52 #define STR_M 255
53 #define STR_L 1023
54 #define STR_XL 4095
55 #define STR_XXL 16383
56
57 /* the TEMPORARY table is only available in 3.23 or later MySQL */
58 #if MYSQL_VERSION_ID >= 32300
59 #define USE_TEMPORARY_TABLE
60 #endif
61
62 /* string to stick in CREATE TABLE statments */
63 #ifdef USE_TEMPORARY_TABLE
64 #define TEMPORARY "TEMPORARY"
65 #else
66 #define TEMPORARY ""
67 #endif
68
69 /*++++++++++++++++++++++++++++++++++++++
70 Function invoked on query cancellation by the watchdog,
71 used from the sql_execute_watched() function.
72
73 It aborts the running query (the abort function in sq kills and
74 reestablished the connection).
75
76 void *qi_kill_body result of sq_execute_query, int cast to (void*)
77
78 void *arg pointer to sql connection
79
80 Author:
81 marek.
82
83 ++++++++++++++++++++++++++++++++++++++*/
84 static
85 void *qi_kill_body(void *arg)
/* [<][>][^][v][top][bottom][index][help] */
86 {
87 SQ_connection_t *sql_connection = arg;
88 ER_dbg_va(FAC_QI, ASP_QI_WATCH,
89 "rtc: killing SQL connection %d", (sql_connection)->thread_id);
90 /* abort the running query */
91 SQ_abort_query(sql_connection);
92
93 return NULL;
94 }
95
96
97
98 /*++++++++++++++++++++++++++++++++++++++
99 wrapper around sq_execute_query: starts a query
100 in a separate thread and starts the socket watchdog to cancel the query
101 if the socket is closed. If the socket has problems already (its
102 reason-to-close flag is set) no query is attempted.
103
104 The execution of the query or watchdog is not guaranteed at all!
105
106 int sql_execute_watched Returns the return code of SQ_execute_query,
107 Returns 0 for cancelled queries.
108
109 sk_conn_st *condat connection to watch
110
111 SQ_connection_t **sql_connection sql connection
112
113 const char *query sql query to execute
114
115 SQ_result_set_t **result_ptr storage for the query result structure
116 (passed to SQ_execute_query). Must either
117 be NULL, or the pointer it points to must
118 be NULL - in that case the result struct.
119 will be allocated in SQ.
120
121 Author:
122 marek.
123 ++++++++++++++++++++++++++++++++++++++*/
124 int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
125 const char *query, SQ_result_set_t **result_ptr)
126 {
127 int retval = 0; /* return value of sq_execute_query */
128 SQ_connection_t *tempcon;
129
130 /* assert that, if defined, result_ptr is initialised to NULL
131 prior to calling this function */
132 if( result_ptr != NULL ) {
133 dieif( *result_ptr != NULL );
134 }
135
136 /* don't even try to perform the query/fire up watchdog
137 if rtc is already set. Do this only if not set yet. */
138 if( condat->rtc == 0 ) {
139
140 /* make clean */
141 SK_watch_setclear(condat);
142
143 /* set watchdog to execute the abort function */
144 SK_watch_setexec(condat, qi_kill_body, *sql_connection);
145
146 /* start the watchdog */
147 SK_watchstart(condat);
148
149 /* start query. An error may be returned if the query is aborted */
150 retval = SQ_execute_query(*sql_connection, query, result_ptr);
151
152 /* but short queries will complete before the watchdog kills the
153 connection */
154
155 SK_watchstop(condat);
156
157
158 /* if the watchdog triggered, then it is guaranteed that
159 the kill_body function was invoked and therefore the sql-connection
160 is now unusable...
161 Close and reopen it for cleanup, use temporary connection
162 to keep the login details */
163 if( condat->rtc != 0 ) {
164 /* can't rely on the error code from mysql!
165 */
166
167 /* one thing: this code must be entered ONLY if the kill_body
168 thing was invoked by the watchdog.
169 */
170
171 /* if result is defined, free it here before destroying the
172 associated connection */
173 if( retval == 0 && result_ptr && *result_ptr ) {
174 SQ_free_result( *result_ptr );
175 *result_ptr = NULL;
176 }
177
178 tempcon = SQ_duplicate_connection(*sql_connection);
179
180 ER_dbg_va(FAC_QI, ASP_QI_WATCH,
181 "rtc: closing SQL thread %d", (*sql_connection)->thread_id);
182 SQ_close_connection(*sql_connection);
183
184 *sql_connection = tempcon;
185 ER_dbg_va(FAC_QI, ASP_QI_WATCH,
186 "rtc: reopened as thread %d", (*sql_connection)->thread_id);
187
188 /* make it look as if there was no error and
189 the result is empty */
190 retval = 0;
191 } /* if watchdog set rtc */
192
193 } /* if rtc not set before */
194
195 return retval;
196 }
197
198 /* create_name_query() */
199 /*++++++++++++++++++++++++++++++++++++++
200 Create an sql query for the names table.
201
202 char *query_str
203
204 const char *sql_query
205
206 const char *keys
207
208 More:
209 +html+ <PRE>
210 Authors:
211 ottrey
212 +html+ </PRE>
213 ++++++++++++++++++++++++++++++++++++++*/
214 static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
/* [<][>][^][v][top][bottom][index][help] */
215 int i;
216 /* Allocate stuff - use dynamic strings (initialised to some length) */
217 GString *from_clause = g_string_sized_new(STR_L);
218 GString *where_clause = g_string_sized_new(STR_L);
219 gchar **words = g_strsplit(keys, " ", 0);
220
221 /* double quotes " are used in queries to allow querying for
222 names like O'Hara */
223
224 g_string_sprintfa(from_clause, "names N%.2d", 0);
225 g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
226
227 for (i=1; words[i] != NULL; i++) {
228 g_string_sprintfa(from_clause, ", names N%.2d", i);
229 g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
230 }
231
232 sprintf(query_str, sql_query, from_clause->str, where_clause->str);
233
234 /* Free up stuff */
235 g_strfreev(words);
236 g_string_free(where_clause,/* CONSTCOND */ TRUE);
237 g_string_free(from_clause, /* CONSTCOND */ TRUE);
238
239 } /* create_name_query() */
240
241
242 /*++++++++++++++++++++++++++++++++++++++
243 construct a range query for the as_block table
244 (a query for an AS block object) given a string like:
245 AS1
246 AS1 - AS10
247 AS1-AS10
248
249 int create_asblock_query Returns 0 on success, -1 on failure
250 (search term not an AS# nor range)
251
252 char *query_str buffer for the final query (must be big enough)
253
254 const char *sql_query rest of the sql query (with %d %d formats for
255 AS numbers)
256
257 const char *keys user-supplied search term.
258
259 Author:
260 marek
261 ++++++++++++++++++++++++++++++++++++++*/
262 static int create_asblock_query(char *query_str,
/* [<][>][^][v][top][bottom][index][help] */
263 const char *sql_query,
264 const char *keys) {
265 char *keycopy = wr_string(keys);
266 char *token, *cursor = keycopy;
267 int asnums[2] = {0,0};
268 int index = 0; /* index into the asnums array */
269
270
271 while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
272 /* discard the letters (or leading whitespace), take the number */
273 if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
274 return -1; /* error */
275 }
276 }
277 /* if only beginning was supplied, copy it as end */
278 if( index == 1 ) {
279 asnums[1] = asnums[0];
280 }
281
282 /* now construct the query */
283 sprintf(query_str, sql_query, asnums[0], asnums[1]);
284
285 wr_free(keycopy);
286 return 0;
287 }
288
289
290 /*++++++++++++++++++++++++++++++++++++++
291 add_filter(): construct a query to limit the objects returned from the last
292 table to predefined types.
293
294 char *query_str buffer for the final query, containing the initial
295 part of the query (must be big enough)
296
297 const Query_command *qc query command structure with the bitmap of
298 object types to be included.
299
300 Author:
301 ottrey.
302 ++++++++++++++++++++++++++++++++++++++*/
303 static void add_filter(char *query_str, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
304 unsigned i;
305 int qlen;
306 char filter_atom[STR_M];
307
308 #if 0
309 /* glib string manipulation - untested yet */
310
311 if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
312 g_string_sprintfa(query_str, " AND (");
313 for (i=0; i < C_END; i++) {
314 if (MA_isset(qc->object_type_bitmap, i)) {
315 g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
316 }
317 }
318 g_string_truncate(query_str, query_str->len-3);
319 g_string_append_c(query_str, ')');
320 }
321
322 #else /* classic string operations */
323
324 /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
325 if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
326 strcat(query_str, " AND (");
327 for (i=0; i < C_END; i++) {
328 if (MA_isset(qc->object_type_bitmap, i)) {
329 strcpy(filter_atom, "");
330 sprintf(filter_atom, "i.object_type = %d OR ", i);
331 /* XXX class codes should be used instead:
332 DF_get_class_dbase_code(i))
333 but currently the tables contain values of enums
334 (C_IN, etc) and not codes
335 */
336 strcat(query_str, filter_atom);
337 }
338 }
339 qlen = strlen(query_str);
340 query_str[qlen-3] = ')';
341 query_str[qlen-2] = '\0';
342 query_str[qlen-1] = '\0';
343 }
344
345 #endif
346
347 } /* add_filter() */
348
349 /* create_query() */
350 /*++++++++++++++++++++++++++++++++++++++
351 Create an sql query from the query_command and the matching keytype and the
352 selected inverse attributes.
353 Note this clears the first inv_attribute it sees, so is called sequentially
354 until there are no inv_attributes left.
355
356 WK_Type keytype The matching keytype.
357
358 const Query_command *qc The query command.
359
360 mask_t *inv_attrs_bitmap The selected inverse attributes.
361
362 More:
363 +html+ <PRE>
364 Authors:
365 ottrey
366 +html+ </PRE>
367
368 ++++++++++++++++++++++++++++++++++++++*/
369 static char *create_query(const Query_t q, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
370 char *result=NULL;
371 char result_buff[STR_XL];
372 Q_Type_t querytype;
373 int addquery = 0; /* controls if the query should be added to the list */
374
375 if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
376 querytype = Q_INVERSE;
377 }
378 else {
379 querytype = Q_LOOKUP;
380 }
381
382 if ( (q.query != NULL)
383 && (q.querytype == querytype) ) {
384
385 /* addquery = 1; */
386 /* if it got here, it should be added, unless.(see asblock)*/
387
388 if (q.keytype == WK_NAME) {
389 /* Name queries require special treatment. */
390 create_name_query(result_buff, q.query, qc->keys);
391 addquery = 1;
392 }
393 else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */
394 ip_range_t myrang;
395 unsigned begin, end;
396 ip_keytype_t key_type;
397
398 /* The only inverse query for IPADDRESS is nserver. */
399 /* We need to insure that we don't try to use the numeric values for this
400 * query, because the address of the server is stored as a string, and
401 * the SQL query is formatted appropriately. */
402 if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
403 if(IP_rang_b2_space(&myrang) == IP_V4 ) {
404 IP_rang_b2v4(&myrang, &begin, &end);
405 if (querytype == Q_INVERSE) {
406 /* for inverse queries, convert number to dotted-quad */
407 char buf[64];
408 const char *inet_ntop_ret;
409 inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf));
410 dieif(inet_ntop_ret == NULL);
411 sprintf(result_buff, q.query, buf);
412 } else {
413 /* otherwise, execute appropriate query on numeric values */
414 sprintf(result_buff, q.query, begin, end);
415 }
416 addquery = 1;
417 }
418 else {
419 die;
420 }
421 }
422 }
423 else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */
424 if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
425 addquery = 0; /* ... unless it's not correct */
426 }
427 else {
428 addquery = 1;
429 }
430 }
431 else {
432 sprintf(result_buff, q.query, qc->keys);
433 addquery = 1;
434 }
435
436 if (q.class == C_ANY && addquery == 1 ) {
437 /* It is class type ANY so add the object filtering */
438 add_filter(result_buff, qc);
439 }
440 }
441
442 if( addquery == 1 ) {
443 dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
444 strcpy(result, result_buff);
445 return result;
446 }
447 else {
448 return NULL;
449 }
450 } /* create_query() */
451
452 /* QI_fast_output() */
453 /*++++++++++++++++++++++++++++++++++++++
454 This is for the '-F' flag.
455 It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
456 Fast isn't fast anymore - it's just there for compatibility reasons.
457
458 const char *str The object to be "fast output'ed".
459
460 More:
461 +html+ <PRE>
462 Authors:
463 ottrey,
464 marek - glib strings + small changes
465 +html+ </PRE>
466 ++++++++++++++++++++++++++++++++++++++*/
467 char *QI_fast_output(const char *str)
/* [<][>][^][v][top][bottom][index][help] */
468 {
469 int i,j;
470 char *result;
471 GString *result_buff = g_string_sized_new(STR_XL);
472 gchar **lines = g_strsplit(str, "\n", 0);
473 unsigned char *value, *colon;
474 char *attr;
475
476 g_string_assign(result_buff, "");
477
478 for (j=0; lines[j] != NULL; j++) {
479
480 switch (lines[j][0]) {
481 /* line continuation */
482 case ' ':
483 case '\t':
484 case '+':
485 value = (unsigned char *) lines[j]+1;
486 while(*value != '\0' && isspace(*value)) {
487 value++;
488 }
489 g_string_append(result_buff, "\n+ ");
490 g_string_append(result_buff, (char *)value);
491 break;
492
493 default:
494 /* a line of the form "attribute: value" */
495 /* first: close the last line (if there was any, i.e. j>0) */
496 if( j > 0 ) {
497 g_string_append_c(result_buff, '\n');
498 }
499
500 /* get attribute name */
501 attr = lines[j];
502 colon = (unsigned char *) strchr(lines[j], ':');
503 /* if there's no colon for whatever reason, dump the object
504 and report the condition */
505 if( colon == NULL ) {
506 ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
507 goto fast_output_cleanup;
508 }
509 *colon = '\0';
510 for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
511 ;
512 }
513
514 if( (i = DF_attribute_name2type(attr)) == -1 ) {
515 /* warning! error in the object format */
516 ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
517 goto fast_output_cleanup;
518
519 }
520 else {
521 /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
522 g_string_append_c(result_buff, '*');
523 g_string_append(result_buff, DF_get_attribute_code(i));
524 g_string_append(result_buff, ": ");
525 g_string_append(result_buff, (char *)value);
526 }
527 } /* switch */
528 } /* for every line */
529
530 fast_output_cleanup:
531
532 g_strfreev(lines);
533
534 g_string_append_c(result_buff, '\n');
535 result = strdup(result_buff->str);
536 dieif(result == NULL);
537
538 g_string_free(result_buff,/* CONSTCOND */ TRUE);
539
540 return result;
541 } /* fast_output() */
542
543 /* filter() */
544 /*++++++++++++++++++++++++++++++++++++++
545 Basically it's for the '-K' flag for non-set (and non-radix) objects.
546 It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
547
548 This could be speed up if there were breaks out of the loops, once it matched something.
549
550 const char *string The string to be filtered.
551
552 More:
553 +html+ <PRE>
554 Authors:
555 ottrey
556 +html+ </PRE>
557
558 ++++++++++++++++++++++++++++++++++++++*/
559 char *filter(const char *str) {
/* [<][>][^][v][top][bottom][index][help] */
560 int i,j, passed=0;
561 char *result;
562 GString *result_buff = g_string_sized_new(STR_XL);
563 gchar **lines = g_strsplit(str, "\n", 0);
564 char * const *filter_names;
565 gboolean filtering_an_attribute = FALSE;
566
567 filter_names = DF_get_filter_names();
568
569 g_string_assign(result_buff, "");
570
571 for (i=0; filter_names[i] != NULL; i++) {
572 for (j=0; lines[j] != NULL; j++) {
573 if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
574
575 g_string_sprintfa(result_buff, "%s\n", lines[j]);
576 passed++;
577
578 /* CONSTCOND */
579 filtering_an_attribute = TRUE;
580 }
581 /* CONSTCOND */
582 else if (filtering_an_attribute == TRUE) {
583 switch (lines[j][0]) {
584 case ' ':
585 case '\t':
586 case '+':
587
588 g_string_sprintfa(result_buff, "%s\n", lines[j]);
589
590 break;
591
592 default:
593 filtering_an_attribute = FALSE;
594 }
595 }
596 }
597 }
598
599 g_strfreev(lines);
600
601 if(passed) {
602 g_string_append(result_buff, "\n");
603 }
604 result = strdup(result_buff->str);
605 g_string_free(result_buff,/* CONSTCOND */ TRUE);
606
607 return result;
608 } /* filter() */
609
610 /* write_results() */
611 /*++++++++++++++++++++++++++++++++++++++
612 Write the results to the client socket.
613
614 SQ_result_set_t *result The result set returned from the sql query.
615 unsigned filtered if the objects should go through a filter (-K)
616 sk_conn_st *condat Connection data for the client
617
618 More:
619 +html+ <PRE>
620 Authors:
621 ottrey - initial design
622 marek - rewritten for accounting and cancellation.
623 +html+ </PRE>
624
625 ++++++++++++++++++++++++++++++++++++++*/
626 static int write_results(SQ_result_set_t *result,
/* [<][>][^][v][top][bottom][index][help] */
627 unsigned filtered,
628 unsigned fast,
629 sk_conn_st *condat,
630 acc_st *acc_credit,
631 acl_st *acl
632 ) {
633 SQ_row_t *row;
634 char *str;
635 char *filtrate;
636 char *fasted;
637 int retrieved_objects=0;
638 char *objt;
639 int type;
640
641 /* Get all the results - one at a time */
642 if (result != NULL) {
643 /* here we are making use of the mysql_store_result capability
644 of interrupting the cycle of reading rows. mysql_use_result
645 would not allow that, would have to be read until end */
646
647 while ( condat->rtc == 0
648 && AC_credit_isdenied( acc_credit ) == 0
649 && (row = SQ_row_next(result)) != NULL ) {
650
651 if ( (str = SQ_get_column_string(result, row, 0)) == NULL
652 || (objt = SQ_get_column_string(result, row, 3)) == NULL ) {
653 /* handle it somehow ? */
654 die;
655 }
656 else {
657 /* get + add object type */
658 type = atoi(objt);
659
660 /* ASP_QI_LAST_DET */
661 ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
662 "Retrieved serial id = %d , type = %s", atoi(str), objt);
663
664 wr_free(str);
665 wr_free(objt);
666 }
667
668 /* decrement credit for accounting purposes */
669 AC_count_object( acc_credit, acl,
670 type == C_PN || type == C_RO ); /* is private? */
671
672 /* break the loop if the credit has just been exceeded and
673 further results denied */
674 if( AC_credit_isdenied( acc_credit ) ) {
675 continue;
676 }
677
678 if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
679 else {
680
681 /* The fast output stage */
682 if (fast == 1) {
683 fasted = QI_fast_output(str);
684 wr_free(str);
685 str = fasted;
686 }
687
688 /* The filtering stage */
689 if (filtered == 0) {
690 SK_cd_puts(condat, str);
691 SK_cd_puts(condat, "\n");
692 }
693 else {
694
695 /* XXX accounting should be done AFTER filtering, not to count
696 objects filtered out */
697
698 filtrate = filter(str);
699 SK_cd_puts(condat, filtrate);
700 wr_free(filtrate);
701 }
702 retrieved_objects++;
703 }
704 wr_free(str);
705 }
706 }
707
708 return retrieved_objects;
709 } /* write_results() */
710
711 /* generic SQL error message - it could be a configurable parameter, but
712 it also shouldn't happen! */
713 static const char *sql_error_text =
714 "% An internal database error has occurred.\n"
715 "% It has been logged, and an adminstrator should look at it shorly.\n"
716 "% Please try your query again.\n"
717 "\n"
718 "\n";
719
720 /* use a macro so we can get our file and line number */
721 #define report_sql_error(condat,sql_connection,sql_command) \
/* [<][>][^][v][top][bottom][index][help] */
722 __report_sql_error((condat), (sql_connection), (sql_command), \
723 __FILE__,__LINE__,pthread_self())
724
725 /* report_sql_error() */
726 /*++++++++++++++++++++++++++++++++++++++
727
728 sk_conn_st *condat connection with user
729 SQ_connection_t *sql_connection connection to MySQL
730 const char *sql_command SQL command sent to MySQL
731
732 ++++++++++++++++++++++++++++++++++++++*/
733
734 void
735 __report_sql_error(sk_conn_st *condat,
/* [<][>][^][v][top][bottom][index][help] */
736 SQ_connection_t *sql_connection,
737 const char *sql_command,
738 const char *file,
739 int line,
740 pthread_t tid)
741 {
742 /* first, let user know what has happened */
743 SK_cd_puts(condat, sql_error_text);
744
745 /* next, log this error */
746 ER_perror(FAC_QI, QI_SQLERR,"sql='%s' at %s:%d, tid:%d [%d] %s",
747 sql_command,
748 file, line, tid,
749 SQ_errno(sql_connection),
750 SQ_error(sql_connection));
751 }
752
753 /* write_objects() */
754 /*++++++++++++++++++++++++++++++++++++++
755
756 SQ_connection_t *sql_connection The connection to the database.
757
758 char *id_table The id of the temporary table (This is a result of the hacky
759 way we've tried to get MySQL to do sub-selects.)
760
761 sk_conn_st *condat Connection data for the client
762
763 More:
764 +html+ <PRE>
765 Authors:
766 ottrey,
767 marek.
768 +html+ </PRE>
769 ++++++++++++++++++++++++++++++++++++++*/
770 static int
771 write_objects(SQ_connection_t **sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
772 char *id_table,
773 unsigned int filtered,
774 unsigned int fast,
775 sk_conn_st *condat,
776 acc_st *acc_credit,
777 acl_st *acl
778 )
779 {
780 SQ_result_set_t *result = NULL;
781 int retrieved_objects=0;
782 char sql_command[STR_XL];
783
784 sprintf(sql_command, Q_OBJECTS, id_table);
785
786 if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) {
787 report_sql_error(condat, *sql_connection, sql_command);
788 return SQ_errno(*sql_connection);
789 }
790
791 /* Problem: if the query was aborted, the result structure does not
792 refer to any existing connection anymore. So we check rtc here.
793 */
794
795 if( condat->rtc == 0) {
796 retrieved_objects = write_results(result, filtered, fast, condat,
797 acc_credit, acl);
798 SQ_free_result(result);
799 }
800 return 0;
801 } /* write_objects() */
802
803 /* insert_radix_serials() */
804 /*++++++++++++++++++++++++++++++++++++++
805 Insert the radix serial numbers into a temporary table in the database.
806
807 mask_t bitmap The bitmap of attribute to be converted.
808
809 SQ_connection_t *sql_connection The connection to the database.
810
811 char *id_table The id of the temporary table (This is a result of the hacky
812 way we've tried to get MySQL to do sub-selects.)
813
814 GList *datlist The list of data from the radix tree.
815
816 More:
817 +html+ <PRE>
818 Authors:
819 ottrey,
820 marek
821 +html+ </PRE>
822
823 ++++++++++++++++++++++++++++++++++++++*/
824 static int insert_radix_serials(sk_conn_st *condat,
/* [<][>][^][v][top][bottom][index][help] */
825 SQ_connection_t *sql_connection,
826 char *id_table, GList *datlist) {
827 GList *qitem;
828 char sql_command[STR_XL];
829 int serial;
830 int sql_error;
831
832 sql_error = 0;
833 for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
834 rx_datcpy_t *datcpy = qitem->data;
835
836 serial = datcpy->leafcpy.data_key;
837
838 /* don't bother to insert values into our temporary table */
839 /* if we've lost the client connection */
840 if ((condat->rtc == 0) && !sql_error) {
841 sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
842 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
843 sql_error = SQ_errno(sql_connection);
844 report_sql_error(condat, sql_connection, sql_command);
845 }
846 }
847
848 wr_free(datcpy->leafcpy.data_ptr);
849 }
850
851 wr_clear_list( &datlist );
852
853 /* return error, if any */
854 return sql_error;
855
856 } /* insert_radix_serials() */
857
858
859 /* write_radix_immediate() */
860 /*++++++++++++++++++++++++++++++++++++++
861 Display the immediate data carried with the objects returned by the
862 radix tree.
863
864 GList *datlist The linked list of dataleaf copies
865
866 sk_conn_st *condat Connection data for the client
867
868 acc_st *acc_credit Accounting struct
869
870 More:
871 +html+ <PRE>
872 Authors:
873 marek
874 +html+ </PRE>
875
876 Also free the list of answers.
877 ++++++++++++++++++++++++++++++++++++++*/
878 static void write_radix_immediate(GList *datlist,
/* [<][>][^][v][top][bottom][index][help] */
879 sk_conn_st *condat,
880 acc_st *acc_credit,
881 acl_st *acl)
882 {
883 GList *qitem;
884
885 for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
886 rx_datcpy_t *datcpy = qitem->data;
887
888 SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
889 SK_cd_puts(condat, "\n");
890
891 wr_free(datcpy->leafcpy.data_ptr);
892
893 AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
894
895 if(condat->rtc != 0) {
896 break;
897 }
898 }
899
900 wr_clear_list( &datlist );
901 } /* write_radix_immediate() */
902
903
904 /* map_qc2rx() */
905 /*++++++++++++++++++++++++++++++++++++++
906 The mapping between a query_command and a radix query.
907
908 Query_instruction *qi The Query Instruction to be created from the mapping
909 of the query command.
910
911 const Query_command *qc The query command to be mapped.
912
913 More:
914 +html+ <PRE>
915 Authors:
916 ottrey,
917 marek - simplified the logic, added stealth -S option
918 +html+ </PRE>
919
920 ++++++++++++++++++++++++++++++++++++++*/
921 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
922 int result=1;
923 int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1)
924 + (qc->m == 1) + (qc->x == 1);
925
926 qi->rx_keys = qc->keys;
927
928 /* only one option can be active at a time */
929
930 if( allflags > 1 ) {
931 /* user error (this should have been checked before) */
932
933 ER_dbg_va(FAC_QI, ASP_QI_SKIP,
934 "ERROR in qc2rx mapping: bad combination of flags");
935 result = 0;
936 }
937 if( allflags == 0 ) {
938 /* no options active - default search */
939 qi->rx_srch_mode = RX_SRCH_EXLESS;
940 qi->rx_par_a = 0;
941 }
942 else if ( qc->L == 1 ) {
943 qi->rx_srch_mode = RX_SRCH_LESS;
944 qi->rx_par_a = RX_ALL_DEPTHS;
945 }
946 else if (qc->M == 1) {
947 qi->rx_srch_mode = RX_SRCH_MORE;
948 qi->rx_par_a = RX_ALL_DEPTHS;
949 }
950 else if (qc->l == 1) {
951 qi->rx_srch_mode = RX_SRCH_LESS;
952 qi->rx_par_a = 1;
953 }
954 else if (qc->m == 1) {
955 qi->rx_srch_mode = RX_SRCH_MORE;
956 qi->rx_par_a = 1;
957 }
958 else if (qc->x == 1) {
959 qi->rx_srch_mode = RX_SRCH_EXACT;
960 qi->rx_par_a = 0;
961 }
962
963 if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
964 qi->rx_srch_mode = RX_SRCH_DBLS;
965 }
966
967 return result;
968
969 } /* map_qc2rx() */
970
971
972 /* run_referral() */
973 /*++++++++++++++++++++++++++++++++++++++
974
975 invoked when no such domain found. Goes through the domain table
976 and searches for shorter domains, then if it finds one with referral
977 it performs it, otherwise it just returns nothing.
978
979 to perform referral, it actually composes the referral query
980 for a given host/port/type and calls the whois query function.
981
982 Well, it returns nothing anyway (void). It just prints to the socket.
983
984 char *ref_host referral server host name
985
986 unsigned ref_port_int referral server port number
987
988 char *qry query to be run
989
990 Author:
991 marek
992 ++++++++++++++++++++++++++++++++++++++*/
993 void run_referral(Query_environ *qe,
/* [<][>][^][v][top][bottom][index][help] */
994 char *ref_host,
995 unsigned ref_port_int,
996 char *qry)
997 {
998
999 #if 1 /* switch off for testing */
1000 er_ret_t err;
1001 char *rep;
1002
1003 /* WH_sock(sock, host, port, query, maxlines, timeout)) */
1004 err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry,
1005 ca_get_referralmaxlines, ca_get_referraltimeout
1006 );
1007
1008 switch( err ) {
1009 case SK_OK:
1010 /* OK */
1011 break;
1012 case SK_TIMEOUT:
1013 /* Referral timeout */
1014 rep = ca_get_qi_ref_tmout ;
1015 SK_cd_puts(&(qe->condat), rep);
1016 wr_free(rep);
1017 break;
1018
1019 case SK_BADHOST:
1020 /* Referral host not found */
1021 rep = ca_get_qi_ref_badhost ;
1022 SK_cd_puts(&(qe->condat), rep);
1023 wr_free(rep);
1024 break;
1025
1026 case SK_CONNECT:
1027 /* Referral host not responding */
1028 rep = ca_get_qi_ref_hostnottresp ;
1029 SK_cd_puts(&(qe->condat), rep);
1030 wr_free(rep);
1031 break;
1032
1033 case SK_BIND:
1034 case SK_SOCKET:
1035 /* XXX internal server problem... */
1036 die;
1037
1038 case WH_MAXLINES:
1039 /* Referral reply line limit exceeded */
1040 rep = ca_get_qi_ref_overmaxlin ;
1041 SK_cd_puts(&(qe->condat), rep);
1042 wr_free(rep);
1043 break;
1044
1045 default: /* any other errors ? */
1046 die;
1047 ;
1048 } /*switch WH_sock */
1049 #endif
1050
1051 }/*run_referral*/
1052
1053
1054
1055
1056
1057 /*++++++++++++++++++++++++++++++++++++++
1058
1059 prepare and run the referral, displaying the results directly to the
1060 client's connection.
1061
1062 XXX still missing protection against a referral loop
1063 XXX handling inverse flag not needed, to be removed
1064
1065 char *domain domain being looked up
1066
1067 Query_instructions *qis original query instructions structure
1068
1069 Query_environ *qe original query environment structure
1070
1071 Query_instruction *qi specific query instruction triggered
1072
1073 SQ_result_set_t *result result of the lookup containing referral details
1074
1075 SQ_row_t *row first row (should be only 1) of the result
1076 this should contain columns: type, port, host
1077
1078 char *sourcename name of the database "source"
1079
1080 Author:
1081 marek
1082 ++++++++++++++++++++++++++++++++++++++*/
1083 static
1084 void qi_prep_run_refer(char *domain,
/* [<][>][^][v][top][bottom][index][help] */
1085 Query_instructions *qis,
1086 Query_environ *qe,
1087 Query_instruction *qi,
1088 SQ_result_set_t *result, SQ_row_t *row,
1089 char *sourcename )
1090 {
1091 int err;
1092 long ref_type;
1093 long ref_port;
1094 char *ref_host;
1095 char querystr[STR_L];
1096
1097 /* get values from SQL query */
1098 err = SQ_get_column_int(result, row, 0, &ref_type);
1099 dieif(err);
1100 err = SQ_get_column_int(result, row, 1, &ref_port);
1101 dieif(err);
1102 ref_host = SQ_get_column_string(result, row, 2);
1103 dieif(ref_host == NULL);
1104
1105 strcpy(querystr,"");
1106
1107 /* put -r if the reftype is RIPE and -r or -i were used */
1108 if( (ref_type == RF_RIPE)
1109 && ( Query[qi->queryindex].querytype == Q_INVERSE
1110 || qis->recursive > 0 ) )
1111 {
1112 strcpy(querystr,"-r ");
1113 }
1114
1115 /* prepend with -Vversion,IP for type CLIENTADDRESS */
1116 if( ref_type == RF_CLIENTADDRESS ) {
1117 snprintf(querystr,sizeof(querystr),"-V%s,%s ",VERSION, qe->condat.ip);
1118 }
1119
1120
1121 /* now set the search term - set to the stripped down version
1122 for inverse query, full-length otherwise */
1123 if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1124 strcat(querystr, domain);
1125 }
1126 else {
1127 strcat(querystr, qis->qc->keys);
1128 }
1129
1130 {
1131 /* the object is not from %s,
1132 it comes from %s %d, use -R to see %s */
1133 char *rep = ca_get_qi_fmt_refheader ;
1134 SK_cd_printf(&(qe->condat), rep,
1135 sourcename,
1136 ref_host, ref_port,
1137 sourcename );
1138 wr_free(rep);
1139 }
1140
1141 /* do the referral */
1142 ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
1143
1144 run_referral( qe, ref_host, ref_port, querystr);
1145
1146 { /* End of referred query result */
1147 char *rep = ca_get_qi_reftrailer ;
1148 SK_cd_puts(&(qe->condat), rep);
1149 wr_free(rep);
1150 }
1151 SK_cd_puts(&(qe->condat), "\n");
1152
1153 wr_free(ref_host);
1154 }
1155
1156
1157 /*++++++++++++++++++++++++++++++++++++++
1158
1159 specific case of the object ID collection: the domains.
1160 Checks to see if the domain exists, and runs the referral if it is defined
1161 and the domain is missing.
1162
1163 Arguments:
1164
1165 char *sourcename name of the database "source"
1166
1167 SQ_connection_t *sql_connection sql connection dedicated to this thread
1168
1169 char *id_table name of the temporary table to be used
1170
1171 char *sub_table name of the temporary subtable
1172
1173 Query_instructions *qis original query instructions structure
1174
1175 Query_environ *qe original query environment structure
1176
1177 Query_instruction *qi specific query instruction triggered
1178
1179 acc_st *acc_credit credit for this client
1180
1181 Author:
1182 marek.
1183 ++++++++++++++++++++++++++++++++++++++*/
1184
1185 static int
1186 qi_collect_domain(char *sourcename,
/* [<][>][^][v][top][bottom][index][help] */
1187 SQ_connection_t *sql_connection,
1188 char *id_table,
1189 char *sub_table,
1190 Query_instructions *qis,
1191 Query_environ *qe,
1192 Query_instruction *qi,
1193 acc_st *acc_credit,
1194 int *sql_error)
1195 {
1196 char *domain = qis->qc->keys;
1197 char *dot = domain;
1198 int subcount = 0;
1199 int foundcount = 0;
1200
1201 /* we MUST NOT have a diconnection from the server here */
1202 dieif(qe->condat.rtc != 0);
1203
1204 /* while nothing found and still some pieces of the name left */
1205 while( dot != NULL && subcount == 0 ) {
1206 int refcount = 0;
1207 SQ_row_t *row;
1208 SQ_result_set_t *result_referrals = NULL;
1209 char sql_command[STR_XL];
1210
1211 ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1212
1213 /* domain lookup -- query into the _S table */
1214 sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
1215
1216 if( SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1217 *sql_error = SQ_errno(sql_connection);
1218 report_sql_error(&qe->condat, sql_connection, sql_command);
1219 return 0;
1220 }
1221 subcount = SQ_get_affected_rows(sql_connection);
1222
1223 if( subcount != 0 ) { /* domain exists in the database */
1224
1225 /* referral check. Always done except for -R and INVERSE queries */
1226 if( qis->qc->R == 0 &&
1227 Query[qi->queryindex].querytype != Q_INVERSE ) {
1228 sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
1229 if( SQ_execute_query(sql_connection, sql_command,
1230 &result_referrals) == -1)
1231 {
1232 *sql_error = SQ_errno(sql_connection);
1233 report_sql_error(&qe->condat, sql_connection, sql_command);
1234 return 0;
1235 }
1236 refcount = SQ_num_rows(result_referrals);
1237 }
1238
1239 /* if referral allowed and defined, even if domain was found but
1240 contained referral - refer the query */
1241 if( refcount != 0 ) {
1242 /* get the referral parameters from the first row
1243 and perform it
1244 */
1245
1246 row = SQ_row_next(result_referrals);
1247 /* now: query for the original domain */
1248 qi_prep_run_refer(domain,
1249 qis, qe, qi, result_referrals, row, sourcename);
1250
1251 acc_credit->referrals -= 1;
1252 }
1253 else {
1254 /* domain found
1255 and (referral undefined or disabled by -R or inverse)
1256 two possible outcomes depending on whether 'dot' is:
1257 * the original search term -> pass what's in _S and quit
1258 * a 'stripped' domain name -> return no result and quit
1259 */
1260 if( dot == domain ) {
1261 sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s",
1262 id_table, sub_table);
1263 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1264 *sql_error = SQ_errno(sql_connection);
1265 report_sql_error(&qe->condat, sql_connection, sql_command);
1266 return 0;
1267 }
1268 foundcount = SQ_get_affected_rows(sql_connection);
1269 }
1270 }
1271 dot = NULL; /* don't make another round */
1272 } /* a domain was found */
1273
1274 if( result_referrals != NULL ) {
1275 SQ_free_result(result_referrals);
1276 result_referrals = NULL;
1277 }
1278
1279 if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1280 dot++;
1281 }
1282 }
1283
1284 return foundcount;
1285 } /* check_domain */
1286
1287
1288 /* add_ref_name */
1289 /*++++++++++++++++++++++++++++++++++++++
1290
1291 Creates a SQL query for a reference-by-name lookup. Uses standard name
1292 lookup query generator (create_name_query), so the order of the names
1293 doesn't matter.
1294
1295 SQ_connection_t *sql_connection sql connection dedicated to this thread
1296
1297 char *rectable table in which to look up
1298
1299 char *allnames all name words to be looked up, space delimited.
1300
1301 ++++++++++++++++++++++++++++++++++++++*/
1302 static
1303 int
1304 add_ref_name(SQ_connection_t *sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
1305 char *rectable,
1306 char *allnames,
1307 sk_conn_st *condat
1308 )
1309 {
1310 /* construct the query, allow zero-length list */
1311 if( strlen(allnames) > 0 ) {
1312 char final_query[STR_XL];
1313 char select_query[STR_XL];
1314
1315 create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1316 "AND N00.object_type != 100 AND N00.thread_id = 0",
1317 allnames);
1318
1319 sprintf(final_query, "INSERT INTO %s %s",
1320 rectable,
1321 select_query);
1322
1323 allnames[0]=0;
1324
1325 if (SQ_execute_query(sql_connection, final_query, NULL) == -1 ) {
1326 report_sql_error(condat, sql_connection, final_query);
1327 return SQ_errno(sql_connection);
1328 }
1329
1330 }
1331
1332 return 0;
1333 }/* add_ref_name */
1334
1335
1336
1337 /* qi_collect_ids */
1338 /*++++++++++++++++++++++++++++++++++++++
1339
1340 collects object ID's from all queries defined in the Query_instructions
1341 array. The results from RADIX trees are maintained in a linked list, the
1342 results from SQL lookups are kept in a temporary table. For domains,
1343 a specific function is invoked that may run the referral.
1344 Any sql lookup will be limited to the maximum number of objects allowed
1345 for the client (acl and credit are checked for this).
1346 The routine uses its own temporary _S table, destroyed at exit.
1347
1348 ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1349
1350 char *sourcename name of the database "source"
1351
1352 SQ_connection_t **sql_connection sql connection dedicated to this thread
1353 (replaced on cancel)
1354
1355 Query_instructions *qis original query instructions structure
1356
1357 Query_environ *qe original query environment structure
1358
1359 char *id_table the table to store the ID's found
1360
1361 GList **datlist the list to store the Radix leaves found
1362
1363 acc_st *acc_credit credit for this client
1364
1365 acl_st *acl acl for this client
1366
1367 ++++++++++++++++++++++++++++++++++++++*/
1368 static
1369 int
1370 qi_collect_ids(ca_dbSource_t *dbhdl,
/* [<][>][^][v][top][bottom][index][help] */
1371 char *sourcename,
1372 SQ_connection_t **sql_connection,
1373 Query_instructions *qis,
1374 Query_environ *qe,
1375 char *id_table,
1376 GList **datlist,
1377 acc_st *acc_credit,
1378 acl_st *acl
1379 )
1380 {
1381 Query_instruction **ins=NULL;
1382 int i;
1383 int count, errors=0;
1384 char sql_command[STR_XL];
1385 er_ret_t err;
1386 char sub_table[32];
1387 int limit ;
1388 /* a limit on the max number of objects to be returned
1389 from a single search. For some queries the object types
1390 are not known at this stage, so the limit must be
1391 the higher number of the two: private / public,
1392 or unlimited if any of them is 'unlimited'.
1393 */
1394 char limit_str[32];
1395 int sql_error;
1396
1397 if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1398 strcpy(limit_str,"");
1399 } else {
1400 sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1401 so that the client hits
1402 the limit */
1403 }
1404
1405 sprintf(sub_table, "%s_S ", id_table);
1406
1407 /* see if there was a leftover table from a crashed session
1408 * (assume the ID cannot be currently in use)
1409 *
1410 * update: this can't ever happen with TEMPORARY tables, but we're going to
1411 * check for it anyway - shane
1412 */
1413 sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1414 if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1415 report_sql_error(&qe->condat, *sql_connection, sql_command);
1416 return SQ_errno(*sql_connection);
1417 }
1418
1419 /* create a table for special subqueries (domain only for now) */
1420 sprintf(sql_command, "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP",
1421 sub_table);
1422 if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1423 report_sql_error(&qe->condat, *sql_connection, sql_command);
1424 return SQ_errno(*sql_connection);
1425 }
1426
1427 /* Iterate through query instructions */
1428 ins = qis->instruction;
1429 sql_error = 0;
1430 for (i=0; ins[i] != NULL && errors == 0; i++) {
1431 Query_instruction *qi = ins[i];
1432
1433 /* check if the client is still there */
1434 if( qe->condat.rtc ) {
1435 break;
1436 }
1437
1438 switch ( qi->search_type ) {
1439 case R_SQL:
1440 if ( qi->query_str != NULL ) {
1441
1442 /* handle special cases first */
1443 if( Query[qi->queryindex].class == C_DN
1444 && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1445
1446 /* if any more cases than just domain appear, we will be
1447 cleaning the _S table from the previous query here
1448
1449 "DELETE FROM %s_S"
1450 */
1451
1452 count = qi_collect_domain(sourcename, *sql_connection, id_table,
1453 sub_table, qis, qe, qi, acc_credit,
1454 &sql_error);
1455 } /* if class DN and Straight lookup */
1456 else {
1457 /* any other class of query */
1458
1459 sprintf(sql_command, "INSERT INTO %s %s %s",
1460 id_table, qi->query_str, limit_str);
1461
1462 if(sql_execute_watched( &(qe->condat), sql_connection,
1463 sql_command, NULL) == -1 ) {
1464 errors++;
1465 sql_error = SQ_errno(*sql_connection);
1466 report_sql_error(&qe->condat, *sql_connection, sql_command);
1467 }
1468 count = SQ_get_affected_rows(*sql_connection);
1469 } /* not DN */
1470 } /* if SQL query not NULL */
1471
1472 ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1473 "%d entries added in %s query for %s",
1474 count, Query[qi->queryindex].descr, qis->qc->keys
1475 );
1476 break;
1477
1478 case R_RADIX:
1479
1480
1481 err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
1482 qi->rx_keys, dbhdl,
1483 Query[qi->queryindex].attribute,
1484 datlist, limit);
1485
1486
1487 if( NOERR(err)) {
1488 if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1489 /* prevent unnecessary g_list_length call */
1490
1491 ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1492 "%d entries after %s (mode %d par %d reg %d) query for %s",
1493 g_list_length(*datlist),
1494 Query[qi->queryindex].descr,
1495 qi->rx_srch_mode, qi->rx_par_a,
1496 dbhdl,
1497 qi->rx_keys);
1498 }
1499 }
1500 else {
1501 ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1502 "RP_asc_search returned %x ", err);
1503 }
1504 break;
1505
1506 default: die;
1507 } /* switch */
1508
1509 } /* for <every instruction> */
1510
1511 /* Now drop the _S table */
1512 sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1513 if(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1514 report_sql_error(&qe->condat, *sql_connection, sql_command);
1515 return SQ_errno(*sql_connection);
1516 }
1517
1518 /* return success */
1519 return sql_error;
1520 }
1521
1522 /* qi_fetch_references */
1523 /*++++++++++++++++++++++++++++++++++++++
1524
1525 given the list of object ID's collects the references from these objects
1526 to person and role objects. Uses its own temporary SQL table (_R)
1527 and upon completion transfers the results from it to the main
1528 temporary table. Runs queries in watched mode, to be able to cancel them.
1529
1530 SQ_connection_t **sql_connection sql connection dedicated to this thread
1531 (replaced on cancel)
1532
1533 Query_environ *qe original query environment structure
1534
1535 char *id_table the table with the ID's found
1536
1537 acc_st *acc_credit credit for this client
1538
1539 acl_st *acl acl for this client
1540
1541 ++++++++++++++++++++++++++++++++++++++*/
1542 static
1543 int
1544 qi_fetch_references(SQ_connection_t **sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
1545 Query_environ *qe,
1546 char *id_table,
1547 acc_st *acc_credit,
1548 acl_st *acl
1549 )
1550 {
1551 char rec_table[32];
1552 SQ_result_set_t *result = NULL;
1553 SQ_row_t *row;
1554 int thisid = 0;
1555 int oldid = 0;
1556 char allnames[STR_L];
1557 char sql_command[STR_XL];
1558 int sql_error;
1559
1560 /* use sql_error to flag errors */
1561 sql_error = 0;
1562
1563 sprintf(rec_table, "%s_R", id_table);
1564
1565 /* see if there was a leftover table from a crashed session
1566 * (assume the ID cannot be currently in use)
1567 */
1568 sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1569 if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1570 report_sql_error(&qe->condat, *sql_connection, sql_command);
1571 return SQ_errno(*sql_connection);
1572 }
1573
1574 /* a temporary table for recursive data must be created, because
1575 a query using the same table as a source and target is illegal
1576 ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1577 */
1578 sprintf(sql_command,
1579 "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP",
1580 rec_table);
1581 if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1582 report_sql_error(&qe->condat, *sql_connection, sql_command);
1583 return SQ_errno(*sql_connection);
1584 }
1585
1586 /* from this point on, we can't just return on error, because
1587 we need to insure the table we just created gets dropped */
1588
1589 /* find the contacts */
1590 sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1591 if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL)
1592 == -1)
1593 {
1594 sql_error = SQ_errno(*sql_connection);
1595 report_sql_error(&qe->condat, *sql_connection, sql_command);
1596 }
1597
1598 if (!sql_error) {
1599 sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1600 if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1601 NULL) == -1)
1602 {
1603 sql_error = SQ_errno(*sql_connection);
1604 report_sql_error(&qe->condat, *sql_connection, sql_command);
1605 }
1606 }
1607
1608 if (!sql_error) {
1609 sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1610 if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1611 NULL) == -1)
1612 {
1613 sql_error = SQ_errno(*sql_connection);
1614 report_sql_error(&qe->condat, *sql_connection, sql_command);
1615 }
1616 }
1617
1618 if (!sql_error) {
1619 sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1620 if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1621 NULL) == -1)
1622 {
1623 sql_error = SQ_errno(*sql_connection);
1624 report_sql_error(&qe->condat, *sql_connection, sql_command);
1625 }
1626 }
1627
1628 /* replace references to dummies by references by name */
1629 if (!sql_error) {
1630 sprintf(sql_command,
1631 " SELECT id, name FROM %s IDS STRAIGHT_JOIN names "
1632 " WHERE IDS.id = names.object_id "
1633 " AND names.object_type = 100"
1634 " ORDER BY id",
1635 rec_table);
1636 if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1637 &result) == -1)
1638 {
1639 sql_error = SQ_errno(*sql_connection);
1640 report_sql_error(&qe->condat, *sql_connection, sql_command);
1641 }
1642 }
1643
1644 /* well, it might not be -1, but if the watchdog worked then the
1645 result is NULL */
1646 if (!sql_error && (result != NULL)) {
1647
1648 allnames[0]=0;
1649 /* now go through the results and collect names */
1650 while ( !sql_error &&
1651 (qe->condat.rtc == 0) &&
1652 (row = SQ_row_next(result)) != NULL )
1653 {
1654 char *id = SQ_get_column_string(result, row, 0);
1655 char *name = SQ_get_column_string(result, row, 1);
1656
1657 thisid = atoi(id);
1658
1659 /* when the id changes, the name is complete */
1660 if( thisid != oldid && oldid != 0 ) {
1661 sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1662 &qe->condat);
1663 }
1664
1665 strcat(allnames, name);
1666 strcat(allnames, " ");
1667 oldid = thisid;
1668 wr_free(id);
1669 wr_free(name);
1670 }
1671 /* also do the last name */
1672 if (!sql_error) {
1673 sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1674 &qe->condat);
1675 }
1676
1677 SQ_free_result(result); /* we can do it only because the watchdog */
1678 /* has not started between the check for non-NULL result and here */
1679 }
1680
1681 /* if we've lost connection, don't bother with this extra work */
1682 if (!sql_error && (qe->condat.rtc == 0)) {
1683 /* now copy things back to the main temporary table */
1684 sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
1685 id_table, rec_table);
1686 if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1687 sql_error = SQ_errno(*sql_connection);
1688 report_sql_error(&qe->condat, *sql_connection, sql_command);
1689 }
1690 }
1691
1692 /* Now drop the IDS recursive table (try to do this even if
1693 we had an SQL error, to avoid leaving extra tables lying around) */
1694 sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1695 if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1696 sql_error = SQ_errno(*sql_connection);
1697 report_sql_error(&qe->condat, *sql_connection, sql_command);
1698 }
1699
1700 /* return error, if any */
1701 return sql_error;
1702 }
1703 /* qi_fetch_references */
1704
1705
1706 /* QI_execute() */
1707 /*++++++++++++++++++++++++++++++++++++++
1708 Execute the query instructions. This is called for each source.
1709 This is linked into MySQL by the fact that MySQL doesn't have sub selects
1710 (yet). The queries are done in two stages. Make some temporary tables and
1711 insert into them. Then use them in the next select.
1712
1713
1714 ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1715
1716 Query_instructions *qis query instructions.
1717
1718 Query_environ *qe query environment.
1719
1720 acc_st *acc_credit object display credit
1721
1722 acl_st *acl copy of the original acl for this client
1723
1724 More:
1725 +html+ <PRE>
1726 Authors:
1727 ottrey - original version,
1728 marek - the rest.
1729 +html+ </PRE>
1730 ++++++++++++++++++++++++++++++++++++++*/
1731 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
/* [<][>][^][v][top][bottom][index][help] */
1732 Query_instructions *qis,
1733 Query_environ *qe,
1734 acc_st *acc_credit,
1735 acl_st *acl
1736 )
1737 {
1738 /* those things must be freed after use! */
1739 char *dbhost = ca_get_srcdbmachine(dbhdl);
1740 char *dbname = ca_get_srcdbname(dbhdl);
1741 char *dbuser = ca_get_srcdbuser(dbhdl);
1742 char *dbpass = ca_get_srcdbpassword(dbhdl);
1743 char *srcnam;
1744 unsigned dbport = ca_get_srcdbport(dbhdl);
1745 char id_table[STR_S];
1746 char sql_command[STR_XL];
1747 GList *datlist=NULL;
1748 SQ_connection_t *sql_connection=NULL;
1749 int sql_error;
1750
1751 sql_connection = SQ_get_connection( dbhost, dbport,
1752 dbname, dbuser, dbpass );
1753 /* free parameters when done */
1754 wr_free(dbhost);
1755 wr_free(dbuser);
1756 wr_free(dbpass);
1757
1758 /* return error if occurred */
1759 if (sql_connection == NULL) {
1760 ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
1761 dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1762 wr_free(dbname);
1763 return QI_CANTDB;
1764 }
1765 wr_free(dbname);
1766
1767 /* from here on out, we use the sql_error flag to verify our
1768 connection to the SQL database is still good */
1769 sql_error = 0;
1770
1771 sprintf(id_table, "ID_%ld_%d", mysql_thread_id(sql_connection),
1772 pthread_self());
1773
1774 /* see if there was a leftover table from a crashed session
1775 * (assume the ID cannot be currently in use)
1776 */
1777 sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1778 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1779 sql_error = SQ_errno(sql_connection);
1780 report_sql_error(&qe->condat, sql_connection, sql_command);
1781 }
1782
1783 /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1784 if (!sql_error) {
1785 sprintf(sql_command,
1786 "CREATE " TEMPORARY
1787 " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1788 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1789 sql_error = SQ_errno(sql_connection);
1790 report_sql_error(&qe->condat, sql_connection, sql_command);
1791 }
1792 }
1793
1794 if (!sql_error) {
1795 srcnam = ca_get_srcname(dbhdl);
1796 sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe,
1797 id_table, &datlist, acc_credit, acl);
1798 wr_free(srcnam);
1799 }
1800
1801 /* post-processing */
1802 if (!sql_error && (qis->filtered == 0)) {
1803 /* start the watchdog just to set the rtc flag */
1804 SK_watch_setclear(&(qe->condat));
1805 SK_watchstart(&(qe->condat));
1806
1807 /* add radix results (only if -K is not active and still connected) */
1808 if (qe->condat.rtc == 0) {
1809 sql_error = insert_radix_serials(&(qe->condat),
1810 sql_connection,
1811 id_table,
1812 datlist);
1813 }
1814
1815 SK_watchstop(&(qe->condat));
1816 }
1817
1818 /* fetch recursive objects (ac,tc,zc,ah) */
1819 if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1820 sql_error = qi_fetch_references(&sql_connection,
1821 qe,
1822 id_table,
1823 acc_credit,
1824 acl);
1825 } /* if recursive */
1826
1827 /* display */
1828 /* -K filtering:
1829 * right now only filtering, no expanding sets like write_set_objects()
1830 */
1831
1832 /* display the immediate data from the radix tree */
1833 if (!sql_error && (qis->filtered == 1)) {
1834 write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1835 }
1836
1837 /* display objects from the IDs table */
1838 if (!sql_error) {
1839 sql_error = write_objects( &sql_connection, id_table, qis->filtered,
1840 qis->fast, &(qe->condat), acc_credit, acl);
1841 }
1842
1843 /* drop the table */
1844 /* try to do this, even if there is an SQL error */
1845 sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1846 if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1847 sql_error = SQ_errno(sql_connection);
1848 report_sql_error(&qe->condat, sql_connection, sql_command);
1849 }
1850
1851 /* Now disconnect (temporary tables get dropped automatically) */
1852 SQ_close_connection(sql_connection);
1853
1854 /* return appropriate value */
1855 if (sql_error) {
1856 return QI_SQLERR;
1857 } else {
1858 return QI_OK;
1859 }
1860 } /* QI_execute() */
1861
1862
1863 /* instruction_free() */
1864 /*++++++++++++++++++++++++++++++++++++++
1865 Free the instruction.
1866
1867 Query_instruction *qi query_instruction to be freed.
1868
1869 More:
1870 +html+ <PRE>
1871 Authors:
1872 ottrey
1873 +html+ </PRE>
1874 ++++++++++++++++++++++++++++++++++++++*/
1875 static void instruction_free(Query_instruction *qi) {
/* [<][>][^][v][top][bottom][index][help] */
1876 if (qi != NULL) {
1877 if (qi->query_str != NULL) {
1878 wr_free(qi->query_str);
1879 }
1880 wr_free(qi);
1881 }
1882 } /* instruction_free() */
1883
1884 /* QI_free() */
1885 /*++++++++++++++++++++++++++++++++++++++
1886 Free the query_instructions.
1887
1888 Query_instructions *qis Query_instructions to be freed.
1889
1890 More:
1891 +html+ <PRE>
1892 Authors:
1893 ottrey, marek
1894 +html+ </PRE>
1895 ++++++++++++++++++++++++++++++++++++++*/
1896 void QI_free(Query_instructions *qis) {
/* [<][>][^][v][top][bottom][index][help] */
1897 int i;
1898
1899 for (i=0; qis->instruction[i] != NULL; i++) {
1900 instruction_free(qis->instruction[i]);
1901 }
1902
1903 if (qis != NULL) {
1904 wr_free(qis);
1905 }
1906
1907 } /* QI_free() */
1908
1909 /*++++++++++++++++++++++++++++++++++++++
1910 Determine if this query should be conducted or not.
1911
1912 If it was an inverse query - if the attribute appears in the query command's bitmap.
1913 If it was a lookup query - if the attribute appears in the object type bitmap or
1914 disregard if there is no object_type bitmap (Ie object filter).
1915
1916 mask_t bitmap The bitmap of attribute to be converted.
1917
1918 const Query_command *qc The query_command that the instructions are created
1919 from.
1920
1921 const Query_t q The query being considered.
1922 +html+ <PRE>
1923 Authors:
1924 ottrey,
1925 marek.
1926 +html+ </PRE>
1927 ++++++++++++++++++++++++++++++++++++++*/
1928 static int valid_query(const Query_command *qc, const Query_t q) {
/* [<][>][^][v][top][bottom][index][help] */
1929 int result=0;
1930
1931 if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1932 if (q.query != NULL) {
1933 switch (q.querytype) {
1934 case Q_INVERSE:
1935 if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1936 result = 1;
1937 }
1938 break;
1939
1940 case Q_LOOKUP:
1941 if (q.class == C_ANY
1942 || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
1943 result=1;
1944 }
1945 break;
1946
1947 default:
1948 /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1949 }
1950 }
1951 }
1952
1953 return result;
1954 } /* valid_query() */
1955
1956 /* QI_new() */
1957 /*++++++++++++++++++++++++++++++++++++++
1958 Create a new set of query_instructions. Returns an allocated structure which
1959 must be freed after use with QI_free().
1960
1961 const Query_command *qc The query_command that the instructions are created
1962 from.
1963
1964 const Query_environ *qe The environmental variables that they query is being
1965 performed under.
1966
1967 +html+ <PRE>
1968 Authors:
1969 ottrey,
1970 marek.
1971 +html+ </PRE>
1972 ++++++++++++++++++++++++++++++++++++++*/
1973 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
/* [<][>][^][v][top][bottom][index][help] */
1974 Query_instructions *qis=NULL;
1975 Query_instruction *qi=NULL;
1976 int i_no=0;
1977 int i;
1978 char *query_str;
1979
1980 dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1981
1982 qis->filtered = qc->filtered;
1983 qis->fast = qc->fast;
1984 qis->recursive = qc->recursive;
1985 qis->qc = (qc);
1986
1987
1988 for (i=0; Query[i].query != NULL; i++) {
1989
1990 /* If a valid query. */
1991 if ( valid_query(qc, Query[i]) == 1) {
1992
1993 dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1994
1995 qi->queryindex = i;
1996
1997 /* SQL Query */
1998 if ( Query[i].refer == R_SQL) {
1999 qi->search_type = R_SQL;
2000 query_str = create_query(Query[i], qc);
2001
2002 if (query_str!= NULL) {
2003 qi->query_str = query_str;
2004 qis->instruction[i_no++] = qi;
2005 }
2006 }
2007 /* Radix Query */
2008 else if (Query[i].refer == R_RADIX) {
2009 qi->search_type = R_RADIX;
2010
2011 if (map_qc2rx(qi, qc) == 1) {
2012 int j;
2013 int found=0;
2014
2015 /* check that there is no such query yet, for example if
2016 more than one keytype (wk) matched */
2017 for (j=0; j<i_no; j++) {
2018 Query_instruction *qij = qis->instruction[j];
2019
2020 if( qij->search_type == R_RADIX
2021 && Query[qij->queryindex].attribute
2022 == Query[qi ->queryindex].attribute) {
2023
2024 found=1;
2025 break;
2026 }
2027 }
2028
2029 if ( found ) {
2030 /* Discard the Query Instruction */
2031 wr_free(qi);
2032 }
2033 else {
2034 /* Add the query_instruction to the array */
2035 qis->instruction[i_no++] = qi;
2036 }
2037 }
2038 }
2039 else {
2040 /* ERROR: bad search_type */
2041 die;
2042 }
2043 }
2044 }
2045 qis->instruction[i_no++] = NULL;
2046
2047
2048 { /* tracing */
2049 char *descrstr = QI_queries_to_string(qis);
2050
2051 ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2052 wr_free( descrstr );
2053 }
2054
2055 return qis;
2056
2057 } /* QI_new() */
2058
2059
2060
2061
2062
2063 /*++++++++++++++++++++++++++++++++++++++
2064
2065 char *QI_queries_to_string returns a list of descriptions for queries
2066 that will be performed (debugging only).
2067 Allocated text, must be freed after use.
2068
2069 Query_instructions *qis query instructions structure
2070
2071 Author:
2072 marek.
2073 ++++++++++++++++++++++++++++++++++++++*/
2074
2075 char *QI_queries_to_string(Query_instructions *qis)
/* [<][>][^][v][top][bottom][index][help] */
2076 {
2077 Query_instruction *qi;
2078 int i;
2079 char *resstr = NULL;
2080
2081 dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
2082 strcpy(resstr, "{");
2083
2084 for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) {
2085 char *descr = Query[qi->queryindex].descr;
2086 int oldres = strlen( resstr );
2087
2088 dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
2089 strcat(resstr, descr);
2090 strcat(resstr, ",");
2091 }
2092 if( i>0 ) {
2093 /* cancel the last comma */
2094 resstr[strlen(resstr)-1] = 0;
2095 }
2096
2097 dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 )
2098 != UT_OK);
2099 strcat(resstr, "}");
2100
2101 return resstr;
2102 }