modules/pw/protocol_whois.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- display_file
- pw_log_query
- PW_process_qc
- PW_stopqueries
- PW_startqueries
- PW_record_query_start
- PW_record_query_end
- PW_interact
- PW_init
1 /***************************************
2 $Revision: 1.60 $
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,2000,2001 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 "rip.h"
34
35 #include <stdio.h>
36 #include <glib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <ctype.h>
40
41 /* XXX: what is this? - shane */
42 #include "NAME"
43
44 #ifndef VERSION
45 #define VERSION "3"
46 #endif
47
48 /*+ Maximum size of input that can be recieved from the client. +*/
49 #define MAX_INPUT_SIZE 1024
50
51 /*++++++++++++++++++++++++++++++++++++++
52
53 void
54 display_file opens a file and displays its contents to the
55 connection described in conn. structure.
56
57
58 sk_conn_st *condat pointer to connection structure
59
60 char *filename file name
61
62 ++++++++++++++++++++++++++++++++++++++*/
63 static void
64 display_file(sk_conn_st *condat, char *filename)
/* [<][>][^][v][top][bottom][index][help] */
65 {
66 FILE *fp;
67 char *buffer;
68 struct stat sb;
69 int bytes;
70 int p;
71
72 /* open our file */
73 fp = fopen(filename, "r");
74 if (fp == NULL) {
75 ER_perror( FAC_PW, PW_CNTOPN, "fopen() failure \"%s\" : %s (%d)",
76 filename, strerror(errno), errno);
77 return;
78 }
79
80 /* get the size of the file */
81 if (fstat(fileno(fp), &sb) != 0) {
82 ER_perror( FAC_PW, PW_CNTOPN, "fstat() failure \"%s\" : %s (%d)",
83 filename, strerror(errno), errno);
84 fclose(fp);
85 return;
86 }
87
88 /* allocate a buffer for the file */
89 buffer = UT_malloc(sb.st_size+1);
90
91 /* read the file contents */
92 bytes = fread(buffer, 1, sb.st_size, fp);
93 fclose(fp);
94
95 /* can't read more bytes that we asked for */
96 dieif(bytes > sb.st_size);
97
98
99 /* remove any newlines (actually any whitespace) at the end of the file */
100 /* we do this because we can have ONLY ONE newline at the end of the */
101 /* output - any more violates our OBJECT, "\n", OBJECT, "\n" format */
102 p = bytes-1;
103 while ((p>=0) && isspace((int)buffer[p])) {
104 p--;
105 }
106
107 /* NUL-terminate our string */
108 buffer[p+1] = '\0';
109
110 /* output our resulting buffer */
111 SK_cd_puts(condat, buffer);
112
113 /* and enough blank lines */
114 SK_cd_puts(condat, "\n\n");
115
116 /* free the allocated memory */
117 UT_free(buffer);
118 }/* display_file */
119
120
121 /*++++++++++++++++++++++++++++++++++++++
122
123 static void
124 pw_log_query logs the query to a file after it has finished.
125 Takes many parameters to have access to as much
126 information as possible, including the original
127 query, accounting, response time, status of the
128 client connection, etc.
129
130
131 Query_environ *qe query environment
132
133 Query_command *qc query command structure
134
135 acc_st *copy_credit numbers of objects returned / referrals made
136 during this query
137 (calculated as original credit assigned before
138 the query minus what's left after the query).
139
140 ut_timer_t begintime time the processing began
141
142 ut_timer_t endtime time the processing finished
143
144 char *hostaddress text address of the real IP
145
146 char *input original query (trailing whitespaces chopped off)
147
148 ++++++++++++++++++++++++++++++++++++++*/
149 static
150 void pw_log_query( Query_environ *qe,
/* [<][>][^][v][top][bottom][index][help] */
151 Query_command *qc,
152 acc_st *copy_credit,
153 ut_timer_t begintime,
154 ut_timer_t endtime,
155 char *hostaddress,
156 char *input)
157 {
158 char *qrystat = AC_credit_to_string(copy_credit);
159 float elapsed;
160 char *qrytypestr =
161 qc->query_type == QC_REAL ? "" : QC_get_qrytype(qc->query_type);
162
163
164 elapsed = UT_timediff( &begintime, &endtime);
165
166 /* log the connection/query/#results/time/denial to file */
167 ER_inf_va(FAC_PW, ASP_PW_I_QRYLOG,
168 "<%s> %s%s %.2fs [%s] -- %s",
169 qrystat,
170 qe->condat.rtc ? "INT " : "",
171 qrytypestr,
172 elapsed, hostaddress, input
173 );
174 UT_free(qrystat);
175 } /* pw_log_query */
176
177
178
179
180 /*++++++++++++++++++++++++++++++++++++++
181
182 void
183 PW_process_qc processes the query commands determined in QC,
184 This is where all the real action of the query
185 part is invoked.
186
187 Query_environ *qe query environment
188
189 Query_command *qc query command structure
190
191 acc_st *acc_credit credit assigned to this IP
192
193 acl_st *acl_eip current acl record applicable to this IP
194
195 ++++++++++++++++++++++++++++++++++++++*/
196 void PW_process_qc(Query_environ *qe,
/* [<][>][^][v][top][bottom][index][help] */
197 Query_command *qc,
198 acc_st *acc_credit,
199 acl_st *acl_eip )
200 {
201 GList *qitem;
202 Query_instructions *qis=NULL;
203 er_ret_t err;
204
205 switch( qc->query_type ) {
206 case QC_SYNERR:
207 SK_cd_puts(&(qe->condat), "\n");
208 SK_cd_puts(&(qe->condat), USAGE);
209 break;
210 case QC_PARERR:
211 /* parameter error. relevant error message is already printed */
212 /* we still need an extra newline after this message though */
213 SK_cd_puts(&(qe->condat), "\n");
214
215 /* force disconnection on error */
216 qe->k = 0;
217 break;
218 case QC_NOKEY:
219 /* no key (this is OK for some operational stuff, like -k) */
220 break;
221 case QC_EMPTY:
222 /* The user didn't specify a key, so
223 - print moron banner
224 - force disconnection of the user. */
225 SK_cd_puts(&(qe->condat), "\n");
226 {
227 char *rep = ca_get_pw_err_nokey ;
228 SK_cd_puts(&(qe->condat), rep);
229 UT_free(rep);
230 }
231 /*
232 EXTRA NEWLINE HERE, because we set condat.rtc, which prevents
233 further output to user, and we need to end our output with multiple
234 blank lines.
235 */
236 SK_cd_puts(&(qe->condat), "\n\n");
237 qe->condat.rtc = SK_NOTEXT;
238 break;
239 case QC_HELP:
240 SK_cd_puts(&(qe->condat), "\n");
241 {
242 char *rep = ca_get_pw_help_file ;
243 display_file( &(qe->condat), rep);
244 UT_free(rep);
245 }
246 break;
247 case QC_TEMPLATE:
248 SK_cd_puts(&(qe->condat), "\n");
249 switch(qc->q) {
250 case QC_Q_SOURCES:
251 /* print source & mirroring info */
252 {
253 GString *srcs = PM_get_nrtm_sources( & qe->condat.rIP, NULL);
254 SK_cd_puts(&(qe->condat), srcs->str);
255 g_string_free (srcs, TRUE);
256 }
257 break;
258 case QC_Q_VERSION:
259 SK_cd_puts(&(qe->condat), "% RIP version " VERSION "\n\n");
260 break;
261 default:
262 /* EMPTY */;
263 } /* -q */
264
265 if (qc->t >= 0) {
266 SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t));
267 SK_cd_puts(&(qe->condat), "\n");
268 }
269 if (qc->v >= 0) {
270 SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v));
271 /* no need for newline here, because it's all broken for any */
272 /* automated processor at this point anyway */
273 }
274 break;
275
276 case QC_FILTERED:
277 {
278 char *rep = ca_get_pw_k_filter ;
279 SK_cd_puts(&(qe->condat), rep);
280 UT_free(rep);
281 }
282 /* FALLTROUGH */
283 case QC_REAL:
284 {
285 char *rep = ca_get_pw_resp_header;
286 SK_cd_puts(&(qe->condat), rep);
287 UT_free(rep);
288 SK_cd_puts(&(qe->condat), "\n");
289 }
290
291 #if 1
292
293 qis = QI_new(qc,qe);
294
295 /* go through all sources,
296 stop if connection broken - further action is meaningless */
297 for( qitem = g_list_first(qe->sources_list);
298 qitem != NULL && qe->condat.rtc == 0;
299 qitem = g_list_next(qitem)) {
300
301
302 /* QI will decrement the credit counters */
303 err = QI_execute(qitem->data, qis, qe, acc_credit, acl_eip );
304 if( !NOERR(err) ) {
305 if( err == QI_CANTDB ) {
306 SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
307 SK_cd_puts(&(qe->condat), (char *)qitem->data);
308 SK_cd_puts(&(qe->condat), " database.\n\n");
309 }
310 break; /* quit the loop after any error */
311 }/* if error*/
312
313 }/* for every source */
314
315 QI_free(qis);
316
317 #else
318 /* test mode: do not run a query, make up some accounting values */
319 {
320 int i, m = random() & 0x0f;
321 for( i=0 ; i<m ; i++ ) {
322 AC_count_object( acc_credit, acl_eip, random() & 0x01 );
323 }
324 }
325
326 #endif
327
328 if( AC_credit_isdenied(acc_credit) ) {
329 /* host reached the limit of returned contact information */
330 char *rep = ca_get_pw_limit_reached ;
331 SK_cd_puts(&(qe->condat), rep);
332 UT_free(rep);
333 SK_cd_puts(&(qe->condat), "\n");
334 }
335
336 break;
337 default: die;
338 }
339 } /* PW_process_qc */
340
341 /*
342 Occasionally, we need to pause queries to the Whois database. This
343 occurs, for instance, when the database is reloaded for one of the
344 databases we mirror without using NRTM.
345
346 The way this works is the caller of PW_stopqueries() gets a "write
347 lock" on queries. Each query gets a "read lock". The lock mechanism
348 that favors writers is used.
349
350 This means that no new read locks can start once PW_stopqueries() is
351 called, and that it doesn't return until all read locks have been
352 released. At this point, queries are stopped and the caller can
353 proceed to munge about the database safely.
354
355 XXX: This is not the best possible solution, because on a very slow
356 query (for instance one with a very common person name), the thread
357 calling PW_stopqueries() as well as ALL query threads cannot proceed
358 until that thread completes. An alternative with one lock per
359 database was considered, and may be pursued in the future, but for
360 now it is not a big problem, since operation occurs normally, just
361 possibly with a delay in response for some users.
362
363 PW_startqueries() releases the write lock, and queries proceed
364 normally.
365 */
366
367 /* pause queries using a thread lock that favors writers */
368 static rw_lock_t queries_lock;
369
370 /* PW_stopqueries() */
371 void
372 PW_stopqueries()
/* [<][>][^][v][top][bottom][index][help] */
373 {
374 TH_acquire_write_lockw(&queries_lock);
375 }
376
377 /* PW_startqueries() */
378 void
379 PW_startqueries()
/* [<][>][^][v][top][bottom][index][help] */
380 {
381 TH_release_write_lockw(&queries_lock);
382 }
383
384 /* PW_record_query_start() */
385 void
386 PW_record_query_start()
/* [<][>][^][v][top][bottom][index][help] */
387 {
388 TH_acquire_read_lockw(&queries_lock);
389 }
390
391 /* PW_record_query_end() */
392 void
393 PW_record_query_end()
/* [<][>][^][v][top][bottom][index][help] */
394 {
395 TH_release_read_lockw(&queries_lock);
396 }
397
398
399
400 /*++++++++++++++++++++++++++++++++++++++
401
402 void
403 PW_interact Main loop for interaction with a single client.
404 The function sets up the accounting for the client,
405 invokes parsing, execution, logging and accounting
406 of the query.
407
408 int sock Socket that client is connected to.
409
410 ++++++++++++++++++++++++++++++++++++++*/
411 void PW_interact(int sock) {
/* [<][>][^][v][top][bottom][index][help] */
412 char input[MAX_INPUT_SIZE];
413 int read_result;
414 char *hostaddress=NULL;
415 acl_st acl_rip, acl_eip;
416 acc_st acc_credit, copy_credit;
417 Query_environ *qe=NULL;
418 Query_command *qc=NULL;
419 ut_timer_t begintime, endtime;
420
421 /* Get the IP of the client */
422 hostaddress = SK_getpeername(sock);
423 ER_dbg_va(FAC_PW, ASP_PW_CONN, "connection from %s", hostaddress);
424
425 /* Initialize the query environment. */
426 qe = QC_environ_new(hostaddress, sock);
427
428 /* init the connection structure, set timeout for reading the query */
429 SK_cd_make( &(qe->condat), sock, (unsigned) ca_get_keepopen);
430
431 TA_setcondat(&(qe->condat));
432
433 /* see if we should be talking at all */
434 /* check the acl using the realIP, get a copy applicable to this IP */
435 AC_check_acl( &(qe->condat.rIP), NULL, &acl_rip);
436
437 do {
438 int unauth_pass=0;
439
440 TA_setactivity("waiting for query");
441 /* Read input */
442 read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE);
443 /* trash trailing whitespaces(including \n) */
444 ut_string_chop(input);
445
446 TA_setactivity(input);
447 TA_increment();
448
449 UT_timeget( &begintime );
450
451 qc = QC_create(input, qe);
452
453 {
454 /* print the greeting text before the query */
455 char *rep = ca_get_pw_banner ;
456 SK_cd_puts(&(qe->condat), rep);
457 UT_free(rep);
458 }
459
460 /* ADDRESS PASSING: check if -V option has passed IP in it */
461 if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC)) {
462 if(acl_rip.trustpass) {
463 acc_st pass_acc;
464
465 /* accounting */
466 memset(&pass_acc, 0, sizeof(acc_st));
467 pass_acc.addrpasses=1;
468 AC_commit( &qe->condat.rIP, &pass_acc, &acl_rip);
469
470 /* set eIP to this IP */
471 qe->condat.eIP = qe->pIP;
472 }
473 else {
474 /* XXX shall we deny such user ? Now we can... */
475 ER_inf_va(FAC_PW, ASP_PW_I_PASSUN,
476 "unauthorised address passing by %s", hostaddress);
477 unauth_pass = 1; /* keep in mind ... */
478 }
479 } /* if an address was passed */
480
481 /* start setting counters in the connection acc from here on
482 decrement the credit counter (needed to prevent QI_execute from
483 returning too many results */
484
485 /* check ACL. Get the proper acl record. Calculate credit */
486 AC_check_acl( &(qe->condat.eIP), &acc_credit, &acl_eip);
487 /* save the original credit, later check how much was used */
488 copy_credit = acc_credit;
489
490 copy_credit.connections ++;
491
492 /* printing notices */
493 if( unauth_pass && ! acl_rip.deny ) {
494 /* host not authorised to pass addresses with -V */
495 char *rep = ca_get_pw_acl_addrpass ;
496 SK_cd_puts(&(qe->condat), "\n");
497 SK_cd_puts(&(qe->condat), rep);
498 UT_free(rep);
499 SK_cd_puts(&(qe->condat), "\n");
500 }
501 if( acl_eip.deny || acl_rip.deny ) {
502 /* access from host has been permanently denied */
503 char *rep = ca_get_pw_acl_permdeny ;
504 SK_cd_puts(&(qe->condat), "\n");
505 SK_cd_puts(&(qe->condat), rep);
506 UT_free(rep);
507 SK_cd_puts(&(qe->condat), "\n");
508 }
509
510 if( acl_eip.deny || acl_rip.deny || unauth_pass ) {
511 copy_credit.denials ++;
512 }
513 else {
514 /************ ACTUAL PROCESSING IS HERE ***********/
515 PW_record_query_start();
516 PW_process_qc(qe, qc, &acc_credit, &acl_eip);
517 PW_record_query_end();
518
519 if( qc->query_type == QC_REAL ) {
520 copy_credit.queries ++;
521 }
522 }/* if denied ... else */
523
524 /* calc. the credit used, result into copy_credit
525 This step MUST NOT be forgotten. It must complement
526 the initial calculation of a credit, otherwise accounting
527 will go bgzzzzzt.
528 */
529 AC_acc_addup(©_credit, &acc_credit, ACC_MINUS);
530
531 /* now we can check how many results there were, etc. */
532
533 /* can say 'nothing found' only if:
534 - the query did not just cause denial
535 - was a 'real' query
536 - nothing was returned
537 */
538
539 if( ! AC_credit_isdenied(©_credit)
540 && (qc->query_type == QC_REAL || qc->query_type == QC_FILTERED)
541 && copy_credit.private_objects + copy_credit.public_objects
542 + copy_credit.referrals == 0 ) {
543
544 /* now: if the rtc flag is zero, the query ran to completion */
545 if( qe->condat.rtc == 0 ) {
546 char *rep = ca_get_pw_notfound ;
547 SK_cd_puts(&(qe->condat), rep);
548 UT_free(rep);
549 SK_cd_puts(&(qe->condat), "\n");
550 }
551 else {
552 /* something happened. Hope for working socket and display message
553 (won't hurt even if socket not operable)
554 */
555 char *rep = ca_get_pw_connclosed ;
556 SK_cd_puts(&(qe->condat), rep);
557 UT_free(rep);
558 }
559 }
560
561
562 UT_timeget(&endtime);
563 /* query logging */
564 pw_log_query(qe, qc, ©_credit, begintime, endtime,
565 hostaddress, input);
566
567 /* Commit the credit. This will deny if bonus limit hit
568 and clear the copy */
569 AC_commit(&(qe->condat.eIP), ©_credit, &acl_eip);
570
571 /* end-of-result -> ONE empty line */
572 SK_cd_puts(&(qe->condat), "\n");
573
574 QC_free(qc);
575 } /* do */
576 while( qe->k && qe->condat.rtc == 0
577 && AC_credit_isdenied( ©_credit ) == 0
578 && CO_get_whois_suspended() == 0);
579
580 /* Free the hostaddress */
581 UT_free(hostaddress);
582 /* Free the connection struct's dynamic data */
583 SK_cd_free(&(qe->condat));
584 /* Free the query_environ */
585 QC_environ_free(qe);
586
587 } /* PW_interact() */
588
589
590 /* *MUST* be called before any other PW functions */
591 void
592 PW_init()
/* [<][>][^][v][top][bottom][index][help] */
593 {
594 TH_init_read_write_lockw(&queries_lock);
595 }
596