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() */