3 * Background reverse name resolution
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
54 /*----- Magic numbers -----------------------------------------------------*/
56 #define BRES_MAX 15 /* Maximum number of resolvers */
57 #define BRES_IDLE 60 /* Lifetime of an idle resolver */
59 /*----- Static variables --------------------------------------------------*/
61 #ifndef BRES_STANDALONE
63 static bres_server servers[BRES_MAX]; /* Statically allocated servers */
65 static bres_server *freelist, *freetail;
66 static bres_client *qhead, *qtail;
67 static sel_state *sel;
68 static const char *server = 0;
70 #define UNLINK(head, tail, p) do { \
71 *((p)->next ? &(p)->next->prev : &(tail)) = (p)->prev; \
72 *((p)->prev ? &(p)->prev->next : &(head)) = (p)->next; \
75 #define LINKHEAD(head, tail, p) do { \
78 *((head) ? &(head)->prev : &(tail)) = (p); \
82 #define LINKTAIL(head, tail, p) do { \
85 *((tail) ? &(tail)->next : &(head)) = (p); \
91 /*----- Background resolver protocol --------------------------------------*/
93 /* --- Requests and responses --- *
95 * There are two types of requests: name and addr, corresponding to the
96 * standard @gethostbyname@ and @gethostbyaddr@ calls. There are two types
97 * of responses too: a positive response consists of an encoded equivalent of
98 * a @struct hostent@ structure containing the requested information; a
99 * negative response consists of an @h_errno@ value explaining the problem.
102 #define BRES_BYNAME 0 /* Request: resolve given name */
103 #define BRES_BYADDR 1 /* Request: resolve given address */
105 #define BRES_HOSTENT 0 /* Response: resolved ok */
106 #define BRES_ERROR 1 /* Response: resolution failed */
108 /* --- Encodings --- *
110 * A string is encoded as a @size_t@ length followed by the actual data. The
111 * null terminator is not transmitted.
113 * Addresses for resolution are transmitted as raw @struct in_addr@
116 * A @hostent@ structure is transmitted as a header containing fixed-size
117 * information, followed by the official name, an array of aliases, and an
118 * array of addresses. The number of items in the arrays is specified in the
121 * The implementation assumes that a complete request or reply is always
122 * sent. Undesirable blocking will occur if this is not the case. Both ends
123 * are assumed to trust each other. A protocol failure results in the child
124 * in question being terminated.
127 typedef struct hostskel {
134 /* --- @doread@, @dowrite@ --- *
136 * Arguments: @int fd@ = file descriptor
137 * @void *buf@ = buffer for data
138 * @size_t sz@ = size of data
140 * Returns: Zero if successful, nonzero otherwise.
142 * Use: Reads or writes a chunk of data. @EINTR@ errors are retried;
143 * incomplete reads and writes are continued from where they
144 * left off. End-of-file is considered an I/O error.
147 static int doread(int fd, void *buf, size_t sz)
151 int r = read(fd, p, sz);
166 static int dowrite(int fd, const void *buf, size_t sz)
170 int r = write(fd, p, sz);
185 /* --- @getstring@ --- *
187 * Arguments: @int fd@ = file descriptor to read
189 * Returns: String in heap-allocated block, or a null pointer.
191 * Use: Decodes a string.
194 static char *getstring(int fd)
199 if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
201 if (doread(fd, p, sz)) {
209 /* --- @putstring@ --- *
211 * Arguments: @int fd@ = file descriptor to write on
212 * @const char *p@ = pointer to string to write
214 * Returns: Zero if successful.
216 * Use: Encodes a string.
219 static int putstring(int fd, const char *p)
221 size_t sz = strlen(p);
222 if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
227 /* --- @gethost@ --- *
229 * Arguments: @int fd@ = file descriptor to read
231 * Returns: Pointer to heap-allocated @struct hostent@, or null.
233 * Use: Decodes a host structure. The resulting structure is all in
234 * one big heap block.
237 #ifndef BRES_STANDALONE
239 static struct hostent *gethost(int fd)
246 /* --- Read the skeleton structure --- */
248 if (doread(fd, &hsk, sizeof(hsk)))
251 /* --- Read the hostname and alias strings --- *
253 * Count the length of the strings as we go.
258 sizeof(struct hostent) +
259 hsk.naddr * hsk.addrsz +
260 (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
262 /* --- Read the primary host name --- */
264 if ((name = getstring(fd)) == 0)
266 sz += strlen(name) + 1;
268 /* --- Read in the alias names --- */
272 if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
274 for (i = 0; i < hsk.nalias; i++)
276 for (i = 0; i < hsk.nalias; i++) {
277 if ((alias[i] = getstring(fd)) == 0)
279 sz += strlen(alias[i]) + 1;
283 /* --- Allocate the output structure --- */
285 if ((h = malloc(sz)) == 0)
289 /* --- Fill in the base structure --- */
291 h->h_addrtype = hsk.addrtype;
292 h->h_length = hsk.addrsz;
294 /* --- Start putting everything else in --- */
297 char **p = (char **)(h + 1);
298 char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
301 /* --- Start with the address table --- */
304 if (doread(fd, a, hsk.naddr * hsk.addrsz))
306 for (i = 0; i < hsk.naddr; i++) {
312 /* --- Finally copy the strings over --- */
314 #define PUT(_p) do { \
315 size_t _len = strlen(_p) + 1; \
316 memcpy(a, (_p), _len); \
324 for (i = 0; i < hsk.nalias; i++) {
337 /* --- Tidy up after various types of failure --- */
342 for (i = 0; i < hsk.nalias && alias[i]; i++)
354 /* --- @puthost@ --- *
356 * Arguments: @int fd@ = file descriptor
357 * @struct hostent *h@ = pointer to host structure
359 * Returns: Zero if successful.
361 * Use: Encodes a host structure.
364 static int puthost(int fd, struct hostent *h)
369 /* --- Fill in and send the skeleton structure --- */
371 for (i = 0; h->h_aliases[i]; i++)
374 for (i = 0; h->h_addr_list[i]; i++)
377 hsk.addrtype = h->h_addrtype;
378 hsk.addrsz = h->h_length;
379 if (dowrite(fd, &hsk, sizeof(hsk)))
382 /* --- Send the name and alias strings --- */
384 if (putstring(fd, h->h_name))
386 for (i = 0; h->h_aliases[i]; i++) {
387 if (putstring(fd, h->h_aliases[i]))
391 /* --- Send the address data --- */
393 for (i = 0; h->h_addr_list[i]; i++) {
394 if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
398 /* --- OK, done --- */
403 /*----- Resolver server ---------------------------------------------------*/
407 * Arguments: @int rfd@ = output file descriptor for resolved hostnames
408 * @int cfd@ = input file descriptor for raw addresses
412 * Use: Asynchronous name resolving process.
415 static void child(int rfd, int cfd)
417 /* --- Close other file descriptors --- */
421 #if defined(_SC_OPEN_MAX)
422 int maxfd = sysconf(_SC_OPEN_MAX);
423 #elif defined(OPEN_MAX)
424 int maxfd = OPEN_MAX;
430 maxfd = 256; /* Fingers crossed... */
431 for (i = 0; i < maxfd; i++) {
432 if (i != rfd && i != cfd && i != 1)
437 signal(SIGTERM, SIG_DFL);
438 signal(SIGHUP, SIG_DFL);
439 signal(SIGQUIT, SIG_DFL);
440 signal(SIGALRM, SIG_DFL);
441 signal(SIGINT, SIG_DFL);
443 /* --- Main request/response loop --- */
449 /* --- Read the request --- */
451 if (doread(cfd, &req, sizeof(req)))
454 /* --- Process it into a host structure --- */
458 /* --- Normal forward lookup --- */
461 char *name = getstring(cfd);
464 h = gethostbyname(name);
468 /* --- Reverse lookup --- */
473 if (doread(cfd, &addr, sizeof(addr)))
475 if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
478 /* --- Do a forward lookup to confirm --- */
481 size_t sz = strlen(h->h_name) + 1;
482 if ((p = malloc(sz)) == 0)
484 memcpy(p, h->h_name, sz);
487 h = gethostbyname(p);
494 for (pp = h->h_addr_list; *pp; pp++) {
496 memcpy(&a, *pp, sizeof(a));
497 if (a.s_addr == addr.s_addr) {
505 h_errno = NO_RECOVERY;
510 /* --- Unknown request -- may have lost sync --- */
516 /* --- Transmit the response --- */
520 if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
524 if (dowrite(rfd, &resp, sizeof(resp)) ||
525 dowrite(rfd, &h_errno, sizeof(h_errno)))
536 * Arguments: @int argc@ = number of command line arguments
537 * @char *argv[]@ = array of arguments
539 * Returns: Runs until killed or an error occurs.
541 * Use: A name resolver server process for mLib programs which need
542 * this sort of thing.
545 #ifdef BRES_STANDALONE
547 int main(int argc, char *argv[])
549 if (isatty(STDIN_FILENO)) {
550 char *p = strrchr(argv[0], '/');
556 "%s: don't run this program unless you know what you're doing.\n",
560 child(STDOUT_FILENO, STDIN_FILENO);
566 /*----- Main code ---------------------------------------------------------*/
568 #ifndef BRES_STANDALONE
572 * Arguments: @bres_server *rs@ = pointer to server block
576 * Use: Kills a server process, reaps the losing child and makes
577 * things generally clean again.
580 static void zap(bres_server *rs)
582 /* --- Close the pipes, kill the child, and reap it --- */
587 kill(rs->kid, SIGTERM);
588 waitpid(rs->kid, 0, 0);
592 /* --- Move the server to the back of the list --- */
594 UNLINK(freelist, freetail, rs);
595 LINKTAIL(freelist, freetail, rs);
598 /* --- @bres_abort@ --- *
600 * Arguments: @bres_client *rc@ = pointer to client block
604 * Use: Removes a queued job.
607 void bres_abort(bres_client *rc)
609 if (rc->q == BRES_BYNAME)
612 UNLINK(qhead, qtail, rc);
614 sel_rmfile(&rc->rs->f);
622 * Arguments: @struct timeval *tv@ = pointer to the current time
623 * @void *vp@ = pointer to a server block
627 * Use: Kills off a child which has been idle for too long.
630 static void idle(struct timeval *tv, void *vp)
632 bres_server *rs = vp;
636 /* --- @answer@ --- *
638 * Arguments: @int fd@ = file descriptor which is ready
639 * @unsigned mode@ = what it's doing now
640 * @void *vp@ = pointer to server block
644 * Use: Retrieves an answer from a name resolver process.
647 static void attach(bres_client */*rc*/);
649 static void answer(int fd, unsigned mode, void *vp)
651 bres_server *rs = vp;
652 bres_client *rc = rs->rc;
653 struct hostent *h = 0;
657 /* --- Report the result to my client --- */
661 if (doread(fd, &resp, sizeof(resp)) == 0) {
664 doread(fd, &h_errno, sizeof(h_errno));
675 if (rc->q == BRES_BYNAME)
685 /* --- Wrap up the various structures --- */
689 LINKHEAD(freelist, freetail, rs);
691 /* --- Tie a timer onto the server block --- */
696 gettimeofday(&tv, 0);
697 tv.tv_sec += BRES_IDLE;
698 sel_addtimer(sel, &rs->t, &tv, idle, rs);
701 /* --- If there are any clients waiting, attach one --- */
705 UNLINK(qhead, qtail, rc);
712 * Arguments: @bres_server *rs@ = pointer to a server block
714 * Returns: Zero if OK, nonzero if something failed.
716 * Use: Starts up a child resolver process.
719 static int start(bres_server *rs)
725 /* --- Make the pipes --- */
732 /* --- Start up the child process --- */
734 if ((kid = fork()) < 0)
741 md[0].cur = cfd[0]; md[0].want = STDIN_FILENO;
742 md[1].cur = rfd[1]; md[1].want = STDOUT_FILENO;
743 if (mdup(md, 2) || execlp(server, server, (char *)0))
744 child(STDOUT_FILENO, STDIN_FILENO);
746 child(rfd[1], cfd[0]);
750 /* --- Fix up everything in the server block --- */
755 sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
759 /* --- Fix up after errors --- */
771 /* --- @attach@ --- *
773 * Arguments: @bres_client *rc@ = pointer to a client block
777 * Use: Attaches a client to a spare server (which is assumed to
781 static void attach(bres_client *rc)
786 /* --- Fix up the server ready for the job --- *
788 * If the server has a process, remove its timer. Otherwise, fork off a
789 * new resolver process. This is also where I go if I find that the child
790 * resolver process has lost while I wasn't looking. Only one attempt at
791 * forking is performed.
799 if (lose || start(rs))
804 /* --- Submit the job to the resolver --- */
807 struct sigaction sa, osa;
810 /* --- Ignore @SIGPIPE@ for now --- *
812 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
815 sa.sa_handler = SIG_IGN;
817 sigemptyset(&sa.sa_mask);
818 sigaction(SIGPIPE, &sa, &osa);
820 /* --- Write the new job to the child --- */
823 if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
825 else switch (rc->q) {
827 if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
831 if (putstring(rs->fd, rc->u.name))
835 sigaction(SIGPIPE, &osa, 0);
837 /* --- Sort out various errors --- *
839 * This was once more complicated, handling @EPIPE@ separately from other
840 * errors. Now everything's handled the same way.
849 /* --- Fiddle with lists so that everything's OK --- */
852 UNLINK(freelist, freetail, rs);
859 if (rc->q == BRES_BYNAME)
863 /* --- @resolve@ --- *
865 * Arguments: @bres_client *rc@ = pointer to filled-in client block
869 * Use: Dispatcher for incoming resolution jobs.
872 static void resolve(bres_client *rc)
874 /* --- If there's a free server, plug it in --- */
880 LINKTAIL(qhead, qtail, rc);
883 /* --- @bres_byaddr@ --- *
885 * Arguments: @bres_client *rc@ = pointer to client block
886 * @struct in_addr addr@ = address to resolve
887 * @void (*func)(struct hostent *h, void *p)@ = handler function
888 * @void *p@ = argument for handler function
892 * Use: Adds an address lookup job to the queue. The job will be
893 * processed when there's a spare resolver process to deal with
897 void bres_byaddr(bres_client *rc, struct in_addr addr,
898 void (*func)(struct hostent */*h*/, void */*p*/),
908 /* --- @bres_byname@ --- *
910 * Arguments: @bres_client *rc@ = pointer to client block
911 * @const char *name@ = name to resolve
912 * @void (*func)(struct hostent *h, void *p)@ = handler function
913 * @void *p@ = argument for handler function
917 * Use: Adds a name lookup job to the queue. The job will be
918 * processed when there's a spare resolver process to deal with
922 void bres_byname(bres_client *rc, const char *name,
923 void (*func)(struct hostent */*h*/, void */*p*/),
927 rc->u.name = xstrdup(name);
933 /* --- @bres_exec@ --- *
935 * Arguments: @const char *file@ = file containing server code or null
939 * Use: Makes `bres' use a standalone server rather than copies of
940 * the current process. This can reduce memory consumption for
941 * large processes, at the expense of startup time (which
942 * shouldn't be too bad anyway, because of the resolver design).
943 * If the filename is null, a default set up at install time is
944 * used. It's probably a good idea to leave it alone.
947 void bres_exec(const char *file)
952 server = BRES_SERVER;
955 /* --- @bres_init@ --- *
957 * Arguments: @sel_state *s@ = pointer to select multiplexor
961 * Use: Initializes the background resolver for use.
964 void bres_init(sel_state *s)
969 for (i = 0; i < BRES_MAX; i++) {
972 LINKTAIL(freelist, freetail, &servers[i]);
978 /*----- That's all, folks -------------------------------------------------*/