NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/57932: getaddrinfo and other nsswitch.conf/nsdispatch front ends should support asynchronous operation



>Number:         57932
>Category:       lib
>Synopsis:       getaddrinfo and other nsswitch.conf/nsdispatch front ends should support asynchronous operation
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Feb 13 19:20:00 +0000 2024
>Originator:     Taylor R Campbell
>Release:        current, 10, 9, 8, 7, 6, ...
>Organization:
The NsswitchBSD Foundation
>Environment:
>Description:
nsdispatch(3) and its front ends like getaddrinfo(3) are all synchronous: they block until they have a complete result.

This means that in order for a process to do name service resolution in the background while remaining interactive, it is essentially required either

(a) to run getaddrinfo(3) in a separate thread; or
(b) to implement its own name resolution, bypassing nsswitch.conf and maybe parsing resolv.conf or bypassing resolv.conf.

Both of these options are bad: thread creation is probably much more expensive than getaddrinfo(3) needs to be, and if you pool threads, slow requests can needlessly hold up fast requests when the pool runs out; and bypassing nsswitch.conf or resolv.conf means losing out on things like /etc/hosts, mdnsd, and resolv.conf options.
>How-To-Repeat:
try to use getaddrinfo(3) in interactive programs
>Fix:
Yes, please!

Here's a candidate API draft:

struct gettingaddrinfo;

struct gettingaddrinfo *gettingaddrinfo_begin(const char *, const char *,
    const struct addrinfo *);
bool gettingaddrinfo_done(struct gettingaddrinfo *, int *, struct addrinfo **);
int gettingaddrinfo_fd(const struct gettingaddrinfo *);
bool gettingaddrinfo_reading(const struct gettingaddrinfo *);
bool gettingaddrinfo_writing(const struct gettingaddrinfo *);
void gettingaddrinfo_step(struct gettingaddrinfo *);
void gettingaddrinfo_end(struct gettingaddrinfo *);

You could use this to implement getaddrinfo like so:

int
getaddrinfo(const char *hostname, const char *servname,
    const struct addrinfo *hints, struct addrinfo **aip)
{
	struct gettingaddrinfo *aing;
	int error;

	aing = gettingaddrinfo_begin(hostname, servname, hints);
	while (!gettingaddrinfo_done(aing, &error, aip)) {
		struct pollfd pfd = {
			.pfd = gettingaddrinfo_fd(aing),
			.events = 0,
		};
		if (gettingaddrinfo_reading(aing))
			pfd.events |= POLLIN;
		if (gettingaddrinfo_writing(aing))
			pfd.events |= POLLOUT;
		if (poll(&pfd, 1, INFTIM) == -1) {
			error = EAI_SYSTEM;
			*aip = NULL;
			break;
		}
		gettingaddrinfo_step(aing);
	}
	gettingaddrinfo_end(aing);

	return error;
}

Of course, you can also multiplex the poll (or select or kqueue or what have you) with any other asynchronous I/O operations you're doing at the same time, and only call gettingaddrinfo_step when the gettingaddrinfo fd is ready for read/write as appropriate according to pfd.revents.

The painful part here will be (a) touching the res(3) code to split it up into asynchronous parts, and (b) adapting all the nsdispatch logic to handle asynchronous operation.



Home | Main Index | Thread Index | Old Index