modules/pm/protocol_mirror.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- IS_Q_QUERY
- IS_G_QUERY
- IS_PERSISTENT
- parse_request
- 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() */