## 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] */