xref: /minix3/minix/net/uds/uds.c (revision 27852ebe53d5bf221cf5058cb7e858fa8fa8895e)
1*27852ebeSDavid van Moolenbroek /* UNIX Domain Sockets - uds.c - socket management */
2cc5b1988SDavid van Moolenbroek 
3cc5b1988SDavid van Moolenbroek #include "uds.h"
4cc5b1988SDavid van Moolenbroek 
5*27852ebeSDavid van Moolenbroek static struct udssock uds_array[NR_UDSSOCK];
6*27852ebeSDavid van Moolenbroek static TAILQ_HEAD(uds_freelist, udssock) uds_freelist;
7*27852ebeSDavid van Moolenbroek static unsigned int uds_in_use;
8*27852ebeSDavid van Moolenbroek static int uds_running;
9cc5b1988SDavid van Moolenbroek 
10*27852ebeSDavid van Moolenbroek static const struct sockevent_ops uds_ops;
11cc5b1988SDavid van Moolenbroek 
SLIST_HEAD(udshash,udssock)12*27852ebeSDavid van Moolenbroek static SLIST_HEAD(udshash, udssock) udshash[UDSHASH_SLOTS];
13cc5b1988SDavid van Moolenbroek 
14cc5b1988SDavid van Moolenbroek /*
15*27852ebeSDavid van Moolenbroek  * Initialize file-to-socket hash table.
16cc5b1988SDavid van Moolenbroek  */
17cc5b1988SDavid van Moolenbroek static void
18*27852ebeSDavid van Moolenbroek udshash_init(void)
19cc5b1988SDavid van Moolenbroek {
20*27852ebeSDavid van Moolenbroek 	unsigned int slot;
21cc5b1988SDavid van Moolenbroek 
22*27852ebeSDavid van Moolenbroek 	for (slot = 0; slot < __arraycount(udshash); slot++)
23*27852ebeSDavid van Moolenbroek 		SLIST_INIT(&udshash[slot]);
24*27852ebeSDavid van Moolenbroek }
25cc5b1988SDavid van Moolenbroek 
26*27852ebeSDavid van Moolenbroek /*
27*27852ebeSDavid van Moolenbroek  * Return a hash table slot number for the given <dev,ino> pair.
28*27852ebeSDavid van Moolenbroek  */
29*27852ebeSDavid van Moolenbroek static unsigned int
udshash_slot(dev_t dev,ino_t ino)30*27852ebeSDavid van Moolenbroek udshash_slot(dev_t dev, ino_t ino)
31*27852ebeSDavid van Moolenbroek {
32cc5b1988SDavid van Moolenbroek 
33*27852ebeSDavid van Moolenbroek 	assert(dev != NO_DEV);
34*27852ebeSDavid van Moolenbroek 	assert(ino != 0);
35*27852ebeSDavid van Moolenbroek 
36*27852ebeSDavid van Moolenbroek 	/*
37*27852ebeSDavid van Moolenbroek 	 * Effectively combining two 64-bit numbers into a single 6-or-so-bit
38*27852ebeSDavid van Moolenbroek 	 * hash is not too easy.  This hash function is probably among the
39*27852ebeSDavid van Moolenbroek 	 * worst options.  Then again it is not all that critical as we are not
40*27852ebeSDavid van Moolenbroek 	 * expecting that many bound UDS sockets in the system anyway.
41*27852ebeSDavid van Moolenbroek 	 */
42*27852ebeSDavid van Moolenbroek 	return (unsigned int)(dev ^ ino) % UDSHASH_SLOTS;
43*27852ebeSDavid van Moolenbroek }
44*27852ebeSDavid van Moolenbroek 
45*27852ebeSDavid van Moolenbroek /*
46*27852ebeSDavid van Moolenbroek  * Look for a socket that is bound to the given <dev,ino> pair.  Return a
47*27852ebeSDavid van Moolenbroek  * pointer to the socket if found, or NULL otherwise.
48*27852ebeSDavid van Moolenbroek  */
49*27852ebeSDavid van Moolenbroek static struct udssock *
udshash_get(dev_t dev,ino_t ino)50*27852ebeSDavid van Moolenbroek udshash_get(dev_t dev, ino_t ino)
51*27852ebeSDavid van Moolenbroek {
52*27852ebeSDavid van Moolenbroek 	struct udssock *uds;
53*27852ebeSDavid van Moolenbroek 	unsigned int slot;
54*27852ebeSDavid van Moolenbroek 
55*27852ebeSDavid van Moolenbroek 	slot = udshash_slot(dev, ino);
56*27852ebeSDavid van Moolenbroek 
57*27852ebeSDavid van Moolenbroek 	SLIST_FOREACH(uds, &udshash[slot], uds_hash) {
58*27852ebeSDavid van Moolenbroek 		if (uds->uds_dev == dev && uds->uds_ino == ino)
59*27852ebeSDavid van Moolenbroek 			return uds;
60*27852ebeSDavid van Moolenbroek 	}
61*27852ebeSDavid van Moolenbroek 
62*27852ebeSDavid van Moolenbroek 	return NULL;
63*27852ebeSDavid van Moolenbroek }
64*27852ebeSDavid van Moolenbroek 
65*27852ebeSDavid van Moolenbroek /*
66*27852ebeSDavid van Moolenbroek  * Add a socket to the file-to-socket hash table.  The socket must have its
67*27852ebeSDavid van Moolenbroek  * device and inode fields set, and must not be in the hash table already.
68*27852ebeSDavid van Moolenbroek  */
69*27852ebeSDavid van Moolenbroek static void
udshash_add(struct udssock * uds)70*27852ebeSDavid van Moolenbroek udshash_add(struct udssock * uds)
71*27852ebeSDavid van Moolenbroek {
72*27852ebeSDavid van Moolenbroek 	unsigned int slot;
73*27852ebeSDavid van Moolenbroek 
74*27852ebeSDavid van Moolenbroek 	slot = udshash_slot(uds->uds_dev, uds->uds_ino);
75*27852ebeSDavid van Moolenbroek 
76*27852ebeSDavid van Moolenbroek 	SLIST_INSERT_HEAD(&udshash[slot], uds, uds_hash);
77*27852ebeSDavid van Moolenbroek }
78*27852ebeSDavid van Moolenbroek 
79*27852ebeSDavid van Moolenbroek /*
80*27852ebeSDavid van Moolenbroek  * Remove a socket from the file-to-socket hash table.  The socket must be in
81*27852ebeSDavid van Moolenbroek  * the hash table.
82*27852ebeSDavid van Moolenbroek  */
83*27852ebeSDavid van Moolenbroek static void
udshash_del(struct udssock * uds)84*27852ebeSDavid van Moolenbroek udshash_del(struct udssock * uds)
85*27852ebeSDavid van Moolenbroek {
86*27852ebeSDavid van Moolenbroek 	unsigned int slot;
87*27852ebeSDavid van Moolenbroek 
88*27852ebeSDavid van Moolenbroek 	slot = udshash_slot(uds->uds_dev, uds->uds_ino);
89*27852ebeSDavid van Moolenbroek 
90*27852ebeSDavid van Moolenbroek 	/* This macro is O(n). */
91*27852ebeSDavid van Moolenbroek 	SLIST_REMOVE(&udshash[slot], uds, udssock, uds_hash);
92*27852ebeSDavid van Moolenbroek }
93*27852ebeSDavid van Moolenbroek 
94*27852ebeSDavid van Moolenbroek /*
95*27852ebeSDavid van Moolenbroek  * Return the socket identifier for the given UDS socket object.
96*27852ebeSDavid van Moolenbroek  */
97*27852ebeSDavid van Moolenbroek sockid_t
uds_get_id(struct udssock * uds)98*27852ebeSDavid van Moolenbroek uds_get_id(struct udssock * uds)
99*27852ebeSDavid van Moolenbroek {
100*27852ebeSDavid van Moolenbroek 
101*27852ebeSDavid van Moolenbroek 	return (sockid_t)(uds - uds_array);
102*27852ebeSDavid van Moolenbroek }
103*27852ebeSDavid van Moolenbroek 
104*27852ebeSDavid van Moolenbroek /*
105*27852ebeSDavid van Moolenbroek  * Given either NULL or a previously returned socket, return the next in-use
106*27852ebeSDavid van Moolenbroek  * UDS socket of the given socket type, or NULL if there are no more matches.
107*27852ebeSDavid van Moolenbroek  * The sockets are returned in random order, but each matching socket is
108*27852ebeSDavid van Moolenbroek  * returned exactly once (until any socket is allocated or freed).
109*27852ebeSDavid van Moolenbroek  */
110*27852ebeSDavid van Moolenbroek struct udssock *
uds_enum(struct udssock * prev,int type)111*27852ebeSDavid van Moolenbroek uds_enum(struct udssock * prev, int type)
112*27852ebeSDavid van Moolenbroek {
113*27852ebeSDavid van Moolenbroek 	sockid_t id;
114*27852ebeSDavid van Moolenbroek 
115*27852ebeSDavid van Moolenbroek 	if (prev != NULL)
116*27852ebeSDavid van Moolenbroek 		id = uds_get_id(prev) + 1;
117*27852ebeSDavid van Moolenbroek 	else
118*27852ebeSDavid van Moolenbroek 		id = 0;
119*27852ebeSDavid van Moolenbroek 
120*27852ebeSDavid van Moolenbroek 	for (; id < NR_UDSSOCK; id++)
121*27852ebeSDavid van Moolenbroek 		if ((uds_array[id].uds_flags & UDSF_IN_USE) &&
122*27852ebeSDavid van Moolenbroek 		    uds_get_type(&uds_array[id]) == type)
123*27852ebeSDavid van Moolenbroek 			return &uds_array[id];
124*27852ebeSDavid van Moolenbroek 
125*27852ebeSDavid van Moolenbroek 	return NULL;
126*27852ebeSDavid van Moolenbroek }
127*27852ebeSDavid van Moolenbroek 
128*27852ebeSDavid van Moolenbroek /*
129*27852ebeSDavid van Moolenbroek  * Invalidate credentials on the socket.
130*27852ebeSDavid van Moolenbroek  */
131*27852ebeSDavid van Moolenbroek static void
uds_clear_cred(struct udssock * uds)132*27852ebeSDavid van Moolenbroek uds_clear_cred(struct udssock * uds)
133*27852ebeSDavid van Moolenbroek {
134*27852ebeSDavid van Moolenbroek 
135*27852ebeSDavid van Moolenbroek 	uds->uds_cred.unp_pid = -1;
136*27852ebeSDavid van Moolenbroek 	uds->uds_cred.unp_euid = -1;
137*27852ebeSDavid van Moolenbroek 	uds->uds_cred.unp_egid = -1;
138*27852ebeSDavid van Moolenbroek }
139*27852ebeSDavid van Moolenbroek 
140*27852ebeSDavid van Moolenbroek /*
141*27852ebeSDavid van Moolenbroek  * Obtain the credentials (process, user, and group ID) of the given user
142*27852ebeSDavid van Moolenbroek  * endpoint and associate them with the socket for later retrieval.  It is
143*27852ebeSDavid van Moolenbroek  * important to note that this information is obtained once at connect time,
144*27852ebeSDavid van Moolenbroek  * and never updated later.  The party receiving the credentials must take this
145*27852ebeSDavid van Moolenbroek  * into account.
146*27852ebeSDavid van Moolenbroek  */
147*27852ebeSDavid van Moolenbroek static void
uds_get_cred(struct udssock * uds,endpoint_t user_endpt)148*27852ebeSDavid van Moolenbroek uds_get_cred(struct udssock * uds, endpoint_t user_endpt)
149*27852ebeSDavid van Moolenbroek {
150*27852ebeSDavid van Moolenbroek 	int r;
151*27852ebeSDavid van Moolenbroek 
152*27852ebeSDavid van Moolenbroek 	if ((uds->uds_cred.unp_pid = r = getepinfo(user_endpt,
153*27852ebeSDavid van Moolenbroek 	    &uds->uds_cred.unp_euid, &uds->uds_cred.unp_egid)) < 0) {
154*27852ebeSDavid van Moolenbroek 		printf("UDS: failed obtaining credentials of %d (%d)\n",
155*27852ebeSDavid van Moolenbroek 		    user_endpt, r);
156*27852ebeSDavid van Moolenbroek 
157*27852ebeSDavid van Moolenbroek 		uds_clear_cred(uds);
158cc5b1988SDavid van Moolenbroek 	}
159cc5b1988SDavid van Moolenbroek }
160cc5b1988SDavid van Moolenbroek 
161*27852ebeSDavid van Moolenbroek /*
162*27852ebeSDavid van Moolenbroek  * Allocate and initialize a UDS socket.  On succes, return OK with a pointer
163*27852ebeSDavid van Moolenbroek  * to the new socket in 'udsp'.  On failure, return a negative error code.
164*27852ebeSDavid van Moolenbroek  */
165cc5b1988SDavid van Moolenbroek static int
uds_alloc(struct udssock ** udsp)166*27852ebeSDavid van Moolenbroek uds_alloc(struct udssock ** udsp)
167cc5b1988SDavid van Moolenbroek {
168*27852ebeSDavid van Moolenbroek 	struct udssock *uds;
169*27852ebeSDavid van Moolenbroek 	int r;
170cc5b1988SDavid van Moolenbroek 
171*27852ebeSDavid van Moolenbroek 	/* Allocate, initialize, and return a UNIX domain socket object. */
172*27852ebeSDavid van Moolenbroek 	if (TAILQ_EMPTY(&uds_freelist))
173*27852ebeSDavid van Moolenbroek 		return ENOBUFS;
174cc5b1988SDavid van Moolenbroek 
175*27852ebeSDavid van Moolenbroek 	uds = TAILQ_FIRST(&uds_freelist);
176cc5b1988SDavid van Moolenbroek 
177*27852ebeSDavid van Moolenbroek 	uds->uds_conn = NULL;		/* not connected */
178*27852ebeSDavid van Moolenbroek 	uds->uds_link = NULL;		/* not connecting or linked */
179*27852ebeSDavid van Moolenbroek 	uds->uds_queued = 0;
180*27852ebeSDavid van Moolenbroek 	uds->uds_flags = UDSF_IN_USE;	/* may be found through enumeration */
181*27852ebeSDavid van Moolenbroek 	uds->uds_pathlen = 0;		/* not bound: no path */
182*27852ebeSDavid van Moolenbroek 	uds->uds_dev = NO_DEV;		/* not hashed: no socket file device */
183*27852ebeSDavid van Moolenbroek 	uds->uds_ino = 0;		/* not hashed: no socket file inode */
184*27852ebeSDavid van Moolenbroek 	uds_clear_cred(uds);		/* no bind/connect-time credentials */
185*27852ebeSDavid van Moolenbroek 	TAILQ_INIT(&uds->uds_queue);	/* an empty queue */
186*27852ebeSDavid van Moolenbroek 
187*27852ebeSDavid van Moolenbroek 	if ((r = uds_io_setup(uds)) != OK)
188*27852ebeSDavid van Moolenbroek 		return r;
189*27852ebeSDavid van Moolenbroek 
190*27852ebeSDavid van Moolenbroek 	TAILQ_REMOVE(&uds_freelist, uds, uds_next);
191*27852ebeSDavid van Moolenbroek 
192*27852ebeSDavid van Moolenbroek 	assert(uds_in_use < NR_UDSSOCK);
193*27852ebeSDavid van Moolenbroek 	uds_in_use++;
194*27852ebeSDavid van Moolenbroek 
195*27852ebeSDavid van Moolenbroek 	*udsp = uds;
196*27852ebeSDavid van Moolenbroek 	return OK;
197*27852ebeSDavid van Moolenbroek }
198*27852ebeSDavid van Moolenbroek 
199*27852ebeSDavid van Moolenbroek /*
200*27852ebeSDavid van Moolenbroek  * Free a previously allocated socket.
201*27852ebeSDavid van Moolenbroek  */
202*27852ebeSDavid van Moolenbroek static void
uds_free(struct sock * sock)203*27852ebeSDavid van Moolenbroek uds_free(struct sock * sock)
204*27852ebeSDavid van Moolenbroek {
205*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
206*27852ebeSDavid van Moolenbroek 
207*27852ebeSDavid van Moolenbroek 	uds_io_cleanup(uds);
208*27852ebeSDavid van Moolenbroek 
209*27852ebeSDavid van Moolenbroek 	uds->uds_flags = 0;		/* no longer in use */
210*27852ebeSDavid van Moolenbroek 
211*27852ebeSDavid van Moolenbroek 	TAILQ_INSERT_HEAD(&uds_freelist, uds, uds_next);
212*27852ebeSDavid van Moolenbroek 
213*27852ebeSDavid van Moolenbroek 	assert(uds_in_use > 0);
214*27852ebeSDavid van Moolenbroek 	if (--uds_in_use == 0 && uds_running == FALSE)
215*27852ebeSDavid van Moolenbroek 		sef_cancel();
216*27852ebeSDavid van Moolenbroek }
217*27852ebeSDavid van Moolenbroek 
218*27852ebeSDavid van Moolenbroek /*
219*27852ebeSDavid van Moolenbroek  * Create a new socket.
220*27852ebeSDavid van Moolenbroek  */
221*27852ebeSDavid van Moolenbroek static sockid_t
uds_socket(int domain,int type,int protocol,endpoint_t user_endpt __unused,struct sock ** sockp,const struct sockevent_ops ** ops)222*27852ebeSDavid van Moolenbroek uds_socket(int domain, int type, int protocol, endpoint_t user_endpt __unused,
223*27852ebeSDavid van Moolenbroek 	struct sock ** sockp, const struct sockevent_ops ** ops)
224*27852ebeSDavid van Moolenbroek {
225*27852ebeSDavid van Moolenbroek 	struct udssock *uds;
226*27852ebeSDavid van Moolenbroek 	int r;
227*27852ebeSDavid van Moolenbroek 
228*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: socket(%d,%d,%d)\n", domain, type, protocol));
229*27852ebeSDavid van Moolenbroek 
230*27852ebeSDavid van Moolenbroek 	if (domain != PF_UNIX) {
231*27852ebeSDavid van Moolenbroek 		/* This means the service was configured incorrectly. */
232*27852ebeSDavid van Moolenbroek 		printf("UDS: got request for domain %d\n", domain);
233*27852ebeSDavid van Moolenbroek 
234*27852ebeSDavid van Moolenbroek 		return EAFNOSUPPORT;
235*27852ebeSDavid van Moolenbroek 	}
236*27852ebeSDavid van Moolenbroek 
237*27852ebeSDavid van Moolenbroek 	/* We support the following three socket types. */
238*27852ebeSDavid van Moolenbroek 	switch (type) {
239*27852ebeSDavid van Moolenbroek 	case SOCK_STREAM:
240*27852ebeSDavid van Moolenbroek 	case SOCK_SEQPACKET:
241*27852ebeSDavid van Moolenbroek 	case SOCK_DGRAM:
242*27852ebeSDavid van Moolenbroek 		break;
243*27852ebeSDavid van Moolenbroek 	default:
244*27852ebeSDavid van Moolenbroek 		return EPROTOTYPE;
245*27852ebeSDavid van Moolenbroek 	}
246*27852ebeSDavid van Moolenbroek 
247*27852ebeSDavid van Moolenbroek 	/*
248*27852ebeSDavid van Moolenbroek 	 * The PF_UNIX domain does not support particular protocols, so the
249*27852ebeSDavid van Moolenbroek 	 * given protocol must be zero (= anything that matches).
250*27852ebeSDavid van Moolenbroek 	 */
251*27852ebeSDavid van Moolenbroek 	if (protocol != UDSPROTO_UDS)
252*27852ebeSDavid van Moolenbroek 		return EPROTONOSUPPORT;
253*27852ebeSDavid van Moolenbroek 
254*27852ebeSDavid van Moolenbroek 	if ((r = uds_alloc(&uds)) != OK)
255*27852ebeSDavid van Moolenbroek 		return r;
256*27852ebeSDavid van Moolenbroek 
257*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: socket returns %d\n", uds_get_id(uds)));
258*27852ebeSDavid van Moolenbroek 
259*27852ebeSDavid van Moolenbroek 	*sockp = &uds->uds_sock;
260*27852ebeSDavid van Moolenbroek 	*ops = &uds_ops;
261*27852ebeSDavid van Moolenbroek 	return uds_get_id(uds);
262*27852ebeSDavid van Moolenbroek }
263*27852ebeSDavid van Moolenbroek 
264*27852ebeSDavid van Moolenbroek /*
265*27852ebeSDavid van Moolenbroek  * Connect a pair of sockets.
266*27852ebeSDavid van Moolenbroek  */
267*27852ebeSDavid van Moolenbroek static int
uds_pair(struct sock * sock1,struct sock * sock2,endpoint_t user_endpt)268*27852ebeSDavid van Moolenbroek uds_pair(struct sock * sock1, struct sock * sock2, endpoint_t user_endpt)
269*27852ebeSDavid van Moolenbroek {
270*27852ebeSDavid van Moolenbroek 	struct udssock *uds1 = (struct udssock *)sock1;
271*27852ebeSDavid van Moolenbroek 	struct udssock *uds2 = (struct udssock *)sock2;
272*27852ebeSDavid van Moolenbroek 
273*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: pair(%d,%d)\n", uds_get_id(uds1), uds_get_id(uds2)));
274*27852ebeSDavid van Moolenbroek 
275*27852ebeSDavid van Moolenbroek 	/* Only connection-oriented types are acceptable. */
276*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds1) == SOCK_DGRAM)
277*27852ebeSDavid van Moolenbroek 		return EOPNOTSUPP;
278*27852ebeSDavid van Moolenbroek 
279*27852ebeSDavid van Moolenbroek 	/* Connect the sockets. */
280*27852ebeSDavid van Moolenbroek 	uds1->uds_conn = uds2;
281*27852ebeSDavid van Moolenbroek 	uds2->uds_conn = uds1;
282*27852ebeSDavid van Moolenbroek 	uds1->uds_flags |= UDSF_CONNECTED;
283*27852ebeSDavid van Moolenbroek 	uds2->uds_flags |= UDSF_CONNECTED;
284*27852ebeSDavid van Moolenbroek 
285*27852ebeSDavid van Moolenbroek 	/* Obtain the (same) credentials for both sides of the connection. */
286*27852ebeSDavid van Moolenbroek 	uds_get_cred(uds1, user_endpt);
287*27852ebeSDavid van Moolenbroek 	memcpy(&uds2->uds_cred, &uds1->uds_cred, sizeof(uds2->uds_cred));
288*27852ebeSDavid van Moolenbroek 
289*27852ebeSDavid van Moolenbroek 	return OK;
290*27852ebeSDavid van Moolenbroek }
291*27852ebeSDavid van Moolenbroek 
292*27852ebeSDavid van Moolenbroek /*
293*27852ebeSDavid van Moolenbroek  * Disconnect a UDS socket, notifying or freeing up the other end of the
294*27852ebeSDavid van Moolenbroek  * connection depending on whether the socket was linked, that is, on the
295*27852ebeSDavid van Moolenbroek  * accept queue of a listening socket.
296*27852ebeSDavid van Moolenbroek  */
297*27852ebeSDavid van Moolenbroek static void
uds_disconnect(struct udssock * uds,int was_linked)298*27852ebeSDavid van Moolenbroek uds_disconnect(struct udssock * uds, int was_linked)
299*27852ebeSDavid van Moolenbroek {
300*27852ebeSDavid van Moolenbroek 	struct udssock *conn;
301*27852ebeSDavid van Moolenbroek 
302*27852ebeSDavid van Moolenbroek 	assert(uds_is_connected(uds));
303*27852ebeSDavid van Moolenbroek 	assert(uds_has_conn(uds));
304*27852ebeSDavid van Moolenbroek 
305*27852ebeSDavid van Moolenbroek 	conn = uds->uds_conn;
306*27852ebeSDavid van Moolenbroek 
307*27852ebeSDavid van Moolenbroek 	assert(uds_is_connected(conn));
308*27852ebeSDavid van Moolenbroek 	assert(uds_has_conn(conn));
309*27852ebeSDavid van Moolenbroek 	assert(!uds_has_link(conn));
310*27852ebeSDavid van Moolenbroek 	assert(conn->uds_conn == uds);
311*27852ebeSDavid van Moolenbroek 
312*27852ebeSDavid van Moolenbroek 	/* Disconnect the sockets. */
313*27852ebeSDavid van Moolenbroek 	uds->uds_conn = NULL;
314*27852ebeSDavid van Moolenbroek 	conn->uds_conn = NULL;
315*27852ebeSDavid van Moolenbroek 
316*27852ebeSDavid van Moolenbroek 	/*
317*27852ebeSDavid van Moolenbroek 	 * If the given socket is linked, then it is a connected socket for
318*27852ebeSDavid van Moolenbroek 	 * which the other end has been created but not yet accepted.  In that
319*27852ebeSDavid van Moolenbroek 	 * case, the other end ('conn') will have to be freed up.  Otherwise,
320*27852ebeSDavid van Moolenbroek 	 * it is a regular user-created socket and we must properly transition
321*27852ebeSDavid van Moolenbroek 	 * it into disconnected state.
322*27852ebeSDavid van Moolenbroek 	 */
323*27852ebeSDavid van Moolenbroek 	if (!was_linked) {
324*27852ebeSDavid van Moolenbroek 		sockevent_raise(&conn->uds_sock, SEV_SEND | SEV_RECV);
325*27852ebeSDavid van Moolenbroek 
326*27852ebeSDavid van Moolenbroek 		/*
327*27852ebeSDavid van Moolenbroek 		 * Clear the peer credentials so that they will not be mistaken
328*27852ebeSDavid van Moolenbroek 		 * for having been obtained at bind time.
329*27852ebeSDavid van Moolenbroek 		 */
330*27852ebeSDavid van Moolenbroek 		uds_clear_cred(conn);
331*27852ebeSDavid van Moolenbroek 	} else
332*27852ebeSDavid van Moolenbroek 		sockevent_raise(&conn->uds_sock, SEV_CLOSE);
333*27852ebeSDavid van Moolenbroek }
334*27852ebeSDavid van Moolenbroek 
335*27852ebeSDavid van Moolenbroek /*
336*27852ebeSDavid van Moolenbroek  * Add the socket 'link' to the queue of the socket 'uds'.  This also implies
337*27852ebeSDavid van Moolenbroek  * that 'link's link socket is set to 'uds'.
338*27852ebeSDavid van Moolenbroek  */
339*27852ebeSDavid van Moolenbroek static void
uds_add_queue(struct udssock * uds,struct udssock * link)340*27852ebeSDavid van Moolenbroek uds_add_queue(struct udssock * uds, struct udssock * link)
341*27852ebeSDavid van Moolenbroek {
342*27852ebeSDavid van Moolenbroek 
343*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: add_queue(%d,%d)\n",
344*27852ebeSDavid van Moolenbroek 	    uds_get_id(uds), uds_get_id(link)));
345*27852ebeSDavid van Moolenbroek 
346*27852ebeSDavid van Moolenbroek 	TAILQ_INSERT_TAIL(&uds->uds_queue, link, uds_next);
347*27852ebeSDavid van Moolenbroek 
348*27852ebeSDavid van Moolenbroek 	uds->uds_queued++;
349*27852ebeSDavid van Moolenbroek 	assert(uds->uds_queued != 0);
350*27852ebeSDavid van Moolenbroek 
351*27852ebeSDavid van Moolenbroek 	link->uds_link = uds;
352*27852ebeSDavid van Moolenbroek }
353*27852ebeSDavid van Moolenbroek 
354*27852ebeSDavid van Moolenbroek /*
355*27852ebeSDavid van Moolenbroek  * Remove the socket 'link' from the queue of the socket 'uds'.  This also
356*27852ebeSDavid van Moolenbroek  * reset 'link's link to NULL.
357*27852ebeSDavid van Moolenbroek  */
358*27852ebeSDavid van Moolenbroek static void
uds_del_queue(struct udssock * uds,struct udssock * link)359*27852ebeSDavid van Moolenbroek uds_del_queue(struct udssock * uds, struct udssock * link)
360*27852ebeSDavid van Moolenbroek {
361*27852ebeSDavid van Moolenbroek 
362*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: del_queue(%d,%d)\n",
363*27852ebeSDavid van Moolenbroek 	    uds_get_id(uds), uds_get_id(link)));
364*27852ebeSDavid van Moolenbroek 
365*27852ebeSDavid van Moolenbroek 	assert(link->uds_link == uds);
366*27852ebeSDavid van Moolenbroek 
367*27852ebeSDavid van Moolenbroek 	TAILQ_REMOVE(&uds->uds_queue, link, uds_next);
368*27852ebeSDavid van Moolenbroek 
369*27852ebeSDavid van Moolenbroek 	assert(uds->uds_queued > 0);
370*27852ebeSDavid van Moolenbroek 	uds->uds_queued--;
371*27852ebeSDavid van Moolenbroek 
372*27852ebeSDavid van Moolenbroek 	link->uds_link = NULL;
373*27852ebeSDavid van Moolenbroek }
374*27852ebeSDavid van Moolenbroek 
375*27852ebeSDavid van Moolenbroek /*
376*27852ebeSDavid van Moolenbroek  * Remove all sockets from the queue of the socket 'uds', with the exception of
377*27852ebeSDavid van Moolenbroek  * 'except' if non-NULL.  Raise an ECONNRESET error on all removed sockets that
378*27852ebeSDavid van Moolenbroek  * are not equal to 'uds'.
379*27852ebeSDavid van Moolenbroek  */
380*27852ebeSDavid van Moolenbroek static void
uds_clear_queue(struct udssock * uds,struct udssock * except)381*27852ebeSDavid van Moolenbroek uds_clear_queue(struct udssock * uds, struct udssock * except)
382*27852ebeSDavid van Moolenbroek {
383*27852ebeSDavid van Moolenbroek 	struct udssock *link, *tmp;
384*27852ebeSDavid van Moolenbroek 	int found;
385*27852ebeSDavid van Moolenbroek 
386*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: clear_queue(%d,%d)\n",
387*27852ebeSDavid van Moolenbroek 	    uds_get_id(uds), (except != NULL) ? uds_get_id(except) : -1));
388*27852ebeSDavid van Moolenbroek 
389*27852ebeSDavid van Moolenbroek 	found = 0;
390*27852ebeSDavid van Moolenbroek 
391*27852ebeSDavid van Moolenbroek 	/*
392*27852ebeSDavid van Moolenbroek 	 * Abort all connecting sockets queued on this socket, except for the
393*27852ebeSDavid van Moolenbroek 	 * given exception, which may be NULL.
394*27852ebeSDavid van Moolenbroek 	 */
395*27852ebeSDavid van Moolenbroek 	TAILQ_FOREACH_SAFE(link, &uds->uds_queue, uds_next, tmp) {
396*27852ebeSDavid van Moolenbroek 		if (link == except) {
397*27852ebeSDavid van Moolenbroek 			found++;
398*27852ebeSDavid van Moolenbroek 
399*27852ebeSDavid van Moolenbroek 			continue;
400*27852ebeSDavid van Moolenbroek 		}
401*27852ebeSDavid van Moolenbroek 
402*27852ebeSDavid van Moolenbroek 		dprintf(("UDS: clear_queue removes %d\n", uds_get_id(link)));
403*27852ebeSDavid van Moolenbroek 
404*27852ebeSDavid van Moolenbroek 		assert(uds_get_type(link) == SOCK_DGRAM ||
405*27852ebeSDavid van Moolenbroek 		    uds_is_connecting(link) || uds_is_connected(link));
406*27852ebeSDavid van Moolenbroek 
407*27852ebeSDavid van Moolenbroek 		uds_del_queue(uds, link);
408*27852ebeSDavid van Moolenbroek 
409*27852ebeSDavid van Moolenbroek 		/*
410*27852ebeSDavid van Moolenbroek 		 * Generate an error only if the socket was not linked to
411*27852ebeSDavid van Moolenbroek 		 * itself (only datagram sockets can be linked to themselves).
412*27852ebeSDavid van Moolenbroek 		 * The error is not helpful for applications in that case.
413*27852ebeSDavid van Moolenbroek 		 */
414*27852ebeSDavid van Moolenbroek 		if (uds != link)
415*27852ebeSDavid van Moolenbroek 			sockevent_set_error(&link->uds_sock, ECONNRESET);
416*27852ebeSDavid van Moolenbroek 
417*27852ebeSDavid van Moolenbroek 		/*
418*27852ebeSDavid van Moolenbroek 		 * If this is a listening socket, disconnect the connecting or
419*27852ebeSDavid van Moolenbroek 		 * connected end.  If a connected peer was already created for
420*27852ebeSDavid van Moolenbroek 		 * the queued socket, dispose of that peer.
421*27852ebeSDavid van Moolenbroek 		 *
422*27852ebeSDavid van Moolenbroek 		 * Clear credentials obtained when starting to connect (in
423*27852ebeSDavid van Moolenbroek 		 * which case the socket is always a connection-oriented
424*27852ebeSDavid van Moolenbroek 		 * socket), so that they will not be mistaken for credentials
425*27852ebeSDavid van Moolenbroek 		 * obtained at bind time.
426*27852ebeSDavid van Moolenbroek 		 */
427*27852ebeSDavid van Moolenbroek 		if (uds_get_type(link) != SOCK_DGRAM) {
428*27852ebeSDavid van Moolenbroek 			if (uds_is_connected(link))
429*27852ebeSDavid van Moolenbroek 				uds_disconnect(link, TRUE /*was_linked*/);
430*27852ebeSDavid van Moolenbroek 			else
431*27852ebeSDavid van Moolenbroek 				uds_clear_cred(link);
432*27852ebeSDavid van Moolenbroek 		}
433*27852ebeSDavid van Moolenbroek 	}
434*27852ebeSDavid van Moolenbroek 
435*27852ebeSDavid van Moolenbroek 	assert(uds->uds_queued == found);
436*27852ebeSDavid van Moolenbroek }
437*27852ebeSDavid van Moolenbroek 
438*27852ebeSDavid van Moolenbroek /*
439*27852ebeSDavid van Moolenbroek  * Check whether the socket address given in 'addr', with length 'addr_len', is
440*27852ebeSDavid van Moolenbroek  * a valid UNIX domain socket address (including a path to a socket file).  On
441*27852ebeSDavid van Moolenbroek  * success, return the (non-zero) length of the socket file's path, minus the
442*27852ebeSDavid van Moolenbroek  * null terminator which may in fact not be present.  The caller is responsible
443*27852ebeSDavid van Moolenbroek  * for copying and terminating the path as needed.  A pointer to the path as
444*27852ebeSDavid van Moolenbroek  * stored in 'addr' is returned in 'pathp'.  On failure, return an error code.
445*27852ebeSDavid van Moolenbroek  */
446*27852ebeSDavid van Moolenbroek static int
uds_check_addr(const struct sockaddr * addr,socklen_t addr_len,const char ** pathp)447*27852ebeSDavid van Moolenbroek uds_check_addr(const struct sockaddr * addr, socklen_t addr_len,
448*27852ebeSDavid van Moolenbroek 	const char ** pathp)
449*27852ebeSDavid van Moolenbroek {
450*27852ebeSDavid van Moolenbroek 	const char *p;
451*27852ebeSDavid van Moolenbroek 	size_t len;
452*27852ebeSDavid van Moolenbroek 
453*27852ebeSDavid van Moolenbroek 	/*
454*27852ebeSDavid van Moolenbroek 	 * We could cast to a sockaddr_un structure pointer first, but that
455*27852ebeSDavid van Moolenbroek 	 * would not provide any benefits here.  Instead, we use sa_data as the
456*27852ebeSDavid van Moolenbroek 	 * generic equivalent of sun_path.
457*27852ebeSDavid van Moolenbroek 	 */
458*27852ebeSDavid van Moolenbroek 	if (addr_len < offsetof(struct sockaddr, sa_data))
459cc5b1988SDavid van Moolenbroek 		return EINVAL;
460cc5b1988SDavid van Moolenbroek 
461*27852ebeSDavid van Moolenbroek 	if (addr->sa_family != AF_UNIX)
462*27852ebeSDavid van Moolenbroek 		return EAFNOSUPPORT;
463cc5b1988SDavid van Moolenbroek 
464*27852ebeSDavid van Moolenbroek 	len = (size_t)addr_len - offsetof(struct sockaddr, sa_data);
465*27852ebeSDavid van Moolenbroek 	if (len > 0 && (p = memchr(addr->sa_data, '\0', len)) != NULL)
466*27852ebeSDavid van Moolenbroek 		len = (size_t)(p - addr->sa_data);
467*27852ebeSDavid van Moolenbroek 
468*27852ebeSDavid van Moolenbroek 	/* The given path name must not be an empty string. */
469*27852ebeSDavid van Moolenbroek 	if (len == 0)
470*27852ebeSDavid van Moolenbroek 		return ENOENT;
471*27852ebeSDavid van Moolenbroek 
472*27852ebeSDavid van Moolenbroek 	/* This check should be redundant but better safe than sorry. */
473*27852ebeSDavid van Moolenbroek 	if (len >= UDS_PATH_MAX)
474*27852ebeSDavid van Moolenbroek 		return EINVAL;
475*27852ebeSDavid van Moolenbroek 
476*27852ebeSDavid van Moolenbroek 	*pathp = (const char *)addr->sa_data;
477*27852ebeSDavid van Moolenbroek 	return len;
478cc5b1988SDavid van Moolenbroek }
479cc5b1988SDavid van Moolenbroek 
480*27852ebeSDavid van Moolenbroek /*
481*27852ebeSDavid van Moolenbroek  * Given the socket file path given as 'path' with length 'path_len' (not
482*27852ebeSDavid van Moolenbroek  * necessarily null terminated), store a socket address with the path in
483*27852ebeSDavid van Moolenbroek  * 'addr', and return the socket address length in 'addr_len'.  The calling
484*27852ebeSDavid van Moolenbroek  * libraries (libsockdriver, libsockevent) and the static assert in uds.h
485*27852ebeSDavid van Moolenbroek  * guarantee that 'addr' is sufficiently large to store any address we generate
486*27852ebeSDavid van Moolenbroek  * here.  The libraries may subsequently copy out only a part of it to the user
487*27852ebeSDavid van Moolenbroek  * process.  This function always succeeds.
488*27852ebeSDavid van Moolenbroek  */
489*27852ebeSDavid van Moolenbroek void
uds_make_addr(const char * path,size_t len,struct sockaddr * addr,socklen_t * addr_len)490*27852ebeSDavid van Moolenbroek uds_make_addr(const char * path, size_t len, struct sockaddr * addr,
491*27852ebeSDavid van Moolenbroek 	socklen_t * addr_len)
492*27852ebeSDavid van Moolenbroek {
493cc5b1988SDavid van Moolenbroek 
494*27852ebeSDavid van Moolenbroek 	/*
495*27852ebeSDavid van Moolenbroek 	 * Generate the address.  The stored length (sa_len/sun_len) does not
496*27852ebeSDavid van Moolenbroek 	 * include a null terminator.  The entire structure does include a null
497*27852ebeSDavid van Moolenbroek 	 * terminator, but only if the socket is bound.
498*27852ebeSDavid van Moolenbroek 	 */
499*27852ebeSDavid van Moolenbroek 	addr->sa_len = offsetof(struct sockaddr, sa_data) + len;
500*27852ebeSDavid van Moolenbroek 	addr->sa_family = AF_UNIX;
501*27852ebeSDavid van Moolenbroek 	if (len > 0) {
502*27852ebeSDavid van Moolenbroek 		/* This call may (intentionally) overrun the sa_data size. */
503*27852ebeSDavid van Moolenbroek 		memcpy((char *)addr->sa_data, path, len);
504*27852ebeSDavid van Moolenbroek 		((char *)addr->sa_data)[len] = '\0';
505cc5b1988SDavid van Moolenbroek 
506*27852ebeSDavid van Moolenbroek 		/* The socket is bound, so include the null terminator. */
507*27852ebeSDavid van Moolenbroek 		len++;
508*27852ebeSDavid van Moolenbroek 		assert(len <= UDS_PATH_MAX);
509*27852ebeSDavid van Moolenbroek 	}
510cc5b1988SDavid van Moolenbroek 
511*27852ebeSDavid van Moolenbroek 	/* Note that this length may be different from sa_len/sun_len now. */
512*27852ebeSDavid van Moolenbroek 	*addr_len = offsetof(struct sockaddr, sa_data) + len;
513*27852ebeSDavid van Moolenbroek }
514*27852ebeSDavid van Moolenbroek 
515*27852ebeSDavid van Moolenbroek /*
516*27852ebeSDavid van Moolenbroek  * Bind a socket to a local address.
517*27852ebeSDavid van Moolenbroek  */
518*27852ebeSDavid van Moolenbroek static int
uds_bind(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)519*27852ebeSDavid van Moolenbroek uds_bind(struct sock * sock, const struct sockaddr * addr, socklen_t addr_len,
520*27852ebeSDavid van Moolenbroek 	endpoint_t user_endpt)
521*27852ebeSDavid van Moolenbroek {
522*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
523*27852ebeSDavid van Moolenbroek 	struct udssock *uds2;
524*27852ebeSDavid van Moolenbroek 	const char *path;
525*27852ebeSDavid van Moolenbroek 	size_t len;
526*27852ebeSDavid van Moolenbroek 	dev_t dev;
527*27852ebeSDavid van Moolenbroek 	ino_t ino;
528*27852ebeSDavid van Moolenbroek 	int r;
529*27852ebeSDavid van Moolenbroek 
530*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: bind(%d)\n", uds_get_id(uds)));
531*27852ebeSDavid van Moolenbroek 
532*27852ebeSDavid van Moolenbroek 	/* A socket may be bound at any time, but only once. */
533*27852ebeSDavid van Moolenbroek 	if (uds_is_bound(uds))
534*27852ebeSDavid van Moolenbroek 		return EINVAL;
535*27852ebeSDavid van Moolenbroek 
536*27852ebeSDavid van Moolenbroek 	/* Verify that the user gave us an acceptable address. */
537*27852ebeSDavid van Moolenbroek 	if ((r = uds_check_addr(addr, addr_len, &path)) < 0)
538*27852ebeSDavid van Moolenbroek 		return r;
539*27852ebeSDavid van Moolenbroek 	len = (size_t)r;
540*27852ebeSDavid van Moolenbroek 
541*27852ebeSDavid van Moolenbroek 	/* Attempt to create the socket file on the file system. */
542*27852ebeSDavid van Moolenbroek 	r = socketpath(user_endpt, path, len, SPATH_CREATE, &dev, &ino);
543*27852ebeSDavid van Moolenbroek 	if (r != OK)
544*27852ebeSDavid van Moolenbroek 		return r;
545*27852ebeSDavid van Moolenbroek 	assert(dev != NO_DEV && ino != 0);
546*27852ebeSDavid van Moolenbroek 
547*27852ebeSDavid van Moolenbroek 	/*
548*27852ebeSDavid van Moolenbroek 	 * It is possible that a socket file of a previously bound socket was
549*27852ebeSDavid van Moolenbroek 	 * unlinked, and due to inode number reuse, a new socket file has now
550*27852ebeSDavid van Moolenbroek 	 * been created with the same <dev,ino> pair.  In that case, we must
551*27852ebeSDavid van Moolenbroek 	 * unbind the old socket, because it must no longer be found.  The old
552*27852ebeSDavid van Moolenbroek 	 * socket will still have a path (and behave as though it is bound) but
553*27852ebeSDavid van Moolenbroek 	 * no longer be found through hash lookups.
554*27852ebeSDavid van Moolenbroek 	 */
555*27852ebeSDavid van Moolenbroek 	if ((uds2 = udshash_get(dev, ino)) != NULL) {
556*27852ebeSDavid van Moolenbroek 		udshash_del(uds2);
557*27852ebeSDavid van Moolenbroek 
558*27852ebeSDavid van Moolenbroek 		uds2->uds_dev = NO_DEV;
559*27852ebeSDavid van Moolenbroek 		uds2->uds_ino = 0;
560*27852ebeSDavid van Moolenbroek 	}
561*27852ebeSDavid van Moolenbroek 
562*27852ebeSDavid van Moolenbroek 	/*
563*27852ebeSDavid van Moolenbroek 	 * Obtain credentials for the socket, unless the socket is already
564*27852ebeSDavid van Moolenbroek 	 * connecting or connected, in which case we must not replace the
565*27852ebeSDavid van Moolenbroek 	 * credentials we obtained already.  We later clear those credentials
566*27852ebeSDavid van Moolenbroek 	 * upon a connection failure or disconnect, so that if the socket is
567*27852ebeSDavid van Moolenbroek 	 * then put in listening mode, we know there are no bind-time
568*27852ebeSDavid van Moolenbroek 	 * credentials.  Not ideal, but we really need two separate sets of
569*27852ebeSDavid van Moolenbroek 	 * credentials if we want to get this right, which is a waste of memory
570*27852ebeSDavid van Moolenbroek 	 * as no sane application writer would ever rely on credential passing
571*27852ebeSDavid van Moolenbroek 	 * after recycling a socket..
572*27852ebeSDavid van Moolenbroek 	 */
573*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) != SOCK_DGRAM && !uds_is_connecting(uds) &&
574*27852ebeSDavid van Moolenbroek 	    !uds_is_connected(uds))
575*27852ebeSDavid van Moolenbroek 		uds_get_cred(uds, user_endpt);
576*27852ebeSDavid van Moolenbroek 
577*27852ebeSDavid van Moolenbroek 	/* Asssign the address to the socket. */
578*27852ebeSDavid van Moolenbroek 	uds->uds_pathlen = len;
579*27852ebeSDavid van Moolenbroek 	memcpy(&uds->uds_path, path, len);
580*27852ebeSDavid van Moolenbroek 	uds->uds_dev = dev;
581*27852ebeSDavid van Moolenbroek 	uds->uds_ino = ino;
582*27852ebeSDavid van Moolenbroek 
583*27852ebeSDavid van Moolenbroek 	udshash_add(uds);
584*27852ebeSDavid van Moolenbroek 
585*27852ebeSDavid van Moolenbroek 	return OK;
586*27852ebeSDavid van Moolenbroek }
587*27852ebeSDavid van Moolenbroek 
588*27852ebeSDavid van Moolenbroek /*
589*27852ebeSDavid van Moolenbroek  * Look up a UDS socket based on a user-given address.  If a socket exists for
590*27852ebeSDavid van Moolenbroek  * the address, check if it is type-compatible with the given UDS socket.
591*27852ebeSDavid van Moolenbroek  * On succes, return OK, with 'peerp' set to the socket that was found.  On
592*27852ebeSDavid van Moolenbroek  * failure, return a negative error code.
593*27852ebeSDavid van Moolenbroek  */
594*27852ebeSDavid van Moolenbroek int
uds_lookup(struct udssock * uds,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt,struct udssock ** peerp)595*27852ebeSDavid van Moolenbroek uds_lookup(struct udssock * uds, const struct sockaddr * addr,
596*27852ebeSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt, struct udssock ** peerp)
597*27852ebeSDavid van Moolenbroek {
598*27852ebeSDavid van Moolenbroek 	struct udssock *peer;
599*27852ebeSDavid van Moolenbroek 	const char *path;
600*27852ebeSDavid van Moolenbroek 	size_t len;
601*27852ebeSDavid van Moolenbroek 	dev_t dev;
602*27852ebeSDavid van Moolenbroek 	ino_t ino;
603*27852ebeSDavid van Moolenbroek 	int r;
604*27852ebeSDavid van Moolenbroek 
605*27852ebeSDavid van Moolenbroek 	/* Verify that the user gave us an acceptable address. */
606*27852ebeSDavid van Moolenbroek 	if ((r = uds_check_addr(addr, addr_len, &path)) < 0)
607*27852ebeSDavid van Moolenbroek 		return r;
608*27852ebeSDavid van Moolenbroek 	len = (size_t)r;
609*27852ebeSDavid van Moolenbroek 
610*27852ebeSDavid van Moolenbroek 	/* Attempt to look up the socket file on the file system. */
611*27852ebeSDavid van Moolenbroek 	r = socketpath(user_endpt, path, len, SPATH_CHECK, &dev, &ino);
612*27852ebeSDavid van Moolenbroek 	if (r != OK)
613*27852ebeSDavid van Moolenbroek 		return r;
614*27852ebeSDavid van Moolenbroek 	assert(dev != NO_DEV && ino != 0);
615*27852ebeSDavid van Moolenbroek 
616*27852ebeSDavid van Moolenbroek 	if ((peer = udshash_get(dev, ino)) == NULL)
617*27852ebeSDavid van Moolenbroek 		return ECONNREFUSED;
618*27852ebeSDavid van Moolenbroek 	if (uds_get_type(peer) != uds_get_type(uds))
619*27852ebeSDavid van Moolenbroek 		return EPROTOTYPE;
620*27852ebeSDavid van Moolenbroek 
621*27852ebeSDavid van Moolenbroek 	*peerp = peer;
622*27852ebeSDavid van Moolenbroek 	return OK;
623*27852ebeSDavid van Moolenbroek }
624*27852ebeSDavid van Moolenbroek 
625*27852ebeSDavid van Moolenbroek /*
626*27852ebeSDavid van Moolenbroek  * Given the listening socket 'uds', and the socket 'link' that is calling or
627*27852ebeSDavid van Moolenbroek  * has called connect(2) and is or will be linked to the listening socket's
628*27852ebeSDavid van Moolenbroek  * queue, create a new socket and connect it to 'link', putting both sockets in
629*27852ebeSDavid van Moolenbroek  * the connected state.  The given link socket may be in unconnected,
630*27852ebeSDavid van Moolenbroek  * connecting, or disconnected state prior to the call.  Return OK or an error
631*27852ebeSDavid van Moolenbroek  * code.  The link state of the link socket remains unchanged in any case.
632*27852ebeSDavid van Moolenbroek  */
633*27852ebeSDavid van Moolenbroek static int
uds_attach(struct udssock * uds,struct udssock * link)634*27852ebeSDavid van Moolenbroek uds_attach(struct udssock * uds, struct udssock * link)
635*27852ebeSDavid van Moolenbroek {
636*27852ebeSDavid van Moolenbroek 	struct udssock *conn;
637*27852ebeSDavid van Moolenbroek 	int r;
638*27852ebeSDavid van Moolenbroek 
639*27852ebeSDavid van Moolenbroek 	/*
640*27852ebeSDavid van Moolenbroek 	 * Allocate a new socket to use as peer socket for the connection that
641*27852ebeSDavid van Moolenbroek 	 * is about to be established.  The new socket is not yet known by
642*27852ebeSDavid van Moolenbroek 	 * libsockevent.
643*27852ebeSDavid van Moolenbroek 	 */
644*27852ebeSDavid van Moolenbroek 	if ((r = uds_alloc(&conn)) != OK)
645*27852ebeSDavid van Moolenbroek 		return r;
646*27852ebeSDavid van Moolenbroek 
647*27852ebeSDavid van Moolenbroek 	/*
648*27852ebeSDavid van Moolenbroek 	 * Ask libsockevent to clone the sock object in the new UDS socket from
649*27852ebeSDavid van Moolenbroek 	 * the listening socket.  This adds the sock object to libsockevent's
650*27852ebeSDavid van Moolenbroek 	 * data structures and ensures that we can safely use the socket
651*27852ebeSDavid van Moolenbroek 	 * despite the fact that it has not yet been accepted (and thus
652*27852ebeSDavid van Moolenbroek 	 * returned to libsockevent).  From this moment on, we must either
653*27852ebeSDavid van Moolenbroek 	 * return the socket's ID (but not a pointer to it!) from uds_accept()
654*27852ebeSDavid van Moolenbroek 	 * or raise SEV_CLOSE on it.
655*27852ebeSDavid van Moolenbroek 	 */
656*27852ebeSDavid van Moolenbroek 	sockevent_clone(&uds->uds_sock, &conn->uds_sock, uds_get_id(conn));
657*27852ebeSDavid van Moolenbroek 
658*27852ebeSDavid van Moolenbroek 	/* Connect the link socket to the new socket. */
659*27852ebeSDavid van Moolenbroek 	link->uds_conn = conn;
660*27852ebeSDavid van Moolenbroek 	link->uds_flags |= UDSF_CONNECTED;
661*27852ebeSDavid van Moolenbroek 
662*27852ebeSDavid van Moolenbroek 	/*
663*27852ebeSDavid van Moolenbroek 	 * Connect the new socket to the link socket as well.  The child
664*27852ebeSDavid van Moolenbroek 	 * socket should also inherit pretty much all settings from the
665*27852ebeSDavid van Moolenbroek 	 * listening socket, including the bind path and the listening socket's
666*27852ebeSDavid van Moolenbroek 	 * bind-time credentials.
667*27852ebeSDavid van Moolenbroek 	 */
668*27852ebeSDavid van Moolenbroek 	conn->uds_conn = link;
669*27852ebeSDavid van Moolenbroek 	conn->uds_flags = uds->uds_flags & (UDSF_PASSCRED | UDSF_CONNWAIT);
670*27852ebeSDavid van Moolenbroek 	conn->uds_flags |= UDSF_CONNECTED;
671*27852ebeSDavid van Moolenbroek 	conn->uds_pathlen = uds->uds_pathlen;
672*27852ebeSDavid van Moolenbroek 	memcpy(conn->uds_path, uds->uds_path, (size_t)uds->uds_pathlen);
673*27852ebeSDavid van Moolenbroek 	memcpy(&conn->uds_cred, &uds->uds_cred, sizeof(conn->uds_cred));
674*27852ebeSDavid van Moolenbroek 
675*27852ebeSDavid van Moolenbroek 	return OK;
676*27852ebeSDavid van Moolenbroek }
677*27852ebeSDavid van Moolenbroek 
678*27852ebeSDavid van Moolenbroek /*
679*27852ebeSDavid van Moolenbroek  * Connect a socket to a remote address.
680*27852ebeSDavid van Moolenbroek  */
681*27852ebeSDavid van Moolenbroek static int
uds_connect(struct sock * sock,const struct sockaddr * addr,socklen_t addr_len,endpoint_t user_endpt)682*27852ebeSDavid van Moolenbroek uds_connect(struct sock * sock, const struct sockaddr * addr,
683*27852ebeSDavid van Moolenbroek 	socklen_t addr_len, endpoint_t user_endpt)
684*27852ebeSDavid van Moolenbroek {
685*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
686*27852ebeSDavid van Moolenbroek 	struct udssock *link;
687*27852ebeSDavid van Moolenbroek 	int r;
688*27852ebeSDavid van Moolenbroek 
689*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: connect(%d)\n", uds_get_id(uds)));
690*27852ebeSDavid van Moolenbroek 
691*27852ebeSDavid van Moolenbroek 	/* For connection-oriented sockets, several state checks apply. */
692*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) != SOCK_DGRAM) {
693*27852ebeSDavid van Moolenbroek 		if (uds_is_listening(uds))
694*27852ebeSDavid van Moolenbroek 			return EOPNOTSUPP;
695*27852ebeSDavid van Moolenbroek 		if (uds_is_connecting(uds))
696*27852ebeSDavid van Moolenbroek 			return EALREADY;
697*27852ebeSDavid van Moolenbroek 		if (uds_is_connected(uds))
698*27852ebeSDavid van Moolenbroek 			return EISCONN;
699*27852ebeSDavid van Moolenbroek 		/* Disconnected sockets may be reconnected, see below. */
700*27852ebeSDavid van Moolenbroek 	} else {
701*27852ebeSDavid van Moolenbroek 		/*
702*27852ebeSDavid van Moolenbroek 		 * Connectionless sockets may be unconnected by providing an
703*27852ebeSDavid van Moolenbroek 		 * address with family AF_UNSPEC.  Handle this case first here.
704*27852ebeSDavid van Moolenbroek 		 */
705*27852ebeSDavid van Moolenbroek 		if (addr_len >= offsetof(struct sockaddr, sa_data) &&
706*27852ebeSDavid van Moolenbroek 		    addr->sa_family == AF_UNSPEC) {
707*27852ebeSDavid van Moolenbroek 			/*
708*27852ebeSDavid van Moolenbroek 			 * Reset this socket's previous connection to another
709*27852ebeSDavid van Moolenbroek 			 * socket, if any.  Unconnecting has no effect on other
710*27852ebeSDavid van Moolenbroek 			 * sockets connected to this socket, though.
711*27852ebeSDavid van Moolenbroek 			 */
712*27852ebeSDavid van Moolenbroek 			if (uds_has_link(uds))
713*27852ebeSDavid van Moolenbroek 				uds_del_queue(uds->uds_link, uds);
714*27852ebeSDavid van Moolenbroek 
715*27852ebeSDavid van Moolenbroek 			return OK;
716*27852ebeSDavid van Moolenbroek 		}
717*27852ebeSDavid van Moolenbroek 	}
718*27852ebeSDavid van Moolenbroek 
719*27852ebeSDavid van Moolenbroek 	/*
720*27852ebeSDavid van Moolenbroek 	 * Find the socket identified by the given address.  If it exists at
721*27852ebeSDavid van Moolenbroek 	 * all, see if it is a proper match.
722*27852ebeSDavid van Moolenbroek 	 */
723*27852ebeSDavid van Moolenbroek 	if ((r = uds_lookup(uds, addr, addr_len, user_endpt, &link)) != OK)
724*27852ebeSDavid van Moolenbroek 		return r;
725*27852ebeSDavid van Moolenbroek 
726*27852ebeSDavid van Moolenbroek 	/*
727*27852ebeSDavid van Moolenbroek 	 * Handle connectionless sockets first, in which case a connect links
728*27852ebeSDavid van Moolenbroek 	 * the socket to a send target and limits receipt to datagrams from
729*27852ebeSDavid van Moolenbroek 	 * that target.  We actually point the socket to the peer socket,
730*27852ebeSDavid van Moolenbroek 	 * through uds_link.  That also means that if the target socket
731*27852ebeSDavid van Moolenbroek 	 * disappears, we have to reset any sockets connected to it, in which
732*27852ebeSDavid van Moolenbroek 	 * case we return them to the unconnected state.  In order to allow
733*27852ebeSDavid van Moolenbroek 	 * finding all sockets connected to a particular socket, we put all
734*27852ebeSDavid van Moolenbroek 	 * those sockets on their target's queue, hence why we use uds_link and
735*27852ebeSDavid van Moolenbroek 	 * not uds_conn.  As mentioned before, we allow reconnecting without
736*27852ebeSDavid van Moolenbroek 	 * restrictions.
737*27852ebeSDavid van Moolenbroek 	 * TODO: see if reconnecting should clear a pending ECONNRESET.
738*27852ebeSDavid van Moolenbroek 	 *
739*27852ebeSDavid van Moolenbroek 	 * An important note: 'uds' and 'link' may actually be the same socket,
740*27852ebeSDavid van Moolenbroek 	 * if the caller chooses to connect a socket with itself!
741*27852ebeSDavid van Moolenbroek 	 */
742*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) == SOCK_DGRAM) {
743*27852ebeSDavid van Moolenbroek 		/* Reconnecting to the same socket has no effect. */
744*27852ebeSDavid van Moolenbroek 		if (uds_has_link(uds) && uds->uds_link == link)
745*27852ebeSDavid van Moolenbroek 			return OK;
746*27852ebeSDavid van Moolenbroek 
747*27852ebeSDavid van Moolenbroek 		/*
748*27852ebeSDavid van Moolenbroek 		 * If the intended target is linked to another socket, we
749*27852ebeSDavid van Moolenbroek 		 * refuse linking to it.  Sending or receiving would never work
750*27852ebeSDavid van Moolenbroek 		 * anyway.  Do allow a socket to link to itself after being
751*27852ebeSDavid van Moolenbroek 		 * linked to another socket.  The error code is the same as in
752*27852ebeSDavid van Moolenbroek 		 * the sending code, borrowed from Linux.
753*27852ebeSDavid van Moolenbroek 		 */
754*27852ebeSDavid van Moolenbroek 		if (uds != link && uds_has_link(link) && link->uds_link != uds)
755*27852ebeSDavid van Moolenbroek 			return EPERM;
756*27852ebeSDavid van Moolenbroek 
757*27852ebeSDavid van Moolenbroek 		/*
758*27852ebeSDavid van Moolenbroek 		 * Reset this socket's previous link to another socket, if any.
759*27852ebeSDavid van Moolenbroek 		 */
760*27852ebeSDavid van Moolenbroek 		if (uds_has_link(uds))
761*27852ebeSDavid van Moolenbroek 			uds_del_queue(uds->uds_link, uds);
762*27852ebeSDavid van Moolenbroek 
763*27852ebeSDavid van Moolenbroek 		/*
764*27852ebeSDavid van Moolenbroek 		 * Reset any links to this socket, except for the one by
765*27852ebeSDavid van Moolenbroek 		 * the intended target.  Sending or receiving would no longer
766*27852ebeSDavid van Moolenbroek 		 * work anyway.  If the socket was linked to itself, clear its
767*27852ebeSDavid van Moolenbroek 		 * self-link without generating an ECONNRESET.  If the socket
768*27852ebeSDavid van Moolenbroek 		 * is relinking to itself, reestablish the link after first
769*27852ebeSDavid van Moolenbroek 		 * clearing it.
770*27852ebeSDavid van Moolenbroek 		 */
771*27852ebeSDavid van Moolenbroek 		uds_clear_queue(uds, (uds != link) ? link : NULL);
772*27852ebeSDavid van Moolenbroek 
773*27852ebeSDavid van Moolenbroek 		uds_add_queue(link, uds);
774*27852ebeSDavid van Moolenbroek 
775*27852ebeSDavid van Moolenbroek 		return OK;
776*27852ebeSDavid van Moolenbroek 	}
777*27852ebeSDavid van Moolenbroek 
778*27852ebeSDavid van Moolenbroek 	/*
779*27852ebeSDavid van Moolenbroek 	 * For connection-oriented sockets there is more to do.  First, make
780*27852ebeSDavid van Moolenbroek 	 * sure that the peer is a listening socket, that it has not been shut
781*27852ebeSDavid van Moolenbroek 	 * down, and that its backlog is not already at the configured maximum.
782*27852ebeSDavid van Moolenbroek 	 */
783*27852ebeSDavid van Moolenbroek 	if (!uds_is_listening(link))
784*27852ebeSDavid van Moolenbroek 		return ECONNREFUSED;
785*27852ebeSDavid van Moolenbroek 
786*27852ebeSDavid van Moolenbroek 	if (uds_is_shutdown(link, SFL_SHUT_RD | SFL_SHUT_WR))
787*27852ebeSDavid van Moolenbroek 		return ECONNREFUSED;
788*27852ebeSDavid van Moolenbroek 
789*27852ebeSDavid van Moolenbroek 	if (link->uds_queued >= link->uds_backlog)
790*27852ebeSDavid van Moolenbroek 		return ECONNREFUSED;
791*27852ebeSDavid van Moolenbroek 
792*27852ebeSDavid van Moolenbroek 	/*
793*27852ebeSDavid van Moolenbroek 	 * The behavior of connect(2) now depends on whether LOCAL_CONNWAIT is
794*27852ebeSDavid van Moolenbroek 	 * set on either the connecting or the listening socket.  If it is not,
795*27852ebeSDavid van Moolenbroek 	 * the socket will be connected to a new as-yet invisible socket, which
796*27852ebeSDavid van Moolenbroek 	 * will be the one returned from accept(2) later.  If it was, the
797*27852ebeSDavid van Moolenbroek 	 * socket will be put in the connecting state.
798*27852ebeSDavid van Moolenbroek 	 */
799*27852ebeSDavid van Moolenbroek 	if (!((uds->uds_flags | link->uds_flags) & UDSF_CONNWAIT)) {
800*27852ebeSDavid van Moolenbroek 		if ((r = uds_attach(link, uds)) != OK)
801*27852ebeSDavid van Moolenbroek 			return r;
802*27852ebeSDavid van Moolenbroek 
803*27852ebeSDavid van Moolenbroek 		assert(uds_is_connected(uds));
804*27852ebeSDavid van Moolenbroek 	} else {
805*27852ebeSDavid van Moolenbroek 		/*
806*27852ebeSDavid van Moolenbroek 		 * Disconnected sockets now stop being connected.  Any pending
807*27852ebeSDavid van Moolenbroek 		 * data can still be received, though.
808*27852ebeSDavid van Moolenbroek 		 */
809*27852ebeSDavid van Moolenbroek 		uds->uds_flags &= ~UDSF_CONNECTED;
810*27852ebeSDavid van Moolenbroek 
811*27852ebeSDavid van Moolenbroek 		r = SUSPEND;
812*27852ebeSDavid van Moolenbroek 	}
813*27852ebeSDavid van Moolenbroek 
814*27852ebeSDavid van Moolenbroek 	/* Obtain credentials for the socket. */
815*27852ebeSDavid van Moolenbroek 	uds_get_cred(uds, user_endpt);
816*27852ebeSDavid van Moolenbroek 
817*27852ebeSDavid van Moolenbroek 	/* Add the socket at the end of the listening socket's queue. */
818*27852ebeSDavid van Moolenbroek 	uds_add_queue(link, uds);
819*27852ebeSDavid van Moolenbroek 
820*27852ebeSDavid van Moolenbroek 	assert(r != SUSPEND || uds_is_connecting(uds));
821*27852ebeSDavid van Moolenbroek 
822*27852ebeSDavid van Moolenbroek 	/*
823*27852ebeSDavid van Moolenbroek 	 * Let an accept call handle the rest, which will in turn resume this
824*27852ebeSDavid van Moolenbroek 	 * connect call.  The sockevent library ensures that this works even if
825*27852ebeSDavid van Moolenbroek 	 * the call is non-blocking.
826*27852ebeSDavid van Moolenbroek 	 */
827*27852ebeSDavid van Moolenbroek 	sockevent_raise(&link->uds_sock, SEV_ACCEPT);
828*27852ebeSDavid van Moolenbroek 
829*27852ebeSDavid van Moolenbroek 	return r;
830*27852ebeSDavid van Moolenbroek }
831*27852ebeSDavid van Moolenbroek 
832*27852ebeSDavid van Moolenbroek /*
833*27852ebeSDavid van Moolenbroek  * Put a socket in listening mode.
834*27852ebeSDavid van Moolenbroek  */
835*27852ebeSDavid van Moolenbroek static int
uds_listen(struct sock * sock,int backlog)836*27852ebeSDavid van Moolenbroek uds_listen(struct sock * sock, int backlog)
837*27852ebeSDavid van Moolenbroek {
838*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
839*27852ebeSDavid van Moolenbroek 
840*27852ebeSDavid van Moolenbroek 	/* The maximum backlog value must not exceed its field size. */
841*27852ebeSDavid van Moolenbroek 	assert(SOMAXCONN <= USHRT_MAX);
842*27852ebeSDavid van Moolenbroek 
843*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: listen(%d)\n", uds_get_id(uds)));
844*27852ebeSDavid van Moolenbroek 
845*27852ebeSDavid van Moolenbroek 	/* Only connection-oriented types may be put in listening mode. */
846*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) == SOCK_DGRAM)
847*27852ebeSDavid van Moolenbroek 		return EOPNOTSUPP;
848*27852ebeSDavid van Moolenbroek 
849*27852ebeSDavid van Moolenbroek 	/* A connecting or connected socket may not listen. */
850*27852ebeSDavid van Moolenbroek 	if (uds_is_connecting(uds) || uds_is_connected(uds))
851*27852ebeSDavid van Moolenbroek 		return EINVAL;
852*27852ebeSDavid van Moolenbroek 
853*27852ebeSDavid van Moolenbroek 	/* POSIX says that this is now the appropriate error code here. */
854*27852ebeSDavid van Moolenbroek 	if (!uds_is_bound(uds))
855*27852ebeSDavid van Moolenbroek 		return EDESTADDRREQ;
856*27852ebeSDavid van Moolenbroek 
857*27852ebeSDavid van Moolenbroek 	/*
858*27852ebeSDavid van Moolenbroek 	 * The socket is now entering the listening state.  If it was
859*27852ebeSDavid van Moolenbroek 	 * previously disconnected, clear the connection flag.
860*27852ebeSDavid van Moolenbroek 	 */
861*27852ebeSDavid van Moolenbroek 	uds->uds_flags &= ~UDSF_CONNECTED;
862*27852ebeSDavid van Moolenbroek 
863*27852ebeSDavid van Moolenbroek 	/*
864*27852ebeSDavid van Moolenbroek 	 * We do not remove sockets from the backlog if it is now being dropped
865*27852ebeSDavid van Moolenbroek 	 * below the current number of queued sockets.  We only refuse newly
866*27852ebeSDavid van Moolenbroek 	 * connecting sockets beyond the backlog size.
867*27852ebeSDavid van Moolenbroek 	 */
868*27852ebeSDavid van Moolenbroek 	uds->uds_backlog = backlog;
869*27852ebeSDavid van Moolenbroek 
870*27852ebeSDavid van Moolenbroek 	return OK;
871*27852ebeSDavid van Moolenbroek }
872*27852ebeSDavid van Moolenbroek 
873*27852ebeSDavid van Moolenbroek /*
874*27852ebeSDavid van Moolenbroek  * Test whether an accept request would block.  Return OK if a socket could be
875*27852ebeSDavid van Moolenbroek  * accepted, an appropriate error code if an accept call would fail instantly,
876*27852ebeSDavid van Moolenbroek  * or SUSPEND if the accept request would block waiting for a connection.
877*27852ebeSDavid van Moolenbroek  */
878*27852ebeSDavid van Moolenbroek static int
uds_test_accept(struct sock * sock)879*27852ebeSDavid van Moolenbroek uds_test_accept(struct sock * sock)
880*27852ebeSDavid van Moolenbroek {
881*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
882*27852ebeSDavid van Moolenbroek 
883*27852ebeSDavid van Moolenbroek 	/*
884*27852ebeSDavid van Moolenbroek 	 * Ensure that the socket is in listening mode.  If not, we must return
885*27852ebeSDavid van Moolenbroek 	 * the error code that is appropriate for this socket type.
886*27852ebeSDavid van Moolenbroek 	 */
887*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) == SOCK_DGRAM)
888*27852ebeSDavid van Moolenbroek 		return EOPNOTSUPP;
889*27852ebeSDavid van Moolenbroek 	if (!uds_is_listening(uds))
890*27852ebeSDavid van Moolenbroek 		return EINVAL;
891*27852ebeSDavid van Moolenbroek 
892*27852ebeSDavid van Moolenbroek 	/*
893*27852ebeSDavid van Moolenbroek 	 * If the socket has been shut down, new connections are no longer
894*27852ebeSDavid van Moolenbroek 	 * accepted and accept calls no longer block.  This is not a POSIX
895*27852ebeSDavid van Moolenbroek 	 * requirement, but rather an application convenience feature.
896*27852ebeSDavid van Moolenbroek 	 */
897*27852ebeSDavid van Moolenbroek 	if (uds->uds_queued == 0) {
898*27852ebeSDavid van Moolenbroek 		if (uds_is_shutdown(uds, SFL_SHUT_RD | SFL_SHUT_WR))
899*27852ebeSDavid van Moolenbroek 			return ECONNABORTED;
900*27852ebeSDavid van Moolenbroek 
901*27852ebeSDavid van Moolenbroek 		return SUSPEND;
902cc5b1988SDavid van Moolenbroek 	}
903cc5b1988SDavid van Moolenbroek 
904cc5b1988SDavid van Moolenbroek 	return OK;
905cc5b1988SDavid van Moolenbroek }
906cc5b1988SDavid van Moolenbroek 
907*27852ebeSDavid van Moolenbroek /*
908*27852ebeSDavid van Moolenbroek  * Accept a connection on a listening socket, creating a new socket.  On
909*27852ebeSDavid van Moolenbroek  * success, return the new socket identifier, with the new socket stored in
910*27852ebeSDavid van Moolenbroek  * 'newsockp'.  Otherwise, return an error code.
911*27852ebeSDavid van Moolenbroek  */
912*27852ebeSDavid van Moolenbroek static sockid_t
uds_accept(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len,endpoint_t user_endpt __unused,struct sock ** newsockp)913*27852ebeSDavid van Moolenbroek uds_accept(struct sock * sock, struct sockaddr * addr, socklen_t * addr_len,
914*27852ebeSDavid van Moolenbroek 	endpoint_t user_endpt __unused, struct sock ** newsockp)
915*27852ebeSDavid van Moolenbroek {
916*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
917*27852ebeSDavid van Moolenbroek 	struct udssock *link, *conn;
918*27852ebeSDavid van Moolenbroek 	sockid_t r;
919*27852ebeSDavid van Moolenbroek 
920*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: accept(%d)\n", uds_get_id(uds)));
921*27852ebeSDavid van Moolenbroek 
922*27852ebeSDavid van Moolenbroek 	if ((r = uds_test_accept(sock)) != OK)
923*27852ebeSDavid van Moolenbroek 		return r;
924*27852ebeSDavid van Moolenbroek 
925*27852ebeSDavid van Moolenbroek 	/*
926*27852ebeSDavid van Moolenbroek 	 * Take the first connecting socket off the listening queue.
927*27852ebeSDavid van Moolenbroek 	 */
928*27852ebeSDavid van Moolenbroek 	assert(!TAILQ_EMPTY(&uds->uds_queue));
929*27852ebeSDavid van Moolenbroek 
930*27852ebeSDavid van Moolenbroek 	link = TAILQ_FIRST(&uds->uds_queue);
931*27852ebeSDavid van Moolenbroek 
932*27852ebeSDavid van Moolenbroek 	/*
933*27852ebeSDavid van Moolenbroek 	 * Depending on the LOCAL_CONNWAIT setting at the time of connect(2),
934*27852ebeSDavid van Moolenbroek 	 * the socket may be connecting or connected.  In the latter case, its
935*27852ebeSDavid van Moolenbroek 	 * attached socket is the socket we will return now.  Otherwise we have
936*27852ebeSDavid van Moolenbroek 	 * to attach a socket first.
937*27852ebeSDavid van Moolenbroek 	 */
938*27852ebeSDavid van Moolenbroek 	assert(uds_is_connecting(link) || uds_is_connected(link));
939*27852ebeSDavid van Moolenbroek 
940*27852ebeSDavid van Moolenbroek 	if (uds_is_connecting(link)) {
941*27852ebeSDavid van Moolenbroek 		/*
942*27852ebeSDavid van Moolenbroek 		 * Attach a new socket.  If this fails, return the error but
943*27852ebeSDavid van Moolenbroek 		 * leave the connecting socket on the listening queue.
944*27852ebeSDavid van Moolenbroek 		 */
945*27852ebeSDavid van Moolenbroek 		if ((r = uds_attach(uds, link)) != OK)
946*27852ebeSDavid van Moolenbroek 			return r;
947*27852ebeSDavid van Moolenbroek 
948*27852ebeSDavid van Moolenbroek 		assert(uds_is_connected(link));
949*27852ebeSDavid van Moolenbroek 
950*27852ebeSDavid van Moolenbroek 		/*
951*27852ebeSDavid van Moolenbroek 		 * Wake up blocked (connect, send, select) calls on the peer
952*27852ebeSDavid van Moolenbroek 		 * socket.
953*27852ebeSDavid van Moolenbroek 		 */
954*27852ebeSDavid van Moolenbroek 		sockevent_raise(&link->uds_sock, SEV_CONNECT);
955*27852ebeSDavid van Moolenbroek 	}
956*27852ebeSDavid van Moolenbroek 
957*27852ebeSDavid van Moolenbroek 	uds_del_queue(uds, link);
958*27852ebeSDavid van Moolenbroek 
959*27852ebeSDavid van Moolenbroek 	/* Return the peer socket's address to the caller. */
960*27852ebeSDavid van Moolenbroek 	uds_make_addr(link->uds_path, link->uds_pathlen, addr, addr_len);
961*27852ebeSDavid van Moolenbroek 
962*27852ebeSDavid van Moolenbroek 	conn = link->uds_conn;
963*27852ebeSDavid van Moolenbroek 
964*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: accept returns %d\n", uds_get_id(conn)));
965*27852ebeSDavid van Moolenbroek 
966*27852ebeSDavid van Moolenbroek 	/*
967*27852ebeSDavid van Moolenbroek 	 * We already cloned the sock object, so return its ID but not a
968*27852ebeSDavid van Moolenbroek 	 * pointer to it.  That tells libsockevent not to reinitialize it.
969*27852ebeSDavid van Moolenbroek 	 */
970*27852ebeSDavid van Moolenbroek 	*newsockp = NULL;
971*27852ebeSDavid van Moolenbroek 	return uds_get_id(conn);
972*27852ebeSDavid van Moolenbroek }
973*27852ebeSDavid van Moolenbroek 
974*27852ebeSDavid van Moolenbroek /*
975*27852ebeSDavid van Moolenbroek  * Set socket options.
976*27852ebeSDavid van Moolenbroek  */
977cc5b1988SDavid van Moolenbroek static int
uds_setsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t len)978*27852ebeSDavid van Moolenbroek uds_setsockopt(struct sock * sock, int level, int name,
979*27852ebeSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t len)
980cc5b1988SDavid van Moolenbroek {
981*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
982*27852ebeSDavid van Moolenbroek 	int r, val;
983cc5b1988SDavid van Moolenbroek 
984*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: setsockopt(%d,%d,%d)\n", uds_get_id(uds), level, name));
985cc5b1988SDavid van Moolenbroek 
986*27852ebeSDavid van Moolenbroek 	switch (level) {
987*27852ebeSDavid van Moolenbroek 	case SOL_SOCKET:
988*27852ebeSDavid van Moolenbroek 		switch (name) {
989*27852ebeSDavid van Moolenbroek 		case SO_SNDBUF:
990*27852ebeSDavid van Moolenbroek 		case SO_RCVBUF:
991*27852ebeSDavid van Moolenbroek 			/*
992*27852ebeSDavid van Moolenbroek 			 * The send buffer size may not be changed because the
993*27852ebeSDavid van Moolenbroek 			 * buffer is the same as the other side's receive
994*27852ebeSDavid van Moolenbroek 			 * buffer, and what the other side is may vary from
995*27852ebeSDavid van Moolenbroek 			 * send call to send call.  Changing the receive buffer
996*27852ebeSDavid van Moolenbroek 			 * size would disallow us from even accurately guessing
997*27852ebeSDavid van Moolenbroek 			 * the send buffer size in getsockopt calls.  Therefore
998*27852ebeSDavid van Moolenbroek 			 * both are hardcoded and cannot actually be changed.
999*27852ebeSDavid van Moolenbroek 			 * In order to support applications that want at least
1000*27852ebeSDavid van Moolenbroek 			 * a certain minimum, we do accept requests to shrink
1001*27852ebeSDavid van Moolenbroek 			 * either buffer, but we ignore the given size.
1002*27852ebeSDavid van Moolenbroek 			 */
1003*27852ebeSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1004*27852ebeSDavid van Moolenbroek 			    len)) != OK)
1005*27852ebeSDavid van Moolenbroek 				return r;
1006cc5b1988SDavid van Moolenbroek 
1007*27852ebeSDavid van Moolenbroek 			if (val <= 0 || (size_t)val > uds_io_buflen())
1008cc5b1988SDavid van Moolenbroek 				return EINVAL;
1009cc5b1988SDavid van Moolenbroek 
1010*27852ebeSDavid van Moolenbroek 			return OK; /* ignore new value */
1011*27852ebeSDavid van Moolenbroek 		}
1012cc5b1988SDavid van Moolenbroek 
1013cc5b1988SDavid van Moolenbroek 		break;
1014cc5b1988SDavid van Moolenbroek 
1015*27852ebeSDavid van Moolenbroek 	case UDSPROTO_UDS:
1016*27852ebeSDavid van Moolenbroek 		switch (name) {
1017*27852ebeSDavid van Moolenbroek 		case LOCAL_CREDS:
1018*27852ebeSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1019*27852ebeSDavid van Moolenbroek 			    len)) != OK)
1020cc5b1988SDavid van Moolenbroek 				return r;
1021cc5b1988SDavid van Moolenbroek 
1022*27852ebeSDavid van Moolenbroek 			if (val)
1023*27852ebeSDavid van Moolenbroek 				uds->uds_flags |= UDSF_PASSCRED;
1024cc5b1988SDavid van Moolenbroek 			else
1025*27852ebeSDavid van Moolenbroek 				uds->uds_flags &= ~UDSF_PASSCRED;
1026cc5b1988SDavid van Moolenbroek 
1027cc5b1988SDavid van Moolenbroek 			/*
1028*27852ebeSDavid van Moolenbroek 			 * In incredibly rare cases, disabling this flag may
1029*27852ebeSDavid van Moolenbroek 			 * allow blocked sends to be resumed, because suddenly
1030*27852ebeSDavid van Moolenbroek 			 * no room for the credentials is needed in the receive
1031*27852ebeSDavid van Moolenbroek 			 * buffer anymore.
1032cc5b1988SDavid van Moolenbroek 			 */
1033*27852ebeSDavid van Moolenbroek 			if (!val)
1034*27852ebeSDavid van Moolenbroek 				sockevent_raise(&uds->uds_sock, SEV_SEND);
1035cc5b1988SDavid van Moolenbroek 
1036*27852ebeSDavid van Moolenbroek 			return OK;
1037cc5b1988SDavid van Moolenbroek 
1038*27852ebeSDavid van Moolenbroek 		case LOCAL_CONNWAIT:
1039*27852ebeSDavid van Moolenbroek 			if ((r = sockdriver_copyin_opt(data, &val, sizeof(val),
1040*27852ebeSDavid van Moolenbroek 			    len)) != OK)
1041*27852ebeSDavid van Moolenbroek 				return r;
1042cc5b1988SDavid van Moolenbroek 
1043*27852ebeSDavid van Moolenbroek 			if (val)
1044*27852ebeSDavid van Moolenbroek 				uds->uds_flags |= UDSF_CONNWAIT;
1045*27852ebeSDavid van Moolenbroek 			else
1046*27852ebeSDavid van Moolenbroek 				uds->uds_flags &= ~UDSF_CONNWAIT;
1047cc5b1988SDavid van Moolenbroek 
1048cc5b1988SDavid van Moolenbroek 			/*
1049*27852ebeSDavid van Moolenbroek 			 * Changing the setting does not affect sockets that
1050*27852ebeSDavid van Moolenbroek 			 * are currently pending to be accepted.  Therefore,
1051*27852ebeSDavid van Moolenbroek 			 * uds_accept() may have to deal with either case on a
1052*27852ebeSDavid van Moolenbroek 			 * socket-by-socket basis.
1053cc5b1988SDavid van Moolenbroek 			 */
1054*27852ebeSDavid van Moolenbroek 			return OK;
1055cc5b1988SDavid van Moolenbroek 
1056*27852ebeSDavid van Moolenbroek 		case LOCAL_PEEREID:
1057*27852ebeSDavid van Moolenbroek 			/* This option may be retrieved but not set. */
1058*27852ebeSDavid van Moolenbroek 			return ENOPROTOOPT;
1059cc5b1988SDavid van Moolenbroek 		}
1060cc5b1988SDavid van Moolenbroek 
1061*27852ebeSDavid van Moolenbroek 		break;
1062*27852ebeSDavid van Moolenbroek 	}
1063cc5b1988SDavid van Moolenbroek 
1064*27852ebeSDavid van Moolenbroek 	return ENOPROTOOPT;
1065cc5b1988SDavid van Moolenbroek }
1066cc5b1988SDavid van Moolenbroek 
1067cc5b1988SDavid van Moolenbroek /*
1068*27852ebeSDavid van Moolenbroek  * Retrieve socket options.
1069cc5b1988SDavid van Moolenbroek  */
1070cc5b1988SDavid van Moolenbroek static int
uds_getsockopt(struct sock * sock,int level,int name,const struct sockdriver_data * data,socklen_t * len)1071*27852ebeSDavid van Moolenbroek uds_getsockopt(struct sock * sock, int level, int name,
1072*27852ebeSDavid van Moolenbroek 	const struct sockdriver_data * data, socklen_t * len)
1073cc5b1988SDavid van Moolenbroek {
1074*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
1075*27852ebeSDavid van Moolenbroek 	int val;
1076cc5b1988SDavid van Moolenbroek 
1077*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: getsockopt(%d,%d,%d)\n", uds_get_id(uds), level, name));
1078cc5b1988SDavid van Moolenbroek 
1079*27852ebeSDavid van Moolenbroek 	switch (level) {
1080*27852ebeSDavid van Moolenbroek 	case SOL_SOCKET:
1081*27852ebeSDavid van Moolenbroek 		switch (name) {
1082*27852ebeSDavid van Moolenbroek 		case SO_SNDBUF:
1083*27852ebeSDavid van Moolenbroek 		case SO_RCVBUF:
1084*27852ebeSDavid van Moolenbroek 			/* See uds_setsockopt() for why this is static. */
1085*27852ebeSDavid van Moolenbroek 			val = (int)uds_io_buflen();
108668afc771SLionel Sambuc 
1087*27852ebeSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1088*27852ebeSDavid van Moolenbroek 			    len);
1089cc5b1988SDavid van Moolenbroek 		}
1090cc5b1988SDavid van Moolenbroek 
1091*27852ebeSDavid van Moolenbroek 		break;
1092*27852ebeSDavid van Moolenbroek 
1093*27852ebeSDavid van Moolenbroek 	case UDSPROTO_UDS:
1094*27852ebeSDavid van Moolenbroek 		switch (name) {
1095*27852ebeSDavid van Moolenbroek 		case LOCAL_CREDS:
1096*27852ebeSDavid van Moolenbroek 			val = !!(uds->uds_flags & UDSF_PASSCRED);
1097*27852ebeSDavid van Moolenbroek 
1098*27852ebeSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1099*27852ebeSDavid van Moolenbroek 			    len);
1100*27852ebeSDavid van Moolenbroek 
1101*27852ebeSDavid van Moolenbroek 		case LOCAL_CONNWAIT:
1102*27852ebeSDavid van Moolenbroek 			val = !!(uds->uds_flags & UDSF_CONNWAIT);
1103*27852ebeSDavid van Moolenbroek 
1104*27852ebeSDavid van Moolenbroek 			return sockdriver_copyout_opt(data, &val, sizeof(val),
1105*27852ebeSDavid van Moolenbroek 			    len);
1106*27852ebeSDavid van Moolenbroek 
1107*27852ebeSDavid van Moolenbroek 		case LOCAL_PEEREID:
1108*27852ebeSDavid van Moolenbroek 			/* getpeereid(3) documents these error codes. */
1109*27852ebeSDavid van Moolenbroek 			if (uds_get_type(uds) == SOCK_DGRAM)
1110*27852ebeSDavid van Moolenbroek 				return EINVAL;
1111*27852ebeSDavid van Moolenbroek 			if (!uds_is_connected(uds))
1112*27852ebeSDavid van Moolenbroek 				return ENOTCONN;
1113*27852ebeSDavid van Moolenbroek 
1114*27852ebeSDavid van Moolenbroek 			/*
1115*27852ebeSDavid van Moolenbroek 			 * This is a custom MINIX3 error, indicating that there
1116*27852ebeSDavid van Moolenbroek 			 * are no credentials to return.  This could be due to
1117*27852ebeSDavid van Moolenbroek 			 * a failure to obtain them (which *should* not happen)
1118*27852ebeSDavid van Moolenbroek 			 * but also if the socket was bound while connected,
1119*27852ebeSDavid van Moolenbroek 			 * disconnected, and then reused as listening socket.
1120*27852ebeSDavid van Moolenbroek 			 */
1121*27852ebeSDavid van Moolenbroek 			if (uds->uds_conn->uds_cred.unp_pid == -1)
1122*27852ebeSDavid van Moolenbroek 				return EINVAL;
1123*27852ebeSDavid van Moolenbroek 
1124*27852ebeSDavid van Moolenbroek 			return sockdriver_copyout_opt(data,
1125*27852ebeSDavid van Moolenbroek 			    &uds->uds_conn->uds_cred,
1126*27852ebeSDavid van Moolenbroek 			    sizeof(uds->uds_conn->uds_cred), len);
1127*27852ebeSDavid van Moolenbroek 		}
1128*27852ebeSDavid van Moolenbroek 
1129*27852ebeSDavid van Moolenbroek 		break;
1130*27852ebeSDavid van Moolenbroek 	}
1131*27852ebeSDavid van Moolenbroek 
1132*27852ebeSDavid van Moolenbroek 	return ENOPROTOOPT;
1133*27852ebeSDavid van Moolenbroek }
1134*27852ebeSDavid van Moolenbroek 
1135*27852ebeSDavid van Moolenbroek /*
1136*27852ebeSDavid van Moolenbroek  * Retrieve a socket's local address.
1137*27852ebeSDavid van Moolenbroek  */
1138*27852ebeSDavid van Moolenbroek static int
uds_getsockname(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1139*27852ebeSDavid van Moolenbroek uds_getsockname(struct sock * sock, struct sockaddr * addr,
1140*27852ebeSDavid van Moolenbroek 	socklen_t * addr_len)
1141*27852ebeSDavid van Moolenbroek {
1142*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
1143*27852ebeSDavid van Moolenbroek 
1144*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: getsockname(%d)\n", uds_get_id(uds)));
1145*27852ebeSDavid van Moolenbroek 
1146*27852ebeSDavid van Moolenbroek 	uds_make_addr(uds->uds_path, uds->uds_pathlen, addr, addr_len);
1147*27852ebeSDavid van Moolenbroek 
1148*27852ebeSDavid van Moolenbroek 	return OK;
1149*27852ebeSDavid van Moolenbroek }
1150*27852ebeSDavid van Moolenbroek 
1151*27852ebeSDavid van Moolenbroek /*
1152*27852ebeSDavid van Moolenbroek  * Retrieve a socket's remote address.
1153*27852ebeSDavid van Moolenbroek  */
1154*27852ebeSDavid van Moolenbroek static int
uds_getpeername(struct sock * sock,struct sockaddr * addr,socklen_t * addr_len)1155*27852ebeSDavid van Moolenbroek uds_getpeername(struct sock * sock, struct sockaddr * addr,
1156*27852ebeSDavid van Moolenbroek 	socklen_t * addr_len)
1157*27852ebeSDavid van Moolenbroek {
1158*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
1159*27852ebeSDavid van Moolenbroek 	struct udssock *peer;
1160*27852ebeSDavid van Moolenbroek 
1161*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: getpeername(%d)\n", uds_get_id(uds)));
1162*27852ebeSDavid van Moolenbroek 
1163*27852ebeSDavid van Moolenbroek 	/*
1164*27852ebeSDavid van Moolenbroek 	 * For disconnected sockets, we no longer have a peer socket and thus
1165*27852ebeSDavid van Moolenbroek 	 * also no peer address.  Too bad, but NetBSD does the same.
1166*27852ebeSDavid van Moolenbroek 	 *
1167*27852ebeSDavid van Moolenbroek 	 * For connecting sockets we could in fact return a peer address, but
1168*27852ebeSDavid van Moolenbroek 	 * POSIX says (and other platforms agree) that we should deny the call.
1169*27852ebeSDavid van Moolenbroek 	 */
1170*27852ebeSDavid van Moolenbroek 	peer = uds_get_peer(uds);
1171*27852ebeSDavid van Moolenbroek 
1172*27852ebeSDavid van Moolenbroek 	if (peer == NULL || uds_is_connecting(uds))
1173*27852ebeSDavid van Moolenbroek 		return ENOTCONN;
1174*27852ebeSDavid van Moolenbroek 
1175*27852ebeSDavid van Moolenbroek 	uds_make_addr(peer->uds_path, peer->uds_pathlen, addr, addr_len);
1176*27852ebeSDavid van Moolenbroek 
1177*27852ebeSDavid van Moolenbroek 	return OK;
1178*27852ebeSDavid van Moolenbroek }
1179*27852ebeSDavid van Moolenbroek 
1180*27852ebeSDavid van Moolenbroek /*
1181*27852ebeSDavid van Moolenbroek  * Shut down socket send and receive operations.  Note that 'flags' is a
1182*27852ebeSDavid van Moolenbroek  * bitwise mask with libsockevent's SFL_SHUT_{RD,WR} flags rather than the set
1183*27852ebeSDavid van Moolenbroek  * of SHUT_{RD,WR,RDWR} values from userland.
1184*27852ebeSDavid van Moolenbroek  */
1185*27852ebeSDavid van Moolenbroek static int
uds_shutdown(struct sock * sock,unsigned int flags)1186*27852ebeSDavid van Moolenbroek uds_shutdown(struct sock * sock, unsigned int flags)
1187*27852ebeSDavid van Moolenbroek {
1188*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
1189*27852ebeSDavid van Moolenbroek 	struct udssock *conn;
1190*27852ebeSDavid van Moolenbroek 	unsigned int mask;
1191*27852ebeSDavid van Moolenbroek 
1192*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: shutdown(%d,0x%x)\n", uds_get_id(uds), flags));
1193*27852ebeSDavid van Moolenbroek 
1194*27852ebeSDavid van Moolenbroek 	/*
1195*27852ebeSDavid van Moolenbroek 	 * If we are shutting down the socket for reading, we can already close
1196*27852ebeSDavid van Moolenbroek 	 * any in-flight file descriptors associated with this socket.
1197*27852ebeSDavid van Moolenbroek 	 */
1198*27852ebeSDavid van Moolenbroek 	if (flags & SFL_SHUT_RD)
1199*27852ebeSDavid van Moolenbroek 		uds_io_reset(uds);
1200*27852ebeSDavid van Moolenbroek 
1201*27852ebeSDavid van Moolenbroek 	/*
1202*27852ebeSDavid van Moolenbroek 	 * A shutdown on this side of a connection may have an effect on
1203*27852ebeSDavid van Moolenbroek 	 * ongoing operations on the other side.  Fire appropriate events.
1204*27852ebeSDavid van Moolenbroek 	 */
1205*27852ebeSDavid van Moolenbroek 	if (uds_is_connected(uds)) {
1206*27852ebeSDavid van Moolenbroek 		assert(uds_get_type(uds) != SOCK_DGRAM);
1207*27852ebeSDavid van Moolenbroek 
1208*27852ebeSDavid van Moolenbroek 		conn = uds->uds_conn;
1209*27852ebeSDavid van Moolenbroek 
1210*27852ebeSDavid van Moolenbroek 		mask = 0;
1211*27852ebeSDavid van Moolenbroek 		if (flags & SFL_SHUT_RD)
1212*27852ebeSDavid van Moolenbroek 			mask |= SEV_SEND;
1213*27852ebeSDavid van Moolenbroek 		if (flags & SFL_SHUT_WR)
1214*27852ebeSDavid van Moolenbroek 			mask |= SEV_RECV;
1215*27852ebeSDavid van Moolenbroek 
1216*27852ebeSDavid van Moolenbroek 		sockevent_raise(&conn->uds_sock, mask);
1217*27852ebeSDavid van Moolenbroek 	}
1218*27852ebeSDavid van Moolenbroek 
1219*27852ebeSDavid van Moolenbroek 	return OK;
1220*27852ebeSDavid van Moolenbroek }
1221*27852ebeSDavid van Moolenbroek 
1222*27852ebeSDavid van Moolenbroek /*
1223*27852ebeSDavid van Moolenbroek  * Close a socket.
1224*27852ebeSDavid van Moolenbroek  *
1225*27852ebeSDavid van Moolenbroek  * The 'force' flag is unused because we need never wait for data to be sent,
1226*27852ebeSDavid van Moolenbroek  * since we keep all in-flight data on the receiver side.
1227*27852ebeSDavid van Moolenbroek  */
1228*27852ebeSDavid van Moolenbroek static int
uds_close(struct sock * sock,int force __unused)1229*27852ebeSDavid van Moolenbroek uds_close(struct sock * sock, int force __unused)
1230*27852ebeSDavid van Moolenbroek {
1231*27852ebeSDavid van Moolenbroek 	struct udssock *uds = (struct udssock *)sock;
1232*27852ebeSDavid van Moolenbroek 
1233*27852ebeSDavid van Moolenbroek 	dprintf(("UDS: close(%d)\n", uds_get_id(uds)));
1234*27852ebeSDavid van Moolenbroek 
1235*27852ebeSDavid van Moolenbroek 	if (uds_get_type(uds) == SOCK_DGRAM) {
1236*27852ebeSDavid van Moolenbroek 		/* If this socket is linked to a target, disconnect it. */
1237*27852ebeSDavid van Moolenbroek 		if (uds_has_link(uds))
1238*27852ebeSDavid van Moolenbroek 			uds_del_queue(uds->uds_link, uds);
1239*27852ebeSDavid van Moolenbroek 
1240*27852ebeSDavid van Moolenbroek 		/* Reset all sockets linked to this socket as a target. */
1241*27852ebeSDavid van Moolenbroek 		uds_clear_queue(uds, NULL);
1242*27852ebeSDavid van Moolenbroek 	} else if (uds_is_listening(uds)) {
1243*27852ebeSDavid van Moolenbroek 		/*
1244*27852ebeSDavid van Moolenbroek 		 * Abort all connecting sockets queued on this socket, and
1245*27852ebeSDavid van Moolenbroek 		 * break all connections for connected sockets queued on this
1246*27852ebeSDavid van Moolenbroek 		 * socket, freeing their peers.
1247*27852ebeSDavid van Moolenbroek 		 */
1248*27852ebeSDavid van Moolenbroek 		uds_clear_queue(uds, NULL);
1249*27852ebeSDavid van Moolenbroek 	} else if (uds_has_link(uds)) {
1250*27852ebeSDavid van Moolenbroek 		/*
1251*27852ebeSDavid van Moolenbroek 		 * This socket is connecting or connected while the other side
1252*27852ebeSDavid van Moolenbroek 		 * has not been accepted yet.  Remove the socket from the
1253*27852ebeSDavid van Moolenbroek 		 * listening socket's queue, and if it was connected, get rid
1254*27852ebeSDavid van Moolenbroek 		 * of its peer socket altogether.
1255*27852ebeSDavid van Moolenbroek 		 */
1256*27852ebeSDavid van Moolenbroek 		assert(uds_is_listening(uds->uds_link));
1257*27852ebeSDavid van Moolenbroek 
1258*27852ebeSDavid van Moolenbroek 		uds_del_queue(uds->uds_link, uds);
1259*27852ebeSDavid van Moolenbroek 
1260*27852ebeSDavid van Moolenbroek 		if (uds_is_connected(uds))
1261*27852ebeSDavid van Moolenbroek 			uds_disconnect(uds, TRUE /*was_linked*/);
1262*27852ebeSDavid van Moolenbroek 	} else if (uds_is_connected(uds)) {
1263*27852ebeSDavid van Moolenbroek 		/*
1264*27852ebeSDavid van Moolenbroek 		 * Decouple the peer socket from this socket, and possibly wake
1265*27852ebeSDavid van Moolenbroek 		 * up any pending operations on it.  The socket remains marked
1266*27852ebeSDavid van Moolenbroek 		 * as connected, but will now be disconnected.
1267*27852ebeSDavid van Moolenbroek 		 */
1268*27852ebeSDavid van Moolenbroek 		uds_disconnect(uds, FALSE /*was_linked*/);
1269*27852ebeSDavid van Moolenbroek 	}
1270*27852ebeSDavid van Moolenbroek 
1271*27852ebeSDavid van Moolenbroek 	if (uds_is_hashed(uds))
1272*27852ebeSDavid van Moolenbroek 		udshash_del(uds);
1273*27852ebeSDavid van Moolenbroek 
1274*27852ebeSDavid van Moolenbroek 	return OK;
1275*27852ebeSDavid van Moolenbroek }
1276*27852ebeSDavid van Moolenbroek 
1277*27852ebeSDavid van Moolenbroek static const struct sockevent_ops uds_ops = {
1278*27852ebeSDavid van Moolenbroek 	.sop_pair		= uds_pair,
1279*27852ebeSDavid van Moolenbroek 	.sop_bind		= uds_bind,
1280*27852ebeSDavid van Moolenbroek 	.sop_connect		= uds_connect,
1281*27852ebeSDavid van Moolenbroek 	.sop_listen		= uds_listen,
1282*27852ebeSDavid van Moolenbroek 	.sop_accept		= uds_accept,
1283*27852ebeSDavid van Moolenbroek 	.sop_test_accept	= uds_test_accept,
1284*27852ebeSDavid van Moolenbroek 	.sop_pre_send		= uds_pre_send,
1285*27852ebeSDavid van Moolenbroek 	.sop_send		= uds_send,
1286*27852ebeSDavid van Moolenbroek 	.sop_test_send		= uds_test_send,
1287*27852ebeSDavid van Moolenbroek 	.sop_pre_recv		= uds_pre_recv,
1288*27852ebeSDavid van Moolenbroek 	.sop_recv		= uds_recv,
1289*27852ebeSDavid van Moolenbroek 	.sop_test_recv		= uds_test_recv,
1290*27852ebeSDavid van Moolenbroek 	.sop_setsockopt		= uds_setsockopt,
1291*27852ebeSDavid van Moolenbroek 	.sop_getsockopt		= uds_getsockopt,
1292*27852ebeSDavid van Moolenbroek 	.sop_getsockname	= uds_getsockname,
1293*27852ebeSDavid van Moolenbroek 	.sop_getpeername	= uds_getpeername,
1294*27852ebeSDavid van Moolenbroek 	.sop_shutdown		= uds_shutdown,
1295*27852ebeSDavid van Moolenbroek 	.sop_close		= uds_close,
1296*27852ebeSDavid van Moolenbroek 	.sop_free		= uds_free
1297*27852ebeSDavid van Moolenbroek };
1298*27852ebeSDavid van Moolenbroek 
1299*27852ebeSDavid van Moolenbroek /*
1300*27852ebeSDavid van Moolenbroek  * Initialize the service.
1301*27852ebeSDavid van Moolenbroek  */
1302*27852ebeSDavid van Moolenbroek static int
uds_init(int type __unused,sef_init_info_t * info __unused)1303*27852ebeSDavid van Moolenbroek uds_init(int type __unused, sef_init_info_t * info __unused)
1304*27852ebeSDavid van Moolenbroek {
1305*27852ebeSDavid van Moolenbroek 	unsigned int i;
1306*27852ebeSDavid van Moolenbroek 
1307*27852ebeSDavid van Moolenbroek 	/* Initialize the list of free sockets. */
1308*27852ebeSDavid van Moolenbroek 	TAILQ_INIT(&uds_freelist);
1309*27852ebeSDavid van Moolenbroek 
1310*27852ebeSDavid van Moolenbroek 	for (i = 0; i < __arraycount(uds_array); i++) {
1311*27852ebeSDavid van Moolenbroek 		uds_array[i].uds_flags = 0;
1312*27852ebeSDavid van Moolenbroek 
1313*27852ebeSDavid van Moolenbroek 		TAILQ_INSERT_TAIL(&uds_freelist, &uds_array[i], uds_next);
1314*27852ebeSDavid van Moolenbroek 	}
1315*27852ebeSDavid van Moolenbroek 
1316*27852ebeSDavid van Moolenbroek 	/* Initialize the file-to-socket hash table. */
1317*27852ebeSDavid van Moolenbroek 	udshash_init();
1318*27852ebeSDavid van Moolenbroek 
1319*27852ebeSDavid van Moolenbroek 	/* Initialize the input/output module. */
1320*27852ebeSDavid van Moolenbroek 	uds_io_init();
1321*27852ebeSDavid van Moolenbroek 
1322*27852ebeSDavid van Moolenbroek 	/* Initialize the status module. */
1323*27852ebeSDavid van Moolenbroek 	uds_stat_init();
1324*27852ebeSDavid van Moolenbroek 
1325*27852ebeSDavid van Moolenbroek 	/* Initialize the sockevent library. */
1326*27852ebeSDavid van Moolenbroek 	sockevent_init(uds_socket);
1327*27852ebeSDavid van Moolenbroek 
1328*27852ebeSDavid van Moolenbroek 	uds_in_use = 0;
1329*27852ebeSDavid van Moolenbroek 	uds_running = TRUE;
1330*27852ebeSDavid van Moolenbroek 
1331*27852ebeSDavid van Moolenbroek 	return OK;
1332*27852ebeSDavid van Moolenbroek }
1333*27852ebeSDavid van Moolenbroek 
1334*27852ebeSDavid van Moolenbroek /*
1335*27852ebeSDavid van Moolenbroek  * Clean up before shutdown.
1336*27852ebeSDavid van Moolenbroek  */
1337*27852ebeSDavid van Moolenbroek static void
uds_cleanup(void)1338*27852ebeSDavid van Moolenbroek uds_cleanup(void)
1339*27852ebeSDavid van Moolenbroek {
1340*27852ebeSDavid van Moolenbroek 
1341*27852ebeSDavid van Moolenbroek 	/* Tell the status module to clean up. */
1342*27852ebeSDavid van Moolenbroek 	uds_stat_cleanup();
1343*27852ebeSDavid van Moolenbroek }
1344*27852ebeSDavid van Moolenbroek 
1345*27852ebeSDavid van Moolenbroek /*
1346*27852ebeSDavid van Moolenbroek  * The service has received a signal.
1347*27852ebeSDavid van Moolenbroek  */
1348cc5b1988SDavid van Moolenbroek static void
uds_signal(int signo)1349cc5b1988SDavid van Moolenbroek uds_signal(int signo)
1350cc5b1988SDavid van Moolenbroek {
1351cc5b1988SDavid van Moolenbroek 
1352*27852ebeSDavid van Moolenbroek 	/* Only check for the termination signal.  Ignore anything else. */
1353*27852ebeSDavid van Moolenbroek 	if (signo != SIGTERM)
1354*27852ebeSDavid van Moolenbroek 		return;
1355cc5b1988SDavid van Moolenbroek 
1356*27852ebeSDavid van Moolenbroek 	/* Exit only once all sockets have been closed. */
1357*27852ebeSDavid van Moolenbroek 	uds_running = FALSE;
1358cc5b1988SDavid van Moolenbroek 
1359*27852ebeSDavid van Moolenbroek 	if (uds_in_use == 0)
1360*27852ebeSDavid van Moolenbroek 		sef_cancel();
1361cc5b1988SDavid van Moolenbroek }
1362cc5b1988SDavid van Moolenbroek 
1363*27852ebeSDavid van Moolenbroek /*
1364*27852ebeSDavid van Moolenbroek  * Perform initialization using the System Event Framework (SEF).
1365*27852ebeSDavid van Moolenbroek  */
1366cc5b1988SDavid van Moolenbroek static void
uds_startup(void)1367cc5b1988SDavid van Moolenbroek uds_startup(void)
1368cc5b1988SDavid van Moolenbroek {
1369*27852ebeSDavid van Moolenbroek 
1370*27852ebeSDavid van Moolenbroek 	/* Register initialization callbacks. */
1371cc5b1988SDavid van Moolenbroek 	sef_setcb_init_fresh(uds_init);
1372cc5b1988SDavid van Moolenbroek 
1373*27852ebeSDavid van Moolenbroek 	/* Register signal callback. */
1374cc5b1988SDavid van Moolenbroek 	sef_setcb_signal_handler(uds_signal);
1375cc5b1988SDavid van Moolenbroek 
1376cc5b1988SDavid van Moolenbroek 	/* Let SEF perform startup. */
1377cc5b1988SDavid van Moolenbroek 	sef_startup();
1378cc5b1988SDavid van Moolenbroek }
1379cc5b1988SDavid van Moolenbroek 
1380cc5b1988SDavid van Moolenbroek /*
1381*27852ebeSDavid van Moolenbroek  * The UNIX Domain Sockets driver.
1382cc5b1988SDavid van Moolenbroek  */
1383cc5b1988SDavid van Moolenbroek int
main(void)1384cc5b1988SDavid van Moolenbroek main(void)
1385cc5b1988SDavid van Moolenbroek {
1386*27852ebeSDavid van Moolenbroek 	message m;
1387*27852ebeSDavid van Moolenbroek 	int r, ipc_status;
1388*27852ebeSDavid van Moolenbroek 
1389*27852ebeSDavid van Moolenbroek 	/* Initialize the service. */
1390cc5b1988SDavid van Moolenbroek 	uds_startup();
1391cc5b1988SDavid van Moolenbroek 
1392*27852ebeSDavid van Moolenbroek 	/* Loop receiving and processing messages until instructed to stop. */
1393*27852ebeSDavid van Moolenbroek 	while (uds_running || uds_in_use > 0) {
1394*27852ebeSDavid van Moolenbroek 		if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) {
1395*27852ebeSDavid van Moolenbroek 			if (r == EINTR)
1396*27852ebeSDavid van Moolenbroek 				continue;	/* sef_cancel() was called */
1397cc5b1988SDavid van Moolenbroek 
1398*27852ebeSDavid van Moolenbroek 			panic("UDS: sef_receive_status failed: %d", r);
1399*27852ebeSDavid van Moolenbroek 		}
1400*27852ebeSDavid van Moolenbroek 
1401*27852ebeSDavid van Moolenbroek 		/*
1402*27852ebeSDavid van Moolenbroek 		 * Messages from the MIB service are (ultimately) for the
1403*27852ebeSDavid van Moolenbroek 		 * status module.  Everything else is assumed to be a socket
1404*27852ebeSDavid van Moolenbroek 		 * request and passed to libsockevent, which will ignore
1405*27852ebeSDavid van Moolenbroek 		 * anything it does not recognize.
1406*27852ebeSDavid van Moolenbroek 		 */
1407*27852ebeSDavid van Moolenbroek 		if (m.m_source == MIB_PROC_NR)
1408*27852ebeSDavid van Moolenbroek 			rmib_process(&m, ipc_status);
1409*27852ebeSDavid van Moolenbroek 		else
1410*27852ebeSDavid van Moolenbroek 			sockevent_process(&m, ipc_status);
1411*27852ebeSDavid van Moolenbroek 	}
1412*27852ebeSDavid van Moolenbroek 
1413*27852ebeSDavid van Moolenbroek 	/* Clean up before graceful shutdown. */
1414*27852ebeSDavid van Moolenbroek 	uds_cleanup();
1415*27852ebeSDavid van Moolenbroek 
1416*27852ebeSDavid van Moolenbroek 	return EXIT_SUCCESS;
1417cc5b1988SDavid van Moolenbroek }
1418