1 | /***************************************
2 | $Revision: 1.50 $
3 |
4 | Protocol whois module (pw). Whois protocol.
5 |
6 | Status: NOT REVUED, TESTED
7 |
8 | ******************/ /******************
9 | Filename : protocol_whois.c
10 | Authors : ottrey@ripe.net - framework and draft implementation
11 | marek@ripe.net - rewritten and extended.
12 | OSs Tested : Solaris 2.6
13 | ******************/ /******************
14 | Copyright (c) 1999 RIPE NCC
15 |
16 | All Rights Reserved
17 |
18 | Permission to use, copy, modify, and distribute this software and its
19 | documentation for any purpose and without fee is hereby granted,
20 | provided that the above copyright notice appear in all copies and that
21 | both that copyright notice and this permission notice appear in
22 | supporting documentation, and that the name of the author not be
23 | used in advertising or publicity pertaining to distribution of the
24 | software without specific, written prior permission.
25 |
26 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 | ***************************************/
33 | #include <stdio.h>
34 | #include <glib.h>
35 |
36 | #include "NAME"
37 |
38 | #include "defs.h"
39 | #include "protocol_whois.h"
40 | #include "mysql_driver.h"
41 | #include "query_command.h"
42 | #include "query_instructions.h"
43 | #include "constants.h"
44 |
45 | #include "access_control.h"
46 | #include "sk.h"
47 | #include "stubs.h"
48 |
49 | #include "ca_configFns.h"
50 | #include "ca_macros.h"
51 | #include "ca_srcAttribs.h"
52 |
53 | #include "protocol_mirror.h"
54 |
55 | #include "ta.h"
56 | #include "timediff.h"
57 |
58 | #ifndef VERSION
59 | #define VERSION "3"
60 | #endif
61 |
62 | /*++++++++++++++++++++++++++++++++++++++
63 |
64 | void
65 | display_file opens a file and displays its contents to the
66 | connection described in conn. structure.
67 |
68 |
69 | sk_conn_st *condat pointer to connection structure
70 |
71 | char *filename file name
72 |
73 | ++++++++++++++++++++++++++++++++++++++*/
74 | static void
75 | display_file(sk_conn_st *condat, char *filename)
76 | {
77 | FILE *fp;
78 | #define READBUFSIZE 148
79 | char buffer[READBUFSIZE+1];
80 | int bytes;
81 |
82 | if( (fp=fopen( filename, "r" )) == NULL ) {
83 | ER_perror( FAC_PW, PW_CNTOPN, "%s : %s (%d)",
84 | filename, strerror(errno), errno);
85 | }
86 | else {
87 | while( (bytes=fread(buffer, 1, READBUFSIZE, fp)) > 0 ) {
88 | buffer[bytes] = 0;
89 | SK_cd_puts(condat, buffer);
90 | }
91 | fclose(fp);
92 | }
93 | }/* display_file */
94 |
95 |
96 | /*++++++++++++++++++++++++++++++++++++++
97 |
98 | static void
99 | pw_log_query logs the query to a file after it has finished.
100 | Takes many parameters to have access to as much
101 | information as possible, including the original
102 | query, accounting, response time, status of the
103 | client connection, etc.
104 |
105 |
106 | Query_environ *qe query environment
107 |
108 | Query_command *qc query command structure
109 |
110 | acc_st *copy_credit numbers of objects returned / referrals made
111 | during this query
112 | (calculated as original credit assigned before
113 | the query minus what's left after the query).
114 |
115 | ut_timer_t begintime time the processing began
116 |
117 | ut_timer_t endtime time the processing finished
118 |
119 | char *hostaddress text address of the real IP
120 |
121 | char *input original query (trailing whitespaces chopped off)
122 |
123 | ++++++++++++++++++++++++++++++++++++++*/
124 | static
125 | void pw_log_query( Query_environ *qe,
126 | Query_command *qc,
127 | acc_st *copy_credit,
128 | ut_timer_t begintime,
129 | ut_timer_t endtime,
130 | char *hostaddress,
131 | char *input)
132 | {
133 | char *qrystat = AC_credit_to_string(copy_credit);
134 | float elapsed;
135 | char *qrytypestr =
136 | qc->query_type == QC_REAL ? "" : QC_get_qrytype(qc->query_type);
137 |
138 |
139 | elapsed = UT_timediff( &begintime, &endtime);
140 |
141 | /* log the connection/query/#results/time/denial to file */
142 | ER_inf_va(FAC_PW, ASP_PW_I_QRYLOG,
143 | "<%s> %s%s %.2fs [%s] -- %s",
144 | qrystat,
145 | qe->condat.rtc ? "INT " : "",
146 | qrytypestr,
147 | elapsed, hostaddress, input
148 | );
149 | wr_free(qrystat);
150 | } /* pw_log_query */
151 |
152 |
153 |
154 |
155 | /*++++++++++++++++++++++++++++++++++++++
156 |
157 | void
158 | PW_process_qc processes the query commands determined in QC,
159 | This is where all the real action of the query
160 | part is invoked.
161 |
162 | Query_environ *qe query environment
163 |
164 | Query_command *qc query command structure
165 |
166 | acc_st *acc_credit credit assigned to this IP
167 |
168 | acl_st *acl_eip current acl record applicable to this IP
169 |
170 | ++++++++++++++++++++++++++++++++++++++*/
171 | void PW_process_qc(Query_environ *qe,
172 | Query_command *qc,
173 | acc_st *acc_credit,
174 | acl_st *acl_eip )
175 | {
176 | GList *qitem;
177 | Query_instructions *qis=NULL;
178 | er_ret_t err;
179 |
180 | switch( qc->query_type ) {
181 | case QC_SYNERR:
182 | SK_cd_puts(&(qe->condat), USAGE);
183 | /* FALLTHROUGH */
184 | case QC_PARERR:
185 | /* parameter error. relevant error message is already printed */
186 |
187 | /* force disconnection on error */
188 | qe->k = 0;
189 | break;
190 | case QC_NOKEY:
191 | /* no key (this is OK for some operational stuff, like -k) */
192 | break;
193 | case QC_EMPTY:
194 | /* The user didn't specify a key, so
195 | - print moron banner
196 | - force disconnection of the user. */
197 | {
198 | char *rep = ca_get_pw_err_nokey ;
199 | SK_cd_puts(&(qe->condat), rep);
200 | wr_free(rep);
201 | }
202 | qe->condat.rtc = SK_NOTEXT;
203 | break;
204 | case QC_HELP:
205 | {
206 | char *rep = ca_get_pw_help_file ;
207 | display_file( &(qe->condat), rep);
208 | wr_free(rep);
209 | }
210 | break;
211 | case QC_TEMPLATE:
212 | switch(qc->q) {
213 | case QC_Q_SOURCES:
214 | /* print source & mirroring info */
215 | {
216 | GString *srcs = PM_get_nrtm_sources( & qe->condat.rIP, NULL);
217 | SK_cd_puts(&(qe->condat), srcs->str);
218 | g_string_free (srcs, TRUE);
219 | }
220 | break;
221 | case QC_Q_VERSION:
222 | SK_cd_puts(&(qe->condat), "% RIP version " VERSION "\n\n");
223 | break;
224 | default:
225 | /* EMPTY */;
226 | } /* -q */
227 |
228 | if (qc->t >= 0) {
229 | SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t));
230 | }
231 | if (qc->v >= 0) {
232 | SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v));
233 | }
234 | break;
235 |
236 | case QC_FILTERED:
237 | {
238 | char *rep = ca_get_pw_k_filter ;
239 | SK_cd_puts(&(qe->condat), rep);
240 | wr_free(rep);
241 | }
242 | /* FALLTROUGH */
243 | case QC_REAL:
244 | {
245 | char *rep = ca_get_pw_resp_header;
246 | SK_cd_puts(&(qe->condat), rep);
247 | wr_free(rep);
248 | SK_cd_puts(&(qe->condat), "\n");
249 | }
250 |
251 | #if 1
252 |
253 | qis = QI_new(qc,qe);
254 |
255 | /* go through all sources,
256 | stop if connection broken - further action is meaningless */
257 | for( qitem = g_list_first(qe->sources_list);
258 | qitem != NULL && qe->condat.rtc == 0;
259 | qitem = g_list_next(qitem)) {
260 |
261 |
262 | /* QI will decrement the credit counters */
263 | err = QI_execute(qitem->data, qis, qe, acc_credit, acl_eip );
264 | if( !NOERR(err) ) {
265 | if( err == QI_CANTDB ) {
266 | SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
267 | SK_cd_puts(&(qe->condat), (char *)qitem->data);
268 | SK_cd_puts(&(qe->condat), " database.\n\n");
269 | }
270 | break; /* quit the loop after any error */
271 | }/* if error*/
272 |
273 | }/* for every source */
274 |
275 | QI_free(qis);
276 |
277 | #else
278 | /* test mode: do not run a query, make up some accounting values */
279 | {
280 | int i, m = random() & 0x0f;
281 | for( i=0 ; i<m ; i++ ) {
282 | AC_count_object( acc_credit, acl_eip, random() & 0x01 );
283 | }
284 | }
285 |
286 | #endif
287 |
288 | if( AC_credit_isdenied(acc_credit) ) {
289 | /* host reached the limit of returned contact information */
290 | char *rep = ca_get_pw_limit_reached ;
291 | SK_cd_puts(&(qe->condat), rep);
292 | wr_free(rep);
293 | }
294 |
295 | break;
296 | default: die;
297 | }
298 | } /* PW_process_qc */
299 |
300 |
301 |
302 |
303 | /*++++++++++++++++++++++++++++++++++++++
304 |
305 | void
306 | PW_interact Main loop for interaction with a single client.
307 | The function sets up the accounting for the client,
308 | invokes parsing, execution, logging and accounting
309 | of the query.
310 |
311 | int sock Socket that client is connected to.
312 |
313 | ++++++++++++++++++++++++++++++++++++++*/
314 | void PW_interact(int sock) {
315 | char input[MAX_INPUT_SIZE];
316 | int read_result;
317 | char *hostaddress=NULL;
318 | acl_st acl_rip, acl_eip;
319 | acc_st acc_credit, copy_credit;
320 | Query_environ *qe=NULL;
321 | Query_command *qc=NULL;
322 | ut_timer_t begintime, endtime;
323 |
324 | /* Get the IP of the client */
325 | hostaddress = SK_getpeername(sock);
326 | ER_dbg_va(FAC_PW, ASP_PW_CONN, "connection from %s", hostaddress);
327 |
328 | /* Initialize the query environment. */
329 | qe = QC_environ_new(hostaddress, sock);
330 |
331 | /* init the connection structure, set timeout for reading the query */
332 | SK_cd_make( &(qe->condat), sock, (unsigned) ca_get_keepopen);
333 |
334 | TA_setcondat(&(qe->condat));
335 |
336 | /* see if we should be talking at all */
337 | /* check the acl using the realIP, get a copy applicable to this IP */
338 | AC_check_acl( &(qe->condat.rIP), NULL, &acl_rip);
339 |
340 | do {
341 | int unauth_pass=0;
342 |
343 | TA_setactivity("waiting for query");
344 | /* Read input */
345 | read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE);
346 | /* trash trailing whitespaces(including \n) */
347 | ut_string_chop(input);
348 |
349 | TA_setactivity(input);
350 | TA_increment();
351 |
352 | UT_timeget( &begintime );
353 |
354 | qc = QC_create(input, qe);
355 |
356 | {
357 | /* print the greeting text before the query */
358 | char *rep = ca_get_pw_banner ;
359 | SK_cd_puts(&(qe->condat), rep);
360 | wr_free(rep);
361 | SK_cd_puts(&(qe->condat), "\n");
362 | }
363 |
364 | /* ADDRESS PASSING: check if -V option has passed IP in it */
365 | if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC)) {
366 | if(acl_rip.trustpass) {
367 | acc_st pass_acc;
368 |
369 | /* accounting */
370 | memset(&pass_acc, 0, sizeof(acc_st));
371 | pass_acc.addrpasses=1;
372 | AC_commit( &qe->condat.rIP, &pass_acc, &acl_rip);
373 |
374 | /* set eIP to this IP */
375 | qe->condat.eIP = qe->pIP;
376 | }
377 | else {
378 | /* XXX shall we deny such user ? Now we can... */
379 | ER_inf_va(FAC_PW, ASP_PW_I_PASSUN,
380 | "unauthorised address passing by %s", hostaddress);
381 | unauth_pass = 1; /* keep in mind ... */
382 | }
383 | } /* if an address was passed */
384 |
385 | /* start setting counters in the connection acc from here on
386 | decrement the credit counter (needed to prevent QI_execute from
387 | returning too many results */
388 |
389 | /* check ACL. Get the proper acl record. Calculate credit */
390 | AC_check_acl( &(qe->condat.eIP), &acc_credit, &acl_eip);
391 | /* save the original credit, later check how much was used */
392 | copy_credit = acc_credit;
393 |
394 | copy_credit.connections ++;
395 |
396 | /* printing notices */
397 | if( unauth_pass && ! acl_rip.deny ) {
398 | /* host not authorised to pass addresses with -V */
399 | char *rep = ca_get_pw_acl_addrpass ;
400 | SK_cd_puts(&(qe->condat), rep);
401 | wr_free(rep);
402 | }
403 | if( acl_eip.deny || acl_rip.deny ) {
404 | /* access from host has been permanently denied */
405 | char *rep = ca_get_pw_acl_permdeny ;
406 | SK_cd_puts(&(qe->condat), rep);
407 | wr_free(rep);
408 | }
409 |
410 | if( acl_eip.deny || acl_rip.deny || unauth_pass ) {
411 | copy_credit.denials ++;
412 | }
413 | else {
414 | /************ ACTUAL PROCESSING IS HERE ***********/
415 | PW_process_qc(qe, qc, &acc_credit, &acl_eip);
416 |
417 | if( qc->query_type == QC_REAL ) {
418 | copy_credit.queries ++;
419 | }
420 | }/* if denied ... else */
421 |
422 | /* calc. the credit used, result into copy_credit
423 | This step MUST NOT be forgotten. It must complement
424 | the initial calculation of a credit, otherwise accounting
425 | will go bgzzzzzt.
426 | */
427 | AC_acc_addup(©_credit, &acc_credit, ACC_MINUS);
428 |
429 | /* now we can check how many results there were, etc. */
430 |
431 | /* can say 'nothing found' only if:
432 | - the query did not just cause denial
433 | - was a 'real' query
434 | - nothing was returned
435 | */
436 |
437 | if( ! AC_credit_isdenied(©_credit)
438 | && (qc->query_type == QC_REAL || qc->query_type == QC_FILTERED)
439 | && copy_credit.private_objects + copy_credit.public_objects
440 | + copy_credit.referrals == 0 ) {
441 |
442 | /* now: if the rtc flag is zero, the query ran to completion */
443 | if( qe->condat.rtc == 0 ) {
444 | char *rep = ca_get_pw_notfound ;
445 | SK_cd_puts(&(qe->condat), rep);
446 | wr_free(rep);
447 | }
448 | else {
449 | /* something happened. Hope for working socket and display message
450 | (won't hurt even if socket not operable)
451 | */
452 | char *rep = ca_get_pw_connclosed ;
453 | SK_cd_puts(&(qe->condat), rep);
454 | wr_free(rep);
455 | }
456 | }
457 |
458 |
459 | UT_timeget(&endtime);
460 | /* query logging */
461 | pw_log_query(qe, qc, ©_credit, begintime, endtime,
462 | hostaddress, input);
463 |
464 | /* Commit the credit. This will deny if bonus limit hit
465 | and clear the copy */
466 | AC_commit(&(qe->condat.eIP), ©_credit, &acl_eip);
467 |
468 | /* end-of-result -> two empty lines */
469 | SK_cd_puts(&(qe->condat), "\n\n");
470 |
471 | QC_free(qc);
472 | } /* do */
473 | while( qe->k && qe->condat.rtc == 0
474 | && AC_credit_isdenied( ©_credit ) == 0
475 | && CO_get_whois_suspended() == 0);
476 |
477 | /* Free the hostaddress */
478 | wr_free(hostaddress);
479 | /* Free the connection struct's dynamic data */
480 | SK_cd_free(&(qe->condat));
481 | /* Free the query_environ */
482 | QC_environ_free(qe);
483 |
484 | } /* PW_interact() */