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