Source File pm/protocol_mirror.c

    1    | /***************************************
    2    |
    3    |   Protocol mirror module (pm).
    4    |
    5    |   Status: NOT REVIEWED, TESTED
    6    |
    7    |   ******************/ /******************
    8    |   Filename            : protocol_mirror.c
    9    |   Author              : andrei
    10   |   OSs Tested          : Solaris
    11   |   ******************/ /******************
    12   |   Copyright (c) 2000,2001,2002                    RIPE NCC
    13   |
    14   |   All Rights Reserved
    15   |
    16   |   Permission to use, copy, modify, and distribute this software and its
    17   |   documentation for any purpose and without fee is hereby granted,
    18   |   provided that the above copyright notice appear in all copies and that
    19   |   both that copyright notice and this permission notice appear in
    20   |   supporting documentation, and that the name of the author not be
    21   |   used in advertising or publicity pertaining to distribution of the
    22   |   software without specific, written prior permission.
    23   |
    24   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    25   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    26   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    27   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    28   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    29   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    30   |   ***************************************/
    31   |
    32   | #include "rip.h"
    33   |
    34   | #include <stdio.h>
    35   | #include <glib.h>
    36   |
    37   | #define MIN_ARG_LENGTH  6
    38   | #define NRTM_DELIM "-:"
    39   |
    40   | #define MAX_OPT_ARG_C 3
    41   |
    42   | #define Q_QUERY      0x01
    43   | #define G_QUERY      0x02
    44   | #define K_QUERY      0x04
    45   |
    46   | #define IS_Q_QUERY(a)          ((a)&Q_QUERY)
    47   | #define IS_G_QUERY(a)          ((a)&G_QUERY)
    48   | #define IS_PERSISTENT(a)       ((a)&K_QUERY)
    49   |
    50   |
    51   | /*
    52   | * parses input and fills nrtm_q_t structure
    53   | *
    54   | * Returns:
    55   | *  -1 in case of garbage
    56   | *  1  in case of -q sources
    57   | *  2  in case of valid -g
    58   | *  3  in case of -k
    59   | */
    60   | static int parse_request(char *input, nrtm_q_t *nrtm_q)
    61   | {
    62   |  int res=0, err=0;
    63   |  int opt_argc;
    64   |  int c;
    65   |  gchar **opt_argv;
    66   |  getopt_state_t *gst = NULL;
    67   |
    68   |   /* Create the arguments. */
    69   |   /* This allows only a maximum of MAX_OPT_ARG_C words in the query. */
    70   |   opt_argv = g_strsplit(input, " ", MAX_OPT_ARG_C);
    71   |
    72   |   /* Determine the number of arguments. */
    73   |   for (opt_argc=0; opt_argv[opt_argc] != NULL; opt_argc++);
    74   |
    75   |   dieif( (gst = mg_new(0)) == NULL );
    76   |
    77   |   while ((c = mg_getopt(opt_argc, opt_argv, "kq:g:", gst)) != EOF)
    78   |   {
    79   |     switch (c) {
    80   | 	    case 'k':
    81   | 	       res |= K_QUERY; /* persistent connection */
    82   | 	    break;
    83   |
    84   | 	    case 'q':
    85   | 	        if (gst->optarg != NULL) {
    86   |                 char *token, *cursor = gst->optarg;
    87   |
    88   | 	           res |= Q_QUERY;
    89   | 		   err=strncmp(cursor, "sources", 7);
    90   | 	           if(err!=0) break;
    91   | 		   cursor+=7;
    92   |                    g_strchug(cursor);
    93   | 	           token=cursor;
    94   | 		   /* if no sourses are specified - put NULL in nrtm_q->source and list them all */
    95   | 	           if ((*token=='\0') || (*token=='\n') || ((int)*token==13))nrtm_q->source=NULL;
    96   | 	           else {
    97   |                      cursor=index(token, ' ');
    98   |                      if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
    99   |                      else {
    100  |                        cursor=index(token, 13); /* search for ctrl-M - telnet loves this */
    101  | 	               if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
    102  |                        else {
    103  |                          cursor=index(token, '\n');
    104  | 		         if (cursor) nrtm_q->source=g_strndup(token, (cursor-token));
    105  |                          else nrtm_q->source=g_strdup(token);
    106  |                        }
    107  |                      }
    108  |                    }
    109  | 		/* if source was specified - convert it to an upper case */
    110  | 		if (nrtm_q->source) g_strup(nrtm_q->source);
    111  |                 } else err=1;
    112  | 	    break;
    113  |
    114  |             case 'g':
    115  | 	        if (gst->optarg != NULL) {
    116  | 		char *cursor = gst->optarg;
    117  | 		char **tokens;
    118  |
    119  |                   res |= G_QUERY;
    120  |                   g_strdelimit(cursor, NRTM_DELIM, ':');
    121  |                   tokens=g_strsplit(cursor, ":", 4);
    122  |                   if(tokens==NULL) { err=1; break; }
    123  |
    124  |                   if(tokens[0]) {
    125  |                   /* first token is source name */
    126  |                      nrtm_q->source=g_strdup(tokens[0]);
    127  | 		     /* convert it to an upper case */
    128  | 		     g_strup(nrtm_q->source);
    129  |                      if(tokens[1]) {
    130  |                     /* second token is version number */
    131  |                         nrtm_q->version=atoi(tokens[1]);
    132  |                         if(tokens[2]) {
    133  |                         /* this is first serial */
    134  |                            nrtm_q->first=atol(tokens[2]);
    135  |                            if (nrtm_q->first>0) {
    136  |                               if(tokens[3]) {
    137  |                              /* this is last serial */
    138  |                                  nrtm_q->last=atol(tokens[3]);
    139  |                                  if (nrtm_q->last==0)
    140  |                                  if (strncasecmp(tokens[3], "LAST", 4)!=0) err=1;
    141  |                               } else err=1;
    142  |                            } else err=1;
    143  |                         } else err=1;
    144  |                      } else err=1;
    145  |                   } else err=1;
    146  |                   g_strfreev(tokens);
    147  |
    148  |                 } else err=1;
    149  |
    150  | 	    break;
    151  | 	    default:
    152  | 	        err=1;
    153  | 	    break;
    154  |     } /* switch */
    155  |   } /* while there are arguments */
    156  |
    157  |  UT_free(gst);
    158  |  g_strfreev(opt_argv);
    159  |
    160  |  if (err) return(-1);
    161  |  else return(res);
    162  |
    163  | }
    164  |
    165  |
    166  | /* PM_interact() */
    167  | /*++++++++++++++++++++++++++++++++++++++
    168  |   Interact with the client.
    169  |
    170  |   int sock Socket that client is connected to.
    171  |
    172  |   More:
    173  |   +html+ <PRE>
    174  |   Authors:
    175  |         ottrey
    176  |         andrei
    177  |
    178  |   +html+ </PRE><DL COMPACT>
    179  |   +html+ <DT>Online References:
    180  |   +html+ <DD><UL>
    181  |   +html+ </UL></DL>
    182  |
    183  |   ++++++++++++++++++++++++++++++++++++++*/
    184  | void PM_interact(int sock) {
    185  |   char input[MAX_PM_INPUT_SIZE+1];
    186  |   char buff[STR_L];
    187  |   ca_dbSource_t *source_hdl;
    188  |   int read_result;
    189  |   int parse_result;
    190  |   ip_addr_t address;
    191  |
    192  |   char *hostaddress=NULL;
    193  |   sk_conn_st condat;
    194  |   nrtm_q_t nrtm_q;
    195  |   long current_serial;
    196  |   long oldest_serial;
    197  |
    198  |   char *object;
    199  |   int operation;
    200  |
    201  |
    202  |   char *db_host;
    203  |   int  db_port;
    204  |   char *db_name;
    205  |   char *db_user;
    206  |   char *db_pswd;
    207  |   int protocol_version;
    208  |
    209  |   GString *gbuff;
    210  |
    211  |   SQ_connection_t *sql_connection;
    212  |   int persistent_connection;
    213  |
    214  |   /* make a record for thread accounting */
    215  |   TA_add(sock, "nrtm_srv");
    216  |
    217  |
    218  |   /* Get the IP of the client */
    219  |   hostaddress = SK_getpeername(sock);
    220  |
    221  |   /* initialise the connection structure */
    222  |   memset( &condat, 0, sizeof(sk_conn_st));
    223  |   /* initialise the nrtm structure */
    224  |   memset( &nrtm_q, 0, sizeof(nrtm_q_t));
    225  |   /* set the connection data: both rIP and eIP to real IP */
    226  |   condat.sock = sock;
    227  |   condat.ip = hostaddress;
    228  |   SK_getpeerip(sock, &(condat.rIP));
    229  |   memcpy( &(condat.eIP), &(condat.rIP), sizeof(ip_addr_t));
    230  |
    231  |
    232  |   /* Read input */
    233  |   read_result = SK_cd_gets(&(condat), input, MAX_PM_INPUT_SIZE);
    234  |
    235  |   /* read_result < 0 is an error and connection should be closed */
    236  |   if (read_result < 0 ) {
    237  |       /* log the fact, rtc was set */
    238  |   }
    239  |
    240  |
    241  |   parse_result = parse_request(input, &nrtm_q);
    242  |
    243  |
    244  |   if (parse_result < 0 ) {
    245  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Garbage received: %s", hostaddress, input);
    246  |       /* log the fact and exit */
    247  |       /* Free the hostaddress */
    248  |       sprintf(buff, "\n%%ERROR:405: syntax error\n\n\n");
    249  |       SK_cd_puts(&condat, buff);
    250  | /*      SK_cd_close(&(condat)); */
    251  |       UT_free(hostaddress);
    252  |       UT_free(nrtm_q.source);
    253  |       return;
    254  |   }
    255  |
    256  |   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input: [%s]", hostaddress, input);
    257  |
    258  |   /* this is -q sources query  - answer and return */
    259  |   if (IS_Q_QUERY(parse_result)) {
    260  |
    261  |       gbuff=PM_get_nrtm_sources(&(condat.rIP), nrtm_q.source);
    262  |       SK_cd_puts(&condat, gbuff->str);
    263  |       /* end-of-result one extra line (2 in total) */
    264  |       SK_cd_puts(&condat, "\n");
    265  |       /* Free allocated memory  */
    266  |       g_string_free(gbuff, TRUE);
    267  |       UT_free(hostaddress);
    268  |       UT_free(nrtm_q.source);
    269  | /*      SK_cd_close(&(condat)); */
    270  |       return;
    271  |   }
    272  |   else if(IS_G_QUERY(parse_result)){
    273  |      if(IS_PERSISTENT(parse_result))persistent_connection=1; else persistent_connection=0;
    274  |   }
    275  |   else {
    276  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Syntax error: %s", hostaddress, input);
    277  |       /* log the fact and exit */
    278  |       /* Free the hostaddress */
    279  |       sprintf(buff, "\n%%ERROR:405: syntax error\n\n\n");
    280  |       SK_cd_puts(&condat, buff);
    281  | /*      SK_cd_close(&(condat)); */
    282  |       UT_free(hostaddress);
    283  |       UT_free(nrtm_q.source);
    284  |       return;
    285  |
    286  |   }
    287  |
    288  |   /* otherwise this is -g query */
    289  |
    290  |
    291  |   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input parsed: %s:%d:%ld-%ld", hostaddress, nrtm_q.source, nrtm_q.version, nrtm_q.first, nrtm_q.last);
    292  |
    293  |   source_hdl = ca_get_SourceHandleByName(nrtm_q.source);
    294  |   if (source_hdl == NULL){
    295  |      ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Unknown source %s", hostaddress, nrtm_q.source);
    296  |      sprintf(buff, "\n%%ERROR:403: unknown source\n\n\n");
    297  |      SK_cd_puts(&condat, buff);
    298  |      UT_free(hostaddress);
    299  |      UT_free(nrtm_q.source);
    300  | /*     SK_cd_close(&(condat)); */
    301  |      return;
    302  |   }
    303  |
    304  |   /* check if the client is authorized to mirror */
    305  |   SK_getpeerip(sock, &address);
    306  |   if(!AA_can_mirror(&address, nrtm_q.source)){
    307  |      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Not authorized to mirror the source %s", hostaddress, nrtm_q.source);
    308  |      sprintf(buff, "\n%%ERROR:402: not authorized to mirror the database\n\n\n");
    309  |      SK_cd_puts(&condat, buff);
    310  |      UT_free(hostaddress);
    311  |      UT_free(nrtm_q.source);
    312  | /*     SK_cd_close(&(condat)); */
    313  |      return;
    314  |   }
    315  |
    316  |   /* get protocol version of the source */
    317  |   protocol_version = ca_get_srcnrtmprotocolvers(source_hdl);
    318  |
    319  | /* XXX this is compatibility mode where we don't care about the protocol version */
    320  | #if 0
    321  |   /* compare to the version requested */
    322  |   if(nrtm_q.version != protocol_version){
    323  |      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Source does not support requested protocol %d", hostaddress, nrtm_q.version);
    324  |      sprintf(buff, "\n%%ERROR:404: version %d of protocol is not supported\n\n\n", nrtm_q.version);
    325  |      SK_cd_puts(&condat, buff);
    326  |      free(hostaddress);
    327  |      free(nrtm_q.source);
    328  | /*     SK_cd_close(&(condat)); */
    329  |      return;
    330  |   }
    331  | #endif
    332  |
    333  |
    334  |   /* get database */
    335  |   db_name = ca_get_srcdbname(source_hdl);
    336  |   /* get database host*/
    337  |   db_host = ca_get_srcdbmachine(source_hdl);
    338  |   /* get database port*/
    339  |   db_port = ca_get_srcdbport(source_hdl);
    340  |   /* get database user*/
    341  |   db_user = ca_get_srcdbuser(source_hdl);
    342  |   /* get database password*/
    343  |   db_pswd = ca_get_srcdbpassword(source_hdl);
    344  |
    345  |   sql_connection = SQ_get_connection(db_host, db_port,db_name, db_user, db_pswd);
    346  |   if(!sql_connection) {
    347  |       ER_perror(FAC_PM, PM_NOSQLC," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
    348  |       return;
    349  |   }
    350  |   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] --  Made SQL connection to %s@%s", hostaddress, db_name, db_host);
    351  |
    352  |   /* free copies of the variables */
    353  |   UT_free(db_host);
    354  |   UT_free(db_name);
    355  |   UT_free(db_user);
    356  |   UT_free(db_pswd);
    357  |
    358  |   /* Not to consume the last serial which may cause crash */
    359  |   current_serial=PM_get_current_serial(sql_connection) - SAFE_BACKLOG;
    360  |   oldest_serial=PM_get_oldest_serial(sql_connection);
    361  |
    362  |   if((current_serial==-1) || (oldest_serial==-1)) {
    363  |       ER_perror(FAC_PM, PM_NOSERN," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
    364  |       /* Free the hostaddress */
    365  | /*      SK_cd_close(&(condat)); */
    366  |       /* close the connection to SQL server */
    367  |       SQ_close_connection(sql_connection);
    368  |       UT_free(hostaddress);
    369  |       UT_free(nrtm_q.source);
    370  |       return;
    371  |   }
    372  |
    373  |   /* zero indicates that LAST keyword has been used */
    374  |   if(nrtm_q.last==0)nrtm_q.last=current_serial;
    375  |   /* for persistent connections end of range has no meaning */
    376  |   if(persistent_connection)nrtm_q.last=current_serial;
    377  |
    378  |
    379  |   if((nrtm_q.first>nrtm_q.last) || (nrtm_q.first<oldest_serial) || (nrtm_q.last>current_serial) ||
    380  |      (nrtm_q.first<=0) || (nrtm_q.last<=0) )
    381  |   {
    382  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Invalid range: %ld-%ld [%ld-%ld]", hostaddress, nrtm_q.first, nrtm_q.last, oldest_serial, current_serial);
    383  |       /* write error message back to the client */
    384  |       sprintf(buff, "\n%%ERROR:401: invalid range: Not within %ld-%ld\n\n\n", oldest_serial, current_serial);
    385  |       SK_cd_puts(&condat, buff);
    386  | /*      SK_cd_close(&(condat)); */
    387  |
    388  |       /* close the connection to SQL server */
    389  |       SQ_close_connection(sql_connection);
    390  |
    391  |       /* Free the hostaddress */
    392  |       UT_free(hostaddress);
    393  |       UT_free(nrtm_q.source);
    394  |       return;
    395  |   }
    396  |
    397  |   current_serial=nrtm_q.first;
    398  |
    399  |   /* print banner */
    400  |   {
    401  |   /* get the header string */
    402  |   char *resp_header = ca_get_pw_resp_header;
    403  |   SK_cd_puts(&condat, "\n");
    404  |   SK_cd_puts(&condat, resp_header);
    405  |   UT_free(resp_header);
    406  |   SK_cd_puts(&condat, "\n");
    407  |   }
    408  |
    409  |   sprintf(buff, "%%START Version: %d %s %ld-%ld\n\n", nrtm_q.version, nrtm_q.source, nrtm_q.first, nrtm_q.last);
    410  |   SK_cd_puts(&condat, buff);
    411  |
    412  |   /* make a record for thread accounting */
    413  |   TA_setactivity(buff);
    414  |
    415  | /*************************** MAIN LOOP ****************************/
    416  | /* now start feeding client with data */
    417  |   do {
    418  |
    419  |     /************ ACTUAL PROCESSING IS HERE ***********/
    420  |     /* this call will block if queries are paused */
    421  |     object=PM_get_serial_object(sql_connection, current_serial, &operation);
    422  |
    423  |     /* there is a probability that mirroring interferes with HS cleanup */
    424  |     /* in such case serial may be deleted before it is read by mirrir client */
    425  |     /* null object will be returned in this case and we need to break the loop */
    426  |     if(object==NULL) break;
    427  |     if (operation == OP_ADD) SK_cd_puts(&condat, "ADD\n\n");
    428  |     else SK_cd_puts(&condat, "DEL\n\n");
    429  |
    430  |     SK_cd_puts(&condat, object);
    431  |
    432  |     SK_cd_puts(&condat, "\n");
    433  |
    434  |     UT_free(object);
    435  |     current_serial++;
    436  |
    437  |    /* for real-time mirroring we need some piece of code */
    438  |     if(persistent_connection && (condat.rtc == 0) )
    439  |     {
    440  | 	while(((nrtm_q.last = PM_get_current_serial(sql_connection) - SAFE_BACKLOG)<current_serial)
    441  | 		&& (CO_get_do_server()==1))sleep(1);
    442  |     }
    443  |
    444  |   } /* do while there are more serials, connection was not reset and XXX do_server is on*/
    445  |    while((current_serial<=nrtm_q.last) && (condat.rtc == 0) && (CO_get_do_server()==1));
    446  | /*******************************************************************/
    447  |
    448  |   sprintf(buff, "%%END %s\n\n\n", nrtm_q.source);
    449  |   SK_cd_puts(&condat, buff);
    450  |
    451  |   ER_inf_va(FAC_PM, ASP_PM_INPUT,"[%s] -- <%s:%ld-%ld (%ld)> ",
    452  |            hostaddress, nrtm_q.source, nrtm_q.first, nrtm_q.last, nrtm_q.last-nrtm_q.first+1);
    453  |
    454  |   /* make a record for thread accounting */
    455  |   TA_delete();
    456  |
    457  |   /* close the connection to SQL server */
    458  |   SQ_close_connection(sql_connection);
    459  |   /* Free the hostaddress */
    460  |   UT_free(hostaddress);
    461  |   UT_free(nrtm_q.source);
    462  |
    463  |
    464  |
    465  | } /* PM_interact() */