modules/pm/protocol_mirror.c

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

FUNCTIONS

This source file includes following functions.
  1. IS_Q_QUERY
  2. IS_G_QUERY
  3. IS_PERSISTENT
  4. parse_request
  5. PM_interact

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  63 #define IS_G_QUERY(a)          ((a)&G_QUERY)
     /* [<][>][^][v][top][bottom][index][help] */
  64 #define IS_PERSISTENT(a)       ((a)&K_QUERY)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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) {
     /* [<][>][^][v][top][bottom][index][help] */
 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() */

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