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");
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() */