modules/pw/protocol_whois.c

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

FUNCTIONS

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.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(&copy_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(&copy_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, &copy_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), &copy_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( &copy_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 

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