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