/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- process_arguments
- put_delete_before
- get_serial_interval
- generate_load_data
- serve_TCP
- fake_sources
- get_operation
- get_minimum_RIPE_serial
- wait_for_whoisd_completion
- process_timestamp_add
- process_timestamp_delete
- correct_timestamps
- main
1 /***************************************
2 $Revision: 1.1 $
3
4 Recreate. recreate.c - whois DB recreation via pseudo mirror server.
5
6 Status: NOT REVIEWED, TESTED, COMPLETE
7
8 Implementation by: Tiago Antao
9
10 ******************/ /******************
11 Copyright (c) 2002 RIPE NCC
12
13 All Rights Reserved
14
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of the author not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22
23 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
25 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
26 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
28 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 ***************************************/
30
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <time.h>
35 #include <regex.h>
36 #include <rip.h>
37 #include "miniconf.h"
38 #include "dbsupport.h"
39 #include "aconf.h"
40
41 int response_sock;
42 long end_serial = 0;
43 long serial_max;
44 long serial_min;
45 long requested_max;
46 long requested_min;
47 char source_name[100];
48 long RIPE_serial_min;
49 long updates_sent;
50
51 /*
52 process_arguments: proccesses command-line arguments.
53 */
54 void process_arguments(int argv, char** argc) {
/* [<][>][^][v][top][bottom][index][help] */
55 if (argv != 2 ) {
56 printf("Usage: %s <serial>\n", argc[0]);
57 exit(1);
58 }
59
60 end_serial = atol(argc[1]); //assume is number
61
62 if (end_serial == 0) {
63 printf("Usage: %s <serial>\n", argc[0]);
64 exit(1);
65 }
66
67 }
68
69 void put_delete_before(char* pkey, long timestamp, char* buffer) {
/* [<][>][^][v][top][bottom][index][help] */
70 SQ_result_set_t* rs;
71 SQ_row_t* row;
72 char query[200];
73 char ppkey[200];
74 long ts;
75 char* object;
76
77 prepare_string_attribute(pkey,
78 ppkey);
79 sprintf(query,
80 "SELECT max(timestamp) FROM archive "
81 " WHERE timestamp<%ld AND pkey='%s'",
82 timestamp, ppkey);
83 SQ_execute_query(archive_conn, query, &rs);
84 row = SQ_row_next(rs);
85 if (row==NULL) { //very bad
86 printf ("Severe error with executing: %s\n", query);
87 exit(1);
88 }
89 SQ_get_column_int(rs, row, 0, &ts);
90 SQ_free_result(rs);
91
92 sprintf(query,
93 "SELECT object FROM archive WHERE timestamp=%ld "
94 " AND pkey='%s' AND operation=1",
95 timestamp, pkey);
96 SQ_execute_query(archive_conn, query, &rs);
97 row = SQ_row_next(rs);
98 if (row==NULL) { //bummer, it has to be on last of RIP DB
99 SQ_free_result(rs);
100 sprintf(query,
101 "SELECT object FROM last WHERE pkey='%s'",
102 pkey);
103 SQ_execute_query(RIPE_conn, query, &rs);
104 row = SQ_row_next(rs);
105 if (row==NULL) { //very bad
106 printf ("Severe error with executing: %s\n", query);
107 exit(1);
108 }
109 }
110 object = SQ_get_column_string_nocopy(rs, row, 0);
111 sprintf(buffer, "DEL\n\n%s\n\n", object);
112 SK_write(response_sock, buffer, strlen(buffer), NULL, NULL);
113 SQ_free_result(rs);
114 }
115
116 /*
117 get_serial_interval: gets the upper and lower bounds of available serials.
118 */
119 void get_serial_interval() {
/* [<][>][^][v][top][bottom][index][help] */
120 SQ_result_set_t* rs;
121 SQ_row_t* row;
122 char query[200];
123
124 sprintf(query,
125 "SELECT min(serial_id), max(serial_id) "
126 " FROM archive "
127 " WHERE serial_id<=%ld ",
128 end_serial);
129 SQ_execute_query(archive_conn, query, &rs);
130 row = SQ_row_next(rs);
131 SQ_get_column_int(rs, row, 0, &serial_min);
132 SQ_get_column_int(rs, row, 1, &serial_max);
133 SQ_free_result(rs);
134 }
135
136 /*
137 generate_load_data: generates and send data to be recreated.
138 */
139 void generate_load_data() {
/* [<][>][^][v][top][bottom][index][help] */
140 SQ_result_set_t* rs;
141 SQ_row_t* row;
142 char query[200];
143 char* object;
144 long operation;
145 char* pkey;
146 long timestamp;
147 char sbuffer[1000000]; // ridiculous but more bug proof?
148
149
150 updates_sent = 0;
151 sprintf(sbuffer,
152 "%%START Version: 2 %s %ld-%ld\n\n",
153 source_name, requested_min, serial_max);
154 SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
155 sprintf(query,
156 " SELECT object, operation, pkey, timestamp "
157 " FROM archive "
158 " WHERE serial_id<=%ld AND serial_id>=%ld "
159 " AND serial_id IS NOT NULL "
160 "ORDER BY timestamp ASC, serial_id ASC ",
161 // order because of special deletes (op=3)
162 end_serial, requested_min);
163 SQ_execute_query(archive_conn, query, &rs);
164 while ((row = SQ_row_next(rs)) != NULL) {
165 updates_sent++;
166 object = SQ_get_column_string_nocopy(rs, row, 0);
167 SQ_get_column_int(rs, row, 1, &operation);
168 switch (operation) {
169 case 1:
170 sprintf(sbuffer, "ADD\n\n%s\n\n", object);
171 SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
172 break;
173 case 2:
174 sprintf(sbuffer, "DEL\n\n%s\n\n", object);
175 SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
176 break;
177 case 3:
178 pkey = SQ_get_column_string_nocopy(rs, row, 2);
179 SQ_get_column_int(rs, row, 3, ×tamp);
180 put_delete_before(pkey, timestamp, sbuffer);
181 sprintf(sbuffer, "ADD\n\n%s\n\n", object);
182 SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
183 break;
184 }
185
186 }
187 SQ_free_result(rs);
188
189 sprintf(sbuffer,"%%END %s\n\n", source_name);
190 SK_write(response_sock, sbuffer, strlen(sbuffer), NULL, NULL);
191 }
192
193 /*
194 serve_TCP: creates a socket and accepts a connection.
195 */
196 void serve_TCP() {
/* [<][>][^][v][top][bottom][index][help] */
197 int sock;
198
199 sock = SK_getsock(SOCK_STREAM, 24444, 128, INADDR_ANY);
200 //printf("Accepting connections\n");
201 response_sock = SK_accept_connection(sock);
202 }
203
204 /*
205 fake_sources: fakes a source
206
207 Irrelevant?
208 */
209 void fake_sources() {
/* [<][>][^][v][top][bottom][index][help] */
210 char line[100];
211 sprintf(line, "RIPE:2:Y:%ld-%ld\n\n", serial_min, serial_max);
212 SK_write(response_sock, line, strlen(line), NULL, NULL);
213 }
214
215 /*
216 get_operation: gets the operation required by the client.
217
218 Also puts the source name in a global variable if -g requested.
219 */
220 int get_operation() {
/* [<][>][^][v][top][bottom][index][help] */
221 char line[201];
222 regex_t reg;
223 regmatch_t match[3];
224
225 bzero(line, 201);
226 if (SK_gets(response_sock, line, 200)>=0) {
227 if (strncmp(line,"-q sources", 10)==0) {
228 return 0;
229 }
230 else {
231 regcomp(®,"-g (.+):2:([0-9]+)-", REG_EXTENDED);
232 if (regexec(®, line, 3, match, 0) != 0) {
233 printf("match failed: %s\n", line);
234 exit(1);
235 }
236 //printf("match OK: %s\n", line);
237 bzero(source_name, 99);
238 strncpy(source_name,
239 line + match[1].rm_so,
240 match[1].rm_eo - match[1].rm_so);
241 requested_min = atol(line+match[2].rm_so);
242 //printf("%sXX\n", line+match[2].rm_so);
243 return 1;
244 }
245 }
246 else {
247 //printf("Input from client ended\n");
248 exit(1);
249 }
250 }
251
252 /*
253 get_minimum_RIPE_serial: returns the minimum serial of recreate
254
255 The minimum serial of recreate is the initial max + 1.
256 Returns in a global variable.
257 */
258 void get_minimum_RIPE_serial() {
/* [<][>][^][v][top][bottom][index][help] */
259 SQ_result_set_t* rs;
260 SQ_row_t* row;
261 long result;
262
263 SQ_execute_query(RIPE_conn,
264 "SELECT max(serial_id) FROM serials",
265 &rs);
266
267 row = SQ_row_next(rs);
268
269 if (SQ_get_column_int(rs, row, 0, &result) == 0) {
270 RIPE_serial_min = result + 1;
271 }
272 else {
273 RIPE_serial_min = 1;
274 }
275
276 SQ_free_result(rs);
277 }
278
279 /*
280 wait_for_whoisd_completion: as named
281
282 As we know the serial when we started and the number of updates sent
283 we know which number should be the max on serials.
284 We wait for it.
285 */
286 void wait_for_whoisd_completion() {
/* [<][>][^][v][top][bottom][index][help] */
287 SQ_result_set_t* rs;
288 SQ_row_t* row;
289 long current_serial=0;
290
291 while (current_serial<RIPE_serial_min+updates_sent) {
292 SQ_execute_query(RIPE_conn,
293 "SELECT max(serial_id) FROM serials",
294 &rs);
295
296 row = SQ_row_next(rs);
297
298 if (SQ_get_column_int(rs, row, 0, ¤t_serial) != 0) {
299 current_serial = 0;
300 }
301
302 SQ_free_result(rs);
303 sleep(1); //ALWAYS wait
304 }
305 }
306
307 /*
308 process_timestamp_add: patches the timestmap of an add
309 */
310 void process_timestamp_add(SQ_result_set_t* rs,
/* [<][>][^][v][top][bottom][index][help] */
311 SQ_row_t* row,
312 long timestamp) {
313 long atlast;
314 long seq_id;
315 long object_id;
316 char upd[200];
317
318 SQ_get_column_int(rs, row, 0, &object_id);
319 SQ_get_column_int(rs, row, 1, &seq_id);
320 SQ_get_column_int(rs, row, 2, &atlast);
321
322 if (atlast) {
323 sprintf(upd,
324 "UPDATE last SET timestamp=%ld WHERE object_id=%ld",
325 timestamp, object_id);
326 }
327 else {
328 sprintf(upd,
329 "UPDATE history "
330 " SET timestamp=%ld "
331 " WHERE object_id=%ld AND sequence_id=%ld",
332 timestamp, object_id, seq_id);
333 }
334 SQ_execute_query(RIPE_conn, upd, NULL);
335 }
336
337 /*
338 process_timestamp_delete: patches the timestamp of a delete
339 */
340 void process_timestamp_delete(SQ_result_set_t* rs,
/* [<][>][^][v][top][bottom][index][help] */
341 SQ_row_t* row,
342 long timestamp) {
343 long object_id;
344 char upd[200];
345
346 SQ_get_column_int(rs, row, 0, &object_id);
347
348 sprintf(upd,
349 "UPDATE last "
350 " SET timestamp=%ld "
351 " WHERE object_id=%ld AND object=''",
352 timestamp, object_id);
353 SQ_execute_query(RIPE_conn, upd, NULL);
354 }
355
356 /*
357 correct_timestamps:
358 */
359 void correct_timestamps() {
/* [<][>][^][v][top][bottom][index][help] */
360 SQ_result_set_t* RIPE_rs;
361 SQ_row_t* RIPE_row;
362 char RIPE_query[200];
363 SQ_result_set_t* archive_rs;
364 SQ_row_t* archive_row;
365 char archive_query[200];
366 long operation;
367 long timestamp;
368
369 sprintf(RIPE_query,
370 " SELECT object_id, sequence_id, atlast "
371 " FROM serials "
372 " WHERE serial_id>=%ld "
373 "ORDER BY serial_id ",
374 RIPE_serial_min);
375 SQ_execute_query(RIPE_conn, RIPE_query, &RIPE_rs);
376
377 sprintf(archive_query,
378 " SELECT timestamp, operation "
379 " FROM archive "
380 " WHERE serial_id<=%ld AND serial_id>=%ld "
381 " AND serial_id IS NOT NULL "
382 "ORDER BY timestamp ASC, serial_id ASC ",
383 end_serial, requested_min);
384 SQ_execute_query(archive_conn, archive_query, &archive_rs);
385
386 while((archive_row = SQ_row_next(archive_rs)) != NULL) {
387 SQ_get_column_int(archive_rs, archive_row, 0, ×tamp);
388 SQ_get_column_int(archive_rs, archive_row, 1, &operation);
389 if (operation==3) {
390 RIPE_row = SQ_row_next(RIPE_rs);
391 if (RIPE_row == NULL) {
392 printf("serials are incomplete irt archive\n");
393 exit(1);
394 }
395 process_timestamp_delete(RIPE_rs, RIPE_row, timestamp);
396 operation = 1;
397 }
398 RIPE_row = SQ_row_next(RIPE_rs);
399 if (RIPE_row == NULL) {
400 printf("serials are incomplete irt archive\n");
401 exit(1);
402 }
403 if (operation==1) {
404 process_timestamp_add(RIPE_rs, RIPE_row, timestamp);
405 }
406 else {
407 process_timestamp_delete(RIPE_rs, RIPE_row, timestamp);
408 }
409
410 }
411 RIPE_row = SQ_row_next(RIPE_rs);
412 if (RIPE_row != NULL) {
413 printf("archive is incomplete irt serials\n");
414 exit(1);
415 }
416
417 SQ_free_result(RIPE_rs);
418 SQ_free_result(archive_rs);
419 }
420
421 /*
422 main: recreate entry point.
423
424 Determine biggest serial on process start (on RIPE DB)
425 Get serials available (on archive DB)
426 Wait for TCP connection
427 while(1)
428 switch (whoisd request)
429 0: fake_sources (-g & -q)
430 1: generate_load_data
431 wait for whois completion
432 patch timestamps
433 */
434 int main (int argv, char** argc) {
/* [<][>][^][v][top][bottom][index][help] */
435 process_arguments(argv, argc);
436 read_configuration();
437 get_db_connections();
438
439 get_minimum_RIPE_serial();
440 get_serial_interval();
441 serve_TCP();
442 while (1) {
443 switch (get_operation()) {
444 case 0:
445 fake_sources();
446 break;
447 case 1:
448 generate_load_data();
449 wait_for_whoisd_completion();
450 correct_timestamps();
451 return 0;
452 }
453 }
454
455 //close_dbs();
456
457 return 0;
458 }