Source File modules/pm/protocol_mirror.c

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