3 * $Id: bres.c,v 1.6 2001/06/22 19:33:38 mdw Exp $
5 * Background reverse name resolution
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the mLib utilities library.
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.6 2001/06/22 19:33:38 mdw
34 * Hack if there is no @_SC_OPEN_MAX@ or @OPEN_MAX@.
36 * Revision 1.5 2001/02/03 16:21:08 mdw
37 * Bug fixes: restore signals to their default dispositions, and set up the
38 * addresses properly when unpacking @hostent@ structures.
40 * Revision 1.4 2000/08/15 17:35:02 mdw
41 * (gethost, and others): Since @gethost@ actually uses @malloc@ rather
42 * than @xmalloc@, it's wrong to use @xfree@ on the result. Fixed the code
43 * to use the right freeing function on the right data.
45 * Revision 1.3 2000/06/17 10:38:35 mdw
46 * Track changes to selbuf interface.
48 * Revision 1.2 1999/10/30 11:28:39 mdw
49 * Fix include error, pointed out by Chris Rutter.
51 * Revision 1.1 1999/10/04 21:40:42 mdw
52 * Added background resolver from `fw'.
56 /*----- Header files ------------------------------------------------------*/
64 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <netinet/in.h>
71 #include <arpa/inet.h>
79 /*----- Magic numbers -----------------------------------------------------*/
81 #define BRES_MAX 15 /* Maximum number of resolvers */
82 #define BRES_IDLE 60 /* Lifetime of an idle resolver */
84 /*----- Static variables --------------------------------------------------*/
86 #ifndef BRES_STANDALONE
88 static bres_server servers
[BRES_MAX
]; /* Statically allocated servers */
90 #define FREE ((bres_server *)&freelist)
91 static struct { bres_server
*next
, *prev
; } freelist
= { FREE
, FREE
};
93 #define QUEUE ((bres_client *)&queue)
94 static struct { bres_client
*next
, *prev
; } queue
= { QUEUE
, QUEUE
};
96 static sel_state
*sel
;
98 static const char *server
= 0;
102 /*----- Background resolver protocol --------------------------------------*/
104 /* --- Requests and responses --- *
106 * There are two types of requests: name and addr, corresponding to the
107 * standard @gethostbyname@ and @gethostbyaddr@ calls. There are two types
108 * of responses too: a positive response consists of an encoded equivalent of
109 * a @struct hostent@ structure containing the requested information; a
110 * negative response consists of an @h_errno@ value explaining the problem.
113 #define BRES_BYNAME 0 /* Request: resolve given name */
114 #define BRES_BYADDR 1 /* Request: resolve given address */
116 #define BRES_HOSTENT 0 /* Response: resolved ok */
117 #define BRES_ERROR 1 /* Response: resolution failed */
119 /* --- Encodings --- *
121 * A string is encoded as a @size_t@ length followed by the actual data. The
122 * null terminator is not transmitted.
124 * Addresses for resolution are transmitted as raw @struct in_addr@
127 * A @hostent@ structure is transmitted as a header containing fixed-size
128 * information, followed by the official name, an array of aliases, and an
129 * array of addresses. The number of items in the arrays is specified in the
132 * The implementation assumes that a complete request or reply is always
133 * sent. Undesirable blocking will occur if this is not the case. Both ends
134 * are assumed to trust each other. A protocol failure results in the child
135 * in question being terminated.
138 typedef struct hostskel
{
145 /* --- @doread@, @dowrite@ --- *
147 * Arguments: @int fd@ = file descriptor
148 * @void *buf@ = buffer for data
149 * @size_t sz@ = size of data
151 * Returns: Zero if successful, nonzero otherwise.
153 * Use: Reads or writes a chunk of data. @EINTR@ errors are retried;
154 * incomplete reads and writes are continued from where they
155 * left off. End-of-file is considered an I/O error.
158 static int doread(int fd
, void *buf
, size_t sz
)
162 int r
= read(fd
, p
, sz
);
177 static int dowrite(int fd
, const void *buf
, size_t sz
)
181 int r
= write(fd
, p
, sz
);
196 /* --- @getstring@ --- *
198 * Arguments: @int fd@ = file descriptor to read
200 * Returns: String in heap-allocated block, or a null pointer.
202 * Use: Decodes a string.
205 static char *getstring(int fd
)
210 if (doread(fd
, &sz
, sizeof(sz
)) || (p
= malloc(sz
+ 1)) == 0)
212 if (doread(fd
, p
, sz
)) {
220 /* --- @putstring@ --- *
222 * Arguments: @int fd@ = file descriptor to write on
223 * @const char *p@ = pointer to string to write
225 * Returns: Zero if successful.
227 * Use: Encodes a string.
230 static int putstring(int fd
, const char *p
)
232 size_t sz
= strlen(p
);
233 if (dowrite(fd
, &sz
, sizeof(sz
)) || dowrite(fd
, p
, sz
))
238 /* --- @gethost@ --- *
240 * Arguments: @int fd@ = file descriptor to read
242 * Returns: Pointer to heap-allocated @struct hostent@, or null.
244 * Use: Decodes a host structure. The resulting structure is all in
245 * one big heap block.
248 #ifndef BRES_STANDALONE
250 static struct hostent
*gethost(int fd
)
257 /* --- Read the skeleton structure --- */
259 if (doread(fd
, &hsk
, sizeof(hsk
)))
262 /* --- Read the hostname and alias strings --- *
264 * Count the length of the strings as we go.
269 sizeof(struct hostent
) +
270 hsk
.naddr
* hsk
.addrsz
+
271 (hsk
.naddr
+ hsk
.nalias
+ 2) * sizeof(char *);
273 /* --- Read the primary host name --- */
275 if ((name
= getstring(fd
)) == 0)
277 sz
+= strlen(name
) + 1;
279 /* --- Read in the alias names --- */
283 if ((alias
= malloc(hsk
.nalias
* sizeof(char *))) == 0)
285 for (i
= 0; i
< hsk
.nalias
; i
++)
287 for (i
= 0; i
< hsk
.nalias
; i
++) {
288 if ((alias
[i
] = getstring(fd
)) == 0)
290 sz
+= strlen(alias
[i
]) + 1;
294 /* --- Allocate the output structure --- */
296 if ((h
= malloc(sz
)) == 0)
300 /* --- Fill in the base structure --- */
302 h
->h_addrtype
= hsk
.addrtype
;
303 h
->h_length
= hsk
.addrsz
;
305 /* --- Start putting everything else in --- */
308 char **p
= (char **)(h
+ 1);
309 char *a
= (char *)(p
+ hsk
.nalias
+ hsk
.naddr
+ 2);
312 /* --- Start with the address table --- */
315 if (doread(fd
, a
, hsk
.naddr
* hsk
.addrsz
))
317 for (i
= 0; i
< hsk
.naddr
; i
++) {
323 /* --- Finally copy the strings over --- */
325 #define PUT(_p) do { \
326 size_t _len = strlen(_p) + 1; \
327 memcpy(a, (_p), _len); \
335 for (i
= 0; i
< hsk
.nalias
; i
++) {
348 /* --- Tidy up after various types of failure --- */
353 for (i
= 0; i
< hsk
.nalias
&& alias
[i
]; i
++)
365 /* --- @puthost@ --- *
367 * Arguments: @int fd@ = file descriptor
368 * @struct hostent *h@ = pointer to host structure
370 * Returns: Zero if successful.
372 * Use: Encodes a host structure.
375 static int puthost(int fd
, struct hostent
*h
)
380 /* --- Fill in and send the skeleton structure --- */
382 for (i
= 0; h
->h_aliases
[i
]; i
++)
385 for (i
= 0; h
->h_addr_list
[i
]; i
++)
388 hsk
.addrtype
= h
->h_addrtype
;
389 hsk
.addrsz
= h
->h_length
;
390 if (dowrite(fd
, &hsk
, sizeof(hsk
)))
393 /* --- Send the name and alias strings --- */
395 if (putstring(fd
, h
->h_name
))
397 for (i
= 0; h
->h_aliases
[i
]; i
++) {
398 if (putstring(fd
, h
->h_aliases
[i
]))
402 /* --- Send the address data --- */
404 for (i
= 0; h
->h_addr_list
[i
]; i
++) {
405 if (dowrite(fd
, h
->h_addr_list
[i
], hsk
.addrsz
))
409 /* --- OK, done --- */
414 /*----- Resolver server ---------------------------------------------------*/
418 * Arguments: @int rfd@ = output file descriptor for resolved hostnames
419 * @int cfd@ = input file descriptor for raw addresses
423 * Use: Asynchronous name resolving process.
426 static void child(int rfd
, int cfd
)
428 /* --- Close other file descriptors --- */
432 #if defined(_SC_OPEN_MAX)
433 int maxfd
= sysconf(_SC_OPEN_MAX
);
434 #elif defined(OPEN_MAX)
435 int maxfd
= OPEN_MAX
;
441 maxfd
= 256; /* Fingers crossed... */
442 for (i
= 0; i
< maxfd
; i
++) {
443 if (i
!= rfd
&& i
!= cfd
&& i
!= 1)
448 signal(SIGTERM
, SIG_DFL
);
449 signal(SIGHUP
, SIG_DFL
);
450 signal(SIGQUIT
, SIG_DFL
);
451 signal(SIGALRM
, SIG_DFL
);
452 signal(SIGINT
, SIG_DFL
);
454 /* --- Main request/response loop --- */
460 /* --- Read the request --- */
462 if (doread(cfd
, &req
, sizeof(req
)))
465 /* --- Process it into a host structure --- */
469 /* --- Normal forward lookup --- */
472 char *name
= getstring(cfd
);
475 h
= gethostbyname(name
);
479 /* --- Reverse lookup --- */
484 if (doread(cfd
, &addr
, sizeof(addr
)))
486 if ((h
= gethostbyaddr((char *)&addr
, sizeof(addr
), AF_INET
)) == 0)
489 /* --- Do a forward lookup to confirm --- */
492 size_t sz
= strlen(h
->h_name
) + 1;
493 if ((p
= malloc(sz
)) == 0)
495 memcpy(p
, h
->h_name
, sz
);
498 h
= gethostbyname(p
);
505 for (pp
= h
->h_addr_list
; *pp
; pp
++) {
507 memcpy(&a
, *pp
, sizeof(a
));
508 if (a
.s_addr
== addr
.s_addr
) {
516 h_errno
= NO_RECOVERY
;
521 /* --- Unknown request -- may have lost sync --- */
527 /* --- Transmit the response --- */
531 if (dowrite(rfd
, &resp
, sizeof(resp
)) || puthost(rfd
, h
))
535 if (dowrite(rfd
, &resp
, sizeof(resp
)) ||
536 dowrite(rfd
, &h_errno
, sizeof(h_errno
)))
547 * Arguments: @int argc@ = number of command line arguments
548 * @char *argv[]@ = array of arguments
550 * Returns: Runs until killed or an error occurs.
552 * Use: A name resolver server process for mLib programs which need
553 * this sort of thing.
556 #ifdef BRES_STANDALONE
558 int main(int argc
, char *argv
[])
560 if (isatty(STDIN_FILENO
)) {
561 char *p
= strrchr(argv
[0], '/');
567 "%s: don't run this program unless you know what you're doing.\n",
571 child(STDOUT_FILENO
, STDIN_FILENO
);
577 /*----- Main code ---------------------------------------------------------*/
579 #ifndef BRES_STANDALONE
583 * Arguments: @bres_server *rs@ = pointer to server block
587 * Use: Kills a server process, reaps the losing child and makes
588 * things generally clean again.
591 static void zap(bres_server
*rs
)
593 /* --- Close the pipes, kill the child, and reap it --- */
598 kill(rs
->kid
, SIGTERM
);
599 waitpid(rs
->kid
, 0, 0);
603 /* --- Move the server to the back of the list --- */
605 rs
->next
->prev
= rs
->prev
;
606 rs
->prev
->next
= rs
->next
;
608 rs
->prev
= FREE
->prev
;
609 FREE
->prev
->next
= rs
;
613 /* --- @bres_abort@ --- *
615 * Arguments: @bres_client *rc@ = pointer to client block
619 * Use: Removes a queued job.
622 void bres_abort(bres_client
*rc
)
624 if (rc
->q
== BRES_BYNAME
)
627 sel_rmfile(&rc
->rs
->f
);
631 rc
->next
->prev
= rc
->prev
;
632 rc
->prev
->next
= rc
->next
;
638 * Arguments: @struct timeval *tv@ = pointer to the current time
639 * @void *vp@ = pointer to a server block
643 * Use: Kills off a child which has been idle for too long.
646 static void idle(struct timeval
*tv
, void *vp
)
648 bres_server
*rs
= vp
;
652 /* --- @answer@ --- *
654 * Arguments: @int fd@ = file descriptor which is ready
655 * @unsigned mode@ = what it's doing now
656 * @void *vp@ = pointer to server block
660 * Use: Retrieves an answer from a name resolver process.
663 static void attach(bres_client */
*rc*/
);
665 static void answer(int fd
, unsigned mode
, void *vp
)
667 bres_server
*rs
= vp
;
668 bres_client
*rc
= rs
->rc
;
669 struct hostent
*h
= 0;
673 /* --- Report the result to my client --- */
677 if (doread(fd
, &resp
, sizeof(resp
)) == 0) {
680 doread(fd
, &h_errno
, sizeof(h_errno
));
691 if (rc
->q
== BRES_BYNAME
)
701 /* --- Wrap up the various structures --- */
705 rs
->next
= FREE
->next
;
707 FREE
->next
->prev
= rs
;
710 /* --- Tie a timer onto the server block --- */
715 gettimeofday(&tv
, 0);
716 tv
.tv_sec
+= BRES_IDLE
;
717 sel_addtimer(sel
, &rs
->t
, &tv
, idle
, rs
);
720 /* --- If there are any clients waiting, attach one --- */
722 if (QUEUE
->next
!= QUEUE
) {
724 QUEUE
->next
= rc
->next
;
725 rc
->next
->prev
= QUEUE
;
732 * Arguments: @bres_server *rs@ = pointer to a server block
734 * Returns: Zero if OK, nonzero if something failed.
736 * Use: Starts up a child resolver process.
739 static int start(bres_server
*rs
)
744 /* --- Make the pipes --- */
751 /* --- Start up the child process --- */
753 if ((kid
= fork()) < 0)
760 dup2(cfd
[0], STDIN_FILENO
);
761 dup2(rfd
[1], STDOUT_FILENO
);
764 execlp(server
, server
, (char *)0);
765 child(STDOUT_FILENO
, STDIN_FILENO
);
767 child(rfd
[1], cfd
[0]);
771 /* --- Fix up everything in the server block --- */
776 sel_initfile(sel
, &rs
->f
, rfd
[0], SEL_READ
, answer
, rs
);
780 /* --- Fix up after errors --- */
792 /* --- @attach@ --- *
794 * Arguments: @bres_client *rc@ = pointer to a client block
798 * Use: Attaches a client to a spare server (which is assumed to
802 static void attach(bres_client
*rc
)
807 /* --- Fix up the server ready for the job --- *
809 * If the server has a process, remove its timer. Otherwise, fork off a
810 * new resolver process. This is also where I go if I find that the child
811 * resolver process has lost while I wasn't looking. Only one attempt at
812 * forking is performed.
820 if (lose
|| start(rs
))
825 /* --- Submit the job to the resolver --- */
828 struct sigaction sa
, osa
;
831 /* --- Ignore @SIGPIPE@ for now --- *
833 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
836 sa
.sa_handler
= SIG_IGN
;
838 sigemptyset(&sa
.sa_mask
);
839 sigaction(SIGPIPE
, &sa
, &osa
);
841 /* --- Write the new job to the child --- */
844 if (dowrite(rs
->fd
, &rc
->q
, sizeof(rc
->q
)))
846 else switch (rc
->q
) {
848 if (dowrite(rs
->fd
, &rc
->u
.addr
, sizeof(rc
->u
.addr
)))
852 if (putstring(rs
->fd
, rc
->u
.name
))
856 sigaction(SIGPIPE
, &osa
, 0);
858 /* --- Sort out various errors --- *
860 * This was once more complicated, handling @EPIPE@ separately from other
861 * errors. Now everything's handled the same way.
870 /* --- Fiddle with lists so that everything's OK --- */
873 rs
->next
->prev
= FREE
;
874 FREE
->next
= rs
->next
;
875 rs
->next
= rs
->prev
= rs
;
882 if (rc
->q
== BRES_BYNAME
)
886 /* --- @resolve@ --- *
888 * Arguments: @bres_client *rc@ = pointer to filled-in client block
892 * Use: Dispatcher for incoming resolution jobs.
895 static void resolve(bres_client
*rc
)
897 /* --- If there's a free server, plug it in --- */
900 if (FREE
->next
== FREE
) {
902 rc
->prev
= QUEUE
->prev
;
903 QUEUE
->prev
->next
= rc
;
909 /* --- @bres_byaddr@ --- *
911 * Arguments: @bres_client *rc@ = pointer to client block
912 * @struct in_addr addr@ = address to resolve
913 * @void (*func)(struct hostent *h, void *p)@ = handler function
914 * @void *p@ = argument for handler function
918 * Use: Adds an address lookup job to the queue. The job will be
919 * processed when there's a spare resolver process to deal with
923 void bres_byaddr(bres_client
*rc
, struct in_addr addr
,
924 void (*func
)(struct hostent */
*h*/
, void */
*p*/
),
934 /* --- @bres_byname@ --- *
936 * Arguments: @bres_client *rc@ = pointer to client block
937 * @const char *name@ = name to resolve
938 * @void (*func)(struct hostent *h, void *p)@ = handler function
939 * @void *p@ = argument for handler function
943 * Use: Adds a name lookup job to the queue. The job will be
944 * processed when there's a spare resolver process to deal with
948 void bres_byname(bres_client
*rc
, const char *name
,
949 void (*func
)(struct hostent */
*h*/
, void */
*p*/
),
953 rc
->u
.name
= xstrdup(name
);
959 /* --- @bres_exec@ --- *
961 * Arguments: @const char *file@ = file containing server code or null
965 * Use: Makes `bres' use a standalone server rather than copies of
966 * the current process. This can reduce memory consumption for
967 * large processes, at the expense of startup time (which
968 * shouldn't be too bad anyway, because of the resolver design).
969 * If the filename is null, a default set up at install time is
970 * used. It's probably a good idea to leave it alone.
973 void bres_exec(const char *file
)
978 server
= BRES_SERVER
;
981 /* --- @bres_init@ --- *
983 * Arguments: @sel_state *s@ = pointer to select multiplexor
987 * Use: Initializes the background resolver for use.
990 void bres_init(sel_state
*s
)
995 for (i
= 0; i
< BRES_MAX
; i
++) {
996 servers
[i
].next
= FREE
;
997 servers
[i
].prev
= FREE
->prev
;
1000 FREE
->prev
->next
= &servers
[i
];
1001 FREE
->prev
= &servers
[i
];
1007 /*----- That's all, folks -------------------------------------------------*/