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

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