## 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] */