1 /*
2  * common.c - includes global variables and functions.
3  *
4  * Copyright (C) 1998 Brad M. Garcia <garsh@home.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <signal.h>
30 #include <sys/types.h>
31 #include <stdlib.h>
32 #include <assert.h>
33 #include <string.h>
34 #include <errno.h>
35 #include "common.h"
36 #include "lib.h"
37 #include "dns.h"
38 
39 #ifdef DEBUG
40 #define OPT_DEBUG 1
41 #else
42 #define OPT_DEBUG 0
43 #endif /* DEBUG */
44 
45 
46 /*
47  * These are all the global variables.
48  */
49 unsigned char
50        opt_debug = OPT_DEBUG;
51 int                 opt_serv = 0;
52 const char*         progname = 0;
53 
54 #ifdef ENABLE_PIDFILE
55 #if defined(__sun__)
56 const char*         pid_file = "/var/tmp/dnrd.pid";
57 #else
58 const char*         pid_file = "/var/run/dnrd.pid";
59 #endif
60 #endif
61 
62 int                 isock = -1;
63 #ifdef ENABLE_TCP
64 int                 tcpsock = -1;
65 #endif
66 int                 select_timeout = SELECT_TIMEOUT;
67 int                 forward_timeout = FORWARD_TIMEOUT;
68 //int                 load_balance = 0;
69 #ifndef __CYGWIN__
70 uid_t               daemonuid = 0;
71 gid_t               daemongid = 0;
72 char                dnrd_user[256] = "dnrd";
73 char                dnrd_group[256] = "dnrd";
74 #endif
75 const char*         version = PACKAGE_VERSION;
76 int                 gotterminal = 1; /* 1 if attached to a terminal */
77 sem_t               dnrd_sem;  /* Used for all thread synchronization */
78 
79 int                 reactivate_interval = REACTIVATE_INTERVAL;
80 int                 stats_interval = 0;
81 int                 stats_reset = 1;
82 
83 /* The path where we chroot. All config files are relative this path */
84 char                dnrd_root[512] = DNRD_ROOT;
85 
86 char                config_file[512] = DNRD_ROOT "/" CONFIG_FILE;
87 
88 domnode_t           *domain_list;
89 /* turn this on to skip cache hits from responses of inactive dns servers */
90 int                 ignore_inactive_cache_hits = 0;
91 
92 /* highest socket number */
93 int                 maxsock;
94 
95 /* maximum number of open sockets. If we have this amount of
96    concurrent queries, we start dropping new ones */
97 int max_sockets = 200;
98 
99 /* the fd set. query modifies this so we make it global */
100 fd_set              fdmaster;
101 
102 
103 
104 /*
105  * This is the address we listen on.  It gets initialized to INADDR_ANY,
106  * which means we listen on all local addresses.  Both the address and
107  * the port can be changed through command-line options.
108  */
109 /*
110 #if defined(__sun__)
111 struct sockaddr_in recv_addr = { AF_INET, 53, { { {0, 0, 0, 0} } } };
112 #else
113 struct sockaddr_in recv_addr = { AF_INET, 53, { INADDR_ANY } };
114 #endif
115 */
116 
117 /* init recv_addr in main.c instead of here */
118 struct sockaddr_in recv_addr;
119 
120 #ifdef ENABLE_PIDFILE
121 /* check if a pid is running
122  * from the unix faq
123  * http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC18
124  */
125 
isrunning(int pid)126 int isrunning(int pid) {
127   if (kill(pid, 0) ) {
128     if (errno==EPERM) {
129       return 1;
130     } else return 0;
131   } else {
132     return 1;
133   }
134 }
135 
136 /* wait_for_exit()
137  *
138  * In: pid     - the process id to wait for
139  *     timeout - maximum time to wait in 1/100 secs
140  *
141  * Returns: 1 if it died in before timeout
142  *
143  * Abstract: Check if a process is running and wait til it does not
144  */
wait_for_exit(int pid,int timeout)145 int wait_for_exit(int pid, int timeout) {
146   while (timeout--) {
147     if (! isrunning(pid)) return 1;
148     usleep(10000);
149   }
150   /* ouch... we timed out */
151   return 0;
152 }
153 
154 /*
155  * kill_current()
156  *
157  * Returns: 1 if a currently running dnrd was found & killed, 0 otherwise.
158  *
159  * Abstract: This function sees if pid_file already exists and, if it does,
160  *           will kill the current dnrd process and remove the file.
161  */
kill_current()162 int kill_current()
163 {
164     int         pid;
165     int         retn;
166     struct stat finfo;
167     FILE*       filep;
168 
169     if (stat(pid_file, &finfo) != 0) return 0;
170 
171     filep = fopen(pid_file, "r");
172     if (!filep) {
173 	log_msg(LOG_ERR, "%s: Can't open %s\n", progname, pid_file);
174 	exit(-1);
175     }
176     if ((retn = (fscanf(filep, "%i%*s", &pid) == 1))) {
177         kill(pid, SIGTERM);
178 	/* dnrd gets 4 seconds to die or we give up */
179 	if (!wait_for_exit(pid, 400)) {
180 	  log_msg(LOG_ERR, "The dnrd process didn't die within 4 seconds");
181 	}
182     }
183     fclose(filep);
184     unlink(pid_file);
185     return retn;
186 }
187 #endif /* ENABLE_PIDFILE*/
188 
get_typestr(int type)189 const char *get_typestr(int type) {
190 	static const char *EMERG = "EMERG: ";
191 	static const char *ALERT = "ALERT: ";
192 	static const char *CRIT = "CRIT:  ";
193 	static const char *ERR = "ERROR: ";
194 	static const char *WARNING = "Warning: ";
195 	static const char *NOTICE = "Notice: ";
196 	static const char *INFO = "Info:  ";
197 	static const char *DEBUG = "Debug: ";
198 	static const char *EMPTY = "";
199 
200 	switch (type) {
201 	  case LOG_EMERG:   return EMERG;
202 	  case LOG_ALERT:   return ALERT;
203 	  case LOG_CRIT:    return CRIT;
204 	  case LOG_ERR:     return ERR;
205 	  case LOG_WARNING: return WARNING;
206 	  case LOG_NOTICE:  return NOTICE;
207 	  case LOG_INFO:    return INFO;
208 	  case LOG_DEBUG:   return DEBUG;
209 	default:          return EMPTY;
210 	}
211 }
212 
213 
214 /*
215  * log_msg()
216  *
217  * In:      type - a syslog priority
218  *          fmt  - a formatting string, ala printf.
219  *          ...  - other printf-style arguments.
220  *
221  * Sends a message to stdout or stderr if attached to a terminal, otherwise
222  * it sends a message to syslog.
223  */
log_msg(int type,const char * fmt,...)224 void log_msg(int type, const char *fmt, ...)
225 {
226     va_list ap;
227 
228     va_start(ap, fmt);
229 
230     if (gotterminal) {
231 			fprintf(stderr, get_typestr(type));
232 			vfprintf(stderr, fmt, ap);
233 			if (fmt[strlen(fmt) - 1] != '\n') fprintf(stderr, "\n");
234     }
235     else {
236 			vsyslog(type, fmt, ap);
237     }
238     va_end(ap);
239 }
240 
241 /*
242  * log_debug()
243  *
244  * In:      fmt - a formatting string, ala printf.
245  *          ... - other printf-style arguments.
246  *
247  * Abstract: If debugging is turned on, this will send the message
248  *           to syslog with LOG_DEBUG priority.
249  */
log_debug(int level,const char * fmt,...)250 void log_debug(int level, const char *fmt, ...)
251 {
252     va_list ap;
253 
254     if (opt_debug < level) return;
255 
256     va_start(ap, fmt);
257     if (gotterminal) {
258 	fprintf(stderr, "Debug: ");
259 	vfprintf(stderr, fmt, ap);
260 	if (fmt[strlen(fmt) - 1] != '\n') fprintf(stderr, "\n");
261     }
262     else {
263 	vsyslog(LOG_DEBUG, fmt, ap);
264     }
265     va_end(ap);
266 }
267 
268 /*
269  * cleanexit()
270  *
271  * In:      status - the exit code.
272  *
273  * Abstract: This closes our sockets, removes /var/run/dnrd.pid,
274  *           and then calls exit.
275  */
cleanexit(int status)276 void cleanexit(int status)
277 {
278   /*    int i;*/
279 
280     /* Only let one process run this code) */
281     sem_wait(&dnrd_sem);
282 
283     log_debug(1, "Shutting down...\n");
284     if (isock >= 0) close(isock);
285 #ifdef ENABLE_TCP
286     if (tcpsock >= 0) close(tcpsock);
287 #endif
288     /*
289     for (i = 0; i < serv_cnt; i++) {
290 	close(dns_srv[i].sock);
291     }
292     */
293     destroy_domlist(domain_list);
294     exit(status);
295 }
296 
297 
298 /*
299  * log_err_exit()
300  *
301  * In:      exitcode - the exitcode returned
302  *          fmt  - a formatting string, ala printf.
303  *          ...  - other printf-style arguments.
304  *
305  * Sends a message to log_msg, LOG_ERR and exit clean
306  */
log_err_exit(int exitcode,const char * fmt,...)307 void log_err_exit(int exitcode, const char *fmt, ...)
308 {
309     va_list ap;
310     va_start(ap, fmt);
311 
312     if (gotterminal) {
313 			fprintf(stderr, get_typestr(LOG_ERR));
314 			vfprintf(stderr, fmt, ap);
315 			if (fmt[strlen(fmt) - 1] != '\n') fprintf(stderr, "\n");
316     }
317     else {
318 			vsyslog(LOG_ERR, fmt, ap);
319     }
320     va_end(ap);
321 		cleanexit(exitcode);
322 }
323 
324 
325 
326 /*
327  * make_cname()
328  *
329  * In:       text - human readable domain name string
330  *
331  * Returns:  Pointer to the allocated, filled in character string on success,
332  *           NULL on failure.
333  *
334  * Abstract: converts the human-readable domain name to the DNS CNAME
335  *           form, where each node has a length byte followed by the
336  *           text characters, and ends in a null byte.  The space for
337  *           this new representation is allocated by this function.
338  */
make_cname(const char * text,const int maxlen)339 char* make_cname(const char *text, const int maxlen)
340 {
341   /* this kind of code can easily contain buffer overflow.
342      I have checked it and double checked it so I believe it does not.
343      Natanael */
344     const char *tptr = text;
345     const char *end = text;
346     char *cname = (char*)allocate(strnlen(text, maxlen) + 2);
347     char *cptr = cname;
348 
349     while (*end != 0) {
350 	size_t diff;
351 	end = strchr(tptr, '.');
352 	if (end == NULL) end = text + strnlen(text, maxlen);
353 	if (end <= tptr) {
354 	    free(cname);
355 	    return NULL;
356 	}
357 	diff = end - tptr;
358 	*cptr++ = diff;
359 	memcpy(cptr, tptr, diff);
360 	cptr += diff;
361 	tptr = end + 1;
362     }
363     *cptr = 0;
364     assert((unsigned)(cptr - cname) == strnlen(text, maxlen) + 1);
365     return cname;
366 }
367 
368 /* convert cname to ascii and return a static buffer */
369 /* this func must *never* be called with an incomming DNS
370    packet as input. Never. */
cname2asc(const char * cname)371 char *cname2asc(const char *cname) {
372   static char buf[256];
373   /* Note: we don't really check the size of the incomming cname. but
374      according to RFC 1035 a name must not be bigger than 255 octets.
375    */
376   if (cname)
377     snprintf_cname((char *)cname, strlen(cname), 0, buf, sizeof(buf));
378   else
379     strncpy(buf, "(default)", sizeof(buf));
380   return buf;
381 }
382