Source File pw/protocol_whois.c

    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)
    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,
    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,
    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()
    378  | {
    379  |     TH_acquire_write_lockw(&queries_lock);
    380  | }
    381  |
    382  | /* PW_startqueries() */
    383  | void
    384  | PW_startqueries()
    385  | {
    386  |     TH_release_write_lockw(&queries_lock);
    387  | }
    388  |
    389  | /* PW_record_query_start() */
    390  | void
    391  | PW_record_query_start()
    392  | {
    393  |     TH_acquire_read_lockw(&queries_lock);
    394  | }
    395  |
    396  | /* PW_record_query_end() */
    397  | void
    398  | PW_record_query_end()
    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) {
    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()
    596  | {
    597  |     TH_init_read_write_lockw(&queries_lock);
    598  | }
    599  |