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