1 /*
2 * main.c
3 *
4 * Copyright 2007-13 VOSTROM Holdings, Inc.
5 * This file is part of the Distribution. See the file COPYING for details.
6 */
7
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include<sys/types.h>
13 #include<sys/socket.h>
14 #include <sys/param.h> /* for NONFILE */
15 #include <sys/stat.h>
16 #include<netinet/in.h>
17 #include<arpa/inet.h>
18 #include <netdb.h>
19 #include <regex.h>
20 #include <time.h>
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <ctype.h>
26 #include <unistd.h>
27
28 #include "geounit.h"
29 #include "tst_malloc.h"
30 #include "logger.h"
31 #include "pwhois_thread.h"
32
33 #ifdef SIGTSTP /* if System is BSD*/
34 #include<sys/file.h>
35 #include<sys/ioctl.h>
36 #endif
37
38 #define DEFAULT_CONFIG "/usr/local/etc/pwhois/pwhoisd.conf"
39 #define DEFAULT_PIDFILE "/var/run/pwhoisd.pid"
40 #define DEFAULT_LOGFILE "/var/log/pwhois/pwhoisd.log"
41 #define DEFAULT_WHOIS_PORT 43
42 #define DEFAULT_UID 65334
43 #define DEFAULT_GID 65334
44
45 #ifndef MAX_PATH
46 #define MAX_PATH 512
47 #endif
48
49 #define DEFAULT_ACLDB_EXPORT_FILENAME "/var/db/pwhois/ACLDB.pwdump"
50 #define DEFAULT_ASNDB_EXPORT_FILENAME "/var/db/pwhois/ASNDB.pwdump"
51 #define DEFAULT_GEODB_EXPORT_FILENAME "/var/db/pwhois/GEODB.pwdump"
52 #define DEFAULT_NETDB_EXPORT_FILENAME "/var/db/pwhois/NETDB.pwdump"
53 #define DEFAULT_ORGDB_EXPORT_FILENAME "/var/db/pwhois/ORGDB.pwdump"
54 #define DEFAULT_POCDB_EXPORT_FILENAME "/var/db/pwhois/POCDB.pwdump"
55 #define DEFAULT_ROUDB_EXPORT_FILENAME "/var/db/pwhois/ROUDB.pwdump"
56
57 char ACLDB_EXPORT_FILENAME[MAX_PATH];
58 char ASNDB_EXPORT_FILENAME[MAX_PATH];
59 char GEODB_EXPORT_FILENAME[MAX_PATH];
60 char NETDB_EXPORT_FILENAME[MAX_PATH];
61 char ORGDB_EXPORT_FILENAME[MAX_PATH];
62 char POCDB_EXPORT_FILENAME[MAX_PATH];
63 char ROUDB_EXPORT_FILENAME[MAX_PATH];
64
65 int FASTLOAD=0;
66
67 char reportpath[MAX_PATH];
68
69 char VERSION[]="2.2.1.0";
70 char PROGNAME[]="Prefix WhoIs";
71 char PROGNAMESHORT[]="pwhoisd";
72 char COPYRIGHT[]="Copyright (c) 2005-14 VOSTROM Holdings, Inc." ;
73
74 /* options descriptor */
75 static struct option longopts[] =
76 {
77 {"help", no_argument, NULL, 'h'},
78 {"verbose", no_argument, NULL, 'v'},
79 {"version", no_argument, NULL, 'V'},
80 {"logfile", required_argument, NULL, 'l'},
81 {"configfile", required_argument, NULL, 'c'},
82 {"daemon", no_argument, NULL, 'd'},
83 {"pidfile", required_argument, NULL, '0'},
84 {"port", required_argument, NULL, 'p'},
85 {"bind", required_argument, NULL, 'b'},
86 {"uid", required_argument, NULL, 'u'},
87 {"gid", required_argument, NULL, 'g'},
88 {"limit-max-queries",required_argument,NULL, 'm'},
89 {"no-load", no_argument, NULL, '1'},
90 {"router-id", required_argument, NULL, 'r'},
91 {"report", required_argument, NULL, 'R'},
92 {NULL, 0, NULL, 0}
93 };
94
95 static char PID_FileName[MAX_PATH]=DEFAULT_PIDFILE;
96 static int LISTEN_QUEUE_LENGTH=5;
97 static int THREADS_POOL_LENGTH=20;
98 //static pwhois_thread_cb * threads_pool=0;
99
usage()100 void usage()
101 {
102 printf("usage: pwhois [*options*]\n\n"
103 " -h, --help display this help and exit\n"
104 " -v, --verbose be verbose about what you do (add more -v's to increase verbosity: above v=2 is considered debug)\n"
105 " -V, --version output version information and exit\n"
106 " -l, --logfile f write misc progress output to logfile instead of stdout\n"
107 " -c, --configfile f read startup settings from configuration file: default is %s \n"
108 " -d, --daemon start in the background\n"
109 " --pidfile use alternative PID file location: default is %s\n"
110 " -p, --port <n> port number to listen on: defaults to %d\n"
111 " --b|bind <ip> the IP address to bind on: defaults to all interfaces (*)\n"
112 " -u, --uid the effective user to run as \n"
113 " -g, --gid the effective group to run as \n"
114 " -R, --report path write rotating CSV reports of activity into directory"
115 " --limit-max-queries <n> The maximum number of queries (per IP/per day) default is %d\n"
116 " -r, --router-id <id> the router id to use for this server; useful if there is more that one set of data\n"
117 " in the database and the server should only serve responses from one set of data.\n"
118 " --no-load Do not load data -- for testing purposes\n",
119 DEFAULT_CONFIG, DEFAULT_PIDFILE, DEFAULT_WHOIS_PORT, DEFAULT_MAX_QUERIES);
120
121 exit(0);
122 }
123
getVersion()124 void getVersion()
125 {
126 printf("%s (%s) %s\n\n",PROGNAME,VERSION,COPYRIGHT);
127 exit(0);
128 }
129
all_digits(register char const * const s)130 static int all_digits (register char const *const s)
131 {
132 register char const *r;
133
134 for (r = s; *r; r++)
135 if (!isdigit (*r))
136 return 0;
137 return 1;
138 }
139
readLine(FILE * f,char * buf,int bfsz)140 void readLine(FILE * f, char * buf, int bfsz)
141 {
142 int i;
143 for(i=0;i<bfsz;i++)
144 {
145 if(fread(buf+i,1,1,f)<1 || buf[i]=='\r' || buf[i]=='\n')
146 {
147 buf[i]=0;
148 break;
149 }
150 }
151 }
152
readConfigFile(char * fname)153 static void readConfigFile(char * fname)
154 {
155 regex_t re;
156 regex_t comment;
157 regex_t blank;
158 regmatch_t args[10];
159 int status,itmp;
160 FILE * cfgFile;
161 char cfgLine[1024], param[512], value[512], tmp;
162
163 cfgLine[1023]=0;
164
165 status=regcomp(&re, "^ *([A-Za-z0-9_.-]+) *= *\"?([^\"]*)\"? ?$", REG_EXTENDED|REG_ICASE);
166 status=regcomp(&comment,"^ *#.*$",REG_EXTENDED|REG_ICASE);
167 status=regcomp(&blank,"^ +$",REG_EXTENDED|REG_ICASE);
168 cfgFile=fopen(fname,"r");
169 if(!cfgFile)
170 {
171 fprintf(stderr,"Can't open config file %s \n\n",fname);
172 exit(0);
173 }
174 strcpy(ACLDB_EXPORT_FILENAME,DEFAULT_ACLDB_EXPORT_FILENAME);
175 strcpy(ASNDB_EXPORT_FILENAME,DEFAULT_ASNDB_EXPORT_FILENAME);
176 strcpy(GEODB_EXPORT_FILENAME,DEFAULT_GEODB_EXPORT_FILENAME);
177 strcpy(NETDB_EXPORT_FILENAME,DEFAULT_NETDB_EXPORT_FILENAME);
178 strcpy(ORGDB_EXPORT_FILENAME,DEFAULT_ORGDB_EXPORT_FILENAME);
179 strcpy(POCDB_EXPORT_FILENAME,DEFAULT_POCDB_EXPORT_FILENAME);
180 strcpy(ROUDB_EXPORT_FILENAME,DEFAULT_ROUDB_EXPORT_FILENAME);
181 while(!feof(cfgFile))
182 {
183 readLine(cfgFile, cfgLine, 1023);
184 if(!strlen(cfgLine) || !regexec(&blank, cfgLine, 10, args, 0) || !regexec(&comment, cfgLine, 10, args, 0))
185 continue;
186 if((status=regexec(&re, cfgLine, 10, args, 0))!=0)
187 continue;
188 tmp=cfgLine[args[1].rm_eo];
189 cfgLine[args[1].rm_eo]=0;
190 strcpy(param,cfgLine+args[1].rm_so);
191 cfgLine[args[1].rm_eo]=tmp;
192
193 tmp=cfgLine[args[2].rm_eo];
194 cfgLine[args[2].rm_eo]=0;
195 strcpy(value,cfgLine+args[2].rm_so);
196 cfgLine[args[2].rm_eo]=tmp;
197
198 if(!strcmp(param,"pwhoisd.listenq"))
199 {
200 itmp=atoi(value);
201 if(itmp>=5)
202 LISTEN_QUEUE_LENGTH=itmp;
203 }
204 else if(!strcmp(param,"pwhoisd.threadsq"))
205 {
206 itmp=atoi(value);
207 if(itmp>=2)
208 THREADS_POOL_LENGTH=itmp;
209 }
210 else if(!strcmp(param,"fastload.acldb"))
211 strncpy(ACLDB_EXPORT_FILENAME,value,MAX_PATH);
212 else if(!strcmp(param,"fastload.asndb"))
213 strncpy(ASNDB_EXPORT_FILENAME,value,MAX_PATH);
214 else if(!strcmp(param,"fastload.geodb"))
215 strncpy(GEODB_EXPORT_FILENAME,value,MAX_PATH);
216 else if(!strcmp(param,"fastload.netdb"))
217 strncpy(NETDB_EXPORT_FILENAME,value,MAX_PATH);
218 else if(!strcmp(param,"fastload.orgdb"))
219 strncpy(ORGDB_EXPORT_FILENAME,value,MAX_PATH);
220 else if(!strcmp(param,"fastload.pocdb"))
221 strncpy(POCDB_EXPORT_FILENAME,value,MAX_PATH);
222 else if(!strcmp(param,"fastload.roudb"))
223 strncpy(ROUDB_EXPORT_FILENAME,value,MAX_PATH);
224 else if(!strcmp(param,"fastload"))
225 {
226 itmp=atoi(value);
227 if(itmp)
228 FASTLOAD=1;
229 else
230 FASTLOAD=0;
231 }
232 }
233 fclose(cfgFile);
234 regfree(&re);
235 }
236
Daemonize()237 void Daemonize()
238 {
239 FILE *pidFp;
240 register int childpid, fd;
241 if(getppid()==1)
242 {
243 for(fd =0;fd<NOFILE; fd++)
244 {
245 close(fd);
246 }
247 errno=0;
248 chdir("/");
249 umask(0);
250 }
251 /*
252 Ignore the terminal signal for BSD
253 */
254 #ifdef SIGTTOU
255 signal(SIGTTOU,SIG_IGN);
256 #endif
257 #ifdef SIGTTIN
258 signal(SIGTTIN, SIG_IGN);
259 #endif
260 #ifdef SIGTSTP
261 signal(SIGTSTP,SIG_IGN);
262 #endif
263 /*
264 fork the process and exit from the parent .Let the daemon start in child
265 */
266 if((childpid =fork())>0)
267 {
268 //Parent process
269 pidFp= fopen(PID_FileName, "w+");
270 if(pidFp == NULL)
271 {
272 printf("Could not open file %s", PID_FileName);
273 }
274 else
275 {
276 printf("pid= %d \n",childpid);
277 fprintf(pidFp,"%d\n",childpid);
278 fclose(pidFp);
279 }
280 exit(0);
281 }
282 //child process
283 if (setsid() < 0)
284 {
285 printf("Can't change the process group\n");
286 }
287 }
288
sigTermination(int stub)289 void sigTermination(int stub)
290 {
291 (void)stub;
292 log_printf(0, "Terminate pwhoisd\n");
293 closeLogger();
294 exit(0);
295 }
296
StartServer(uint32_t bindaddr,int port,int uid,int gid)297 int StartServer(uint32_t bindaddr, int port, int uid, int gid)
298 {
299 int listenfd;
300 int sockopt;
301 struct sockaddr_in serv_addr;
302 struct linger fix_ling;
303
304 signal(SIGURG,SIG_IGN);
305 signal(SIGPIPE,SIG_IGN);
306 //Open TCPIP socket
307 if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
308 {
309 log_printf(0, "Server:can't open stream socket: (errno: %d).\n", errno);
310 return errno;
311 }
312 fix_ling.l_onoff=1;
313 fix_ling.l_linger=1;
314 if(setsockopt(listenfd,SOL_SOCKET,SO_LINGER,&fix_ling,sizeof(fix_ling))<0)
315 {
316 log_printf(0,"Server can not setsockopt (SO_LINGER): (errno: %d).\n", errno);
317 return errno;
318 }
319 sockopt=1;
320 if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&sockopt,sizeof(sockopt))<0)
321 {
322 log_printf(0,"Server can not setsockopt (SO_REUSEADDR): (errno: %d).\n", errno);
323 return errno;
324 }
325 #ifdef SO_REUSEPORT
326 sockopt=1;
327 if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEPORT,&sockopt,sizeof(sockopt))<0)
328 {
329 log_printf(0,"Server can not setsockopt (SO_REUSEPORT): (errno: %d).\n", errno);
330 return errno;
331 }
332 #endif
333 /*sockopt=1;
334 if(setsockopt(listenfd,SOL_SOCKET,SO_KEEPALIVE,&sockopt,sizeof(sockopt))<0)
335 {
336 log_printf(0,"Server can not setsockopt (SO_KEEPALIVE): (errno: %d).\n", errno);
337 return errno;
338 }*/
339 bzero((char*)&serv_addr,sizeof(serv_addr));
340 serv_addr.sin_family= AF_INET;
341 serv_addr.sin_addr.s_addr= bindaddr;
342 serv_addr.sin_port= htons(port);
343 if(bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
344 {
345 log_printf(0,"Server can not bind to the local address: (errno: %d).\n", errno);
346 return errno;
347 }
348 listen(listenfd,LISTEN_QUEUE_LENGTH);
349 setuid(uid);
350 setgid(gid);
351 //save port for getVersion request
352 save_listen_port(port);
353 initialize_threads(listenfd, sizeof(serv_addr), THREADS_POOL_LENGTH);
354 return 0;
355 }
356
main(int argc,char * argv[])357 int main (int argc, char * argv[])
358 {
359 int ch, option_errors=0, portno=DEFAULT_WHOIS_PORT, runasdaemon=0;
360 int get_version_request=0;
361 int uid=DEFAULT_UID, gid=DEFAULT_GID, no_load=0, filt_by_router=0, router_id=0;
362 char logpath[MAX_PATH];
363 char cfgpath[MAX_PATH];
364 //char pidpath[MAX_PATH];
365 struct in_addr bindaddr;
366
367 //Initialize default values
368 bindaddr.s_addr=INADDR_ANY;
369 reportpath[0]='\0';
370 strncpy(cfgpath,DEFAULT_CONFIG,MAX_PATH);
371 strncpy(logpath,DEFAULT_LOGFILE,MAX_PATH);
372
373 while((ch = getopt_long(argc, argv, "hvVl:R:c:dp:b:u:g:m:r:", longopts, NULL)) != -1)
374 {
375 switch(ch)
376 {
377 case 'R':
378 strncpy(reportpath,optarg,MAX_PATH);
379 break;
380 case 'v':
381 inc_verbose_level();
382 break;
383 case 'V':
384 //we will execute this request later because we need get all command line arguments
385 get_version_request=1;
386 break;
387 case 'l':
388 strncpy(logpath,optarg,MAX_PATH);
389 break;
390 case 'c':
391 strncpy(cfgpath,optarg,MAX_PATH);
392 break;
393 case 'd':
394 runasdaemon=1;
395 break;
396 case '0':
397 strncpy(PID_FileName,optarg,MAX_PATH);
398 break;
399 case 'p':
400 if (strlen (optarg) == 0 || !all_digits (optarg))
401 {
402 fprintf(stderr, "Invalid argument for -p option: %s\n\n", optarg);
403 option_errors++;
404 break;
405 }
406 portno = atoi (optarg);
407 if (portno <= 0 || portno >= 65536)
408 {
409 fprintf(stderr, "Invalid argument for -p option: %s\n\n", optarg);
410 option_errors++;
411 }
412 break;
413 case 'b':
414 if(!inet_aton(optarg,&bindaddr))
415 {
416 fprintf(stderr,"Can't parse ip (%s)\n\n",optarg);
417 option_errors++;
418 }
419 break;
420 case 'u':
421 if (strlen (optarg) == 0 || !all_digits (optarg))
422 {
423 fprintf(stderr, "Invalid argument for -u option: %s\n\n", optarg);
424 option_errors++;
425 break;
426 }
427 uid = atoi (optarg);
428 if (uid == 0 || uid >= 65536)
429 {
430 fprintf(stderr, "Invalid argument for -u option: %s\n\n", optarg);
431 option_errors++;
432 }
433 break;
434 case 'g':
435 if (strlen (optarg) == 0 || !all_digits (optarg))
436 {
437 fprintf(stderr, "Invalid argument for -g option: %s\n\n", optarg);
438 option_errors++;
439 break;
440 }
441 gid = atoi (optarg);
442 if (gid == 0 || gid >= 65536)
443 {
444 fprintf(stderr, "Invalid argument for -g option: %s\n\n", optarg);
445 option_errors++;
446 }
447 break;
448 case 'm':
449 if (strlen (optarg) == 0 || !all_digits (optarg))
450 {
451 fprintf(stderr, "Invalid argument for -m option: %s\n\n", optarg);
452 option_errors++;
453 break;
454 }
455 setQueriesLimit(atoi(optarg));
456 break;
457 case '1':
458 no_load=1;
459 break;
460 case 'r':
461 if (strlen (optarg) == 0 || !all_digits (optarg))
462 {
463 fprintf(stderr, "Invalid argument for -r option: %s\n\n", optarg);
464 option_errors++;
465 break;
466 }
467 router_id = atoi (optarg);
468 filt_by_router=1;
469 break;
470 case 'h':
471 default:
472 usage();
473 break;
474 }
475 }
476 if(option_errors)
477 usage();
478 if(get_version_request)
479 getVersion();
480 readConfigFile(cfgpath);
481 if (!FASTLOAD)
482 {
483 fprintf(stderr, "Fastload option MUST be set!\n");
484 return 1;
485 }
486 if(runasdaemon)
487 {
488 //daemonize here, before database loading
489 Daemonize();
490 }
491 if(initLogger(logpath)!=0)
492 {
493 fprintf(stderr, "Can't open logfile: %s\n\n", logpath);
494 exit(0);
495 }
496 //load ACL
497 loadACL_fromFile(ACLDB_EXPORT_FILENAME);
498 //Start server here
499 if(StartServer(bindaddr.s_addr, portno, uid, gid))
500 return 0;
501 signal(SIGINT, sigTermination);
502 signal(SIGHUP, databaseReload);
503 signal(SIGTERM, sigTermination);
504 signal(SIGKILL, sigTermination);
505 signal(SIGUSR1, ACL_Reload_fromFile);
506 signal(SIGUSR2, ACL_Reload_fromFile);
507 signal(SIGQUIT,SIG_IGN); // Ignore the SIGQUIT signal
508 SetupFilterParameters(filt_by_router, router_id);
509 if(no_load)
510 EnableEchoServer();
511 else
512 databaseReload(0);
513 //suspend main thread
514 while(1)
515 pause();
516 return 0;
517 }
518