1    | /***************************************
2    |   $Revision: 1.17 $
3    | 
4    |   Wrapper for NRTM client
5    | 
6    |   Status: NOT REVUED, NOT TESTED
7    | 
8    |  Author(s):       Andrei Robachevsky
9    | 
10   |   ******************/ /******************
11   |   Modification History:
12   |         andrei (17/01/2000) Created.
13   |   ******************/ /******************
14   |   Copyright (c) 2000                              RIPE NCC
15   |  
16   |   All Rights Reserved
17   |   
18   |   Permission to use, copy, modify, and distribute this software and its
19   |   documentation for any purpose and without fee is hereby granted,
20   |   provided that the above copyright notice appear in all copies and that
21   |   both that copyright notice and this permission notice appear in
22   |   supporting documentation, and that the name of the author not be
23   |   used in advertising or publicity pertaining to distribution of the
24   |   software without specific, written prior permission.
25   |   
26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32   |  ***************************************/
33   | #include <sys/types.h>
34   | #include <sys/socket.h>
35   | #include <netinet/in.h>
36   | #include <arpa/inet.h>
37   | #include <fcntl.h>
38   | #include <signal.h>
39   | #include "ud.h"
40   | #include "ud_int.h"
41   | #include "constants.h"
42   | 
43   | #include "server.h"
44   | #include "protocol_mirror.h"
45   | #include "ta.h"
46   | 
47   | /* here we store sockets for update threads */
48   | /* they are from SV module */
49   | extern int SV_update_sock[];
50   | 
51   | /* Response time to swtching updates on and off */
52   | #define TIMEOUT 60 
53   | /* Maximum number of objects(serials) we can consume at a time */
54   | #define SBUNCH 1000
55   | 
56   | /************************************************************
57   | * int get_NRTM_fd()                                         *
58   | *                                                           *
59   | * Gets the NRTM stream                                      *
60   | *                                                           *
61   | * First tries to request the serials from the NRTM server   *
62   | * If the name of the server appears to be not a network name*
63   | * it tries to open the file with this name                  *
64   | *                                                           *
65   | * nrtm - pointer to _nrtm structure                         *
66   | * upto_last - if==1 then requests to download serials using *
67   | * LAST keyword                                              *
68   | *                                                           *
69   | * Returns:                                                  *
70   | * A file descriptor for a data stream                       *
71   | * -1 - error                                                *
72   | *                                                           *
73   | ************************************************************/
74   | int get_NRTM_fd(struct _nrtm *nrtm, int upto_last, char *source)
75   | {
76   | int sockfd;
77   | struct hostent *hptr;
78   | struct sockaddr_in serv_addr;
79   | struct in_addr *paddr;
80   | char line_buff[STR_XXL];
81   | int fd;
82   | int nwrite;
83   | struct hostent result;
84   | int error;
85   | int network;
86   | 
87   | 
88   |  fprintf(stderr, "Making connection to NRTM server ...\n");
89   |  if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){
90   |    perror("socket");
91   |    return(-1);
92   |  }  
93   | /* hptr=gethostbyname(nrtm->server);*/
94   |  hptr=gethostbyname_r(nrtm->server,  &result, line_buff, sizeof(line_buff), &error);
95   | 
96   |  /* Check if it is a network stream or a file */
97   |  if (hptr) { /* this is a network stream*/
98   |    paddr=(struct in_addr *)hptr->h_addr;
99   |    bzero(&serv_addr, sizeof(serv_addr));
100  |    serv_addr.sin_family=AF_INET;
101  |    serv_addr.sin_port=nrtm->port;
102  |    memcpy(&serv_addr.sin_addr, paddr, sizeof(struct in_addr));
103  |    fprintf(stderr,"Trying %s port %d\n", inet_ntoa(serv_addr.sin_addr), nrtm->port);
104  |    if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1) { 
105  |      perror("connect");
106  |      return(-1);
107  |    }  
108  |    fprintf(stderr, "Sending Invitation\n");
109  |    
110  |    /* Request all available serials (upto LAST), or SBUNCH of them */
111  |    if(upto_last)
112  |       sprintf(line_buff, "-g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
113  |    else
114  |       sprintf(line_buff, "-g %s:%d:%ld-%ld\n", source, nrtm->version, nrtm->current_serial+1, nrtm->current_serial+SBUNCH);   
115  |    nwrite=SK_write(sockfd, line_buff, strlen(line_buff) );
116  |    if(nwrite != strlen(line_buff)) { perror("write"); return(-1); }
117  |    fd=sockfd;
118  |    network=1;
119  |    fprintf(stderr, "Returning stream pointer\n");
120  |  }
121  |  else { /* this is a file stream*/
122  |    network=0;
123  |    close(sockfd);
124  |    fprintf(stderr, "Trying file ...\n");
125  |    if((fd=open(nrtm->server, O_RDONLY, 0666))==-1) {
126  |       perror("open");
127  |       return(-1);
128  |    }  
129  |  }  
130  |  return(fd);
131  | } 
132  | 
133  | /************************************************************
134  | *  void UD_do_nrtm()                                        *
135  | *                                                           *
136  | * Processes NRTM stream                                     *
137  | *                                                           *
138  | * It cycles requesting objects from the NRTM server,        * 
139  | * processing them and then sleeping a specified amount of   *
140  | * time.                                                     *
141  | *                                                           *
142  | * It starts by requesting SBUNCH number of serials and does *
143  | * so untill no serials are received (actually a warning     *
144  | * is received saying that the requested range is invalid)   *
145  | * This approach avoids excessive load on the NRTM server    *
146  | *                                                           *
147  | * After that it requests serials using LAST keyward keeping *
148  | * almost in sync with the server                            *
149  | *                                                           *
150  | ************************************************************/
151  |  
152  | void UD_do_nrtm(void *arg)
153  | {
154  | int source = (int)arg;
155  | UD_stream_t ud_stream;
156  | struct _nrtm *nrtm;
157  | int delay;
158  | int do_update=1;
159  | int do_server;
160  | char *logfilename;
161  | FILE *file;
162  | int nrtm_fd;
163  | int num_ok;
164  | int upto_last;
165  | char ta_activity[STR_M];
166  | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
167  | char *db_host, *db_name, *db_user, *db_passwd;
168  | int db_port;
169  | char *source_name;
170  | 
171  | 
172  | 
173  |   nrtm=calloc(1, sizeof(struct _nrtm));
174  |   if(nrtm==NULL) {
175  | 	  printf("Cannot allocate memory\n");
176  | 	  die;
177  |   }	  
178  | /* get mode of operation: protected/unprotected (dummy) */
179  |   ud_stream.source_hdl=source_hdl;
180  |   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
181  | 
182  |   fprintf(stderr, "Mode of operation:\n");
183  |   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
184  |    else fprintf(stderr, "* dummy not allowed\n");
185  |   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
186  |    else fprintf(stderr, "* NRTM\n");
187  |   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
188  |    else fprintf(stderr, "* running as a server\n");
189  |   
190  | /* get mirror server */
191  |   nrtm->server=ca_get_srcnrtmhost(source_hdl);
192  | 
193  |   
194  | /* get mirror port */
195  |   nrtm->port = htons(ca_get_srcnrtmport(source_hdl));
196  |   printf("XXX nrtm_port=%d\n", ntohs(nrtm->port));
197  | 
198  | /*
199  |   if(nrtm->port == -1) {
200  |     printf("Invalid service/port: %d\n", nrtm->port);
201  |     return;
202  |   }
203  | */  
204  |               
205  | /* get mirror version */
206  |   nrtm->version=ca_get_srcnrtmprotocolvers(source_hdl);
207  |  
208  | /* get source we are going to mirror */
209  |   source_name = ca_get_srcname(source_hdl);  
210  | 
211  |   
212  | /* get error log facility */
213  |    logfilename=ca_get_srcnrtmlog(source_hdl);
214  | 
215  |    db_host = ca_get_srcdbmachine(source_hdl);
216  |    db_port = ca_get_srcdbport(source_hdl);
217  |    db_name = ca_get_srcdbname(source_hdl);
218  |    db_user = ca_get_srcdbuser(source_hdl);
219  |    db_passwd = ca_get_srcdbpassword(source_hdl);
220  |   
221  | /* Connect to the database */
222  |   fprintf(stderr, "D: Making SQL connection to %s@%s ...", db_name, db_host);
223  |   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
224  |      
225  |  
226  |   if(! ud_stream.db_connection) {
227  |    fprintf(stderr, "D: ERROR: no SQL connection\n");
228  |     return;
229  |   }
230  |   	
231  |   fprintf(stderr, "OK\n");
232  | 
233  |   ud_stream.num_skip=0;
234  |   ud_stream.load_pass=0;
235  |   ud_stream.nrtm=nrtm;
236  |   ud_stream.log.logfile = fopen(logfilename, "a+");
237  |   if(!ud_stream.log.logfile){
238  | 	  fprintf(stderr, "D: ERROR: cannot open log file %s\n", logfilename);
239  | 	  return;
240  |   }
241  |   
242  |   free(db_host);
243  |   free(db_name);
244  |   free(db_user);
245  |   free(db_passwd);
246  |   free(logfilename);
247  | 
248  |   
249  |   upto_last=0; /* let's start gradually if the backlog is > SBUNCH (1000) serials*/
250  | 
251  | /*+++ main cycle +++*/
252  | 
253  |  do {
254  |   do_update=CO_get_do_update();
255  |   if(do_update) {
256  |  
257  | 
258  |   /* get current serial */
259  |    nrtm->current_serial=PM_get_current_serial(ud_stream.db_connection);
260  |    
261  |    if(nrtm->current_serial == -1) {
262  |      fprintf(stderr, "D: ERROR: Error obtaining current serial: %ld\n", nrtm->current_serial);
263  |      return;
264  |    }
265  | 
266  |    fprintf(stderr, "current_serial:\t%ld\n", nrtm->current_serial);
267  |    fprintf(stderr, "conecting to server...\n");
268  |       
269  |   /* Get file descriptor of the data stream (RPSL format, use mirror reflector to convert if needed)*/
270  |     nrtm_fd=get_NRTM_fd(nrtm, upto_last, source_name);
271  |    
272  |     /* make a record for thread accounting */
273  |     TA_add(nrtm_fd, "nrtm_clnt");
274  |     sprintf(ta_activity,"[%s]%ld->", source_name, nrtm->current_serial);
275  |     TA_setactivity(ta_activity);
276  |     file=fdopen(nrtm_fd, "r+");
277  | 
278  |     
279  |     fprintf(stderr, "OK\n");
280  |     printf("OK\n");
281  | 
282  | 
283  |     if (file==NULL) { 
284  |      fprintf(stderr, "Cannot open data stream. Trying...\n");
285  |      sleep(100);
286  |      continue;
287  |     }  
288  | 
289  | 
290  |    ud_stream.stream=file;
291  |    ud_stream.log.num_ok=0; 
292  |    ud_stream.log.num_failed=0;
293  |   
294  | 
295  |    fprintf(stderr, "starting processing stream\n");
296  | 
297  |    num_ok=UD_process_stream(&ud_stream);
298  |   
299  |   
300  |      /*Check for errors */
301  |    if(num_ok<0) {
302  | 	 fprintf(stderr, "processing stream failed\n");
303  | 	 do_server=0;
304  | 	 break;    
305  |    }	   
306  |    else fprintf(stderr, "processing stream finished\n"); 
307  |    
308  |   /* Now we can process serials in normal way (upto LAST)*/ 
309  |    if(num_ok==0) upto_last=1;
310  | 
311  |    fprintf(ud_stream.log.logfile, "forwarded to serial:\t%ld\n", (nrtm->current_serial+num_ok));
312  |    fflush(ud_stream.log.logfile);
313  |    fprintf(stderr, "forwarded to serial:\t%ld\n", (nrtm->current_serial+num_ok));
314  |    printf("Objects received: %d\n-----------\n", num_ok);
315  | 
316  |    /* set activity for thread record */
317  |    sprintf(ta_activity,"[%s]->%ld", source_name, (nrtm->current_serial+num_ok));
318  |    TA_setactivity(ta_activity);
319  | 
320  | 
321  |   /* get delay */
322  |    delay=ca_get_srcnrtmdelay(source_hdl);
323  |    SV_sleep(LOCK_SHTDOWN, delay);
324  |   } /* if do_updates */
325  |   else SV_sleep(LOCK_SHTDOWN, TIMEOUT); 
326  | 
327  |   do_server=CO_get_do_server();
328  |   TA_delete();
329  |   
330  |  } while(do_server);  /* main cycle */
331  | 
332  |    fclose(ud_stream.log.logfile);
333  |    free(source_name);
334  | /* free data associated with nrtm structure */         
335  |  if(nrtm) {
336  |    free(nrtm->server);
337  |    free(nrtm);
338  |  }
339  |  
340  |  /* That's all. Close connection to the DB */ 
341  |  SQ_close_connection(ud_stream.db_connection);
342  |  fprintf(stderr, "NRTM stopped\n");  
343  | 
344  | } /* UD_do_nrtm() */
345  | 
346  | /************************************************************
347  | *  void UD_do_updates()                                     *
348  | *                                                           *
349  | * Processes updates                                         *
350  | *                                                           *
351  | * It cycles accepting connections and processing them       * 
352  | * (interactive server). This assures that there is only     *
353  | * one write thread per database/source.                     *
354  | *                                                           *
355  | ************************************************************/
356  |    
357  | void UD_do_updates(void *arg)
358  | {
359  | int source = (int)arg;
360  | int listening_socket = SV_update_sock[source];
361  | int connected_socket;
362  | UD_stream_t ud_stream;
363  | int do_update=1;
364  | int do_server;
365  | char *logfilename;
366  | FILE *file, *file_ack;
367  | int num_ok;
368  | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
369  | char *db_host, *db_name, *db_user, *db_passwd;
370  | int db_port;
371  | 
372  | 
373  | 
374  | /* get mode of operation: protected/unprotected (dummy) */
375  | /*  ud_stream.ud_mode=CO_get_update_mode(); */
376  |   ud_stream.source_hdl=source_hdl;
377  |   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
378  | 
379  |   fprintf(stderr, "Mode of operation:\n");
380  |   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
381  |    else fprintf(stderr, "* dummy not allowed\n");
382  |   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
383  |    else fprintf(stderr, "* NRTM\n");
384  |   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
385  |    else fprintf(stderr, "* running as a server\n");
386  | 
387  | 
388  | /* get error log facility */
389  |   logfilename=ca_get_srcnrtmlog(source_hdl);
390  |   db_host = ca_get_srcdbmachine(source_hdl);
391  |   db_port = ca_get_srcdbport(source_hdl);
392  |   db_name = ca_get_srcdbname(source_hdl);
393  |   db_user = ca_get_srcdbuser(source_hdl);
394  |   db_passwd = ca_get_srcdbpassword(source_hdl);
395  |   
396  | /* Connect to the database */
397  |   fprintf(stderr, "D: Making SQL connection to %s@%s ...", db_name, db_host);
398  | 
399  | /*  ud_stream.db_connection=SQ_get_connection2(); */
400  |   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
401  |    
402  |   if(! ud_stream.db_connection) {
403  |    fprintf(stderr, "D: ERROR: no SQL connection\n");
404  |     return;
405  |   }
406  |   	
407  |   fprintf(stderr, "OK\n");
408  | 
409  |   ud_stream.num_skip=0;
410  |   ud_stream.load_pass=0;
411  |   ud_stream.nrtm=NULL;
412  |   ud_stream.log.logfile = fopen(logfilename, "a+");
413  |   if(!ud_stream.log.logfile){
414  | 	  fprintf(stderr, "D: ERROR: cannot open log file %s\n", logfilename);
415  | 	  return;
416  |   }
417  | 
418  |   free(db_host);
419  |   free(db_name);
420  |   free(db_user);
421  |   free(db_passwd);
422  |   free(logfilename);
423  | 
424  |  
425  | /*+++ main cycle +++*/
426  | 
427  | do { /* be alive while do_server is 1. do_server is turned off by SIGINT */
428  |  
429  |   /* make a record for thread accounting */
430  |   TA_add(listening_socket, "update");
431  |   TA_setactivity("waiting");
432  |   
433  |  
434  | /* accept connection */
435  |    connected_socket = SK_accept_connection(listening_socket);
436  |    if(connected_socket==-1) break;
437  |    
438  | 
439  |    /* make a record for thread accounting */
440  |    TA_delete(); /* Delete 'waiting' record */
441  |    TA_add(connected_socket, "update");
442  | 
443  |    file=fdopen(connected_socket, "r");
444  |    file_ack=fdopen(connected_socket, "w");
445  | 
446  |  do_update=CO_get_do_update();
447  |  if(do_update) {
448  |  
449  |    TA_setactivity("suspended");
450  |    
451  |    fprintf(stderr, "Connection accepted...\n");
452  |    
453  |   if ((file==NULL) || (file_ack==NULL)) {
454  |     fprintf(stderr, "Cannot open data stream. Closing connction\n");
455  |     return; 
456  |   }
457  |   fprintf(stderr, "Connection accepted...\n");
458  | 
459  |   ud_stream.stream=file;
460  |   ud_stream.log.num_ok=0; 
461  |   ud_stream.log.num_failed=0;
462  |   
463  | 
464  |   fprintf(stderr, "starting processing object\n");
465  | 
466  |   num_ok=UD_process_stream(&ud_stream);
467  |   
468  |   fprintf(stderr, "processing object finished\n");  
469  | 
470  |   if(num_ok==1) {
471  |    fprintf(file_ack, "%%ERROR 0\n");
472  |    fprintf(stderr, "%%ERROR 0\n");
473  |   } 
474  |    else {
475  |       num_ok=(-1)*num_ok;
476  |       fprintf(file_ack, "%%ERROR %d\n",num_ok);
477  |       fprintf(stderr, "%%ERROR %d\n",num_ok);
478  |       fprintf(file_ack, "Transaction had the following problems:\n");
479  |       if(num_ok & ERROR_U_MEM) fprintf(file_ack, "Memory allocation error\n");
480  | /*      if(num_ok & ERROR_U_DBS) fprintf(file_ack, "Database (SQL) error\n");*/
481  | /*      if(num_ok & ERROR_U_OBJ) fprintf(file_ack, "Object (RF) error\n");*/
482  | /*      if(num_ok & ERROR_U_AUT) fprintf(file_ack, "Object authentication error\n");*/
483  |       if(num_ok & ERROR_U_BADOP) fprintf(file_ack, "Bad operation\n");
484  |       if(num_ok & ERROR_U_COP) fprintf(file_ack, "Conflicting operation\n");
485  |       if(num_ok & ERROR_U_NSUP) fprintf(file_ack, "Object of this type is not supported\n");
486  |       if(num_ok & ERROR_U_BUG) fprintf(file_ack, "Software bug - report to <ripe-dbm@ripe.net>\n");
487  |    }
488  |    if(ud_stream.error_script)fprintf(file_ack, "%s\n", ud_stream.error_script);
489  |    
490  |    if(ud_stream.error_script) free(ud_stream.error_script);
491  |    
492  |    fflush(file_ack); fclose(file_ack);
493  |    fclose(file);
494  |  }  /* if do_update*/
495  | else { /* Otherwise print a message*/
496  |  /* To display with 'show threads' */
497  |   TA_setactivity("suspended");
498  |  
499  |   fprintf(file_ack, "%%ERROR 1000\n%%Updates are suspended\n");
500  |   fflush(file_ack); fclose(file_ack);
501  |   fclose(file);
502  |  }
503  |   /* make a record for thread accounting */
504  |    TA_delete();
505  | 
506  |    do_server=CO_get_do_server();  
507  | 
508  | } while (do_server);  /* main cycle */
509  |   
510  |    fclose(ud_stream.log.logfile);
511  |  /* That's all. Close connection to the DB */ 
512  |  SQ_close_connection(ud_stream.db_connection);
513  |  
514  | 
515  |  fprintf(stderr, "server stopped\n");  
516  | 
517  | } /* UD_do_update() */
518  | 
519  | 
520  | 
521  |