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