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