modules/pw/protocol_whois.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. display_file
  2. pw_log_query
  3. PW_process_qc
  4. PW_stopqueries
  5. PW_startqueries
  6. PW_record_query_start
  7. PW_record_query_end
  8. PW_interact
  9. PW_init

   1 /***************************************
   2   $Revision: 1.63 $
   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,2002               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     /* we still need an extra newline to hand control back to the
 221        client in the "-k" scenerio */
 222     SK_cd_puts(&(qe->condat), "\n");
 223     break;       
 224   case QC_EMPTY:
 225     /* The user didn't specify a key, so
 226        - print moron banner
 227        - force disconnection of the user. */
 228     SK_cd_puts(&(qe->condat), "\n");
 229     {
 230       char *rep = ca_get_pw_err_nokey ;
 231       SK_cd_puts(&(qe->condat), rep);
 232       UT_free(rep);
 233     }
 234     /* 
 235       EXTRA NEWLINE HERE, because we set condat.rtc, which prevents 
 236       further output to user, and we need to end our output with multiple
 237       blank lines.
 238      */
 239     SK_cd_puts(&(qe->condat), "\n\n");   
 240     qe->condat.rtc = SK_NOTEXT;
 241     break;
 242   case QC_HELP:
 243     SK_cd_puts(&(qe->condat), "\n");
 244     {
 245       char *rep = ca_get_pw_help_file ;
 246       display_file( &(qe->condat), rep);
 247       UT_free(rep);
 248     }
 249     break;
 250   case QC_TEMPLATE:
 251     SK_cd_puts(&(qe->condat), "\n");
 252     switch(qc->q) {
 253     case QC_Q_SOURCES:
 254       /* print source & mirroring info */
 255       {
 256         GString *srcs = PM_get_nrtm_sources( & qe->condat.rIP, NULL);
 257         SK_cd_puts(&(qe->condat), srcs->str);
 258         g_string_free (srcs, TRUE);
 259       }
 260       break;
 261     case QC_Q_VERSION:
 262       SK_cd_puts(&(qe->condat), "% RIP version " VERSION "\n\n"); 
 263       break;
 264     default: 
 265       /* EMPTY */;
 266     } /* -q */
 267     
 268     if (qc->t >= 0) {
 269       SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t)); 
 270       SK_cd_puts(&(qe->condat), "\n"); 
 271     }
 272     if (qc->v >= 0) {
 273       SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v)); 
 274       /* no need for newline here, because it's all broken for any */
 275       /* automated processor at this point anyway */
 276     }
 277     break;
 278     
 279   case QC_FILTERED:
 280     {
 281       char *rep = ca_get_pw_k_filter ;
 282       SK_cd_puts(&(qe->condat), rep);
 283       UT_free(rep);
 284     }
 285     /* FALLTROUGH */
 286   case QC_REAL:
 287     {
 288       char *rep = ca_get_pw_resp_header;
 289       SK_cd_puts(&(qe->condat), rep);
 290       UT_free(rep);
 291       SK_cd_puts(&(qe->condat), "\n");
 292     }
 293 
 294 #if 1   
 295 
 296     qis = QI_new(qc,qe);
 297  
 298     /* go through all sources, 
 299        stop if connection broken - further action is meaningless */
 300     for( qitem = g_list_first(qe->sources_list);
 301          qitem != NULL && qe->condat.rtc == 0;
 302          qitem = g_list_next(qitem)) {
 303 
 304 
 305       /* QI will decrement the credit counters */
 306       PW_record_query_start();
 307       err = QI_execute(qitem->data, qis, qe, acc_credit, acl_eip );      
 308       PW_record_query_end();
 309       if( !NOERR(err) ) { 
 310         if( err == QI_CANTDB ) {
 311           SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
 312           SK_cd_puts(&(qe->condat), (char *)qitem->data);
 313           SK_cd_puts(&(qe->condat), " database.\n\n");
 314         }
 315         break; /* quit the loop after any error */
 316       }/* if error*/
 317 
 318     }/* for every source */
 319 
 320     QI_free(qis);
 321     
 322 #else
 323     /* test mode: do not run a query, make up some accounting values */
 324     {
 325       int i, m = random() & 0x0f;
 326       for( i=0 ; i<m ; i++ ) {
 327         AC_count_object( acc_credit, acl_eip, random() & 0x01 ); 
 328       }
 329     }
 330 
 331 #endif
 332     
 333     if( AC_credit_isdenied(acc_credit) ) {
 334       /* host reached the limit of returned contact information */
 335       char *rep = ca_get_pw_limit_reached ;
 336       SK_cd_puts(&(qe->condat), rep);
 337       UT_free(rep);
 338       SK_cd_puts(&(qe->condat), "\n");
 339     }
 340     
 341     break;
 342   default: die;
 343   }
 344 } /* PW_process_qc */
 345 
 346 /* 
 347    Occasionally, we need to pause queries to the Whois database.  This
 348    occurs, for instance, when the database is reloaded for one of the
 349    databases we mirror without using NRTM.
 350 
 351    The way this works is the caller of PW_stopqueries() gets a "write
 352    lock" on queries.  Each query gets a "read lock".  The lock mechanism
 353    that favors writers is used.
 354 
 355    This means that no new read locks can start once PW_stopqueries() is
 356    called, and that it doesn't return until all read locks have been
 357    released.  At this point, queries are stopped and the caller can
 358    proceed to munge about the database safely.
 359 
 360    XXX: This is not the best possible solution, because on a very slow
 361    query (for instance one with a very common person name), the thread
 362    calling PW_stopqueries() as well as ALL query threads cannot proceed
 363    until that thread completes.  An alternative with one lock per
 364    database was considered, and may be pursued in the future, but for
 365    now it is not a big problem, since operation occurs normally, just
 366    possibly with a delay in response for some users.
 367 
 368    PW_startqueries() releases the write lock, and queries proceed
 369    normally.
 370  */
 371 
 372 /* pause queries using a thread lock that favors writers */
 373 static rw_lock_t queries_lock;
 374 
 375 /* PW_stopqueries() */
 376 void
 377 PW_stopqueries()
     /* [<][>][^][v][top][bottom][index][help] */
 378 {
 379     TH_acquire_write_lockw(&queries_lock);
 380 }
 381 
 382 /* PW_startqueries() */
 383 void
 384 PW_startqueries()
     /* [<][>][^][v][top][bottom][index][help] */
 385 {
 386     TH_release_write_lockw(&queries_lock);
 387 }
 388 
 389 /* PW_record_query_start() */
 390 void 
 391 PW_record_query_start()
     /* [<][>][^][v][top][bottom][index][help] */
 392 {
 393     TH_acquire_read_lockw(&queries_lock);
 394 }
 395 
 396 /* PW_record_query_end() */
 397 void 
 398 PW_record_query_end()
     /* [<][>][^][v][top][bottom][index][help] */
 399 {
 400     TH_release_read_lockw(&queries_lock);
 401 }
 402 
 403 
 404 
 405 /*++++++++++++++++++++++++++++++++++++++
 406   
 407   void 
 408   PW_interact             Main loop for interaction with a single client.
 409                           The function sets up the accounting for the client,
 410                           invokes parsing, execution, logging and accounting
 411                           of the query.
 412 
 413   int sock                Socket that client is connected to.
 414 
 415   ++++++++++++++++++++++++++++++++++++++*/
 416 void PW_interact(int sock) {
     /* [<][>][^][v][top][bottom][index][help] */
 417   char input[MAX_INPUT_SIZE];
 418   int read_result;
 419   char *hostaddress=NULL;
 420   acl_st acl_rip,   acl_eip;
 421   acc_st acc_credit, copy_credit;
 422   Query_environ *qe=NULL;
 423   Query_command *qc=NULL;
 424   ut_timer_t begintime, endtime;
 425 
 426   /* Get the IP of the client */
 427   hostaddress = SK_getpeername(sock);     
 428   ER_dbg_va(FAC_PW, ASP_PW_CONN, "connection from %s", hostaddress);
 429   
 430   /* Initialize the query environment. */
 431   qe = QC_environ_new(hostaddress, sock);
 432   
 433   /* init the connection structure, set timeout for reading the query */
 434   SK_cd_make( &(qe->condat), sock, (unsigned) ca_get_keepopen); 
 435 
 436   TA_setcondat(&(qe->condat));
 437 
 438   /* see if we should be talking at all */
 439   /* check the acl using the realIP, get a copy applicable to this IP */
 440   AC_check_acl( &(qe->condat.rIP), NULL, &acl_rip);
 441   
 442   do {
 443     int unauth_pass=0;
 444 
 445     TA_setactivity("waiting for query");
 446     /* Read input */
 447     read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE);
 448     /* trash trailing whitespaces(including \n) */
 449     ut_string_chop(input);
 450       
 451     TA_setactivity(input);
 452     TA_increment();
 453 
 454     UT_timeget( &begintime );
 455     
 456     qc = QC_create(input, qe);
 457 
 458     {
 459       /* print the greeting text before the query */
 460       char *rep = ca_get_pw_banner ; 
 461       SK_cd_puts(&(qe->condat), rep);
 462       UT_free(rep);
 463     }
 464 
 465     /* ADDRESS PASSING: check if -V option has passed IP in it */
 466     if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC)) {
 467       if(acl_rip.trustpass) {     
 468         acc_st pass_acc;
 469 
 470         /* accounting */
 471         memset(&pass_acc, 0, sizeof(acc_st));
 472         pass_acc.addrpasses=1;
 473         AC_commit( &qe->condat.rIP, &pass_acc, &acl_rip);
 474 
 475         /* set eIP to this IP */
 476         qe->condat.eIP = qe->pIP;                 
 477       }
 478       else {
 479         /* XXX shall we deny such user ? Now we can... */
 480         ER_inf_va(FAC_PW, ASP_PW_I_PASSUN, 
 481                   "unauthorised address passing by %s", hostaddress);
 482         unauth_pass = 1; /* keep in mind ... */
 483       }
 484     } /* if an address was passed */
 485     
 486     /* start setting counters in the connection acc from here on 
 487        decrement the credit counter (needed to prevent QI_execute from
 488        returning too many results */
 489     
 490     /* check ACL. Get the proper acl record. Calculate credit */
 491     AC_check_acl( &(qe->condat.eIP), &acc_credit, &acl_eip);
 492     /* save the original credit, later check how much was used */
 493     copy_credit = acc_credit;
 494 
 495     copy_credit.connections ++;
 496 
 497     /* printing notices */
 498     if( unauth_pass && ! acl_rip.deny ) {
 499       /* host not authorised to pass addresses with -V */
 500       char *rep = ca_get_pw_acl_addrpass ;
 501       SK_cd_puts(&(qe->condat), "\n");
 502       SK_cd_puts(&(qe->condat), rep);
 503       UT_free(rep);
 504       SK_cd_puts(&(qe->condat), "\n");
 505     }
 506     if( acl_eip.deny || acl_rip.deny ) {
 507       /* access from host has been permanently denied */
 508       char *rep = ca_get_pw_acl_permdeny ;
 509       SK_cd_puts(&(qe->condat), "\n");
 510       SK_cd_puts(&(qe->condat), rep);
 511       UT_free(rep);
 512       SK_cd_puts(&(qe->condat), "\n");
 513     }
 514     
 515     if( acl_eip.deny || acl_rip.deny || unauth_pass ) {
 516       copy_credit.denials ++; 
 517     }
 518     else {
 519       /************ ACTUAL PROCESSING IS HERE ***********/
 520       PW_process_qc(qe, qc, &acc_credit, &acl_eip);
 521 
 522       if( qc->query_type == QC_REAL ) {
 523         copy_credit.queries ++;
 524       }
 525     }/* if denied ... else */
 526       
 527     /* calc. the credit used, result  into copy_credit 
 528        This step MUST NOT be forgotten. It must complement
 529        the initial calculation of a credit, otherwise accounting
 530        will go bgzzzzzt.
 531     */
 532     AC_acc_addup(&copy_credit, &acc_credit, ACC_MINUS);
 533     
 534     /* now we can check how many results there were, etc. */
 535 
 536     /* can say 'nothing found' only if:
 537        - the query did not just cause denial
 538        - was a 'real' query
 539        - nothing was returned
 540     */
 541 
 542     if(  ! AC_credit_isdenied(&copy_credit)  
 543          && (qc->query_type == QC_REAL || qc->query_type == QC_FILTERED)
 544          && copy_credit.private_objects + copy_credit.public_objects
 545          + copy_credit.referrals == 0 ) {
 546       
 547       /* now: if the rtc flag is zero, the query ran to completion */
 548       if( qe->condat.rtc == 0 ) {
 549         char *rep = ca_get_pw_notfound ;
 550         SK_cd_puts(&(qe->condat), rep);
 551         UT_free(rep);
 552         SK_cd_puts(&(qe->condat), "\n");
 553       }
 554       else {
 555         /* something happened. Hope for working socket and display message
 556            (won't hurt even if socket not operable) 
 557         */
 558         char *rep = ca_get_pw_connclosed ;
 559         SK_cd_puts(&(qe->condat), rep);
 560         UT_free(rep);
 561       }
 562     }
 563         
 564 
 565     UT_timeget(&endtime);
 566     /* query logging */
 567     pw_log_query(qe, qc, &copy_credit, begintime, endtime, 
 568                  hostaddress, input);
 569     
 570     /* Commit the credit. This will deny if bonus limit hit 
 571        and clear the copy */ 
 572     AC_commit(&(qe->condat.eIP), &copy_credit, &acl_eip); 
 573     
 574     /* end-of-result -> ONE empty line */
 575     SK_cd_puts(&(qe->condat), "\n");
 576       
 577     QC_free(qc);      
 578   } /* do */
 579   while( qe->k && qe->condat.rtc == 0 
 580          && AC_credit_isdenied( &copy_credit ) == 0
 581          && CO_get_whois_suspended() == 0);
 582 
 583   /* Free the hostaddress */
 584   UT_free(hostaddress);
 585   /* Free the connection struct's dynamic data */
 586   SK_cd_free(&(qe->condat));
 587   /* Free the query_environ */
 588   QC_environ_free(qe);
 589 
 590 } /* PW_interact() */
 591 
 592 
 593 /* *MUST* be called before any other PW functions */
 594 void
 595 PW_init()
     /* [<][>][^][v][top][bottom][index][help] */
 596 {
 597     TH_init_read_write_lockw(&queries_lock);
 598 }
 599 

/* [<][>][^][v][top][bottom][index][help] */