2 * copyright (c) 2001-2008 Emil Mikulic.
4 * dns.c: synchronous DNS in a child process.
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
19 #include <sys/param.h>
20 #include <sys/socket.h>
30 static void dns_main(void); /* this is what the child process runs */
32 #define CHILD 0 /* child process uses this socket */
35 static pid_t pid
= -1;
39 int error
; /* h_errno, or 0 if no error */
40 char name
[MAXHOSTNAMELEN
];
44 dns_init(const char *privdrop_user
)
46 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, sock
) == -1)
54 /* We are the child. */
55 privdrop(NULL
/* don't chroot */, privdrop_user
);
58 daemonize_finish(); /* drop our copy of the lifeline! */
59 if (signal(SIGUSR1
, SIG_IGN
) == SIG_ERR
)
60 errx(1, "signal(SIGUSR1, ignore) failed");
62 verbosef("fell out of dns_main()");
65 /* We are the parent. */
68 fd_set_nonblock(sock
[PARENT
]);
69 verbosef("DNS child has PID %d", pid
);
77 return; /* no child was started */
79 if (kill(pid
, SIGINT
) == -1)
81 verbosef("dns_stop() waiting for child");
82 if (waitpid(pid
, NULL
, 0) == -1)
84 verbosef("dns_stop() done waiting for child");
88 RB_ENTRY(tree_rec
) ptree
;
93 tree_cmp(struct tree_rec
*a
, struct tree_rec
*b
)
95 if (a
->ip
< b
->ip
) return (-1); else
96 if (a
->ip
> b
->ip
) return (+1); else
100 static RB_HEAD(tree_t
, tree_rec
) ip_tree
= RB_INITIALIZER(&tree_rec
);
101 /* Quiet warnings. */
102 static struct tree_rec
* tree_t_RB_NEXT(struct tree_rec
*elm
)
104 static struct tree_rec
* tree_t_RB_MINMAX(struct tree_t
*head
, int val
)
106 RB_GENERATE(tree_t
, tree_rec
, ptree
, tree_cmp
)
109 dns_queue(const in_addr_t ip
)
111 struct tree_rec
*rec
;
115 return; /* no child was started - we're not doing any DNS */
117 rec
= xmalloc(sizeof(*rec
));
119 if (RB_INSERT(tree_t
, &ip_tree
, rec
) != NULL
) {
120 /* Already queued - this happens seldom enough that we don't care about
121 * the performance hit of needlessly malloc()ing. */
122 verbosef("already queued %s", ip_to_str(ip
));
127 num_w
= write(sock
[PARENT
], &ip
, sizeof(ip
)); /* won't block */
129 warnx("dns_queue: write: ignoring end of file");
130 else if (num_w
== -1)
131 warn("dns_queue: ignoring write error");
132 else if (num_w
!= sizeof(ip
))
133 err(1, "dns_queue: wrote %d instead of %d",
134 (int)num_w
, (int)sizeof(ip
));
138 dns_unqueue(const in_addr_t ip
)
140 struct tree_rec tmp
, *rec
;
143 if ((rec
= RB_FIND(tree_t
, &ip_tree
, &tmp
)) != NULL
) {
144 RB_REMOVE(tree_t
, &ip_tree
, rec
);
148 verbosef("couldn't unqueue %s - not in queue!", ip_to_str(ip
));
152 * Returns non-zero if result waiting, stores IP and name into given pointers
153 * (name buffer is allocated by dns_poll)
156 dns_get_result(in_addr_t
*ip
, char **name
)
158 struct dns_reply reply
;
161 numread
= read(sock
[PARENT
], &reply
, sizeof(reply
));
164 return (0); /* no input waiting */
169 goto error
; /* EOF */
170 if (numread
!= sizeof(reply
))
171 errx(1, "dns_get_result read got %d, expected %d",
172 (int)numread
, (int)sizeof(reply
));
174 /* Return successful reply. */
176 if (reply
.error
!= 0)
177 xasprintf(name
, "(%s)", hstrerror(reply
.error
));
179 *name
= xstrdup(reply
.name
);
180 dns_unqueue(reply
.ip
);
184 warn("dns_get_result: ignoring read error");
185 /* FIXME: re-align to stream? restart dns child? */
196 return; /* no child was started - we're not doing any DNS */
198 while (dns_get_result(&ip
, &name
)) {
199 /* push into hosts_db */
200 struct bucket
*b
= host_find(ip
);
202 verbosef("resolved %s to %s but it's not in the DB!",
203 ip_to_str(ip
), name
);
206 if (b
->u
.host
.dns
!= NULL
) {
207 verbosef("resolved %s to %s but it's already in the DB!",
208 ip_to_str(ip
), name
);
211 b
->u
.host
.dns
= name
;
215 /* ------------------------------------------------------------------------ */
218 STAILQ_ENTRY(qitem
) entries
;
222 STAILQ_HEAD(qhead
, qitem
) queue
= STAILQ_HEAD_INITIALIZER(queue
);
225 enqueue(const in_addr_t ip
)
229 i
= xmalloc(sizeof(*i
));
231 STAILQ_INSERT_TAIL(&queue
, i
, entries
);
232 verbosef("DNS: enqueued %s", ip_to_str(ip
));
235 /* Return non-zero and populate <ip> pointer if queue isn't empty. */
237 dequeue(in_addr_t
*ip
)
241 i
= STAILQ_FIRST(&queue
);
244 STAILQ_REMOVE_HEAD(&queue
, entries
);
251 xwrite(const int d
, const void *buf
, const size_t nbytes
)
253 ssize_t ret
= write(d
, buf
, nbytes
);
257 if (ret
!= (ssize_t
)nbytes
)
258 err(1, "wrote %d bytes instead of all %d bytes", (int)ret
, (int)nbytes
);
266 #ifdef HAVE_SETPROCTITLE
267 setproctitle("DNS child");
269 fd_set_nonblock(sock
[CHILD
]);
270 verbosef("DNS child entering main DNS loop");
274 if (STAILQ_EMPTY(&queue
)) {
276 fd_set_block(sock
[CHILD
]);
277 verbosef("entering blocking read loop");
280 fd_set_nonblock(sock
[CHILD
]);
281 verbosef("non-blocking poll");
284 /* While we have input to process... */
285 ssize_t numread
= read(sock
[CHILD
], &ip
, sizeof(ip
));
287 exit(0); /* end of file, nothing more to do here. */
289 if (!blocking
&& (errno
== EAGAIN
))
290 break; /* ran out of input */
292 err(1, "DNS: read failed");
294 if (numread
!= sizeof(ip
))
295 err(1, "DNS: read got %d bytes, expecting %d",
296 (int)numread
, (int)sizeof(ip
));
299 /* After one blocking read, become non-blocking so that when we
300 * run out of input we fall through to queue processing.
303 fd_set_nonblock(sock
[CHILD
]);
309 struct dns_reply reply
;
313 he
= gethostbyaddr((char *)&ip
, sizeof(ip
), AF_INET
);
315 /* On some platforms (for example Linux with GLIBC 2.3.3), h_errno
316 * will be non-zero here even though the lookup succeeded.
319 reply
.name
[0] = '\0';
320 reply
.error
= h_errno
;
322 assert(sizeof(reply
.name
) > sizeof(char *)); /* not just a ptr */
323 strlcpy(reply
.name
, he
->h_name
, sizeof(reply
.name
));
326 fd_set_block(sock
[CHILD
]);
327 xwrite(sock
[CHILD
], &reply
, sizeof(reply
));
328 verbosef("DNS: %s is %s", ip_to_str(ip
),
329 (h_errno
== 0)?reply
.name
:hstrerror(h_errno
));
334 /* vim:set ts=3 sw=3 tw=78 expandtab: */