## modules/pm/protocol_mirror.c

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

## DEFINITIONS

This source file includes following functions.

1. parse_request
2. 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,2002                    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)
      47 #define IS_G_QUERY(a)          ((a)&G_QUERY)
      48 #define IS_PERSISTENT(a)       ((a)&K_QUERY)
      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] */