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      0x00
59   | #define G_QUERY      0x01
60   | #define K_QUERY      0x02
61   | 
62   | #define IS_Q_QUERY(a)          ((a)&Q_QUERY)
63   | #define IS_G_QUERY(a)          ((a)&G_QUERY)
64   | #define IS_PERSISTENT(a) ((a)&K_QUERY)
65   | 
66   | 
67   | /*
68   | * parses input and fills nrtm_q_t structure
69   | *
70   | * Returns:
71   | *  -1 in case of garbage
72   | *  0  in case of -q sources
73   | *  1  in case of valid -g
74   | *  2  in case of -k
75   | */
76   | static int parse_request(char *input, nrtm_q_t *nrtm_q)
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  |                 } else err=1;
126  | 	    break;
127  | 
128  |             case 'g':
129  | 	        if (gst->optarg != NULL) {
130  | 		char *token, *cursor = gst->optarg;
131  | 		char **tokens;	
132  | 		
133  |                   res |= G_QUERY;
134  |                   g_strdelimit(cursor, NRTM_DELIM, ':');
135  |                   tokens=g_strsplit(cursor, ":", 4);
136  |                   if(tokens==NULL) { err=1; break; }
137  |  
138  |                   if(tokens[0]) {
139  |                   /* first token is source name */	 
140  |                      nrtm_q->source=g_strdup(tokens[0]);
141  |                      if(tokens[1]) {
142  |                     /* second token is version number */
143  |                         nrtm_q->version=atoi(tokens[1]);
144  |                         if(tokens[2]) {
145  |                         /* this is first serial */      
146  |                            nrtm_q->first=atol(tokens[2]);
147  |                            if (nrtm_q->first>0) {
148  |                               if(tokens[3]) {
149  |                              /* this is last serial */
150  |                                  nrtm_q->last=atol(tokens[3]);
151  |                                  if (nrtm_q->last==0) 
152  |                                  if (strncasecmp(tokens[3], "LAST", 4)!=0) err=1;
153  |                               } else err=1;
154  |                            } else err=1;    
155  |                         } else err=1; 
156  |                      } else err=1;  
157  |                   } else err=1;   
158  |                   g_strfreev(tokens);
159  |  
160  |                 } else err=1;
161  | 	        
162  | 	    break;
163  | 	    default:
164  | 	        err=1;
165  | 	    break;
166  |     } /* switch */
167  |   } /* while there are arguments */
168  | 
169  |  free(gst);
170  | 
171  |  if (err) return(-1);
172  |  else return(res); 
173  |    
174  | }
175  | 
176  | 
177  | /* PM_interact() */
178  | /*++++++++++++++++++++++++++++++++++++++
179  |   Interact with the client.
180  | 
181  |   int sock Socket that client is connected to.
182  | 
183  |   More:
184  |   +html+ <PRE>
185  |   Authors:
186  |         ottrey
187  |         andrei
188  | 
189  |   +html+ </PRE><DL COMPACT>
190  |   +html+ <DT>Online References:
191  |   +html+ <DD><UL>
192  |   +html+ </UL></DL>
193  | 
194  |   ++++++++++++++++++++++++++++++++++++++*/
195  | void PM_interact(int sock) {
196  |   char input[MAX_INPUT_SIZE];
197  |   char buff[STR_L];
198  |   ca_dbSource_t *source_hdl;
199  |   int read_result;
200  |   int parse_result;
201  |   ip_addr_t address;
202  | 
203  |   char *hostaddress=NULL;
204  |   sk_conn_st condat;
205  |   nrtm_q_t nrtm_q;
206  |   long current_serial;
207  |   long oldest_serial;
208  |   
209  |   char *object;
210  |   int operation;
211  |   
212  |   
213  |   char *db_host;
214  |   int  db_port;
215  |   char *db_name;
216  |   char *db_user;
217  |   char *db_pswd;
218  | 
219  |   GString *gbuff;
220  |   
221  |   SQ_connection_t *sql_connection;     
222  |   int persistent_connection;
223  | 
224  |   /* make a record for thread accounting */
225  |   TA_add(sock, "nrtm_srv");
226  | 
227  |   
228  |   /* Get the IP of the client */
229  |   hostaddress = SK_getpeername(sock);
230  |   
231  |   /* initialise the connection structure */
232  |   memset( &condat, 0, sizeof(sk_conn_st));
233  |   /* initialise the nrtm structure */
234  |   memset( &nrtm_q, 0, sizeof(nrtm_q_t));
235  |   /* set the connection data: both rIP and eIP to real IP */
236  |   condat.sock = sock;
237  |   condat.ip = hostaddress;
238  |   SK_getpeerip(sock, &(condat.rIP));
239  |   memcpy( &(condat.eIP), &(condat.rIP), sizeof(ip_addr_t));
240  | 
241  | 
242  |   /* Read input */
243  |   read_result = SK_cd_gets(&(condat), input, MAX_INPUT_SIZE);
244  |     
245  |   /* read_result < 0 is an error and connection should be closed */
246  |   if (read_result < 0 ) {
247  |       /* log the fact, rtc was set */
248  |   }
249  | 
250  |     
251  |   parse_result = parse_request(input, &nrtm_q);
252  | 
253  |   
254  |   if (parse_result < 0 ) {
255  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Garbage received: %s", hostaddress, input);
256  |       /* log the fact and exit */
257  |       /* Free the hostaddress */
258  |       sprintf(buff, "\n%%ERROR:1: Syntax error\n\n");
259  |       SK_cd_puts(&condat, buff);
260  |       SK_cd_close(&(condat));
261  |       free(hostaddress);
262  |       free(nrtm_q.source);
263  |       return;
264  |   }
265  |   
266  |   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] -- input: [%s]", hostaddress, input); 
267  |   
268  |   /* this is -q sources query  - answer and return */    
269  |   if (IS_Q_QUERY(parse_result)) {
270  | 	  
271  |       gbuff=PM_get_nrtm_sources(&(condat.rIP), nrtm_q.source);
272  |       SK_cd_puts(&condat, gbuff->str);
273  |       /* Free allocated memory  */
274  |       g_string_free(gbuff, TRUE);
275  |       free(hostaddress);
276  |       free(nrtm_q.source);
277  |       SK_cd_close(&(condat));
278  |       return;
279  |   }
280  |   else if(IS_G_QUERY(parse_result)){
281  |      if(IS_PERSISTENT(parse_result))persistent_connection=1; else persistent_connection=0;
282  |   }
283  |   else {
284  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] -- Syntax error: %s", hostaddress, input);
285  |       /* log the fact and exit */
286  |       /* Free the hostaddress */
287  |       sprintf(buff, "\n%%ERROR:1: Syntax error\n\n");
288  |       SK_cd_puts(&condat, buff);
289  |       SK_cd_close(&(condat));
290  |       free(hostaddress);
291  |       free(nrtm_q.source);
292  |       return;
293  | 	  
294  |   }
295  |   
296  |   /* otherwise this is -g query */
297  |   
298  |   
299  |   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);
300  |    
301  |   source_hdl = ca_get_SourceHandleByName(nrtm_q.source); 
302  |   if (source_hdl == NULL){
303  |      ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Unknown source %s", hostaddress, nrtm_q.source);
304  |      sprintf(buff, "\n%%ERROR:4: Unknown source\n\n");
305  |      SK_cd_puts(&condat, buff);
306  |      free(hostaddress);
307  |      free(nrtm_q.source);
308  |      SK_cd_close(&(condat));
309  |      return;
310  |   }
311  | 	 
312  |   /* check if the client is authorized to mirror */
313  |   SK_getpeerip(sock, &address);
314  |   if(!AA_can_mirror(&address, nrtm_q.source)){
315  |      ER_inf_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Not authorized to mirror the source %s", hostaddress, nrtm_q.source);
316  |      sprintf(buff, "\n%%ERROR:3: You are not authorized to mirror the database\n\n");
317  |      SK_cd_puts(&condat, buff);
318  |      free(hostaddress);
319  |      free(nrtm_q.source);
320  |      SK_cd_close(&(condat));
321  |      return;
322  |   }
323  | 
324  |       
325  |     
326  |   /* get database */
327  |   db_name = ca_get_srcdbname(source_hdl);
328  |   /* get database host*/
329  |   db_host = ca_get_srcdbmachine(source_hdl);      
330  |   /* get database port*/
331  |   db_port = ca_get_srcdbport(source_hdl);        
332  |   /* get database user*/
333  |   db_user = ca_get_srcdbuser(source_hdl);          
334  |   /* get database password*/
335  |   db_pswd = ca_get_srcdbpassword(source_hdl);
336  |   
337  |   sql_connection = SQ_get_connection(db_host, db_port,db_name, db_user, db_pswd);
338  |   if(!sql_connection) {
339  |       ER_perror(FAC_PM, PM_NOSQLC," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
340  |       return;
341  |   }
342  |   ER_dbg_va(FAC_PM, ASP_PM_INPUT,"[%s] --  Made SQL connection to %s@%s", hostaddress, db_name, db_host); 
343  | 
344  |   /* free copies of the variables */
345  |   free(db_host);
346  |   free(db_name);
347  |   free(db_user);
348  |   free(db_pswd);
349  |                                                         
350  |   current_serial=PM_get_current_serial(sql_connection);
351  |   oldest_serial=PM_get_oldest_serial(sql_connection);
352  |     
353  |   if((current_serial==-1) || (oldest_serial==-1)) {
354  |       ER_perror(FAC_PM, PM_NOSERN," database='%s' [%d] %s",db_name, SQ_errno(sql_connection), SQ_error(sql_connection));
355  |       /* Free the hostaddress */
356  |       SK_cd_close(&(condat));
357  |       /* close the connection to SQL server */
358  |       SQ_close_connection(sql_connection); 
359  |       free(hostaddress);
360  |       free(nrtm_q.source);
361  |       return;
362  |   }
363  |   
364  |   /* zero indicates that LAST keyword has been used */    
365  |   if(nrtm_q.last==0)nrtm_q.last=current_serial;
366  |   /* for persistent connections end of range has no meaning */
367  |   if(persistent_connection)nrtm_q.last=current_serial;
368  | 
369  |     
370  |   if((nrtm_q.first>nrtm_q.last) || (nrtm_q.first<oldest_serial) || (nrtm_q.last>current_serial) ||
371  |      (nrtm_q.first<=0) || (nrtm_q.last<=0) ) 
372  |   {
373  |       ER_dbg_va(FAC_PM, ASP_PM_ERESP,"[%s] --  Invalid range: %ld-%ld", hostaddress, nrtm_q.first, nrtm_q.last);
374  |       /* write error message back to the client */
375  |       sprintf(buff, "\n%%ERROR:2: Invalid range: Not within %ld-%ld\n\n", oldest_serial, current_serial);
376  |       SK_cd_puts(&condat, buff);
377  |       SK_cd_close(&(condat));
378  |       
379  |       /* close the connection to SQL server */
380  |       SQ_close_connection(sql_connection); 
381  | 
382  |       /* Free the hostaddress */
383  |       free(hostaddress);
384  |       free(nrtm_q.source);
385  |       return;
386  |   }
387  |   
388  |   current_serial=nrtm_q.first;
389  |  
390  |   /* print banner */
391  |   {
392  |   /* get the header string */
393  |   char *resp_header = ca_get_pw_resp_header;
394  | /*  sprintf(buff, "\n%% Rights restricted by copyright. See http://www.ripe.net/ripencc/pub-services/db/copyright.html\n\n"); */
395  |   SK_cd_puts(&condat, "\n");
396  |   SK_cd_puts(&condat, resp_header);
397  |   free(resp_header);
398  |   SK_cd_puts(&condat, "\n");
399  |   } 
400  |    
401  |   sprintf(buff, "%%START Version: %d %s %ld-%ld\n\n", nrtm_q.version, nrtm_q.source, nrtm_q.first, nrtm_q.last);
402  |   SK_cd_puts(&condat, buff);
403  | 
404  |   /* make a record for thread accounting */
405  |   TA_setactivity(buff);
406  | 
407  | /*************************** MAIN LOOP ****************************/   
408  | /* now start feeding client with data */    
409  |   do {    
410  | 
411  |     object=PM_get_serial_object(sql_connection, current_serial, &operation);
412  |     if (operation == OP_ADD) SK_cd_puts(&condat, "ADD\n\n");
413  |     else SK_cd_puts(&condat, "DEL\n\n");
414  |     
415  |     SK_cd_puts(&condat, object);
416  | 
417  |     SK_cd_puts(&condat, "\n");
418  |       
419  |     free(object);
420  |     current_serial++;
421  | 
422  | /* for real-time mirroring we need some piece of code */
423  | if(persistent_connection && (condat.rtc == 0) )
424  | {
425  | 	while(((nrtm_q.last = PM_get_current_serial(sql_connection))<current_serial) 
426  | 		&& (CO_get_do_server()==1))sleep(1);
427  | }
428  | 
429  |   } /* do while there are more serials, connection was not reset and XXX do_server is on*/
430  |    while((current_serial<=nrtm_q.last) && (condat.rtc == 0) && (CO_get_do_server()==1));
431  | /*******************************************************************/
432  |   
433  |   sprintf(buff, "%%END %s\n\n\n", nrtm_q.source);
434  |   SK_cd_puts(&condat, buff);
435  | 
436  |   ER_inf_va(FAC_PM, ASP_PM_INPUT,"[%s] -- <%s:%ld-%ld (%ld)> ", 
437  |            hostaddress, nrtm_q.source, nrtm_q.first, nrtm_q.last, nrtm_q.last-nrtm_q.first+1); 
438  | 
439  |   /* make a record for thread accounting */
440  |   TA_delete();
441  | 
442  |   /* close the connection to SQL server */
443  |   SQ_close_connection(sql_connection); 
444  |   /* Free the hostaddress */
445  |   free(hostaddress);
446  |   free(nrtm_q.source);
447  | 
448  |   
449  |   
450  | } /* PM_interact() */