1    | /***************************************
2    |   $Revision: 1.29 $
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 <stream.h>*/
40   | 
41   | 
42   | #include "ud.h"
43   | #include "ud_int.h"
44   | 
45   | #include "constants.h"
46   | 
47   | #include "er_macro.h"
48   | #include "er_paths.h"
49   | 
50   | #include "server.h"
51   | #include "protocol_mirror.h"
52   | #include "ta.h"
53   | 
54   | /* here we store sockets for update threads */
55   | /* they are from SV module */
56   | extern int SV_update_sock[];
57   | 
58   | /* Response time to swtching updates on and off */
59   | #define TIMEOUT 60 
60   | 
61   | /* timeout between successive attempts to establish connection with server */
62   | #define PM_CONNECTION_TIMEOUT 10 
63   | 
64   | /* Maximum number of objects(serials) we can consume at a time */
65   | #define SBUNCH 1000
66   | 
67   | /* Timeout in seconds when reading from DBupdate */
68   | #define STREAM_TIMEOUT 120
69   | 
70   | /************************************************************
71   | * int get_NRTM_fd()                                         *
72   | *                                                           *
73   | * Gets the NRTM stream                                      *
74   | *                                                           *
75   | * First tries to request the serials from the NRTM server   *
76   | * If the name of the server appears to be not a network name*
77   | * it tries to open the file with this name                  *
78   | *                                                           *
79   | * nrtm - pointer to _nrtm structure                         *
80   | * upto_last - if==1 then requests to download serials using *
81   | * LAST keyword                                              *
82   | *                                                           *
83   | * Returns:                                                  *
84   | * A file descriptor for a data stream                       *
85   | * -1 - error                                                *
86   | *                                                           *
87   | ************************************************************/
88   | int get_NRTM_fd(struct _nrtm *nrtm, int upto_last, char *source)
89   | {
90   | int sockfd;
91   | struct hostent *hptr;
92   | struct sockaddr_in serv_addr;
93   | struct in_addr *paddr;
94   | char line_buff[STR_XXL];
95   | int fd;
96   | int nwrite;
97   | struct hostent result;
98   | int error;
99   | int network;
100  | 
101  | 
102  | /* fprintf(stderr, "Making connection to NRTM server ...\n");*/
103  |  if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){
104  |    ER_perror(FAC_UD, UD_FS, "cannot create socket");
105  |    return(-1);
106  |  }  
107  | #ifdef __linux__
108  |  if(gethostbyname_r(nrtm->server,  &result, line_buff, sizeof(line_buff), &hptr, &error)<0)  hptr=NULL;
109  | #else/* default is Solaris implementation */
110  |  hptr=gethostbyname_r(nrtm->server,  &result, line_buff, sizeof(line_buff), &error);
111  | #endif
112  | 
113  |  /* Check if it is a network stream or a file */
114  |  if (hptr) { /* this is a network stream*/
115  |    paddr=(struct in_addr *)hptr->h_addr;
116  |    bzero(&serv_addr, sizeof(serv_addr));
117  |    serv_addr.sin_family=AF_INET;
118  |    serv_addr.sin_port=nrtm->port;
119  |    memcpy(&serv_addr.sin_addr, paddr, sizeof(struct in_addr));
120  | /*   fprintf(stderr,"Trying %s port %d\n", inet_ntoa(serv_addr.sin_addr), nrtm->port);*/
121  |    if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))==-1) {
122  |      ER_perror(FAC_UD, UD_FS, "cannot cannect"); 
123  |      close(sockfd);
124  |      return(-1);
125  |    }  
126  | /*   fprintf(stderr, "Sending Invitation\n"); */
127  |    
128  |    /* Request all available serials (upto LAST), or SBUNCH of them */
129  |    if(upto_last==1)
130  |       sprintf(line_buff, "-g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
131  |    else if(upto_last==-1) /* persistent mirroring */
132  |       sprintf(line_buff, "-k -g %s:%d:%ld-LAST\n", source, nrtm->version, nrtm->current_serial+1);
133  |    else
134  |       sprintf(line_buff, "-g %s:%d:%ld-%ld\n", source, nrtm->version, nrtm->current_serial+1, nrtm->current_serial+SBUNCH);   
135  |    nwrite=SK_write(sockfd, line_buff, strlen(line_buff) );
136  |    if(nwrite != strlen(line_buff)) { 
137  | 	   ER_perror(FAC_UD, UD_FS, "cannot write");
138  | 	   close(sockfd);
139  | 	   return(-1); 
140  |    }
141  |    fd=sockfd;
142  |    network=1;
143  | /*   fprintf(stderr, "Returning stream pointer\n"); */
144  |  }
145  |  else { /* this is a file stream*/
146  |    network=0;
147  |    close(sockfd);
148  | /*   fprintf(stderr, "Trying file ...\n");*/
149  |    if((fd=open(nrtm->server, O_RDONLY, 0666))==-1) {
150  |       ER_perror(FAC_UD, UD_FS, "cannot open");	   
151  |       return(-1);
152  |    }  
153  |  }  
154  |  return(fd);
155  | } 
156  | 
157  | 
158  | 
159  | /************************************************************
160  | *  void UD_do_nrtm()                                        *
161  | *                                                           *
162  | * Processes NRTM stream                                     *
163  | *                                                           *
164  | * It cycles requesting objects from the NRTM server,        * 
165  | * processing them and then sleeping a specified amount of   *
166  | * time.                                                     *
167  | *                                                           *
168  | * It starts by requesting SBUNCH number of serials and does *
169  | * so untill no serials are received (actually a warning     *
170  | * is received saying that the requested range is invalid)   *
171  | * This approach avoids excessive load on the NRTM server    *
172  | *                                                           *
173  | * After that it requests serials using LAST keyward keeping *
174  | * almost in sync with the server                            *
175  | *                                                           *
176  | ************************************************************/
177  |  
178  | void UD_do_nrtm(void *arg)
179  | {
180  | int source = (int)arg;
181  | UD_stream_t ud_stream;
182  | struct _nrtm *nrtm;
183  | int nrtm_delay;
184  | int do_update=1;
185  | int do_server;
186  | int nrtm_fd;
187  | int num_ok;
188  | int upto_last;
189  | char ta_activity[STR_M];
190  | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
191  | char *db_host, *db_name, *db_user, *db_passwd;
192  | int db_port;
193  | /* get source we are going to mirror */
194  | char *source_name = ca_get_srcname(source_hdl);  
195  | 
196  |   { /* set up the lohgging path */
197  |    int res;
198  |    char *er_ud_def = ca_get_er_ud_def; /* something like 'RIPUPDLOG basename' */
199  |    char er_def[256];
200  |    char *erret = NULL;
201  | 
202  |    sprintf(er_def, "%s %s", er_ud_def, source_name);
203  |    fprintf(stderr, "[%s]\n", er_def);
204  |    if( (res = ER_macro_spec(er_def, &erret)) != 0 ) {
205  |         fputs(erret, stderr);
206  |         die;
207  |         /* or some other error handling */
208  |    }     
209  |    free(erret); /* the response is allocated and must be freed */
210  |    free(er_ud_def);
211  |   }  
212  |          
213  |   nrtm=calloc(1, sizeof(struct _nrtm));
214  |   if(nrtm==NULL) {
215  | 	  ER_perror(FAC_UD, UD_MEM, "cannot allocate memory");
216  | 	  die;
217  |   }	  
218  | /* get mode of operation: protected/unprotected (dummy) */
219  |   memset(&ud_stream, 0, sizeof(ud_stream));
220  |   ud_stream.source_hdl=source_hdl;
221  |   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
222  |   nrtm_delay=ca_get_srcnrtmdelay(source_hdl);
223  |   /* Zero delay means persistent connection */
224  |   if (nrtm_delay==0) ud_stream.ud_mode |= B_PERSIST_MIRR;
225  | 
226  |   fprintf(stderr, "Mode of operation:\n");
227  |   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
228  |   else fprintf(stderr, "* dummy not allowed\n");
229  |   
230  |   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
231  |   else if(IS_NRTM_CLNT(ud_stream.ud_mode)) {
232  | 	   
233  | 	   if(IS_PERSIST_MIRR(ud_stream.ud_mode))fprintf(stderr, "* NRTM: persistent conection\n");
234  | 	   else fprintf(stderr, "* NRTM\n");
235  | 	}   
236  |         else fprintf(stderr, "* STATIC\n");
237  |   
238  |   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
239  |    else fprintf(stderr, "* running as a server\n");
240  |   
241  |   if(IS_NO_NHR(ud_stream.ud_mode))fprintf(stderr, "* NHR is not maintained\n");
242  |    else fprintf(stderr, "* NHR is maintained\n");
243  | 
244  | 
245  | /* get mirror server */
246  |   nrtm->server=ca_get_srcnrtmhost(source_hdl);
247  | 
248  |   
249  | /* get mirror port */
250  |   nrtm->port = htons(ca_get_srcnrtmport(source_hdl));
251  |   printf("XXX nrtm_port=%d\n", ntohs(nrtm->port));
252  | 
253  | /* get mirror version */
254  |   nrtm->version=ca_get_srcnrtmprotocolvers(source_hdl);
255  |  
256  | 
257  | /* get error log facility */
258  | /*   logfilename=ca_get_srcnrtmlog(source_hdl); */
259  | 
260  |    db_host = ca_get_srcdbmachine(source_hdl);
261  |    db_port = ca_get_srcdbport(source_hdl);
262  |    db_name = ca_get_srcdbname(source_hdl);
263  |    db_user = ca_get_srcdbuser(source_hdl);
264  |    db_passwd = ca_get_srcdbpassword(source_hdl);
265  |   
266  | /* Connect to the database */
267  |   ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s making SQL connection to %s@%s ...", UD_TAG, db_name, db_host);
268  |   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
269  |      
270  |  
271  |   if(! ud_stream.db_connection) {
272  |     ER_perror(FAC_UD, UD_SQL, "no connection to SQL server");
273  |     die;
274  |   }
275  |   	
276  |   ud_stream.num_skip=0;
277  |   ud_stream.load_pass=0;
278  |   ud_stream.nrtm=nrtm;
279  |   
280  |   if(IS_PERSIST_MIRR(ud_stream.ud_mode))upto_last=-1; /* the faster the better */
281  |   else upto_last=0; /* let's start gradually if the backlog is > SBUNCH (1000) serials*/
282  | 
283  | /*+++ main cycle +++*/
284  | 
285  |  do {
286  |   do_update=CO_get_do_update();
287  |   if(do_update) {
288  |  
289  |    /* Check connection to the database and try to reconnect */
290  |    if(SQ_ping(ud_stream.db_connection)) {
291  |     ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s connection to SQL server timed out - reistablishing", UD_TAG);
292  |    }
293  | 
294  |   /* get current serial */
295  |    nrtm->current_serial=PM_get_current_serial(ud_stream.db_connection);
296  |    
297  |    if(nrtm->current_serial == -1) {
298  |       ER_perror(FAC_UD, UD_SQL, "cannot obtain current serial: %ld", nrtm->current_serial);
299  |      die;
300  |    }
301  | 
302  |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s connecting to NRTM server (current serial=%ld)", UD_TAG, nrtm->current_serial);
303  | 
304  |   /* Get file descriptor of the data stream (RPSL format, use mirror reflector to convert if needed)*/
305  |     nrtm_fd=get_NRTM_fd(nrtm, upto_last, source_name);
306  |     if (nrtm_fd==-1) { 
307  |      ER_inf_va(FAC_UD, ASP_UD_OBJ, "%s Cannot open data stream. Trying...", UD_TAG);
308  |      SV_sleep(PM_CONNECTION_TIMEOUT);
309  |      continue;
310  |     }  
311  | 
312  |    
313  |     /* make a record for thread accounting */
314  |     TA_add(nrtm_fd, "nrtm_clnt");
315  |     sprintf(ta_activity,"[%s]%ld->", source_name, nrtm->current_serial);
316  |     TA_setactivity(ta_activity);
317  |    
318  |    
319  |    ud_stream.condat.sock = nrtm_fd;
320  |    ud_stream.log.num_ok=0; 
321  |    ud_stream.log.num_failed=0;
322  |   
323  | 
324  |    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s starting processing stream", UD_TAG);
325  | 
326  | /***************** process stream ****************/
327  | 
328  |       num_ok=UD_process_stream(&ud_stream);
329  |   
330  | /***************** process stream ****************/
331  |   
332  |    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s processing stream finished", UD_TAG);
333  |    
334  |   /* close the socket of the NRTM stream */
335  |    close(ud_stream.condat.sock);
336  |    
337  |   
338  | 
339  |    ER_inf_va(FAC_UD, ASP_UD_OBJ, "%s forwarded to serial:%ld", UD_TAG, (nrtm->current_serial+num_ok));
340  |    
341  |    /* set activity for thread record */
342  |    sprintf(ta_activity,"[%s]->%ld", source_name, (nrtm->current_serial+num_ok));
343  |    TA_setactivity(ta_activity);
344  | 
345  | /* if we are NOT in persistent mode */
346  |    if(!IS_PERSIST_MIRR(ud_stream.ud_mode)) {
347  |      /* Now we can process serials in normal way (upto LAST)*/ 
348  |      if(num_ok==0) upto_last=1;
349  |      /* get delay */
350  |      nrtm_delay=ca_get_srcnrtmdelay(source_hdl);
351  |      /* sleep the delay seconds or untill the shutdown requested */
352  |      SV_sleep(nrtm_delay);
353  |    } /* otherwise - no delay at all */
354  |    
355  |   } /* if do_updates */
356  |   else SV_sleep(TIMEOUT); 
357  |   
358  | 
359  |   TA_delete();
360  |   
361  |  } while((do_server=CO_get_do_server()));  /* main cycle */
362  | 
363  | /*   fclose(ud_stream.log.logfile);*/
364  |    free(source_name);
365  | /* free data associated with nrtm structure */         
366  |  if(nrtm) {
367  |    free(nrtm->server);
368  |    free(nrtm);
369  |  }
370  |  
371  |  /* That's all. Close connection to the DB */ 
372  |  SQ_close_connection(ud_stream.db_connection);
373  |  free(db_host);
374  |  free(db_name);
375  |  free(db_user);
376  |  free(db_passwd);
377  | 
378  |  ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s NRTM client stopped", UD_TAG); 
379  | } /* UD_do_nrtm() */
380  | 
381  | /************************************************************
382  | *  void UD_do_updates()                                     *
383  | *                                                           *
384  | * Processes updates                                         *
385  | *                                                           *
386  | * It cycles accepting connections and processing them       * 
387  | * (interactive server). This assures that there is only     *
388  | * one write thread per database/source.                     *
389  | *                                                           *
390  | ************************************************************/
391  |    
392  | void UD_do_updates(void *arg)
393  | {
394  | int source = (int)arg;
395  | int listening_socket = SV_update_sock[source];
396  | int connected_socket;
397  | UD_stream_t ud_stream;
398  | int do_update=1;
399  | int do_server;
400  | int num_ok;
401  | ca_dbSource_t *source_hdl = ca_get_SourceHandleByPosition(source);
402  | char *db_host, *db_name, *db_user, *db_passwd;
403  | int db_port;
404  | 
405  |   { /* set up the lohgging path */
406  |    /* get source we are going to update */
407  |    char *source_name = ca_get_srcname(source_hdl);  
408  |    int res;
409  |    char *er_ud_def = ca_get_er_ud_def; /* something like 'RIPUPDLOG basename' */
410  |    char er_def[256];
411  |    char *erret = NULL;
412  | 
413  |    sprintf(er_def, "%s %s", er_ud_def, source_name);
414  |    if( (res = ER_macro_spec(er_def, &erret)) != 0 ) {
415  |         fputs(erret, stderr);
416  |         die;
417  |         /* or some other error handling */
418  |    }     
419  |    free(erret); /* the response is allocated and must be freed */
420  |    free(er_ud_def);
421  |    free(source_name);
422  |   } 
423  | 
424  | /* get mode of operation: protected/unprotected (dummy) */
425  |   memset(&ud_stream, 0, sizeof(ud_stream));
426  |   ud_stream.source_hdl=source_hdl;
427  |   ud_stream.ud_mode=ca_get_srcmode(source_hdl);
428  | 
429  |   fprintf(stderr, "Mode of operation:\n");
430  |   if(IS_DUMMY_ALLOWED(ud_stream.ud_mode))fprintf(stderr, "* dummy allowed\n"); 
431  |    else fprintf(stderr, "* dummy not allowed\n");
432  |   if(IS_UPDATE(ud_stream.ud_mode))fprintf(stderr, "* DBupdate\n");
433  |    else fprintf(stderr, "* NRTM\n");
434  |   if(IS_STANDALONE(ud_stream.ud_mode))fprintf(stderr, "* running standalone\n");
435  |    else fprintf(stderr, "* running as a server\n");
436  | 
437  | 
438  | /* get error log facility */
439  |   db_host = ca_get_srcdbmachine(source_hdl);
440  |   db_port = ca_get_srcdbport(source_hdl);
441  |   db_name = ca_get_srcdbname(source_hdl);
442  |   db_user = ca_get_srcdbuser(source_hdl);
443  |   db_passwd = ca_get_srcdbpassword(source_hdl);
444  |   
445  | /* Connect to the database */
446  |   ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s making SQL connection to %s@%s ...", UD_TAG, db_name, db_host);
447  |   ud_stream.db_connection=SQ_get_connection(db_host, db_port, db_name, db_user, db_passwd);
448  |    
449  |   if(! ud_stream.db_connection) {
450  |    ER_perror(FAC_UD, UD_SQL, "no connection to SQL server\n");
451  |    die;
452  |   }
453  |   	
454  | 
455  |   ud_stream.condat.rd_timeout.tv_sec=STREAM_TIMEOUT;
456  |   ud_stream.num_skip=0;
457  |   ud_stream.load_pass=0;
458  |   ud_stream.nrtm=NULL;
459  |  
460  | /*+++ main cycle +++*/
461  | 
462  | do { /* be alive while do_server is 1. do_server is turned off by SIGINT */
463  |  
464  |   /* make a record for thread accounting */
465  |   TA_add(listening_socket, "update");
466  |   TA_setactivity("waiting");
467  |   
468  |  
469  | /* accept connection */
470  |    connected_socket = SK_accept_connection(listening_socket);
471  |    if(connected_socket==-1) break;
472  |    
473  | 
474  |    /* make a record for thread accounting */
475  |    TA_delete(); /* Delete 'waiting' record */
476  |    TA_add(connected_socket, "update");
477  | 
478  | 
479  |    ud_stream.condat.sock = connected_socket;
480  |    ud_stream.condat.rtc = 0;
481  | 
482  |  do_update=CO_get_do_update();
483  |  if(do_update) {
484  |  
485  |    TA_setactivity("suspended");
486  |    
487  |    ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s Connection accepted...", UD_TAG);
488  |   
489  |   ud_stream.log.num_ok=0; 
490  |   ud_stream.log.num_failed=0;
491  |  
492  |   /* Check connection to the database and try to reconnect*/
493  |  if(SQ_ping(ud_stream.db_connection)) {
494  |    ER_perror(FAC_UD, UD_SQL, "%s", SQ_error(ud_stream.db_connection));
495  |    die;
496  |   }
497  | 
498  |   ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s starting processing object", UD_TAG);
499  |   
500  | /***************** process stream ****************/
501  | 
502  |     num_ok=UD_process_stream(&ud_stream);
503  | 
504  | /***************** process stream ****************/    
505  |   
506  |   ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s processing object finished", UD_TAG);
507  |   
508  |   /* close the socket of the NRTM stream */
509  |    close(ud_stream.condat.sock);
510  |     
511  |  }  /* if do_update*/
512  | else { /* Otherwise print a message*/
513  |  /* To display with 'show threads' */
514  |   TA_setactivity("suspended");
515  |  
516  |  }
517  |   /* make a record for thread accounting */
518  |    TA_delete();
519  | 
520  |    do_server=CO_get_do_server();  
521  | 
522  | } while (do_server);  /* main cycle */
523  |   
524  | /*   fclose(ud_stream.log.logfile); */
525  |  /* That's all. Close connection to the DB */ 
526  |  SQ_close_connection(ud_stream.db_connection);
527  |  free(db_host);
528  |  free(db_name);
529  |  free(db_user);
530  |  free(db_passwd);
531  | 
532  | 
533  |  ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s update server stopped", UD_TAG);
534  | } /* UD_do_update() */
535  | 
536  | 
537  | 
538  |