1/*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1995-1999 by Internet Software Consortium
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* ev_connects.c - implement asynch connect/accept for the eventlib
19 * vix 16sep96 [initial]
20 */
21
22#include "port_before.h"
23#include "fd_setsize.h"
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/ioctl.h>
28
29#include <unistd.h>
30
31#include <isc/eventlib.h>
32#include <isc/assertions.h>
33#include "eventlib_p.h"
34
35#include "port_after.h"
36
37/* Macros. */
38
39#define GETXXXNAME(f, s, sa, len) ( \
40	(f((s), (&sa), (&len)) >= 0) ? 0 : \
41		(errno != EAFNOSUPPORT && errno != EOPNOTSUPP) ? -1 : ( \
42			memset(&(sa), 0, sizeof (sa)), \
43			(len) = sizeof (sa), \
44			(sa).sa_family = AF_UNIX, \
45			0 \
46		) \
47	)
48
49/* Forward. */
50
51static void	listener(evContext ctx, void *uap, int fd, int evmask);
52static void	connector(evContext ctx, void *uap, int fd, int evmask);
53
54/* Public. */
55
56int
57evListen(evContext opaqueCtx, int fd, int maxconn,
58	 evConnFunc func, void *uap, evConnID *id)
59{
60	evContext_p *ctx = opaqueCtx.opaque;
61	evConn *new;
62	int mode;
63
64	OKNEW(new);
65	new->flags = EV_CONN_LISTEN;
66	OKFREE(mode = fcntl(fd, F_GETFL, NULL), new);	/*%< side effect: validate fd. */
67	/*
68	 * Remember the nonblocking status.  We assume that either evSelectFD
69	 * has not been done to this fd, or that if it has then the caller
70	 * will evCancelConn before they evDeselectFD.  If our assumptions
71	 * are not met, then we might restore the old nonblocking status
72	 * incorrectly.
73	 */
74	if ((mode & PORT_NONBLOCK) == 0) {
75#ifdef USE_FIONBIO_IOCTL
76		int on = 1;
77		OKFREE(ioctl(fd, FIONBIO, (char *)&on), new);
78#else
79		OKFREE(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK), new);
80#endif
81		new->flags |= EV_CONN_BLOCK;
82	}
83	OKFREE(listen(fd, maxconn), new);
84	if (evSelectFD(opaqueCtx, fd, EV_READ, listener, new, &new->file) < 0){
85		int save = errno;
86
87		FREE(new);
88		errno = save;
89		return (-1);
90	}
91	new->flags |= EV_CONN_SELECTED;
92	new->func = func;
93	new->uap = uap;
94	new->fd = fd;
95	if (ctx->conns != NULL)
96		ctx->conns->prev = new;
97	new->prev = NULL;
98	new->next = ctx->conns;
99	ctx->conns = new;
100	if (id)
101		id->opaque = new;
102	return (0);
103}
104
105int
106evConnect(evContext opaqueCtx, int fd, const void *ra, int ralen,
107	  evConnFunc func, void *uap, evConnID *id)
108{
109	evContext_p *ctx = opaqueCtx.opaque;
110	evConn *new;
111
112	OKNEW(new);
113	new->flags = 0;
114	/* Do the select() first to get the socket into nonblocking mode. */
115	if (evSelectFD(opaqueCtx, fd, EV_MASK_ALL,
116		       connector, new, &new->file) < 0) {
117		int save = errno;
118
119		FREE(new);
120		errno = save;
121		return (-1);
122	}
123	new->flags |= EV_CONN_SELECTED;
124	if (connect(fd, ra, ralen) < 0 &&
125	    errno != EWOULDBLOCK &&
126	    errno != EAGAIN &&
127	    errno != EINPROGRESS) {
128		int save = errno;
129
130		(void) evDeselectFD(opaqueCtx, new->file);
131		FREE(new);
132		errno = save;
133		return (-1);
134	}
135	/* No error, or EWOULDBLOCK.  select() tells when it's ready. */
136	new->func = func;
137	new->uap = uap;
138	new->fd = fd;
139	if (ctx->conns != NULL)
140		ctx->conns->prev = new;
141	new->prev = NULL;
142	new->next = ctx->conns;
143	ctx->conns = new;
144	if (id)
145		id->opaque = new;
146	return (0);
147}
148
149int
150evCancelConn(evContext opaqueCtx, evConnID id) {
151	evContext_p *ctx = opaqueCtx.opaque;
152	evConn *this = id.opaque;
153	evAccept *acc, *nxtacc;
154	int mode;
155
156	if ((this->flags & EV_CONN_SELECTED) != 0)
157		(void) evDeselectFD(opaqueCtx, this->file);
158	if ((this->flags & EV_CONN_BLOCK) != 0) {
159		mode = fcntl(this->fd, F_GETFL, NULL);
160		if (mode == -1) {
161			if (errno != EBADF)
162				return (-1);
163		} else {
164#ifdef USE_FIONBIO_IOCTL
165			int off = 0;
166			OK(ioctl(this->fd, FIONBIO, (char *)&off));
167#else
168			OK(fcntl(this->fd, F_SETFL, mode & ~PORT_NONBLOCK));
169#endif
170		}
171	}
172
173	/* Unlink from ctx->conns. */
174	if (this->prev != NULL)
175		this->prev->next = this->next;
176	else
177		ctx->conns = this->next;
178	if (this->next != NULL)
179		this->next->prev = this->prev;
180
181	/*
182	 * Remove `this' from the ctx->accepts list (zero or more times).
183	 */
184	for (acc = HEAD(ctx->accepts), nxtacc = NULL;
185	     acc != NULL;
186	     acc = nxtacc)
187	{
188		nxtacc = NEXT(acc, link);
189		if (acc->conn == this) {
190			UNLINK(ctx->accepts, acc, link);
191			close(acc->fd);
192			FREE(acc);
193		}
194	}
195
196	/* Wrap up and get out. */
197	FREE(this);
198	return (0);
199}
200
201int evHold(evContext opaqueCtx, evConnID id) {
202	evConn *this = id.opaque;
203
204	if ((this->flags & EV_CONN_LISTEN) == 0) {
205		errno = EINVAL;
206		return (-1);
207	}
208	if ((this->flags & EV_CONN_SELECTED) == 0)
209		return (0);
210	this->flags &= ~EV_CONN_SELECTED;
211	return (evDeselectFD(opaqueCtx, this->file));
212}
213
214int evUnhold(evContext opaqueCtx, evConnID id) {
215	evConn *this = id.opaque;
216	int ret;
217
218	if ((this->flags & EV_CONN_LISTEN) == 0) {
219		errno = EINVAL;
220		return (-1);
221	}
222	if ((this->flags & EV_CONN_SELECTED) != 0)
223		return (0);
224	ret = evSelectFD(opaqueCtx, this->fd, EV_READ, listener, this,
225			 &this->file);
226	if (ret == 0)
227		this->flags |= EV_CONN_SELECTED;
228	return (ret);
229}
230
231int
232evTryAccept(evContext opaqueCtx, evConnID id, int *sys_errno) {
233	evContext_p *ctx = opaqueCtx.opaque;
234	evConn *conn = id.opaque;
235	evAccept *new;
236
237	if ((conn->flags & EV_CONN_LISTEN) == 0) {
238		errno = EINVAL;
239		return (-1);
240	}
241	OKNEW(new);
242	new->conn = conn;
243	new->ralen = sizeof new->ra;
244	new->fd = accept(conn->fd, &new->ra.sa, &new->ralen);
245	if (new->fd > ctx->highestFD) {
246		close(new->fd);
247		new->fd = -1;
248		new->ioErrno = ENOTSOCK;
249	}
250	if (new->fd >= 0) {
251		new->lalen = sizeof new->la;
252		if (GETXXXNAME(getsockname, new->fd, new->la.sa, new->lalen) < 0) {
253			new->ioErrno = errno;
254			(void) close(new->fd);
255			new->fd = -1;
256		} else
257			new->ioErrno = 0;
258	} else {
259		new->ioErrno = errno;
260		if (errno == EAGAIN || errno == EWOULDBLOCK) {
261			FREE(new);
262			return (-1);
263		}
264	}
265	INIT_LINK(new, link);
266	APPEND(ctx->accepts, new, link);
267	*sys_errno = new->ioErrno;
268	return (0);
269}
270
271/* Private. */
272
273static void
274listener(evContext opaqueCtx, void *uap, int fd, int evmask) {
275	evContext_p *ctx = opaqueCtx.opaque;
276	evConn *conn = uap;
277	union {
278		struct sockaddr    sa;
279		struct sockaddr_in in;
280#ifndef NO_SOCKADDR_UN
281		struct sockaddr_un un;
282#endif
283	} la, ra;
284	int new;
285	ISC_SOCKLEN_T lalen = 0, ralen;
286
287	REQUIRE((evmask & EV_READ) != 0);
288	ralen = sizeof ra;
289	new = accept(fd, &ra.sa, &ralen);
290	if (new > ctx->highestFD) {
291		close(new);
292		new = -1;
293		errno = ENOTSOCK;
294	}
295	if (new >= 0) {
296		lalen = sizeof la;
297		if (GETXXXNAME(getsockname, new, la.sa, lalen) < 0) {
298			int save = errno;
299
300			(void) close(new);
301			errno = save;
302			new = -1;
303		}
304	} else if (errno == EAGAIN || errno == EWOULDBLOCK)
305		return;
306	(*conn->func)(opaqueCtx, conn->uap, new, &la.sa, lalen, &ra.sa, ralen);
307}
308
309static void
310connector(evContext opaqueCtx, void *uap, int fd, int evmask) {
311	evConn *conn = uap;
312	union {
313		struct sockaddr    sa;
314		struct sockaddr_in in;
315#ifndef NO_SOCKADDR_UN
316		struct sockaddr_un un;
317#endif
318	} la, ra;
319	ISC_SOCKLEN_T lalen, ralen;
320#ifndef NETREAD_BROKEN
321	char buf[1];
322#endif
323	void *conn_uap;
324	evConnFunc conn_func;
325	evConnID id;
326	int socket_errno = 0;
327	ISC_SOCKLEN_T optlen;
328
329	UNUSED(evmask);
330
331	lalen = sizeof la;
332	ralen = sizeof ra;
333	conn_uap = conn->uap;
334	conn_func = conn->func;
335	id.opaque = conn;
336#ifdef SO_ERROR
337	optlen = sizeof socket_errno;
338	if (fd < 0 &&
339	    getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char *)&socket_errno,
340		       &optlen) < 0)
341		socket_errno = errno;
342	else
343		errno = socket_errno;
344#endif
345	if (evCancelConn(opaqueCtx, id) < 0 ||
346	    socket_errno ||
347#ifdef NETREAD_BROKEN
348	    0 ||
349#else
350	    read(fd, buf, 0) < 0 ||
351#endif
352	    GETXXXNAME(getsockname, fd, la.sa, lalen) < 0 ||
353	    GETXXXNAME(getpeername, fd, ra.sa, ralen) < 0) {
354		int save = errno;
355
356		(void) close(fd);	/*%< XXX closing caller's fd */
357		errno = save;
358		fd = -1;
359	}
360	(*conn_func)(opaqueCtx, conn_uap, fd, &la.sa, lalen, &ra.sa, ralen);
361}
362
363/*! \file */
364