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. init_stopqueries
  5. PW_stopqueries
  6. PW_startqueries
  7. 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(&copy_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(&copy_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, &copy_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), &copy_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( &copy_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() */

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