## modules/pw/protocol_whois.c

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

## DEFINITIONS

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.63 $
       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     /* we still need an extra newline to hand control back to the
     221        client in the "-k" scenerio */
     222     SK_cd_puts(&(qe->condat), "\n");
     223     break;
     224   case QC_EMPTY:
     225     /* The user didn't specify a key, so
     226        - print moron banner
     227        - force disconnection of the user. */
     228     SK_cd_puts(&(qe->condat), "\n");
     229     {
     230       char *rep = ca_get_pw_err_nokey ;
     231       SK_cd_puts(&(qe->condat), rep);
     232       UT_free(rep);
     233     }
     234     /*
     235       EXTRA NEWLINE HERE, because we set condat.rtc, which prevents
     236       further output to user, and we need to end our output with multiple
     237       blank lines.
     238      */
     239     SK_cd_puts(&(qe->condat), "\n\n");
     240     qe->condat.rtc = SK_NOTEXT;
     241     break;
     242   case QC_HELP:
     243     SK_cd_puts(&(qe->condat), "\n");
     244     {
     245       char *rep = ca_get_pw_help_file ;
     246       display_file( &(qe->condat), rep);
     247       UT_free(rep);
     248     }
     249     break;
     250   case QC_TEMPLATE:
     251     SK_cd_puts(&(qe->condat), "\n");
     252     switch(qc->q) {
     253     case QC_Q_SOURCES:
     254       /* print source & mirroring info */
     255       {
     256         GString *srcs = PM_get_nrtm_sources( & qe->condat.rIP, NULL);
     257         SK_cd_puts(&(qe->condat), srcs->str);
     258         g_string_free (srcs, TRUE);
     259       }
     260       break;
     261     case QC_Q_VERSION:
     262       SK_cd_puts(&(qe->condat), "% RIP version " VERSION "\n\n");
     263       break;
     264     default:
     265       /* EMPTY */;
     266     } /* -q */
     267
     268     if (qc->t >= 0) {
     269       SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t));
     270       SK_cd_puts(&(qe->condat), "\n");
     271     }
     272     if (qc->v >= 0) {
     273       SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v));
     274       /* no need for newline here, because it's all broken for any */
     275       /* automated processor at this point anyway */
     276     }
     277     break;
     278
     279   case QC_FILTERED:
     280     {
     281       char *rep = ca_get_pw_k_filter ;
     282       SK_cd_puts(&(qe->condat), rep);
     283       UT_free(rep);
     284     }
     285     /* FALLTROUGH */
     286   case QC_REAL:
     287     {
     288       char *rep = ca_get_pw_resp_header;
     289       SK_cd_puts(&(qe->condat), rep);
     290       UT_free(rep);
     291       SK_cd_puts(&(qe->condat), "\n");
     292     }
     293
     294 #if 1
     295
     296     qis = QI_new(qc,qe);
     297
     298     /* go through all sources,
     299        stop if connection broken - further action is meaningless */
     300     for( qitem = g_list_first(qe->sources_list);
     301          qitem != NULL && qe->condat.rtc == 0;
     302          qitem = g_list_next(qitem)) {
     303
     304
     305       /* QI will decrement the credit counters */
     306       PW_record_query_start();
     307       err = QI_execute(qitem->data, qis, qe, acc_credit, acl_eip );
     308       PW_record_query_end();
     309       if( !NOERR(err) ) {
     310         if( err == QI_CANTDB ) {
     311           SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
     312           SK_cd_puts(&(qe->condat), (char *)qitem->data);
     313           SK_cd_puts(&(qe->condat), " database.\n\n");
     314         }
     315         break; /* quit the loop after any error */
     316       }/* if error*/
     317
     318     }/* for every source */
     319
     320     QI_free(qis);
     321
     322 #else
     323     /* test mode: do not run a query, make up some accounting values */
     324     {
     325       int i, m = random() & 0x0f;
     326       for( i=0 ; i<m ; i++ ) {
     327         AC_count_object( acc_credit, acl_eip, random() & 0x01 );
     328       }
     329     }
     330
     331 #endif
     332
     333     if( AC_credit_isdenied(acc_credit) ) {
     334       /* host reached the limit of returned contact information */
     335       char *rep = ca_get_pw_limit_reached ;
     336       SK_cd_puts(&(qe->condat), rep);
     337       UT_free(rep);
     338       SK_cd_puts(&(qe->condat), "\n");
     339     }
     340
     341     break;
     342   default: die;
     343   }
     344 } /* PW_process_qc */
     345
     346 /*
     347    Occasionally, we need to pause queries to the Whois database.  This
     348    occurs, for instance, when the database is reloaded for one of the
     349    databases we mirror without using NRTM.
     350
     351    The way this works is the caller of PW_stopqueries() gets a "write
     352    lock" on queries.  Each query gets a "read lock".  The lock mechanism
     353    that favors writers is used.
     354
     355    This means that no new read locks can start once PW_stopqueries() is
     356    called, and that it doesn't return until all read locks have been
     357    released.  At this point, queries are stopped and the caller can
     358    proceed to munge about the database safely.
     359
     360    XXX: This is not the best possible solution, because on a very slow
     361    query (for instance one with a very common person name), the thread
     362    calling PW_stopqueries() as well as ALL query threads cannot proceed
     363    until that thread completes.  An alternative with one lock per
     364    database was considered, and may be pursued in the future, but for
     365    now it is not a big problem, since operation occurs normally, just
     366    possibly with a delay in response for some users.
     367
     368    PW_startqueries() releases the write lock, and queries proceed
     369    normally.
     370  */
     371
     372 /* pause queries using a thread lock that favors writers */
     373 static rw_lock_t queries_lock;
     374
     375 /* PW_stopqueries() */
     376 void
     377 PW_stopqueries()
         /* [<][>][^][v][top][bottom][index][help] */
     378 {
     379     TH_acquire_write_lockw(&queries_lock);
     380 }
     381
     382 /* PW_startqueries() */
     383 void
     384 PW_startqueries()
         /* [<][>][^][v][top][bottom][index][help] */
     385 {
     386     TH_release_write_lockw(&queries_lock);
     387 }
     388
     389 /* PW_record_query_start() */
     390 void
     391 PW_record_query_start()
         /* [<][>][^][v][top][bottom][index][help] */
     392 {
     393     TH_acquire_read_lockw(&queries_lock);
     394 }
     395
     396 /* PW_record_query_end() */
     397 void
     398 PW_record_query_end()
         /* [<][>][^][v][top][bottom][index][help] */
     399 {
     400     TH_release_read_lockw(&queries_lock);
     401 }
     402
     403
     404
     405 /*++++++++++++++++++++++++++++++++++++++
     406
     407   void
     408   PW_interact             Main loop for interaction with a single client.
     409                           The function sets up the accounting for the client,
     410                           invokes parsing, execution, logging and accounting
     411                           of the query.
     412
     413   int sock                Socket that client is connected to.
     414
     415   ++++++++++++++++++++++++++++++++++++++*/
     416 void PW_interact(int sock) {
         /* [<][>][^][v][top][bottom][index][help] */
     417   char input[MAX_INPUT_SIZE];
     418   int read_result;
     419   char *hostaddress=NULL;
     420   acl_st acl_rip,   acl_eip;
     421   acc_st acc_credit, copy_credit;
     422   Query_environ *qe=NULL;
     423   Query_command *qc=NULL;
     424   ut_timer_t begintime, endtime;
     425
     426   /* Get the IP of the client */
     427   hostaddress = SK_getpeername(sock);
     428   ER_dbg_va(FAC_PW, ASP_PW_CONN, "connection from %s", hostaddress);
     429
     430   /* Initialize the query environment. */
     431   qe = QC_environ_new(hostaddress, sock);
     432
     433   /* init the connection structure, set timeout for reading the query */
     434   SK_cd_make( &(qe->condat), sock, (unsigned) ca_get_keepopen);
     435
     436   TA_setcondat(&(qe->condat));
     437
     438   /* see if we should be talking at all */
     439   /* check the acl using the realIP, get a copy applicable to this IP */
     440   AC_check_acl( &(qe->condat.rIP), NULL, &acl_rip);
     441
     442   do {
     443     int unauth_pass=0;
     444
     445     TA_setactivity("waiting for query");
     446     /* Read input */
     447     read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE);
     448     /* trash trailing whitespaces(including \n) */
     449     ut_string_chop(input);
     450
     451     TA_setactivity(input);
     452     TA_increment();
     453
     454     UT_timeget( &begintime );
     455
     456     qc = QC_create(input, qe);
     457
     458     {
     459       /* print the greeting text before the query */
     460       char *rep = ca_get_pw_banner ;
     461       SK_cd_puts(&(qe->condat), rep);
     462       UT_free(rep);
     463     }
     464
     465     /* ADDRESS PASSING: check if -V option has passed IP in it */
     466     if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC)) {
     467       if(acl_rip.trustpass) {
     468         acc_st pass_acc;
     469
     470         /* accounting */
     471         memset(&pass_acc, 0, sizeof(acc_st));
     472         pass_acc.addrpasses=1;
     473         AC_commit( &qe->condat.rIP, &pass_acc, &acl_rip);
     474
     475         /* set eIP to this IP */
     476         qe->condat.eIP = qe->pIP;
     477       }
     478       else {
     479         /* XXX shall we deny such user ? Now we can... */
     480         ER_inf_va(FAC_PW, ASP_PW_I_PASSUN,
     481                   "unauthorised address passing by %s", hostaddress);
     482         unauth_pass = 1; /* keep in mind ... */
     483       }
     484     } /* if an address was passed */
     485
     486     /* start setting counters in the connection acc from here on
     487        decrement the credit counter (needed to prevent QI_execute from
     488        returning too many results */
     489
     490     /* check ACL. Get the proper acl record. Calculate credit */
     491     AC_check_acl( &(qe->condat.eIP), &acc_credit, &acl_eip);
     492     /* save the original credit, later check how much was used */
     493     copy_credit = acc_credit;
     494
     495     copy_credit.connections ++;
     496
     497     /* printing notices */
     498     if( unauth_pass && ! acl_rip.deny ) {
     499       /* host not authorised to pass addresses with -V */
     500       char *rep = ca_get_pw_acl_addrpass ;
     501       SK_cd_puts(&(qe->condat), "\n");
     502       SK_cd_puts(&(qe->condat), rep);
     503       UT_free(rep);
     504       SK_cd_puts(&(qe->condat), "\n");
     505     }
     506     if( acl_eip.deny || acl_rip.deny ) {
     507       /* access from host has been permanently denied */
     508       char *rep = ca_get_pw_acl_permdeny ;
     509       SK_cd_puts(&(qe->condat), "\n");
     510       SK_cd_puts(&(qe->condat), rep);
     511       UT_free(rep);
     512       SK_cd_puts(&(qe->condat), "\n");
     513     }
     514
     515     if( acl_eip.deny || acl_rip.deny || unauth_pass ) {
     516       copy_credit.denials ++;
     517     }
     518     else {
     519       /************ ACTUAL PROCESSING IS HERE ***********/
     520       PW_process_qc(qe, qc, &acc_credit, &acl_eip);
     521
     522       if( qc->query_type == QC_REAL ) {
     523         copy_credit.queries ++;
     524       }
     525     }/* if denied ... else */
     526
     527     /* calc. the credit used, result  into copy_credit
     528        This step MUST NOT be forgotten. It must complement
     529        the initial calculation of a credit, otherwise accounting
     530        will go bgzzzzzt.
     531     */
     532     AC_acc_addup(&copy_credit, &acc_credit, ACC_MINUS);
     533
     534     /* now we can check how many results there were, etc. */
     535
     536     /* can say 'nothing found' only if:
     537        - the query did not just cause denial
     538        - was a 'real' query
     539        - nothing was returned
     540     */
     541
     542     if(  ! AC_credit_isdenied(&copy_credit)
     543          && (qc->query_type == QC_REAL || qc->query_type == QC_FILTERED)
     544          && copy_credit.private_objects + copy_credit.public_objects
     545          + copy_credit.referrals == 0 ) {
     546
     547       /* now: if the rtc flag is zero, the query ran to completion */
     548       if( qe->condat.rtc == 0 ) {
     549         char *rep = ca_get_pw_notfound ;
     550         SK_cd_puts(&(qe->condat), rep);
     551         UT_free(rep);
     552         SK_cd_puts(&(qe->condat), "\n");
     553       }
     554       else {
     555         /* something happened. Hope for working socket and display message
     556            (won't hurt even if socket not operable)
     557         */
     558         char *rep = ca_get_pw_connclosed ;
     559         SK_cd_puts(&(qe->condat), rep);
     560         UT_free(rep);
     561       }
     562     }
     563
     564
     565     UT_timeget(&endtime);
     566     /* query logging */
     567     pw_log_query(qe, qc, &copy_credit, begintime, endtime,
     568                  hostaddress, input);
     569
     570     /* Commit the credit. This will deny if bonus limit hit
     571        and clear the copy */
     572     AC_commit(&(qe->condat.eIP), &copy_credit, &acl_eip);
     573
     574     /* end-of-result -> ONE empty line */
     575     SK_cd_puts(&(qe->condat), "\n");
     576
     577     QC_free(qc);
     578   } /* do */
     579   while( qe->k && qe->condat.rtc == 0
     580          && AC_credit_isdenied( &copy_credit ) == 0
     581          && CO_get_whois_suspended() == 0);
     582
     583   /* Free the hostaddress */
     584   UT_free(hostaddress);
     585   /* Free the connection struct's dynamic data */
     586   SK_cd_free(&(qe->condat));
     587   /* Free the query_environ */
     588   QC_environ_free(qe);
     589
     590 } /* PW_interact() */
     591
     592
     593 /* *MUST* be called before any other PW functions */
     594 void
     595 PW_init()
         /* [<][>][^][v][top][bottom][index][help] */
     596 {
     597     TH_init_read_write_lockw(&queries_lock);
     598 }
     599

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