modules/qi/query_instructions.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- log_inst_print
- create_name_query
- add_filter
- create_query
- fast_output
- filter
- write_results
- write_objects
- insert_radix_serials
- write_radix_immediate
- map_qc2rx
- run_referral
- QI_execute
- instruction_free
- QI_free
- valid_query
- QI_new
/***************************************
$Revision: 1.30 $
Sql module (sq). This is a mysql implementation of an sql module.
Status: NOT REVUED, NOT TESTED
Note: this code has been heavily coupled to MySQL, and may need to be changed
(to improve performance) if a new RDBMS is used.
******************/ /******************
Filename : query_instructions.c
Author : ottrey@ripe.net
OSs Tested : Solaris
Problems : Moderately linked to MySQL. Not sure which inverse
attributes each option has. Would like to modify this
after re-designing the objects module.
Comments : Not sure about the different keytypes.
******************/ /******************
Copyright (c) 1999 RIPE NCC
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the author not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
***************************************/
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include "which_keytypes.h"
#include "query_instructions.h"
#include "mysql_driver.h"
#include "rxroutines.h"
#include "stubs.h"
#include "constants.h"
#include "memwrap.h"
/*+ String sizes +*/
#define STR_S 63
#define STR_M 255
#define STR_L 1023
#define STR_XL 4095
#define STR_XXL 16383
#include "QI_queries.def"
/* log_inst_print() */
/*++++++++++++++++++++++++++++++++++++++
Log the instruction.
char *str instruction to be logged.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
void log_inst_print(char *str) {
/* [<][>][^][v][top][bottom][index][help] */
FILE *logf;
if (CO_get_instr_logging() == 1) {
if (strcmp(CO_get_instr_logfile(), "stdout") == 0) {
printf("%s", str);
}
else {
logf = fopen(CO_get_instr_logfile(), "a");
fprintf(logf, "%s", str);
fclose(logf);
}
}
} /* log_inst_print() */
/* create_name_query() */
/*++++++++++++++++++++++++++++++++++++++
Create an sql query for the names table.
char *query_str
const char *sql_query
const char *keys
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
/* [<][>][^][v][top][bottom][index][help] */
int i;
/* Allocate stuff */
GString *from_clause = g_string_sized_new(STR_XL);
GString *where_clause = g_string_sized_new(STR_XL);
gchar **words = g_strsplit(keys, " ", 0);
g_string_sprintfa(from_clause, "names N%.2d", 0);
g_string_sprintfa(where_clause, "N%.2d.name='%s'", 0, words[0]);
for (i=1; words[i] != NULL; i++) {
g_string_sprintfa(from_clause, ", names N%.2d", i);
g_string_sprintfa(where_clause, " AND N%.2d.name='%s' AND N00.object_id = N%.2d.object_id", i, words[i], i);
}
sprintf(query_str, sql_query, from_clause->str, where_clause->str);
/* Free up stuff */
g_strfreev(words);
g_string_free(where_clause, TRUE);
g_string_free(from_clause, TRUE);
} /* create_name_query() */
static void add_filter(char *query_str, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
int i;
int qlen;
char filter_atom[STR_M];
/*
if (MA_bitcount(qc->object_type_bitmap) > 0) {
g_string_sprintfa(query_str, " AND (");
for (i=0; i < C_END; i++) {
if (MA_isset(qc->object_type_bitmap, i)) {
g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
}
}
g_string_truncate(query_str, query_str->len-3);
g_string_append_c(query_str, ')');
}
*/
if (MA_bitcount(qc->object_type_bitmap) > 0) {
strcat(query_str, " AND (");
for (i=0; i < C_END; i++) {
if (MA_isset(qc->object_type_bitmap, i)) {
strcpy(filter_atom, "");
sprintf(filter_atom, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
strcat(query_str, filter_atom);
}
}
qlen = strlen(query_str);
query_str[qlen-3] = ')';
query_str[qlen-2] = '\0';
query_str[qlen-1] = '\0';
}
} /* add_filter() */
/* create_query() */
/*++++++++++++++++++++++++++++++++++++++
Create an sql query from the query_command and the matching keytype and the
selected inverse attributes.
Note this clears the first inv_attribute it sees, so is called sequentially
until there are no inv_attributes left.
WK_Type keytype The matching keytype.
const Query_command *qc The query command.
mask_t *inv_attrs_bitmap The selected inverse attributes.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static char *create_query(const Query_t q, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
char *result=NULL;
char result_buff[STR_XL];
Q_Type_t querytype;
int conduct_test = 0;
if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
querytype = Q_INVERSE;
}
else {
querytype = Q_LOOKUP;
}
if ( (q.query != NULL)
&& (q.querytype == querytype) ) {
conduct_test=1;
}
if (conduct_test == 1) {
if (q.keytype == WK_NAME) {
/* Name queries require special treatment. */
create_name_query(result_buff, q.query, qc->keys);
}
else {
sprintf(result_buff, q.query, qc->keys);
}
if (q.class == -1) {
/* It is class type ANY so add the object filtering */
add_filter(result_buff, qc);
}
//result = (char *)malloc(strlen(result_buff)+1);
dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
strcpy(result, result_buff);
}
return result;
} /* create_query() */
/* fast_output() */
/*++++++++++++++++++++++++++++++++++++++
This is for the '-F' flag.
It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
Fast isn't fast anymore - it's just there for compatibility reasons.
This could be speed up if there were breaks out of the loops, once it matched something.
(Wanna add a goto Marek? :-) ).
const char *string The string to be "fast outputed".
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
char *fast_output(const char *str)
/* [<][>][^][v][top][bottom][index][help] */
{
int i,j;
char *result;
char result_bit[STR_L];
char result_buff[STR_XL];
gchar **lines = g_strsplit(str, "\n", 0);
char * const *attribute_names;
gboolean filtering_an_attribute = FALSE;
char *value;
attribute_names = DF_get_attribute_names();
strcpy(result_buff, "");
for(i=0; attribute_names[i] != NULL; i++) {
for (j=0; lines[j] != NULL; j++) {
if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
strcpy(result_bit, "");
/* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
value = strchr(lines[j], ':');
value++;
/* Now get rid of whitespace. */
while (*value == ' ' || *value == '\t') {
value++;
}
sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
strcat(result_buff, result_bit);
}
else if (filtering_an_attribute == TRUE) {
switch (lines[j][0]) {
case ' ':
case '\t':
case '+':
strcpy(result_bit, "");
sprintf(result_bit, "%s\n", lines[j]);
strcat(result_buff, result_bit);
break;
default:
filtering_an_attribute = FALSE;
}
}
}
}
// result = (char *)malloc(strlen(result_buff)+1);
dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
strcpy(result, result_buff);
return result;
} /* fast_output() */
/* filter() */
/*++++++++++++++++++++++++++++++++++++++
Basically it's for the '-K' flag for non-set (and non-radix) objects.
It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
This could be speed up if there were breaks out of the loops, once it matched something.
(Wanna add a goto Marek? :-) ).
const char *string The string to be filtered.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
char *filter(const char *str) {
/* [<][>][^][v][top][bottom][index][help] */
int i,j;
char *result;
char result_bit[STR_L];
char result_buff[STR_XL];
gchar **lines = g_strsplit(str, "\n", 0);
char * const *filter_names;
gboolean filtering_an_attribute = FALSE;
filter_names = DF_get_filter_names();
strcpy(result_buff, "");
for (i=0; filter_names[i] != NULL; i++) {
for (j=0; lines[j] != NULL; j++) {
if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
strcpy(result_bit, "");
sprintf(result_bit, "%s\n", lines[j]);
strcat(result_buff, result_bit);
filtering_an_attribute = TRUE;
}
else if (filtering_an_attribute == TRUE) {
switch (lines[j][0]) {
case ' ':
case '\t':
case '+':
strcpy(result_bit, "");
sprintf(result_bit, "%s\n", lines[j]);
strcat(result_buff, result_bit);
break;
default:
filtering_an_attribute = FALSE;
}
}
}
}
//result = (char *)malloc(strlen(result_buff)+1);
dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
strcpy(result, result_buff);
return result;
} /* filter() */
/* write_results() */
/*++++++++++++++++++++++++++++++++++++++
Write the results to the client socket.
SQ_result_set_t *result The result set returned from the sql query.
unsigned filtered if the objects should go through a filter (-K)
sk_conn_st *condat Connection data for the client
int maxobjects max # of objects to write
XXX NB. this is very dependendant on what rows are returned in the result!!!
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static int write_results(SQ_result_set_t *result,
/* [<][>][^][v][top][bottom][index][help] */
unsigned filtered,
unsigned fast,
sk_conn_st *condat,
acc_st *acc_credit,
acl_st *acl
) {
SQ_row_t *row;
char *str;
char *filtrate;
char *fasted;
char log_str[STR_L];
int retrieved_objects=0;
char *objt;
int type;
/* Get all the results - one at a time */
if (result != NULL) {
/* here we are making use of the mysql_store_result capability
of interrupting the cycle of reading rows. mysql_use_result
does not allow that, must be read until end */
while ( (row = SQ_row_next(result)) != NULL && acc_credit->denials == 0 ) {
if ((str = SQ_get_column_string(result, row, 0)) == NULL) { die; }
else
{
sprintf(log_str, "Retrieved serial id = %d , type = ", atoi(str));
wr_free(str);
}
/* get + add object type */
if( (objt = SQ_get_column_string(result, row, 3)) == NULL ) { die; }
else {
type = atoi(objt);
strcat(log_str, objt);
strcat(log_str, "\n");
log_inst_print(log_str);
wr_free(objt);
}
/* decrement credit for accounting purposes */
/* XXX the definition of private/public should go into the defs (xml) */
switch( type ) {
case C_PN:
case C_RO:
if( acc_credit->private_objects <= 0 && acl->maxbonus != -1 ) {
/* must be negative, will be subtracted */
acc_credit->denials = -1;
continue; /* go to the head of the loop */
}
acc_credit->private_objects --;
break;
default:
if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
acc_credit->denials = -1;
continue; /* go to the head of the loop */
}
acc_credit->public_objects --;
}
if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
else {
/* The fast output stage */
if (fast == 1) {
fasted = fast_output(str);
wr_free(str);
str = fasted;
}
/* The filtering stage */
if (filtered == 0) {
SK_cd_puts(condat, str);
}
else {
filtrate = filter(str);
SK_cd_puts(condat, filtrate);
wr_free(filtrate);
}
SK_cd_puts(condat, "\n");
retrieved_objects++;
}
wr_free(str);
}
}
return retrieved_objects;
} /* write_results() */
/* write_objects() */
/*++++++++++++++++++++++++++++++++++++++
This is linked into MySQL by the fact that MySQL doesn't have sub selects
(yet). The queries are done in two stages. Make some temporary tables and
insert into them. Then use them in the next select.
SQ_connection_t *sql_connection The connection to the database.
char *id_table The id of the temporary table (This is a result of the hacky
way we've tried to get MySQL to do sub-selects.)
unsigned int recursive A recursive query.
sk_conn_st *condat Connection data for the client
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
++++++++++++++++++++++++++++++++++++++*/
static void write_objects(SQ_connection_t *sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
char *id_table,
unsigned int recursive,
unsigned int filtered,
unsigned int fast,
sk_conn_st *condat,
acc_st *acc_credit,
acl_st *acl
)
{
/* XXX This should really return a linked list of the objects */
SQ_result_set_t *result;
int retrieved_objects=0;
char sql_command[STR_XL];
char log_str[STR_L];
/* XXX These may and should change a lot. */
sprintf(sql_command, Q_OBJECTS, id_table, id_table);
result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
retrieved_objects = write_results(result, filtered, fast, condat, acc_credit, acl);
SQ_free_result(result);
/* Now for recursive queries (unless limit exceeded already) */
if (recursive == 1 && acc_credit->denials == 0) {
/* create a table for recursive data */
sprintf(sql_command, "CREATE TABLE %s_R ( id int ) TYPE=HEAP", id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
/* find the contacts */
sprintf(sql_command, Q_REC, id_table, "admin_c", id_table, id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
sprintf(sql_command, Q_REC, id_table, "tech_c", id_table, id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
sprintf(sql_command, Q_REC, id_table, "zone_c", id_table, id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
/* XXX These may and should change a lot. */
sprintf(sql_command, Q_REC_OBJECTS, id_table, id_table);
result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
retrieved_objects = write_results(result, filtered, fast, condat, acc_credit, acl);
SQ_free_result(result);
/* Now drop the IDS recursive table */
sprintf(sql_command, "DROP TABLE %s_R", id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
}
if( acc_credit->denials != 0 ) {
SK_cd_puts(condat,
"% You have reached the limit of returned contact information objects.\n"
"% This connection will be terminated now.\n"
"% This is a mechanism to prevent abusive use of contact data in the RIPE Database.\n"
"% You will not be allowed to query for more CONTACT information for a while.\n"
"% Continued attempts to return excessive amounts of contact\n"
"% information will result in permanent denial of service.\n"
"% Please do not try to use CONTACT information information in the\n"
"% RIPE Database for non-operational purposes.\n"
"% Refer to http://www.ripe.net/db/dbcopyright.html for more information.\n"
);
}
} /* write_objects() */
/* insert_radix_serials() */
/*++++++++++++++++++++++++++++++++++++++
Insert the radix serial numbers into a temporary table in the database.
mask_t bitmap The bitmap of attribute to be converted.
SQ_connection_t *sql_connection The connection to the database.
char *id_table The id of the temporary table (This is a result of the hacky
way we've tried to get MySQL to do sub-selects.)
GList *datlist The list of data from the radix tree.
XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty. :-(
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
<LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) {
/* [<][>][^][v][top][bottom][index][help] */
GList *qitem;
char sql_command[STR_XL];
int serial;
for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
rx_datcpy_t *datcpy = qitem->data;
serial = datcpy->leafcpy.data_key;
sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
wr_free(datcpy->leafcpy.data_ptr);
}
g_list_foreach(datlist, rx_free_list_element, NULL);
g_list_free(datlist);
} /* insert_radix_serials() */
/* write_radix_immediate() */
/*++++++++++++++++++++++++++++++++++++++
Display the immediate data carried with the objects returned by the
radix tree.
GList *datlist
sk_conn_st *condat Connection data for the client
More:
+html+ <PRE>
Authors:
marek
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
Also free the list of answers.
*/
static void write_radix_immediate(GList *datlist, sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
{
GList *qitem;
for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
rx_datcpy_t *datcpy = qitem->data;
SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
SK_cd_puts(condat, "\n");
wr_free(datcpy->leafcpy.data_ptr);
}
g_list_foreach(datlist, rx_free_list_element, NULL);
g_list_free(datlist);
} /* write_radix_immediate() */
/* map_qc2rx() */
/*++++++++++++++++++++++++++++++++++++++
The mapping between a query_command and a radix query.
Query_instruction *qi The Query Instruction to be created from the mapping
of the query command.
const Query_command *qc The query command to be mapped.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
int result=0;
char log_str[STR_XL];
qi->rx_keys = qc->keys;
if (MA_bitcount(qc->object_type_bitmap) == 0) {
/* Ie. there was no object typed specified with the -T flag. */
result=1;
}
else {
switch(qi->family) {
case RX_FAM_IN:
if (MA_isset(qc->object_type_bitmap, C_IN)) {
result=1;
}
break;
case RX_FAM_RT:
if (MA_isset(qc->object_type_bitmap, C_RT)) {
result=1;
}
break;
default:
fprintf(stderr, "ERROR: Bad family type in radix query\n");
}
}
if (result == 1) {
if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
qi->rx_srch_mode = RX_SRCH_EXLESS;
qi->rx_par_a = 0;
}
else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
qi->rx_srch_mode = RX_SRCH_LESS;
qi->rx_par_a = RX_ALL_DEPTHS;
}
else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
qi->rx_srch_mode = RX_SRCH_MORE;
qi->rx_par_a = RX_ALL_DEPTHS;
}
else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
qi->rx_srch_mode = RX_SRCH_LESS;
qi->rx_par_a = 1;
}
else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
qi->rx_srch_mode = RX_SRCH_MORE;
qi->rx_par_a = 1;
}
else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
qi->rx_srch_mode = RX_SRCH_EXACT;
qi->rx_par_a = 0;
}
else {
sprintf(log_str, "ERROR in qc2rx mapping: bad combination of flags\n");
log_inst_print(log_str);
result = 0;
}
}
return result;
} /* map_qc2rx() */
/* run_referral() */
/*
invoked when no such domain found. Goes through the domain table
and searches for shorter domains, then if it finds one with referral
it performs it, otherwise it just returns nothing.
to perform referral, it actually composes the referral query
for a given host/port/type and calls the whois query function.
Well, it returns nothing anyway (void). It just prints to the socket.
*/
void run_referral(SQ_connection_t *sql_connection, Query_instructions *qis, Query_environ *qe, int qi_index) {
/* [<][>][^][v][top][bottom][index][help] */
char *dot = qis->qc->keys;
char querystr[STR_L];
char log_str[STR_L];
SQ_row_t *row;
SQ_result_set_t *result;
char sql_command[STR_XL];
int stop_loop=0;
char *ref_host;
char *ref_type;
char *ref_port;
int ref_port_int;
strcpy(querystr,"");
while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
dot++;
sprintf(log_str, "run_referral: checking %s\n", dot);
log_inst_print(log_str);
/* Changed for RIPE4 - ottrey 27/12/1999
sprintf(sql_command, "SELECT * FROM domain WHERE domain = '%s'", dot);
*/
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);
result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
switch( SQ_num_rows(result) ) {
case 0: /* no such domain -> no action, will try next chunk */
break;
case 1: /* check for referral host and perform query if present
in any case end the loop */
stop_loop=1;
assert( (row = SQ_row_next(result)) != NULL);
ref_host = SQ_get_column_string(result, row, 4);
sprintf(log_str, "referral host is >%s<\n",ref_host);
log_inst_print(log_str);
if( ref_host != NULL && strlen(ref_host) > 0 ) {
ref_type = SQ_get_column_string(result, row, 2);
ref_port = SQ_get_column_string(result, row, 3);
/* get the integer value, it should be correct */
if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
die;
}
/* compose the query: */
/* put -r if the reftype is RIPE and -r or -i were used */
if( strcmp(ref_type,"RIPE") == 0
&& ( Query[qis->instruction[qi_index]->queryindex]
.querytype == Q_INVERSE
|| qis->recursive > 0 ) ) {
strcat(querystr," -r ");
}
/* prepend with -Vversion,IP for type CLIENTADDRESS */
if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
char optv[STR_M];
snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
strcat(querystr,optv);
}
/* now set the search term - set to the stripped down version
for inverse query, full-length otherwise */
if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
strcat(querystr,dot);
}
else {
strcat(querystr,qis->qc->keys);
}
/* WH_sock(sock, host, port, query, maxlines, timeout)) */
switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr, 25, 5) ) {
case WH_TIMEOUT:
SK_cd_puts(&(qe->condat),"referral timeout\n");
break;
case WH_MAXLINES:
SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
break;
default:
;
} /*switch WH_sock */
}
break;
default: /* more than one domain in this file: something broken */
die;
}
SQ_free_result(result);
}
} /*run_referral*/
/* QI_execute() */
/*++++++++++++++++++++++++++++++++++++++
Execute the query instructions. This is called by a g_list_foreach
function, so each of the sources in the "database source" list can be passed
into this function.
This function has bloated itself. Can we split it up Marek? (ottrey 13/12/99)
void *database_voidptr Pointer to the database.
void *qis_voidptr Pointer to the query_instructions.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
<LI><A
HREF="http://www.gtk.org/rdp/glib/glib-singly-linked-lists.html#G-SLIST-FOREACH">g_list_foreach</A>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
void QI_execute(void *database_voidptr,
/* [<][>][^][v][top][bottom][index][help] */
Query_instructions *qis,
Query_environ *qe,
acc_st *acc_credit,
acl_st *acl
) {
char *database = (char *)database_voidptr;
Query_instruction **ins=NULL;
char id_table[STR_S];
char sql_command[STR_XL];
GList *datlist=NULL;
int i;
SQ_row_t *row;
char log_str[STR_L];
SQ_result_set_t *result;
SQ_connection_t *sql_connection=NULL;
char *countstr;
int count;
sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), database, CO_get_user(), CO_get_password() );
if (sql_connection == NULL) {
SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
SK_cd_puts(&(qe->condat), database);
SK_cd_puts(&(qe->condat), " database mirror.\n\n");
/* XXX void prevents us from sending any error code back. It is OK ? */
return;
}
/* XXX This is a really bad thing to do.
It should'nt _have_ to be called here.
But unfortunately it does. -- Sigh. */
sprintf(id_table, "ID_%d", mysql_thread_id(sql_connection) );
/* create a table for id's of all objects found */
sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
/* create a table for individual subqueries (one keytype) */
sprintf(sql_command, "CREATE TABLE %s_S ( id int ) TYPE=HEAP", id_table);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
/* Iterate through query instructions */
ins = qis->instruction;
for (i=0; ins[i] != NULL; i++) {
Query_instruction *qi = ins[i];
switch ( qi->search_type ) {
case R_SQL:
if ( qi->query_str != NULL ) {
/* handle special cases first */
if( Query[qi->queryindex].class == C_DN ) {
/* XXX if any more cases than just domain appear, we will be
cleaning the _S table from the previous query here */
/* now query into the _S table */
sprintf(sql_command, "INSERT INTO %s_S %s", id_table, qi->query_str);
SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
/* if any results - copy to the id's table.
Otherwise, run referral */
sprintf(sql_command, "SELECT COUNT(*) FROM %s_S", id_table);
result = SQ_execute_query(SQ_STORE,sql_connection, sql_command);
row = SQ_row_next(result);
countstr = SQ_get_column_string(result, row, 0);
sscanf(countstr, "%d", &count);
SQ_free_result(result);
sprintf(log_str, "DN lookup for %s found %d entries\n",
qis->qc->keys, count);
log_inst_print(log_str);
if( count ) {
sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s_S",
id_table, id_table);
SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
}
if( count == 0
|| Query[qi->queryindex].querytype == Q_INVERSE ) {
/* now: if the domain was not found, we run referral.
unless prohibited by a flag
But for inverse queries we return the things that were
or were not found AND also do the referral unless prohibited.
*/
if (qis->qc->R == 0) {
run_referral(sql_connection, qis, qe, i);
}
}
}
else {
sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str);
SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
sprintf(sql_command, "SELECT COUNT(*) FROM %s", id_table);
result = SQ_execute_query(SQ_STORE,sql_connection, sql_command);
row = SQ_row_next(result);
countstr = SQ_get_column_string(result, row, 0);
sscanf(countstr, "%d", &count);
SQ_free_result(result);
sprintf(log_str, "%d entries after class %s/%s lookup for %s found\n", count,
DF_get_class_code(Query[qi->queryindex].class),
DF_get_attribute_code(Query[qi->queryindex].attribute),
qis->qc->keys);
log_inst_print(log_str);
}
}
break;
#define RIPE_REG 17
case R_RADIX:
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 ) {
sprintf(log_str, "After RX query (mode %d par %d spc %d fam %d reg %d key %s) datlist has %d objects\n",
qi->rx_srch_mode, qi->rx_par_a, qi->space, qi->family,
RIPE_REG, qi->rx_keys,
g_list_length(datlist) );
log_inst_print(log_str);
}
else {
/* Skip query */
sprintf(log_str, " /* Skip in query */\n");
log_inst_print(log_str);
}
break;
default: die;
} /* switch */
}
/* post-processing */
if( qis->filtered == 0 ) {
/* add radix results to the end */
insert_radix_serials(sql_connection, id_table, datlist);
/* display objects */
write_objects(sql_connection, id_table, qis->recursive, 0 /*nofilter*/,
qis->fast, &(qe->condat), acc_credit, acl);
}
else {
/* XXX TODO display filtered objects, expanding sets */
/* right now only the ugly filter thing instead */
/* write_set_objects */
/* write the remaining (non-set,non-radix) objects through the filter.
imply no recursion */
write_objects(sql_connection, id_table, 0, qis->filtered, qis->fast, &(qe->condat), acc_credit, acl);
/* display the immediate data from the radix tree */
/* XXX pass+decrease credit here */
write_radix_immediate(datlist, &(qe->condat));
}
/* Now drop the _S table */
sprintf(sql_command, "DROP TABLE %s_S", id_table);
SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
/* Now drop the IDS table */
sprintf(sql_command, "DROP TABLE %s", id_table);
SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
SQ_close_connection(sql_connection);
} /* QI_execute() */
/* instruction_free() */
/*++++++++++++++++++++++++++++++++++++++
Free the instruction.
Query_instruction *qi query_instruction to be freed.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
static void instruction_free(Query_instruction *qi) {
/* [<][>][^][v][top][bottom][index][help] */
if (qi != NULL) {
if (qi->query_str != NULL) {
wr_free(qi->query_str);
}
wr_free(qi);
}
} /* instruction_free() */
/* QI_free() */
/*++++++++++++++++++++++++++++++++++++++
Free the query_instructions.
Query_instructions *qis Query_instructions to be freed.
XXX This isn't working too well at the moment.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
void QI_free(Query_instructions *qis) {
/* [<][>][^][v][top][bottom][index][help] */
/* XXX huh!?H?
int i;
for (i=0; qis[i] != NULL; i++) {
instruction_free(*qis[i]);
}
*/
if (qis != NULL) {
wr_free(qis);
}
} /* QI_free() */
/*++++++++++++++++++++++++++++++++++++++
Determine if this query should be conducted or not.
If it was an inverse query - it the attribute appears in the query command's bitmap.
If it was a lookup query - if the attribute appears in the object type bitmap or
disregard if there is no object_type bitmap (Ie object filter).
mask_t bitmap The bitmap of attribute to be converted.
const Query_command *qc The query_command that the instructions are created
from.
const Query_t q The query being investigated.
++++++++++++++++++++++++++++++++++++++*/
static int valid_query(const Query_command *qc, const Query_t q) {
/* [<][>][^][v][top][bottom][index][help] */
int result=0;
if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
if (q.query != NULL) {
switch (q.querytype) {
case Q_INVERSE:
if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
result = 1;
}
break;
case Q_LOOKUP:
if (MA_bitcount(qc->object_type_bitmap) == 0) {
result=1;
}
else if (MA_isset(qc->object_type_bitmap, q.class)) {
result=1;
}
break;
default:
fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
}
}
}
return result;
} /* valid_query() */
/* QI_new() */
/*++++++++++++++++++++++++++++++++++++++
Create a new set of query_instructions.
const Query_command *qc The query_command that the instructions are created
from.
const Query_environ *qe The environmental variables that they query is being
performed under.
More:
+html+ <PRE>
Authors:
ottrey
+html+ </PRE><DL COMPACT>
+html+ <DT>Online References:
+html+ <DD><UL>
+html+ </UL></DL>
++++++++++++++++++++++++++++++++++++++*/
Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
/* [<][>][^][v][top][bottom][index][help] */
Query_instructions *qis=NULL;
Query_instruction *qi=NULL;
int i_no=0;
int i;
char *query_str;
char log_str[STR_L];
//qis = (Query_instructions *)calloc(1, sizeof(Query_instructions));
dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
qis->filtered = qc->filtered;
qis->fast = qc->fast;
qis->recursive = qc->recursive;
qis->qc = (qc);
for (i=0; Query[i].query != NULL; i++) {
/* If a valid query. */
if ( valid_query(qc, Query[i]) == 1) {
//qi = (Query_instruction *)calloc(1, sizeof(Query_instruction));
dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
qi->queryindex = i;
/* SQL Query */
if ( Query[i].refer == R_SQL) {
qi->search_type = R_SQL;
query_str = create_query(Query[i], qc);
if (query_str!= NULL) {
qi->query_str = query_str;
qis->instruction[i_no++] = qi;
}
}
/* Radix Query */
else if (Query[i].refer == R_RADIX) {
qi->search_type = R_RADIX;
qi->space = Query[i].space;
qi->family = Query[i].family;
if (map_qc2rx(qi, qc) == 1) {
int j;
int found=0;
/* check that there is no such query yet */
for (j=0; j<i_no; j++) {
Query_instruction *qij = qis->instruction[j];
if( qij->search_type == R_RADIX
&& qij->space == qi->space
&& qij->family == qi->family) {
found=1;
break;
}
}
if ( found ) {
/* Discard the Query Instruction */
wr_free(qi);
}
else {
/* Add the query_instruction to the array */
qis->instruction[i_no++] = qi;
}
}
}
else {
sprintf(log_str, "ERROR: bad search_type\n");
log_inst_print(log_str);
}
}
}
qis->instruction[i_no++] = NULL;
return qis;
} /* QI_new() */