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