## 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\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       /* end-of-result one extra line (2 in total) */
     280       SK_cd_puts(&condat, "\n");
     281       /* Free allocated memory  */
     282       g_string_free(gbuff, TRUE);
     283       free(hostaddress);
     284       free(nrtm_q.source);
     285       SK_cd_close(&(condat));
     286       return;
     287   }
     288   else if(IS_G_QUERY(parse_result)){
     289      if(IS_PERSISTENT(parse_result))persistent_connection=1; else persistent_connection=0;
     290   }
     291   else {
     292       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Syntax error: %s", hostaddress, input);
     293       /* log the fact and exit */
     294       /* Free the hostaddress */
     295       sprintf(buff, "\n%%ERROR:405: syntax error\n\n\n");
     296       SK_cd_puts(&condat, buff);
     297       SK_cd_close(&(condat));
     298       free(hostaddress);
     299       free(nrtm_q.source);
     300       return;
     301
     302   }
     303
     304   /* otherwise this is -g query */
     305
     306
     307   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);
     308
     309   source_hdl = ca_get_SourceHandleByName(nrtm_q.source);
     310   if (source_hdl == NULL){
     311      ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Unknown source %s", hostaddress, nrtm_q.source);
     312      sprintf(buff, "\n%%ERROR:403: unknown source\n\n\n");
     313      SK_cd_puts(&condat, buff);
     314      free(hostaddress);
     315      free(nrtm_q.source);
     316      SK_cd_close(&(condat));
     317      return;
     318   }
     319
     320   /* check if the client is authorized to mirror */
     321   SK_getpeerip(sock, &address);
     322   if(!AA_can_mirror(&address, nrtm_q.source)){
     323      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Not authorized to mirror the source %s", hostaddress, nrtm_q.source);
     324      sprintf(buff, "\n%%ERROR:402: not authorized to mirror the database\n\n\n");
     325      SK_cd_puts(&condat, buff);
     326      free(hostaddress);
     327      free(nrtm_q.source);
     328      SK_cd_close(&(condat));
     329      return;
     330   }
     331
     332   /* get protocol version of the source */
     333   protocol_version = ca_get_srcnrtmprotocolvers(source_hdl);
     334
     335 /* XXX this is compatibility mode where we don't care about the protocol version */
     336 #if 0
     337   /* compare to the version requested */
     338   if(nrtm_q.version != protocol_version){
     339      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Source does not support requested protocol %d", hostaddress, nrtm_q.version);
     340      sprintf(buff, "\n%%ERROR:404: version %d of protocol is not supported\n\n\n", nrtm_q.version);
     341      SK_cd_puts(&condat, buff);
     342      free(hostaddress);
     343      free(nrtm_q.source);
     344      SK_cd_close(&(condat));
     345      return;
     346   }
     347 #endif
     348
     349
     350   /* get database */
     351   db_name = ca_get_srcdbname(source_hdl);
     352   /* get database host*/
     353   db_host = ca_get_srcdbmachine(source_hdl);
     354   /* get database port*/
     355   db_port = ca_get_srcdbport(source_hdl);
     356   /* get database user*/
     357   db_user = ca_get_srcdbuser(source_hdl);
     358   /* get database password*/
     359   db_pswd = ca_get_srcdbpassword(source_hdl);
     360
     361   sql_connection = SQ_get_connection(db_host, db_port,db_name, db_user, db_pswd);
     362   if(!sql_connection) {
     363       ER_perror(FAC_PM, PM_NOSQLC," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
     364       return;
     365   }
     366   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] --  Made SQL connection to %s@%s", hostaddress, db_name, db_host);
     367
     368   /* free copies of the variables */
     369   free(db_host);
     370   free(db_name);
     371   free(db_user);
     372   free(db_pswd);
     373
     374   /* Not to consume the last serial which may cause crash */
     375   current_serial=PM_get_current_serial(sql_connection) - SAFE_BACKLOG;
     376   oldest_serial=PM_get_oldest_serial(sql_connection);
     377
     378   if((current_serial==-1) || (oldest_serial==-1)) {
     379       ER_perror(FAC_PM, PM_NOSERN," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
     380       /* Free the hostaddress */
     381       SK_cd_close(&(condat));
     382       /* close the connection to SQL server */
     383       SQ_close_connection(sql_connection);
     384       free(hostaddress);
     385       free(nrtm_q.source);
     386       return;
     387   }
     388
     389   /* zero indicates that LAST keyword has been used */
     390   if(nrtm_q.last==0)nrtm_q.last=current_serial;
     391   /* for persistent connections end of range has no meaning */
     392   if(persistent_connection)nrtm_q.last=current_serial;
     393
     394
     395   if((nrtm_q.first>nrtm_q.last) || (nrtm_q.first<oldest_serial) || (nrtm_q.last>current_serial) ||
     396      (nrtm_q.first<=0) || (nrtm_q.last<=0) )
     397   {
     398       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Invalid range: %ld-%ld", hostaddress, nrtm_q.first, nrtm_q.last);
     399       /* write error message back to the client */
     400       sprintf(buff, "\n%%ERROR:401: invalid range: Not within %ld-%ld\n\n\n", oldest_serial, current_serial);
     401       SK_cd_puts(&condat, buff);
     402       SK_cd_close(&(condat));
     403
     404       /* close the connection to SQL server */
     405       SQ_close_connection(sql_connection);
     406
     407       /* Free the hostaddress */
     408       free(hostaddress);
     409       free(nrtm_q.source);
     410       return;
     411   }
     412
     413   current_serial=nrtm_q.first;
     414
     415   /* print banner */
     416   {
     417   /* get the header string */
     418   char *resp_header = ca_get_pw_resp_header;
     419   SK_cd_puts(&condat, "\n");
     420   SK_cd_puts(&condat, resp_header);
     421   free(resp_header);
     422   SK_cd_puts(&condat, "\n");
     423   }
     424
     425   sprintf(buff, "%%START Version: %d %s %ld-%ld\n\n", nrtm_q.version, nrtm_q.source, nrtm_q.first, nrtm_q.last);
     426   SK_cd_puts(&condat, buff);
     427
     428   /* make a record for thread accounting */
     429   TA_setactivity(buff);
     430
     431 /*************************** MAIN LOOP ****************************/
     432 /* now start feeding client with data */
     433   do {
     434
     435     /************ ACTUAL PROCESSING IS HERE ***********/
     436     /* this call will block if queries are paused */
     437     object=PM_get_serial_object(sql_connection, current_serial, &operation);
     438
     439     /* there is a probability that mirroring interferes with HS cleanup */
     440     /* in such case serial may be deleted before it is read by mirrir client */
     441     /* null object will be returned in this case and we need to break the loop */
     442     if(object==NULL) break;
     443     if (operation == OP_ADD) SK_cd_puts(&condat, "ADD\n\n");
     444     else SK_cd_puts(&condat, "DEL\n\n");
     445
     446     SK_cd_puts(&condat, object);
     447
     448     SK_cd_puts(&condat, "\n");
     449
     450     free(object);
     451     current_serial++;
     452
     453    /* for real-time mirroring we need some piece of code */
     454     if(persistent_connection && (condat.rtc == 0) )
     455     {
     456         while(((nrtm_q.last = PM_get_current_serial(sql_connection) - SAFE_BACKLOG)<current_serial)
     457                 && (CO_get_do_server()==1))sleep(1);
     458     }
     459
     460   } /* do while there are more serials, connection was not reset and XXX do_server is on*/
     461    while((current_serial<=nrtm_q.last) && (condat.rtc == 0) && (CO_get_do_server()==1));
     462 /*******************************************************************/
     463
     464   sprintf(buff, "%%END %s\n\n\n", nrtm_q.source);
     465   SK_cd_puts(&condat, buff);
     466
     467   ER_inf_va(FAC_PM, ASP_PM_INPUT,"[%s] -- <%s:%ld-%ld (%ld)> ",
     468            hostaddress, nrtm_q.source, nrtm_q.first, nrtm_q.last, nrtm_q.last-nrtm_q.first+1);
     469
     470   /* make a record for thread accounting */
     471   TA_delete();
     472
     473   /* close the connection to SQL server */
     474   SQ_close_connection(sql_connection);
     475   /* Free the hostaddress */
     476   free(hostaddress);
     477   free(nrtm_q.source);
     478
     479
     480
     481 } /* PM_interact() */

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