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