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 0x00
59 #define G_QUERY 0x01
60 #define K_QUERY 0x02
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 * 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)
/* [<][>][^][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 } 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) {
/* [<][>][^][v][top][bottom][index][help] */
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() */