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