1 | /*************************************** 2 | $Revision: 1.27 $ 3 | 4 | 5 | Sql module (sq). This is a mysql implementation of an sql module. 6 | 7 | Status: NOT REVUED, NOT TESTED 8 | 9 | Note: this code has been heavily coupled to MySQL, and may need to be changed 10 | (to improve performance) if a new RDBMS is used. 11 | 12 | ******************/ /****************** 13 | Filename : query_instructions.c 14 | Author : ottrey@ripe.net 15 | OSs Tested : Solaris 16 | Problems : Moderately linked to MySQL. Not sure which inverse 17 | attributes each option has. Would like to modify this 18 | after re-designing the objects module. 19 | Comments : Not sure about the different keytypes. 20 | ******************/ /****************** 21 | Copyright (c) 1999 RIPE NCC 22 | 23 | All Rights Reserved 24 | 25 | Permission to use, copy, modify, and distribute this software and its 26 | documentation for any purpose and without fee is hereby granted, 27 | provided that the above copyright notice appear in all copies and that 28 | both that copyright notice and this permission notice appear in 29 | supporting documentation, and that the name of the author not be 30 | used in advertising or publicity pertaining to distribution of the 31 | software without specific, written prior permission. 32 | 33 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 34 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 35 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 36 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 37 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 38 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 39 | ***************************************/ 40 | #include <stdio.h> 41 | #include <string.h> 42 | #include <glib.h> 43 | 44 | #include "which_keytypes.h" 45 | #include "query_instructions.h" 46 | #include "mysql_driver.h" 47 | #include "rxroutines.h" 48 | #include "stubs.h" 49 | #include "constants.h" 50 | 51 | /*+ String sizes +*/ 52 | #define STR_S 63 53 | #define STR_M 255 54 | #define STR_L 1023 55 | #define STR_XL 4095 56 | #define STR_XXL 16383 57 | 58 | 59 | #include "QI_queries.def" 60 | 61 | /* log_inst_print() */ 62 | /*++++++++++++++++++++++++++++++++++++++ 63 | Log the instruction. 64 | 65 | char *str instruction to be logged. 66 | 67 | More: 68 | +html+ <PRE> 69 | Authors: 70 | ottrey 71 | +html+ </PRE><DL COMPACT> 72 | +html+ <DT>Online References: 73 | +html+ <DD><UL> 74 | +html+ </UL></DL> 75 | 76 | ++++++++++++++++++++++++++++++++++++++*/ 77 | void log_inst_print(char *str) { 78 | FILE *logf; 79 | 80 | if (CO_get_instr_logging() == 1) { 81 | if (strcmp(CO_get_instr_logfile(), "stdout") == 0) { 82 | printf("%s", str); 83 | } 84 | else { 85 | logf = fopen(CO_get_instr_logfile(), "a"); 86 | fprintf(logf, "%s", str); 87 | fclose(logf); 88 | } 89 | } 90 | 91 | } /* log_inst_print() */ 92 | 93 | /* create_name_query() */ 94 | /*++++++++++++++++++++++++++++++++++++++ 95 | Create an sql query for the names table. 96 | 97 | char *query_str 98 | 99 | const char *sql_query 100 | 101 | const char *keys 102 | 103 | More: 104 | +html+ <PRE> 105 | Authors: 106 | ottrey 107 | +html+ </PRE><DL COMPACT> 108 | +html+ <DT>Online References: 109 | +html+ <DD><UL> 110 | +html+ </UL></DL> 111 | 112 | ++++++++++++++++++++++++++++++++++++++*/ 113 | static void create_name_query(char *query_str, const char *sql_query, const char *keys) { 114 | int i; 115 | /* Allocate stuff */ 116 | GString *result = g_string_sized_new(STR_XXL); 117 | GString *from_clause = g_string_sized_new(STR_XL); 118 | GString *where_clause = g_string_sized_new(STR_XL); 119 | gchar **words = g_strsplit(keys, " ", 0); 120 | 121 | g_string_sprintfa(from_clause, "names N%.2d", 0); 122 | g_string_sprintfa(where_clause, "N%.2d.name='%s'", 0, words[0]); 123 | 124 | for (i=1; words[i] != NULL; i++) { 125 | g_string_sprintfa(from_clause, ", names N%.2d", i); 126 | g_string_sprintfa(where_clause, " AND N%.2d.name='%s' AND N00.object_id = N%.2d.object_id", i, words[i], i); 127 | } 128 | 129 | sprintf(query_str, sql_query, from_clause->str, where_clause->str); 130 | 131 | /* Free up stuff */ 132 | g_strfreev(words); 133 | g_string_free(where_clause, TRUE); 134 | g_string_free(from_clause, TRUE); 135 | 136 | } /* create_name_query() */ 137 | 138 | static void add_filter(char *query_str, const Query_command *qc) { 139 | int i; 140 | int qlen; 141 | char filter_atom[STR_M]; 142 | 143 | /* 144 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 145 | g_string_sprintfa(query_str, " AND ("); 146 | for (i=0; i < C_END; i++) { 147 | if (MA_isset(qc->object_type_bitmap, i)) { 148 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i)); 149 | } 150 | } 151 | g_string_truncate(query_str, query_str->len-3); 152 | g_string_append_c(query_str, ')'); 153 | } 154 | */ 155 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 156 | strcat(query_str, " AND ("); 157 | for (i=0; i < C_END; i++) { 158 | if (MA_isset(qc->object_type_bitmap, i)) { 159 | strcpy(filter_atom, ""); 160 | sprintf(filter_atom, "i.object_type = %d OR ", DF_get_class_dbase_code(i)); 161 | strcat(query_str, filter_atom); 162 | } 163 | } 164 | qlen = strlen(query_str); 165 | query_str[qlen-3] = ')'; 166 | query_str[qlen-2] = '\0'; 167 | query_str[qlen-1] = '\0'; 168 | } 169 | 170 | } /* add_filter() */ 171 | 172 | /* create_query() */ 173 | /*++++++++++++++++++++++++++++++++++++++ 174 | Create an sql query from the query_command and the matching keytype and the 175 | selected inverse attributes. 176 | Note this clears the first inv_attribute it sees, so is called sequentially 177 | until there are no inv_attributes left. 178 | 179 | WK_Type keytype The matching keytype. 180 | 181 | const Query_command *qc The query command. 182 | 183 | mask_t *inv_attrs_bitmap The selected inverse attributes. 184 | 185 | More: 186 | +html+ <PRE> 187 | Authors: 188 | ottrey 189 | +html+ </PRE><DL COMPACT> 190 | +html+ <DT>Online References: 191 | +html+ <DD><UL> 192 | +html+ </UL></DL> 193 | 194 | ++++++++++++++++++++++++++++++++++++++*/ 195 | static char *create_query(const Query_t q, const Query_command *qc) { 196 | char *result=NULL; 197 | char result_buff[STR_XL]; 198 | Q_Type_t querytype; 199 | int conduct_test = 0; 200 | 201 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) { 202 | querytype = Q_INVERSE; 203 | } 204 | else { 205 | querytype = Q_LOOKUP; 206 | } 207 | 208 | if ( (q.query != NULL) 209 | && (q.querytype == querytype) ) { 210 | conduct_test=1; 211 | } 212 | 213 | if (conduct_test == 1) { 214 | 215 | if (q.keytype == WK_NAME) { 216 | /* Name queries require special treatment. */ 217 | create_name_query(result_buff, q.query, qc->keys); 218 | } 219 | else { 220 | sprintf(result_buff, q.query, qc->keys); 221 | } 222 | 223 | if (q.class == -1) { 224 | /* It is class type ANY so add the object filtering */ 225 | add_filter(result_buff, qc); 226 | } 227 | 228 | result = (char *)malloc(strlen(result_buff)+1); 229 | strcpy(result, result_buff); 230 | } 231 | 232 | return result; 233 | } /* create_query() */ 234 | 235 | /* fast_output() */ 236 | /*++++++++++++++++++++++++++++++++++++++ 237 | This is for the '-F' flag. 238 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 239 | 240 | Fast isn't fast anymore - it's just there for compatibility reasons. 241 | This could be speed up if there were breaks out of the loops, once it matched something. 242 | (Wanna add a goto Marek? :-) ). 243 | 244 | const char *string The string to be "fast outputed". 245 | 246 | More: 247 | +html+ <PRE> 248 | Authors: 249 | ottrey 250 | +html+ </PRE><DL COMPACT> 251 | +html+ <DT>Online References: 252 | +html+ <DD><UL> 253 | +html+ </UL></DL> 254 | 255 | ++++++++++++++++++++++++++++++++++++++*/ 256 | char *fast_output(const char *str) { 257 | int i,j; 258 | char *result; 259 | char result_bit[STR_L]; 260 | char result_buff[STR_XL]; 261 | gchar **lines = g_strsplit(str, "\n", 0); 262 | char * const *attribute_names; 263 | gboolean filtering_an_attribute = FALSE; 264 | char *value; 265 | 266 | attribute_names = DF_get_attribute_names(); 267 | 268 | strcpy(result_buff, ""); 269 | for (i=0; attribute_names[i] != NULL; i++) { 270 | for (j=0; lines[j] != NULL; j++) { 271 | if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) { 272 | strcpy(result_bit, ""); 273 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */ 274 | value = strchr(lines[j], ':'); 275 | value++; 276 | /* Now get rid of whitespace. */ 277 | while (*value == ' ' || *value == '\t') { 278 | value++; 279 | } 280 | sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value); 281 | strcat(result_buff, result_bit); 282 | } 283 | else if (filtering_an_attribute == TRUE) { 284 | switch (lines[j][0]) { 285 | case ' ': 286 | case '\t': 287 | case '+': 288 | strcpy(result_bit, ""); 289 | sprintf(result_bit, "%s\n", lines[j]); 290 | strcat(result_buff, result_bit); 291 | break; 292 | 293 | default: 294 | filtering_an_attribute = FALSE; 295 | } 296 | } 297 | } 298 | } 299 | 300 | result = (char *)malloc(strlen(result_buff)+1); 301 | strcpy(result, result_buff); 302 | 303 | return result; 304 | } /* fast_output() */ 305 | 306 | /* filter() */ 307 | /*++++++++++++++++++++++++++++++++++++++ 308 | Basically it's for the '-K' flag for non-set (and non-radix) objects. 309 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 310 | 311 | This could be speed up if there were breaks out of the loops, once it matched something. 312 | (Wanna add a goto Marek? :-) ). 313 | 314 | const char *string The string to be filtered. 315 | 316 | More: 317 | +html+ <PRE> 318 | Authors: 319 | ottrey 320 | +html+ </PRE><DL COMPACT> 321 | +html+ <DT>Online References: 322 | +html+ <DD><UL> 323 | +html+ </UL></DL> 324 | 325 | ++++++++++++++++++++++++++++++++++++++*/ 326 | char *filter(const char *str) { 327 | int i,j; 328 | char *result; 329 | char result_bit[STR_L]; 330 | char result_buff[STR_XL]; 331 | gchar **lines = g_strsplit(str, "\n", 0); 332 | char * const *filter_names; 333 | gboolean filtering_an_attribute = FALSE; 334 | 335 | filter_names = DF_get_filter_names(); 336 | 337 | strcpy(result_buff, ""); 338 | for (i=0; filter_names[i] != NULL; i++) { 339 | for (j=0; lines[j] != NULL; j++) { 340 | if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) { 341 | strcpy(result_bit, ""); 342 | sprintf(result_bit, "%s\n", lines[j]); 343 | strcat(result_buff, result_bit); 344 | filtering_an_attribute = TRUE; 345 | } 346 | else if (filtering_an_attribute == TRUE) { 347 | switch (lines[j][0]) { 348 | case ' ': 349 | case '\t': 350 | case '+': 351 | strcpy(result_bit, ""); 352 | sprintf(result_bit, "%s\n", lines[j]); 353 | strcat(result_buff, result_bit); 354 | break; 355 | 356 | default: 357 | filtering_an_attribute = FALSE; 358 | } 359 | } 360 | } 361 | } 362 | 363 | result = (char *)malloc(strlen(result_buff)+1); 364 | strcpy(result, result_buff); 365 | 366 | return result; 367 | } /* filter() */ 368 | 369 | /* write_results() */ 370 | /*++++++++++++++++++++++++++++++++++++++ 371 | Write the results to the client socket. 372 | 373 | SQ_result_set_t *result The result set returned from the sql query. 374 | unsigned filtered if the objects should go through a filter (-K) 375 | sk_conn_st *condat Connection data for the client 376 | int maxobjects max # of objects to write 377 | 378 | XXX NB. this is very dependendant on what rows are returned in the result!!! 379 | 380 | More: 381 | +html+ <PRE> 382 | Authors: 383 | ottrey 384 | +html+ </PRE><DL COMPACT> 385 | +html+ <DT>Online References: 386 | +html+ <DD><UL> 387 | +html+ </UL></DL> 388 | 389 | ++++++++++++++++++++++++++++++++++++++*/ 390 | static int write_results(SQ_result_set_t *result, 391 | unsigned filtered, 392 | unsigned fast, 393 | sk_conn_st *condat, 394 | int maxobjects /* -1 == unlimited */ 395 | ) { 396 | SQ_row_t *row; 397 | char *str; 398 | char *filtrate; 399 | char *fasted; 400 | char log_str[STR_L]; 401 | int retrieved_objects=0; 402 | 403 | /* Get all the results - one at a time */ 404 | if (result != NULL) { 405 | while ( (row = SQ_row_next(result)) != NULL 406 | && (maxobjects == -1 || retrieved_objects < maxobjects) ) { 407 | str = SQ_get_column_string(result, row, 0); 408 | if (str != NULL) { 409 | strcpy(log_str, ""); 410 | sprintf(log_str, "Retrieved serial id = %d\n", atoi(str)); 411 | log_inst_print(log_str); 412 | } 413 | free(str); 414 | 415 | str = SQ_get_column_string(result, row, 2); 416 | if (str != NULL) { 417 | 418 | /* The fast output stage */ 419 | if (fast == 1) { 420 | fasted = fast_output(str); 421 | free(str); 422 | str = fasted; 423 | } 424 | 425 | /* The filtering stage */ 426 | if (filtered == 0) { 427 | SK_cd_puts(condat, str); 428 | } 429 | else { 430 | filtrate = filter(str); 431 | SK_cd_puts(condat, filtrate); 432 | free(filtrate); 433 | } 434 | SK_cd_puts(condat, "\n"); 435 | retrieved_objects++; 436 | } 437 | free(str); 438 | } 439 | } 440 | 441 | return retrieved_objects; 442 | } /* write_results() */ 443 | 444 | /* write_objects() */ 445 | /*++++++++++++++++++++++++++++++++++++++ 446 | This is linked into MySQL by the fact that MySQL doesn't have sub selects 447 | (yet). The queries are done in two stages. Make some temporary tables and 448 | insert into them. Then use them in the next select. 449 | 450 | SQ_connection_t *sql_connection The connection to the database. 451 | 452 | char *id_table The id of the temporary table (This is a result of the hacky 453 | way we've tried to get MySQL to do sub-selects.) 454 | 455 | unsigned int recursive A recursive query. 456 | 457 | sk_conn_st *condat Connection data for the client 458 | 459 | More: 460 | +html+ <PRE> 461 | Authors: 462 | ottrey 463 | +html+ </PRE><DL COMPACT> 464 | ++++++++++++++++++++++++++++++++++++++*/ 465 | static void write_objects(SQ_connection_t *sql_connection, 466 | char *id_table, 467 | unsigned int recursive, 468 | unsigned int filtered, 469 | unsigned int fast, 470 | sk_conn_st *condat, 471 | acc_st *acc_credit 472 | ) { 473 | /* XXX This should really return a linked list of the objects */ 474 | 475 | SQ_result_set_t *result; 476 | int retrieved_objects=0; 477 | int retrieved_contacts=0; 478 | 479 | char sql_command[STR_XL]; 480 | char log_str[STR_L]; 481 | 482 | strcpy(sql_command, ""); 483 | /* XXX These may and should change a lot. */ 484 | sprintf(sql_command, Q_OBJECTS, id_table, id_table); 485 | result = SQ_execute_query(SQ_STORE, sql_connection, sql_command); 486 | 487 | retrieved_objects += write_results(result, filtered, fast, condat, 488 | acc_credit->public_objects); 489 | 490 | if( retrieved_objects < SQ_num_rows(result) ) { 491 | SK_cd_puts(condat, 492 | "% You have reached the limit of returned contact information objects.\n" 493 | "% This connection will be terminated now.\n" 494 | "% This is a mechanism to prevent abusive use of contact data in the RIPE Database.\n" 495 | "% You will not be allowed to query for more CONTACT information for a while.\n" 496 | "% Continued attempts to return excessive amounts of contact\n" 497 | "% information will result in permanent denial of service.\n" 498 | "% Please do not try to use CONTACT information information in the\n" 499 | "% RIPE Database for non-operational purposes.\n" 500 | "% Refer to http://www.ripe.net/db/dbcopyright.html for more information.\n" 501 | ); 502 | } 503 | SQ_free_result(result); 504 | 505 | /* Now for recursive queries */ 506 | if (recursive == 1) { 507 | 508 | /* create a table for recursive data */ 509 | sprintf(sql_command, "CREATE TABLE %s_R ( id int ) TYPE=HEAP", id_table); 510 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 511 | 512 | /* find the contacts */ 513 | sprintf(sql_command, Q_REC, id_table, "admin_c", id_table, id_table); 514 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 515 | 516 | sprintf(sql_command, Q_REC, id_table, "tech_c", id_table, id_table); 517 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 518 | 519 | sprintf(sql_command, Q_REC, id_table, "zone_c", id_table, id_table); 520 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 521 | 522 | /* XXX These may and should change a lot. */ 523 | sprintf(sql_command, Q_REC_OBJECTS, id_table, id_table, id_table); 524 | result = SQ_execute_query(SQ_STORE, sql_connection, sql_command); 525 | 526 | retrieved_contacts += write_results(result, filtered, fast, condat, 527 | acc_credit->private_objects); 528 | if( retrieved_contacts < SQ_num_rows(result) ) { 529 | SK_cd_puts(condat, 530 | "% The contact data has been truncated due to inadequate privileges\n" 531 | "% The amount of data you have requested is considered excessive\n" 532 | "% You will be denied to get any more contact objects for some time\n" 533 | "% Please do not check if the access has been restored more often\n" 534 | "% than once every few hours or you will be denied access permanently\n" 535 | ); 536 | } 537 | SQ_free_result(result); 538 | 539 | /* Now drop the IDS recursive table */ 540 | sprintf(sql_command, "DROP TABLE %s_R", id_table); 541 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 542 | 543 | acc_credit->private_objects -= retrieved_contacts; 544 | } 545 | 546 | /* If nothing is retreived default to return the 0th object - I.e "not found" */ 547 | if (retrieved_objects == 0) { 548 | result = SQ_execute_query(SQ_STORE, sql_connection, Q_NO_OBJECTS); 549 | 550 | if (result != NULL) { 551 | write_results(result, filtered, fast, condat, 1); 552 | SQ_free_result(result); 553 | } 554 | else { 555 | fprintf(stderr, "QI - ERROR: Couldn't perform no objects query.\n"); 556 | } 557 | } 558 | else { 559 | acc_credit->public_objects -= retrieved_objects; 560 | } 561 | 562 | sprintf(log_str, "%d public / %d contact object(s) retrieved\n\n", 563 | retrieved_objects, retrieved_contacts ); 564 | log_inst_print(log_str); 565 | 566 | } /* write_objects() */ 567 | 568 | /* insert_radix_serials() */ 569 | /*++++++++++++++++++++++++++++++++++++++ 570 | Insert the radix serial numbers into a temporary table in the database. 571 | 572 | mask_t bitmap The bitmap of attribute to be converted. 573 | 574 | SQ_connection_t *sql_connection The connection to the database. 575 | 576 | char *id_table The id of the temporary table (This is a result of the hacky 577 | way we've tried to get MySQL to do sub-selects.) 578 | 579 | GList *datlist The list of data from the radix tree. 580 | 581 | XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty. :-( 582 | 583 | More: 584 | +html+ <PRE> 585 | Authors: 586 | ottrey 587 | +html+ </PRE><DL COMPACT> 588 | +html+ <DT>Online References: 589 | +html+ <DD><UL> 590 | <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A> 591 | +html+ </UL></DL> 592 | 593 | ++++++++++++++++++++++++++++++++++++++*/ 594 | static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) { 595 | GList *qitem; 596 | char sql_command[STR_XL]; 597 | int serial; 598 | int i; 599 | 600 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 601 | rx_datcpy_t *datcpy = qitem->data; 602 | 603 | serial = datcpy->leafcpy.data_key; 604 | 605 | sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial); 606 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 607 | 608 | wr_free(datcpy->leafcpy.data_ptr); 609 | } 610 | 611 | g_list_foreach(datlist, rx_free_list_element, NULL); 612 | g_list_free(datlist); 613 | 614 | } /* insert_radix_serials() */ 615 | 616 | 617 | /* write_radix_immediate() */ 618 | /*++++++++++++++++++++++++++++++++++++++ 619 | Display the immediate data carried with the objects returned by the 620 | radix tree. 621 | 622 | GList *datlist 623 | sk_conn_st *condat Connection data for the client 624 | More: 625 | +html+ <PRE> 626 | Authors: 627 | marek 628 | +html+ </PRE><DL COMPACT> 629 | +html+ <DT>Online References: 630 | +html+ <DD><UL> 631 | +html+ </UL></DL> 632 | 633 | 634 | Also free the list of answers. 635 | */ 636 | static void write_radix_immediate(GList *datlist, sk_conn_st *condat) 637 | { 638 | GList *qitem; 639 | 640 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 641 | rx_datcpy_t *datcpy = qitem->data; 642 | 643 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr ); 644 | SK_cd_puts(condat, "\n"); 645 | 646 | wr_free(datcpy->leafcpy.data_ptr); 647 | } 648 | 649 | g_list_foreach(datlist, rx_free_list_element, NULL); 650 | g_list_free(datlist); 651 | } /* write_radix_immediate() */ 652 | 653 | 654 | /* map_qc2rx() */ 655 | /*++++++++++++++++++++++++++++++++++++++ 656 | The mapping between a query_command and a radix query. 657 | 658 | Query_instruction *qi The Query Instruction to be created from the mapping 659 | of the query command. 660 | 661 | const Query_command *qc The query command to be mapped. 662 | 663 | More: 664 | +html+ <PRE> 665 | Authors: 666 | ottrey 667 | +html+ </PRE><DL COMPACT> 668 | +html+ <DT>Online References: 669 | +html+ <DD><UL> 670 | +html+ </UL></DL> 671 | 672 | ++++++++++++++++++++++++++++++++++++++*/ 673 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) { 674 | int result=0; 675 | char log_str[STR_XL]; 676 | 677 | qi->rx_keys = qc->keys; 678 | 679 | if (MA_bitcount(qc->object_type_bitmap) == 0) { 680 | /* Ie. there was no object typed specified with the -T flag. */ 681 | result=1; 682 | } 683 | else { 684 | switch(qi->family) { 685 | case RX_FAM_IN: 686 | if (MA_isset(qc->object_type_bitmap, C_IN)) { 687 | result=1; 688 | } 689 | break; 690 | 691 | case RX_FAM_RT: 692 | if (MA_isset(qc->object_type_bitmap, C_RT)) { 693 | result=1; 694 | } 695 | break; 696 | 697 | default: 698 | fprintf(stderr, "ERROR: Bad family type in radix query\n"); 699 | } 700 | } 701 | 702 | if (result == 1) { 703 | if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 704 | qi->rx_srch_mode = RX_SRCH_EXLESS; 705 | qi->rx_par_a = 0; 706 | } 707 | else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 708 | qi->rx_srch_mode = RX_SRCH_LESS; 709 | qi->rx_par_a = RX_ALL_DEPTHS; 710 | } 711 | else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 712 | qi->rx_srch_mode = RX_SRCH_MORE; 713 | qi->rx_par_a = RX_ALL_DEPTHS; 714 | } 715 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) { 716 | qi->rx_srch_mode = RX_SRCH_LESS; 717 | qi->rx_par_a = 1; 718 | } 719 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) { 720 | qi->rx_srch_mode = RX_SRCH_MORE; 721 | qi->rx_par_a = 1; 722 | } 723 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) { 724 | qi->rx_srch_mode = RX_SRCH_EXACT; 725 | qi->rx_par_a = 0; 726 | } 727 | else { 728 | sprintf(log_str, "ERROR in qc2rx mapping: bad combination of flags\n"); 729 | log_inst_print(log_str); 730 | result = 0; 731 | } 732 | } 733 | 734 | return result; 735 | 736 | } /* map_qc2rx() */ 737 | 738 | /* run_referral() */ 739 | /* 740 | invoked when no such domain found. Goes through the domain table 741 | and searches for shorter domains, then if it finds one with referral 742 | it performs it, otherwise it just returns nothing. 743 | 744 | to perform referral, it actually composes the referral query 745 | for a given host/port/type and calls the whois query function. 746 | 747 | Well, it returns nothing anyway (void). It just prints to the socket. 748 | 749 | */ 750 | void run_referral(SQ_connection_t *sql_connection, Query_instructions *qis, Query_environ *qe, int qi_index) { 751 | char *dot = qis->qc->keys; 752 | char querystr[STR_L]; 753 | char *query; 754 | char log_str[STR_L]; 755 | SQ_row_t *row; 756 | SQ_result_set_t *result; 757 | char sql_command[STR_XL]; 758 | int i; 759 | int stop_loop=0; 760 | char *ref_host; 761 | char *ref_type; 762 | char *ref_port; 763 | int ref_port_int; 764 | 765 | strcpy(querystr,""); 766 | 767 | while( !stop_loop && (dot=index(dot,'.')) != NULL ) { 768 | dot++; 769 | 770 | sprintf(log_str, "run_referral: checking %s\n", dot); 771 | log_inst_print(log_str); 772 | 773 | /* Changed for RIPE4 - ottrey 27/12/1999 774 | sprintf(sql_command, "SELECT * FROM domain WHERE domain = '%s'", dot); 775 | */ 776 | sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot); 777 | result = SQ_execute_query(SQ_STORE, sql_connection, sql_command); 778 | 779 | switch( SQ_num_rows(result) ) { 780 | case 0: /* no such domain -> no action, will try next chunk */ 781 | break; 782 | 783 | case 1: /* check for referral host and perform query if present 784 | in any case end the loop */ 785 | stop_loop=1; 786 | assert( (row = SQ_row_next(result)) != NULL); 787 | 788 | ref_host = SQ_get_column_string(result, row, 4); 789 | sprintf(log_str, "referral host is >%s<\n",ref_host); 790 | log_inst_print(log_str); 791 | if( ref_host != NULL && strlen(ref_host) > 0 ) { 792 | ref_type = SQ_get_column_string(result, row, 2); 793 | ref_port = SQ_get_column_string(result, row, 3); 794 | 795 | /* get the integer value, it should be correct */ 796 | if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) { 797 | die; 798 | } 799 | 800 | /* compose the query: */ 801 | 802 | /* put -r if the reftype is RIPE and -r or -i were used */ 803 | if( strcmp(ref_type,"RIPE") == 0 804 | && ( Query[qis->instruction[qi_index]->queryindex] 805 | .querytype == Q_INVERSE 806 | || qis->recursive > 0 ) ) { 807 | strcat(querystr," -r "); 808 | } 809 | 810 | /* prepend with -Vversion,IP for type CLIENTADDRESS */ 811 | if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) { 812 | char optv[STR_M]; 813 | 814 | snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip); 815 | strcat(querystr,optv); 816 | } 817 | 818 | /* now set the search term - set to the stripped down version 819 | for inverse query, full-length otherwise */ 820 | if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) { 821 | strcat(querystr,dot); 822 | } 823 | else { 824 | strcat(querystr,qis->qc->keys); 825 | } 826 | 827 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */ 828 | switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr, 25, 5) ) { 829 | case WH_TIMEOUT: 830 | SK_cd_puts(&(qe->condat),"referral timeout\n"); 831 | break; 832 | 833 | case WH_MAXLINES: 834 | SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n"); 835 | break; 836 | 837 | default: 838 | ; 839 | } /*switch WH_sock */ 840 | } 841 | break; 842 | 843 | default: /* more than one domain in this file: something broken */ 844 | die; 845 | } 846 | SQ_free_result(result); 847 | } 848 | } /*run_referral*/ 849 | 850 | /* QI_execute() */ 851 | /*++++++++++++++++++++++++++++++++++++++ 852 | Execute the query instructions. This is called by a g_list_foreach 853 | function, so each of the sources in the "database source" list can be passed 854 | into this function. 855 | 856 | This function has bloated itself. Can we split it up Marek? (ottrey 13/12/99) 857 | 858 | void *database_voidptr Pointer to the database. 859 | 860 | void *qis_voidptr Pointer to the query_instructions. 861 | 862 | More: 863 | +html+ <PRE> 864 | Authors: 865 | ottrey 866 | +html+ </PRE><DL COMPACT> 867 | +html+ <DT>Online References: 868 | +html+ <DD><UL> 869 | <LI><A 870 | HREF="http://www.gtk.org/rdp/glib/glib-singly-linked-lists.html#G-SLIST-FOREACH">g_list_foreach</A> 871 | +html+ </UL></DL> 872 | 873 | ++++++++++++++++++++++++++++++++++++++*/ 874 | void QI_execute(void *database_voidptr, 875 | Query_instructions *qis, 876 | Query_environ *qe, 877 | acc_st *acc_credit 878 | ) { 879 | char *database = (char *)database_voidptr; 880 | Query_instruction **ins=NULL; 881 | char id_table[STR_S]; 882 | char sql_command[STR_XL]; 883 | GList *datlist=NULL; 884 | int i; 885 | SQ_row_t *row; 886 | char log_str[STR_L]; 887 | SQ_result_set_t *result; 888 | SQ_connection_t *sql_connection=NULL; 889 | 890 | sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), database, CO_get_user(), CO_get_password() ); 891 | 892 | if (sql_connection == NULL) { 893 | SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to "); 894 | SK_cd_puts(&(qe->condat), database); 895 | SK_cd_puts(&(qe->condat), " database mirror.\n\n"); 896 | 897 | /* XXX void prevents us from sending any error code back. It is OK ? */ 898 | return; 899 | } 900 | 901 | /* XXX This is a really bad thing to do. 902 | It should'nt _have_ to be called here. 903 | But unfortunately it does. -- Sigh. */ 904 | sprintf(id_table, "ID_%d", mysql_thread_id(sql_connection) ); 905 | 906 | /* create a table for id's of all objects found */ 907 | sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", id_table); 908 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 909 | 910 | /* create a table for individual subqueries (one keytype) */ 911 | sprintf(sql_command, "CREATE TABLE %s_S ( id int ) TYPE=HEAP", id_table); 912 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 913 | 914 | /* Iterate through query instructions */ 915 | ins = qis->instruction; 916 | for (i=0; ins[i] != NULL; i++) { 917 | Query_instruction *qi = ins[i]; 918 | 919 | switch ( qi->search_type ) { 920 | case R_SQL: 921 | if ( qi->query_str != NULL ) { 922 | 923 | /* handle special cases first */ 924 | if( Query[qi->queryindex].class == C_DN ) { 925 | 926 | char *countstr; 927 | int count; 928 | 929 | /* XXX if any more cases than just domain appear, we will be 930 | cleaning the _S table from the previous query here */ 931 | 932 | /* now query into the _S table */ 933 | sprintf(sql_command, "INSERT INTO %s_S %s", id_table, qi->query_str); 934 | SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command); 935 | 936 | /* if any results - copy to the id's table. 937 | Otherwise, run referral */ 938 | 939 | sprintf(sql_command, "SELECT COUNT(*) FROM %s_S", id_table); 940 | result = SQ_execute_query(SQ_STORE,sql_connection, sql_command); 941 | row = SQ_row_next(result); 942 | countstr = SQ_get_column_string(result, row, 0); 943 | sscanf(countstr, "%d", &count); 944 | SQ_free_result(result); 945 | 946 | sprintf(log_str, "DN lookup for %s found %d entries\n", 947 | qis->qc->keys, count); 948 | log_inst_print(log_str); 949 | 950 | 951 | if( count ) { 952 | sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s_S", 953 | id_table, id_table); 954 | SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command); 955 | } 956 | 957 | if( count == 0 958 | || Query[qi->queryindex].querytype == Q_INVERSE ) { 959 | /* now: if the domain was not found, we run referral. 960 | unless prohibited by a flag 961 | 962 | But for inverse queries we return the things that were 963 | or were not found AND also do the referral unless prohibited. 964 | */ 965 | if (qis->qc->R == 0) { 966 | run_referral(sql_connection, qis, qe, i); 967 | } 968 | } 969 | 970 | } 971 | else { 972 | sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str); 973 | SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command); 974 | } 975 | } 976 | break; 977 | 978 | #define RIPE_REG 17 979 | case R_RADIX: 980 | if ( RX_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, qi->rx_keys, RIPE_REG, qi->space, qi->family, &datlist, RX_ANS_ALL) == RX_OK ) { 981 | sprintf(log_str, "After RX query (mode %d par %d spc %d fam %d reg %d key %s) datlist has %d objects\n", 982 | qi->rx_srch_mode, qi->rx_par_a, qi->space, qi->family, 983 | RIPE_REG, qi->rx_keys, 984 | g_list_length(datlist) ); 985 | log_inst_print(log_str); 986 | 987 | } 988 | else { 989 | /* Skip query */ 990 | sprintf(log_str, " /* Skip in query */\n"); 991 | log_inst_print(log_str); 992 | } 993 | break; 994 | 995 | default: die; 996 | } /* switch */ 997 | } 998 | 999 | /* post-processing */ 1000 | 1001 | if( qis->filtered == 0 ) { 1002 | /* add radix results to the end */ 1003 | insert_radix_serials(sql_connection, id_table, datlist); 1004 | 1005 | /* display objects */ 1006 | write_objects(sql_connection, id_table, qis->recursive, 0 /*nofilter*/, 1007 | qis->fast, &(qe->condat), acc_credit); 1008 | } 1009 | else { 1010 | /* XXX TODO display filtered objects, expanding sets */ 1011 | /* right now only the ugly filter thing instead */ 1012 | 1013 | /* write_set_objects */ 1014 | 1015 | /* write the remaining (non-set,non-radix) objects through the filter. 1016 | imply no recursion */ 1017 | write_objects(sql_connection, id_table, 0, qis->filtered, qis->fast, &(qe->condat), acc_credit); 1018 | 1019 | /* display the immediate data from the radix tree */ 1020 | /* XXX pass+decrease credit here */ 1021 | write_radix_immediate(datlist, &(qe->condat)); 1022 | } 1023 | /* Now drop the _S table */ 1024 | sprintf(sql_command, "DROP TABLE %s_S", id_table); 1025 | SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command); 1026 | 1027 | /* Now drop the IDS table */ 1028 | sprintf(sql_command, "DROP TABLE %s", id_table); 1029 | SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command); 1030 | SQ_close_connection(sql_connection); 1031 | 1032 | 1033 | } /* QI_execute() */ 1034 | /* instruction_free() */ 1035 | /*++++++++++++++++++++++++++++++++++++++ 1036 | Free the instruction. 1037 | 1038 | Query_instruction *qi query_instruction to be freed. 1039 | 1040 | More: 1041 | +html+ <PRE> 1042 | Authors: 1043 | ottrey 1044 | +html+ </PRE><DL COMPACT> 1045 | +html+ <DT>Online References: 1046 | +html+ <DD><UL> 1047 | +html+ </UL></DL> 1048 | 1049 | ++++++++++++++++++++++++++++++++++++++*/ 1050 | static void instruction_free(Query_instruction *qi) { 1051 | if (qi != NULL) { 1052 | if (qi->query_str != NULL) { 1053 | free(qi->query_str); 1054 | } 1055 | free(qi); 1056 | } 1057 | } /* instruction_free() */ 1058 | 1059 | /* QI_free() */ 1060 | /*++++++++++++++++++++++++++++++++++++++ 1061 | Free the query_instructions. 1062 | 1063 | Query_instructions *qis Query_instructions to be freed. 1064 | 1065 | XXX This isn't working too well at the moment. 1066 | 1067 | More: 1068 | +html+ <PRE> 1069 | Authors: 1070 | ottrey 1071 | +html+ </PRE><DL COMPACT> 1072 | +html+ <DT>Online References: 1073 | +html+ <DD><UL> 1074 | +html+ </UL></DL> 1075 | 1076 | ++++++++++++++++++++++++++++++++++++++*/ 1077 | void QI_free(Query_instructions *qis) { 1078 | /* XXX huh!?H? 1079 | int i; 1080 | 1081 | for (i=0; qis[i] != NULL; i++) { 1082 | instruction_free(*qis[i]); 1083 | } 1084 | */ 1085 | if (qis != NULL) { 1086 | free(qis); 1087 | } 1088 | 1089 | } /* QI_free() */ 1090 | 1091 | /*++++++++++++++++++++++++++++++++++++++ 1092 | Determine if this query should be conducted or not. 1093 | 1094 | If it was an inverse query - it the attribute appears in the query command's bitmap. 1095 | If it was a lookup query - if the attribute appears in the object type bitmap or 1096 | disregard if there is no object_type bitmap (Ie object filter). 1097 | 1098 | mask_t bitmap The bitmap of attribute to be converted. 1099 | 1100 | const Query_command *qc The query_command that the instructions are created 1101 | from. 1102 | 1103 | const Query_t q The query being investigated. 1104 | 1105 | ++++++++++++++++++++++++++++++++++++++*/ 1106 | static int valid_query(const Query_command *qc, const Query_t q) { 1107 | int result=0; 1108 | 1109 | if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) { 1110 | if (q.query != NULL) { 1111 | switch (q.querytype) { 1112 | case Q_INVERSE: 1113 | if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) { 1114 | result = 1; 1115 | } 1116 | break; 1117 | 1118 | case Q_LOOKUP: 1119 | if (MA_bitcount(qc->object_type_bitmap) == 0) { 1120 | result=1; 1121 | } 1122 | else if (MA_isset(qc->object_type_bitmap, q.class)) { 1123 | result=1; 1124 | } 1125 | break; 1126 | 1127 | default: 1128 | fprintf(stderr, "qi:valid_query() -> Bad querytype\n"); 1129 | } 1130 | } 1131 | } 1132 | 1133 | return result; 1134 | } /* valid_query() */ 1135 | 1136 | /* QI_new() */ 1137 | /*++++++++++++++++++++++++++++++++++++++ 1138 | Create a new set of query_instructions. 1139 | 1140 | const Query_command *qc The query_command that the instructions are created 1141 | from. 1142 | 1143 | const Query_environ *qe The environmental variables that they query is being 1144 | performed under. 1145 | More: 1146 | +html+ <PRE> 1147 | Authors: 1148 | ottrey 1149 | +html+ </PRE><DL COMPACT> 1150 | +html+ <DT>Online References: 1151 | +html+ <DD><UL> 1152 | +html+ </UL></DL> 1153 | 1154 | ++++++++++++++++++++++++++++++++++++++*/ 1155 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) { 1156 | Query_instructions *qis=NULL; 1157 | Query_instruction *qi=NULL; 1158 | int i_no=0,j; 1159 | int i; 1160 | char *query_str; 1161 | 1162 | char log_str[STR_L]; 1163 | 1164 | qis = (Query_instructions *)calloc(1, sizeof(Query_instructions)); 1165 | 1166 | qis->filtered = qc->filtered; 1167 | qis->fast = qc->fast; 1168 | qis->recursive = qc->recursive; 1169 | qis->qc = (qc); 1170 | 1171 | 1172 | for (i=0; Query[i].query != NULL; i++) { 1173 | 1174 | /* If a valid query. */ 1175 | if ( valid_query(qc, Query[i]) == 1) { 1176 | qi = (Query_instruction *)calloc(1, sizeof(Query_instruction)); 1177 | 1178 | qi->queryindex = i; 1179 | 1180 | /* SQL Query */ 1181 | if ( Query[i].refer == R_SQL) { 1182 | qi->search_type = R_SQL; 1183 | query_str = create_query(Query[i], qc); 1184 | 1185 | if (query_str!= NULL) { 1186 | qi->query_str = query_str; 1187 | qis->instruction[i_no++] = qi; 1188 | } 1189 | } 1190 | /* Radix Query */ 1191 | else if (Query[i].refer == R_RADIX) { 1192 | qi->search_type = R_RADIX; 1193 | qi->space = Query[i].space; 1194 | qi->family = Query[i].family; 1195 | 1196 | if (map_qc2rx(qi, qc) == 1) { 1197 | int j; 1198 | int found=0; 1199 | 1200 | /* check that there is no such query yet */ 1201 | for (j=0; j<i_no; j++) { 1202 | Query_instruction *qij = qis->instruction[j]; 1203 | 1204 | if( qij->search_type == R_RADIX 1205 | && qij->space == qi->space 1206 | && qij->family == qi->family) { 1207 | found=1; 1208 | break; 1209 | } 1210 | } 1211 | 1212 | if ( found ) { 1213 | /* Discard the Query Instruction */ 1214 | free(qi); 1215 | } 1216 | else { 1217 | /* Add the query_instruction to the array */ 1218 | qis->instruction[i_no++] = qi; 1219 | } 1220 | } 1221 | } 1222 | else { 1223 | sprintf(log_str, "ERROR: bad search_type\n"); 1224 | log_inst_print(log_str); 1225 | } 1226 | } 1227 | } 1228 | qis->instruction[i_no++] = NULL; 1229 | 1230 | return qis; 1231 | 1232 | } /* QI_new() */