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 (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)
/* [<][>][^][v][top][bottom][index][help] */
47 #define IS_G_QUERY(a) ((a)&G_QUERY)
/* [<][>][^][v][top][bottom][index][help] */
48 #define IS_PERSISTENT(a) ((a)&K_QUERY)
/* [<][>][^][v][top][bottom][index][help] */
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() */