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

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