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