Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
2557c1fc | 2 | * Copyright (c) 1982, 1986, 1988 Regents of the University of California. |
5b519e94 | 3 | * All rights reserved. |
da7c5cc6 | 4 | * |
5b519e94 | 5 | * Redistribution and use in source and binary forms are permitted |
616d42db KB |
6 | * provided that the above copyright notice and this paragraph are |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
5b519e94 | 16 | * |
c4ec2128 | 17 | * @(#)uipc_socket.c 7.14 (Berkeley) %G% |
da7c5cc6 | 18 | */ |
ce9d8eb4 | 19 | |
94368568 | 20 | #include "param.h" |
94368568 JB |
21 | #include "user.h" |
22 | #include "proc.h" | |
23 | #include "file.h" | |
2557c1fc | 24 | #include "malloc.h" |
94368568 | 25 | #include "mbuf.h" |
94368568 JB |
26 | #include "domain.h" |
27 | #include "protosw.h" | |
28 | #include "socket.h" | |
29 | #include "socketvar.h" | |
ce9d8eb4 | 30 | |
ce9d8eb4 | 31 | /* |
cf012934 BJ |
32 | * Socket operation routines. |
33 | * These routines are called by the routines in | |
34 | * sys_socket.c or from a system process, and | |
35 | * implement the semantics of socket operations by | |
36 | * switching out to the protocol specific routines. | |
88a7a62a SL |
37 | * |
38 | * TODO: | |
88a7a62a | 39 | * test socketpair |
8c0650b0 | 40 | * clean up async |
88a7a62a | 41 | * out-of-band is a kludge |
ce9d8eb4 | 42 | */ |
a8d3bf7f | 43 | /*ARGSUSED*/ |
98422daa | 44 | socreate(dom, aso, type, proto) |
ce9d8eb4 | 45 | struct socket **aso; |
88a7a62a SL |
46 | register int type; |
47 | int proto; | |
ce9d8eb4 BJ |
48 | { |
49 | register struct protosw *prp; | |
50 | register struct socket *so; | |
88a7a62a | 51 | register int error; |
cc15ab5d | 52 | |
cc15ab5d | 53 | if (proto) |
8c0650b0 | 54 | prp = pffindproto(dom, proto, type); |
cc15ab5d | 55 | else |
4f083fd7 | 56 | prp = pffindtype(dom, type); |
cc15ab5d BJ |
57 | if (prp == 0) |
58 | return (EPROTONOSUPPORT); | |
cf012934 BJ |
59 | if (prp->pr_type != type) |
60 | return (EPROTOTYPE); | |
a2aebb63 KS |
61 | MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_WAIT); |
62 | bzero((caddr_t)so, sizeof(*so)); | |
4f083fd7 | 63 | so->so_type = type; |
62364f0e BJ |
64 | if (u.u_uid == 0) |
65 | so->so_state = SS_PRIV; | |
ce9d8eb4 | 66 | so->so_proto = prp; |
88a7a62a SL |
67 | error = |
68 | (*prp->pr_usrreq)(so, PRU_ATTACH, | |
8c0650b0 | 69 | (struct mbuf *)0, (struct mbuf *)proto, (struct mbuf *)0); |
b91acce4 | 70 | if (error) { |
90aaea96 | 71 | so->so_state |= SS_NOFDREF; |
de48daf3 | 72 | sofree(so); |
cc15ab5d | 73 | return (error); |
ce9d8eb4 BJ |
74 | } |
75 | *aso = so; | |
76 | return (0); | |
77 | } | |
78 | ||
98422daa | 79 | sobind(so, nam) |
cf012934 BJ |
80 | struct socket *so; |
81 | struct mbuf *nam; | |
cf012934 BJ |
82 | { |
83 | int s = splnet(); | |
84 | int error; | |
85 | ||
86 | error = | |
88a7a62a SL |
87 | (*so->so_proto->pr_usrreq)(so, PRU_BIND, |
88 | (struct mbuf *)0, nam, (struct mbuf *)0); | |
cf012934 BJ |
89 | splx(s); |
90 | return (error); | |
91 | } | |
92 | ||
93 | solisten(so, backlog) | |
88a7a62a | 94 | register struct socket *so; |
cf012934 BJ |
95 | int backlog; |
96 | { | |
88a7a62a | 97 | int s = splnet(), error; |
cf012934 | 98 | |
88a7a62a SL |
99 | error = |
100 | (*so->so_proto->pr_usrreq)(so, PRU_LISTEN, | |
101 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); | |
cf012934 BJ |
102 | if (error) { |
103 | splx(s); | |
104 | return (error); | |
105 | } | |
106 | if (so->so_q == 0) { | |
107 | so->so_q = so; | |
108 | so->so_q0 = so; | |
109 | so->so_options |= SO_ACCEPTCONN; | |
110 | } | |
111 | if (backlog < 0) | |
112 | backlog = 0; | |
2557c1fc | 113 | so->so_qlimit = min(backlog, SOMAXCONN); |
9e87be97 | 114 | splx(s); |
cf012934 BJ |
115 | return (0); |
116 | } | |
117 | ||
ae921915 | 118 | sofree(so) |
88a7a62a | 119 | register struct socket *so; |
ae921915 BJ |
120 | { |
121 | ||
bb73a14e MK |
122 | if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) |
123 | return; | |
90aaea96 BJ |
124 | if (so->so_head) { |
125 | if (!soqremque(so, 0) && !soqremque(so, 1)) | |
126 | panic("sofree dq"); | |
127 | so->so_head = 0; | |
128 | } | |
4ad99bae | 129 | sbrelease(&so->so_snd); |
88a7a62a | 130 | sorflush(so); |
a2aebb63 | 131 | FREE(so, M_SOCKET); |
ae921915 BJ |
132 | } |
133 | ||
92a533e6 | 134 | /* |
cc15ab5d BJ |
135 | * Close a socket on last file table reference removal. |
136 | * Initiate disconnect if connected. | |
137 | * Free socket when disconnect complete. | |
92a533e6 | 138 | */ |
88a7a62a | 139 | soclose(so) |
92a533e6 | 140 | register struct socket *so; |
92a533e6 | 141 | { |
cc15ab5d | 142 | int s = splnet(); /* conservative */ |
e58562f2 | 143 | int error = 0; |
cc15ab5d | 144 | |
90aaea96 BJ |
145 | if (so->so_options & SO_ACCEPTCONN) { |
146 | while (so->so_q0 != so) | |
26225f25 | 147 | (void) soabort(so->so_q0); |
90aaea96 | 148 | while (so->so_q != so) |
26225f25 | 149 | (void) soabort(so->so_q); |
90aaea96 | 150 | } |
cc15ab5d BJ |
151 | if (so->so_pcb == 0) |
152 | goto discard; | |
153 | if (so->so_state & SS_ISCONNECTED) { | |
154 | if ((so->so_state & SS_ISDISCONNECTING) == 0) { | |
dedd6629 | 155 | error = sodisconnect(so); |
88a7a62a SL |
156 | if (error) |
157 | goto drop; | |
cc15ab5d | 158 | } |
98422daa | 159 | if (so->so_options & SO_LINGER) { |
b8acc34d | 160 | if ((so->so_state & SS_ISDISCONNECTING) && |
88a7a62a SL |
161 | (so->so_state & SS_NBIO)) |
162 | goto drop; | |
b8acc34d BJ |
163 | while (so->so_state & SS_ISCONNECTED) |
164 | sleep((caddr_t)&so->so_timeo, PZERO+1); | |
72857acf | 165 | } |
cc15ab5d | 166 | } |
89900a09 | 167 | drop: |
37c0974a | 168 | if (so->so_pcb) { |
88a7a62a SL |
169 | int error2 = |
170 | (*so->so_proto->pr_usrreq)(so, PRU_DETACH, | |
171 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); | |
172 | if (error == 0) | |
173 | error = error2; | |
37c0974a | 174 | } |
cc15ab5d | 175 | discard: |
26225f25 SL |
176 | if (so->so_state & SS_NOFDREF) |
177 | panic("soclose: NOFDREF"); | |
90aaea96 | 178 | so->so_state |= SS_NOFDREF; |
4ad99bae | 179 | sofree(so); |
cc15ab5d | 180 | splx(s); |
88a7a62a | 181 | return (error); |
92a533e6 BJ |
182 | } |
183 | ||
26225f25 SL |
184 | /* |
185 | * Must be called at splnet... | |
186 | */ | |
187 | soabort(so) | |
188 | struct socket *so; | |
189 | { | |
26225f25 | 190 | |
88a7a62a SL |
191 | return ( |
192 | (*so->so_proto->pr_usrreq)(so, PRU_ABORT, | |
193 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); | |
92a533e6 BJ |
194 | } |
195 | ||
98422daa | 196 | soaccept(so, nam) |
88a7a62a | 197 | register struct socket *so; |
cf012934 | 198 | struct mbuf *nam; |
2b4b57cd BJ |
199 | { |
200 | int s = splnet(); | |
201 | int error; | |
202 | ||
26225f25 SL |
203 | if ((so->so_state & SS_NOFDREF) == 0) |
204 | panic("soaccept: !NOFDREF"); | |
98422daa | 205 | so->so_state &= ~SS_NOFDREF; |
cf012934 | 206 | error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, |
88a7a62a | 207 | (struct mbuf *)0, nam, (struct mbuf *)0); |
2b4b57cd BJ |
208 | splx(s); |
209 | return (error); | |
210 | } | |
211 | ||
98422daa | 212 | soconnect(so, nam) |
88a7a62a | 213 | register struct socket *so; |
cf012934 | 214 | struct mbuf *nam; |
ce9d8eb4 | 215 | { |
7bcf9d13 | 216 | int s; |
cc15ab5d | 217 | int error; |
ce9d8eb4 | 218 | |
7bcf9d13 MK |
219 | if (so->so_options & SO_ACCEPTCONN) |
220 | return (EOPNOTSUPP); | |
221 | s = splnet(); | |
de2c74a5 MK |
222 | /* |
223 | * If protocol is connection-based, can only connect once. | |
224 | * Otherwise, if connected, try to disconnect first. | |
225 | * This allows user to disconnect by connecting to, e.g., | |
226 | * a null address. | |
227 | */ | |
228 | if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && | |
229 | ((so->so_proto->pr_flags & PR_CONNREQUIRED) || | |
230 | (error = sodisconnect(so)))) | |
cc15ab5d | 231 | error = EISCONN; |
de2c74a5 MK |
232 | else |
233 | error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, | |
234 | (struct mbuf *)0, nam, (struct mbuf *)0); | |
cc15ab5d BJ |
235 | splx(s); |
236 | return (error); | |
ce9d8eb4 BJ |
237 | } |
238 | ||
88a7a62a SL |
239 | soconnect2(so1, so2) |
240 | register struct socket *so1; | |
241 | struct socket *so2; | |
242 | { | |
243 | int s = splnet(); | |
244 | int error; | |
245 | ||
5a48956d SL |
246 | error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, |
247 | (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0); | |
88a7a62a SL |
248 | splx(s); |
249 | return (error); | |
250 | } | |
88a7a62a | 251 | |
dedd6629 | 252 | sodisconnect(so) |
88a7a62a | 253 | register struct socket *so; |
ce9d8eb4 | 254 | { |
cc15ab5d BJ |
255 | int s = splnet(); |
256 | int error; | |
ce9d8eb4 | 257 | |
cc15ab5d BJ |
258 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
259 | error = ENOTCONN; | |
260 | goto bad; | |
ce9d8eb4 | 261 | } |
cc15ab5d BJ |
262 | if (so->so_state & SS_ISDISCONNECTING) { |
263 | error = EALREADY; | |
264 | goto bad; | |
ce9d8eb4 | 265 | } |
cf012934 | 266 | error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, |
dedd6629 | 267 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); |
cc15ab5d BJ |
268 | bad: |
269 | splx(s); | |
270 | return (error); | |
ce9d8eb4 BJ |
271 | } |
272 | ||
cc15ab5d BJ |
273 | /* |
274 | * Send on a socket. | |
275 | * If send must go all at once and message is larger than | |
276 | * send buffering, then hard error. | |
277 | * Lock against other senders. | |
278 | * If must go all at once and not enough room now, then | |
279 | * inform user that this would block and do nothing. | |
8250a099 | 280 | * Otherwise, if nonblocking, send as much as possible. |
cc15ab5d | 281 | */ |
2557c1fc | 282 | sosend(so, nam, uio, flags, rights, control) |
ce9d8eb4 | 283 | register struct socket *so; |
cf012934 | 284 | struct mbuf *nam; |
88a7a62a | 285 | register struct uio *uio; |
970108c7 | 286 | int flags; |
2557c1fc | 287 | struct mbuf *rights, *control; |
ce9d8eb4 | 288 | { |
2557c1fc MK |
289 | struct mbuf *top = 0, **mp; |
290 | register struct mbuf *m; | |
291 | register int space, len; | |
292 | int rlen = 0, error = 0, s, dontroute, first = 1, mlen; | |
293 | int atomic = sosendallatonce(so); | |
ce9d8eb4 | 294 | |
2557c1fc | 295 | if (atomic && uio->uio_resid > so->so_snd.sb_hiwat) |
cc15ab5d | 296 | return (EMSGSIZE); |
88a7a62a SL |
297 | dontroute = |
298 | (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && | |
299 | (so->so_proto->pr_flags & PR_ATOMIC); | |
8250a099 | 300 | u.u_ru.ru_msgsnd++; |
c34d38f4 MK |
301 | if (rights) |
302 | rlen = rights->m_len; | |
cc15ab5d BJ |
303 | #define snderr(errno) { error = errno; splx(s); goto release; } |
304 | ||
8250a099 MK |
305 | restart: |
306 | sblock(&so->so_snd); | |
307 | do { | |
308 | s = splnet(); | |
af9c562f | 309 | if (so->so_state & SS_CANTSENDMORE) |
8250a099 | 310 | snderr(EPIPE); |
a2aebb63 KS |
311 | if (so->so_error) |
312 | snderr(so->so_error); | |
8250a099 | 313 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
a2aebb63 KS |
314 | if (so->so_proto->pr_flags & PR_CONNREQUIRED) { |
315 | if (!uio->uio_resid && !rights && control) { | |
316 | snderr((*so->so_proto->pr_usrreq)(so, | |
317 | (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, | |
318 | top, (caddr_t)0, rights, control)); | |
319 | } else if (so->so_state & SS_ISCONFIRMING) | |
320 | /* is ok */; | |
321 | else | |
322 | snderr(ENOTCONN); | |
323 | } else if (nam == 0) | |
8250a099 MK |
324 | snderr(EDESTADDRREQ); |
325 | } | |
326 | if (flags & MSG_OOB) | |
327 | space = 1024; | |
328 | else { | |
329 | space = sbspace(&so->so_snd); | |
c34d38f4 | 330 | if (space <= rlen || |
2557c1fc MK |
331 | (atomic && space < uio->uio_resid + rlen) || |
332 | (uio->uio_resid >= MCLBYTES && space < MCLBYTES && | |
333 | so->so_snd.sb_cc >= MCLBYTES && | |
09338457 | 334 | (so->so_state & SS_NBIO) == 0)) { |
8250a099 MK |
335 | if (so->so_state & SS_NBIO) { |
336 | if (first) | |
337 | error = EWOULDBLOCK; | |
338 | splx(s); | |
339 | goto release; | |
340 | } | |
341 | sbunlock(&so->so_snd); | |
342 | sbwait(&so->so_snd); | |
343 | splx(s); | |
344 | goto restart; | |
345 | } | |
346 | } | |
4c078bb2 | 347 | splx(s); |
8250a099 | 348 | mp = ⊤ |
c34d38f4 | 349 | space -= rlen; |
2557c1fc MK |
350 | do { |
351 | do { | |
352 | if (top == 0) { | |
353 | MGETHDR(m, M_WAIT, MT_DATA); | |
354 | mlen = MHLEN; | |
355 | m->m_pkthdr.len = 0; | |
356 | m->m_pkthdr.rcvif = (struct ifnet *)0; | |
357 | } else { | |
358 | MGET(m, M_WAIT, MT_DATA); | |
359 | mlen = MLEN; | |
360 | } | |
361 | if (uio->uio_resid >= MINCLSIZE && space >= MCLBYTES) { | |
362 | MCLGET(m, M_WAIT); | |
363 | if ((m->m_flags & M_EXT) == 0) | |
8250a099 | 364 | goto nopages; |
2557c1fc MK |
365 | mlen = MCLBYTES; |
366 | #ifdef MAPPED_MBUFS | |
367 | len = min(MCLBYTES, uio->uio_resid); | |
368 | if (len < mlen - max_hdr) | |
369 | m->m_data += max_hdr; | |
370 | #else | |
371 | len = min(MCLBYTES - max_hdr, uio->uio_resid); | |
372 | m->m_data += max_hdr; | |
373 | #endif | |
374 | space -= MCLBYTES; | |
8250a099 MK |
375 | } else { |
376 | nopages: | |
2557c1fc | 377 | len = min(min(mlen, uio->uio_resid), space); |
8c0650b0 | 378 | space -= len; |
2557c1fc MK |
379 | /* |
380 | * For datagram protocols, leave room | |
381 | * for protocol headers in first mbuf. | |
382 | */ | |
84efcd38 | 383 | if (atomic && top == 0 && len < mlen) |
2557c1fc | 384 | MH_ALIGN(m, len); |
8250a099 | 385 | } |
c4ec2128 | 386 | error = uiomove(mtod(m, caddr_t), len, uio); |
8250a099 MK |
387 | m->m_len = len; |
388 | *mp = m; | |
2557c1fc | 389 | top->m_pkthdr.len += len; |
8250a099 MK |
390 | if (error) |
391 | goto release; | |
392 | mp = &m->m_next; | |
2557c1fc MK |
393 | if (uio->uio_resid <= 0) { |
394 | if ((flags & MSG_EOR) && top) | |
395 | top->m_flags |= M_EOR; | |
af9c562f | 396 | break; |
2557c1fc MK |
397 | } |
398 | } while (space > 0 && atomic); | |
399 | if (dontroute) | |
400 | so->so_options |= SO_DONTROUTE; | |
401 | s = splnet(); /* XXX */ | |
402 | error = (*so->so_proto->pr_usrreq)(so, | |
403 | (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, | |
404 | top, (caddr_t)nam, rights, control); | |
405 | splx(s); | |
406 | if (dontroute) | |
407 | so->so_options &= ~SO_DONTROUTE; | |
408 | rights = 0; | |
409 | rlen = 0; | |
410 | top = 0; | |
411 | mp = ⊤ | |
412 | first = 0; | |
413 | if (error) | |
414 | goto release; | |
415 | } while (uio->uio_resid && space > 0); | |
8250a099 | 416 | } while (uio->uio_resid); |
cc15ab5d | 417 | |
ce9d8eb4 | 418 | release: |
cc15ab5d | 419 | sbunlock(&so->so_snd); |
0f90f987 BJ |
420 | if (top) |
421 | m_freem(top); | |
af9c562f MK |
422 | if (error == EPIPE) |
423 | psignal(u.u_procp, SIGPIPE); | |
ce9d8eb4 BJ |
424 | return (error); |
425 | } | |
426 | ||
c34d38f4 MK |
427 | /* |
428 | * Implement receive operations on a socket. | |
429 | * We depend on the way that records are added to the sockbuf | |
430 | * by sbappend*. In particular, each record (mbufs linked through m_next) | |
431 | * must begin with an address if the protocol so specifies, | |
432 | * followed by an optional mbuf containing access rights if supported | |
433 | * by the protocol, and then zero or more mbufs of data. | |
434 | * In order to avoid blocking network interrupts for the entire time here, | |
435 | * we splx() while doing the actual copy to user space. | |
436 | * Although the sockbuf is locked, new data may still be appended, | |
437 | * and thus we must maintain consistency of the sockbuf during that time. | |
438 | */ | |
2557c1fc | 439 | soreceive(so, aname, uio, flagsp, rightsp, controlp) |
ce9d8eb4 | 440 | register struct socket *so; |
cf012934 | 441 | struct mbuf **aname; |
88a7a62a | 442 | register struct uio *uio; |
2557c1fc MK |
443 | int *flagsp; |
444 | struct mbuf **rightsp, **controlp; | |
ce9d8eb4 | 445 | { |
6ff43975 | 446 | register struct mbuf *m; |
2557c1fc | 447 | register int flags, len, error = 0, s, offset; |
88a7a62a | 448 | struct protosw *pr = so->so_proto; |
261a8548 | 449 | struct mbuf *nextrecord; |
88a7a62a SL |
450 | int moff; |
451 | ||
452 | if (rightsp) | |
453 | *rightsp = 0; | |
454 | if (aname) | |
455 | *aname = 0; | |
2557c1fc MK |
456 | if (controlp) |
457 | *controlp = 0; | |
458 | if (flagsp) | |
459 | flags = *flagsp &~ MSG_EOR; | |
460 | else | |
461 | flags = 0; | |
88a7a62a | 462 | if (flags & MSG_OOB) { |
cce93e4b | 463 | m = m_get(M_WAIT, MT_DATA); |
88a7a62a | 464 | error = (*pr->pr_usrreq)(so, PRU_RCVOOB, |
de2c74a5 | 465 | m, (struct mbuf *)(flags & MSG_PEEK), (struct mbuf *)0); |
a8d3bf7f | 466 | if (error) |
5fe6f9d1 | 467 | goto bad; |
970108c7 | 468 | do { |
5fe6f9d1 | 469 | len = uio->uio_resid; |
970108c7 BJ |
470 | if (len > m->m_len) |
471 | len = m->m_len; | |
c4ec2128 | 472 | error = uiomove(mtod(m, caddr_t), (int)len, uio); |
970108c7 | 473 | m = m_free(m); |
a8d3bf7f | 474 | } while (uio->uio_resid && error == 0 && m); |
5fe6f9d1 | 475 | bad: |
970108c7 | 476 | if (m) |
39d536e6 | 477 | m_freem(m); |
a8d3bf7f | 478 | return (error); |
970108c7 | 479 | } |
a2aebb63 | 480 | if (so->so_state & SS_ISCONFIRMING && uio->uio_resid) |
2557c1fc MK |
481 | (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, |
482 | (struct mbuf *)0, (struct mbuf *)0); | |
ce9d8eb4 | 483 | |
cc15ab5d BJ |
484 | restart: |
485 | sblock(&so->so_rcv); | |
486 | s = splnet(); | |
487 | ||
a2aebb63 KS |
488 | m = so->so_rcv.sb_mb; |
489 | if (m == 0) { | |
490 | if (so->so_rcv.sb_cc) | |
491 | panic("receive 1"); | |
4c078bb2 BJ |
492 | if (so->so_error) { |
493 | error = so->so_error; | |
494 | so->so_error = 0; | |
4c078bb2 BJ |
495 | goto release; |
496 | } | |
f02d4eaa | 497 | if (so->so_state & SS_CANTRCVMORE) |
cc15ab5d | 498 | goto release; |
196d84fd | 499 | if ((so->so_state & SS_ISCONNECTED) == 0 && |
f02d4eaa KB |
500 | (so->so_proto->pr_flags & PR_CONNREQUIRED)) { |
501 | error = ENOTCONN; | |
502 | goto release; | |
503 | } | |
c34d38f4 MK |
504 | if (uio->uio_resid == 0) |
505 | goto release; | |
f02d4eaa KB |
506 | if (so->so_state & SS_NBIO) { |
507 | error = EWOULDBLOCK; | |
508 | goto release; | |
509 | } | |
cc15ab5d | 510 | sbunlock(&so->so_rcv); |
2752c877 | 511 | sbwait(&so->so_rcv); |
a4f6d93d | 512 | splx(s); |
cc15ab5d | 513 | goto restart; |
ce9d8eb4 | 514 | } |
c7256358 | 515 | u.u_ru.ru_msgrcv++; |
2557c1fc MK |
516 | if (m->m_type == 0) |
517 | panic("receive 3a"); | |
518 | nextrecord = m->m_nextpkt; | |
88a7a62a | 519 | if (pr->pr_flags & PR_ADDR) { |
c34d38f4 | 520 | if (m->m_type != MT_SONAME) |
261a8548 MK |
521 | panic("receive 1a"); |
522 | if (flags & MSG_PEEK) { | |
523 | if (aname) | |
970108c7 | 524 | *aname = m_copy(m, 0, m->m_len); |
c34d38f4 | 525 | m = m->m_next; |
261a8548 | 526 | } else { |
c34d38f4 | 527 | sbfree(&so->so_rcv, m); |
261a8548 MK |
528 | if (aname) { |
529 | *aname = m; | |
2557c1fc MK |
530 | so->so_rcv.sb_mb = m->m_next; |
531 | m->m_next = 0; | |
532 | m = so->so_rcv.sb_mb; | |
c34d38f4 | 533 | } else { |
6ff43975 MK |
534 | MFREE(m, so->so_rcv.sb_mb); |
535 | m = so->so_rcv.sb_mb; | |
c34d38f4 | 536 | } |
261a8548 MK |
537 | } |
538 | } | |
539 | if (m && m->m_type == MT_RIGHTS) { | |
540 | if ((pr->pr_flags & PR_RIGHTS) == 0) | |
6ff43975 | 541 | panic("receive 2"); |
261a8548 MK |
542 | if (flags & MSG_PEEK) { |
543 | if (rightsp) | |
88a7a62a | 544 | *rightsp = m_copy(m, 0, m->m_len); |
c34d38f4 | 545 | m = m->m_next; |
261a8548 | 546 | } else { |
c34d38f4 | 547 | sbfree(&so->so_rcv, m); |
261a8548 MK |
548 | if (rightsp) { |
549 | *rightsp = m; | |
6ff43975 | 550 | so->so_rcv.sb_mb = m->m_next; |
c34d38f4 | 551 | m->m_next = 0; |
6ff43975 | 552 | m = so->so_rcv.sb_mb; |
c34d38f4 | 553 | } else { |
6ff43975 MK |
554 | MFREE(m, so->so_rcv.sb_mb); |
555 | m = so->so_rcv.sb_mb; | |
c34d38f4 | 556 | } |
88a7a62a | 557 | } |
cc15ab5d | 558 | } |
2557c1fc MK |
559 | if (m && m->m_type == MT_CONTROL) { |
560 | if (flags & MSG_PEEK) { | |
561 | if (controlp) | |
562 | *controlp = m_copy(m, 0, m->m_len); | |
563 | m = m->m_next; | |
564 | } else { | |
565 | sbfree(&so->so_rcv, m); | |
566 | if (controlp) { | |
567 | *controlp = m; | |
568 | so->so_rcv.sb_mb = m->m_next; | |
569 | m->m_next = 0; | |
570 | m = so->so_rcv.sb_mb; | |
571 | } else { | |
572 | MFREE(m, so->so_rcv.sb_mb); | |
573 | m = so->so_rcv.sb_mb; | |
574 | } | |
575 | } | |
576 | } | |
577 | if (m) | |
578 | m->m_nextpkt = nextrecord; | |
970108c7 | 579 | moff = 0; |
dd1ca18d | 580 | offset = 0; |
261a8548 | 581 | while (m && uio->uio_resid > 0 && error == 0) { |
2557c1fc MK |
582 | if (m->m_type == MT_OOBDATA) |
583 | flags |= MSG_OOB; | |
584 | else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) | |
c34d38f4 | 585 | panic("receive 3"); |
2557c1fc MK |
586 | if (m->m_flags & M_EOR) |
587 | flags |= MSG_EOR; | |
5699bf69 | 588 | len = uio->uio_resid; |
32a43ee2 | 589 | so->so_state &= ~SS_RCVATMARK; |
dd1ca18d MK |
590 | if (so->so_oobmark && len > so->so_oobmark - offset) |
591 | len = so->so_oobmark - offset; | |
8c0650b0 | 592 | if (len > m->m_len - moff) |
970108c7 | 593 | len = m->m_len - moff; |
ce9d8eb4 | 594 | splx(s); |
c4ec2128 | 595 | error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); |
ce9d8eb4 | 596 | s = splnet(); |
8c0650b0 | 597 | if (len == m->m_len - moff) { |
c34d38f4 MK |
598 | if (flags & MSG_PEEK) { |
599 | m = m->m_next; | |
600 | moff = 0; | |
601 | } else { | |
2557c1fc | 602 | nextrecord = m->m_nextpkt; |
6ff43975 MK |
603 | sbfree(&so->so_rcv, m); |
604 | MFREE(m, so->so_rcv.sb_mb); | |
605 | m = so->so_rcv.sb_mb; | |
606 | if (m) | |
2557c1fc | 607 | m->m_nextpkt = nextrecord; |
c34d38f4 | 608 | } |
ce9d8eb4 | 609 | } else { |
88a7a62a | 610 | if (flags & MSG_PEEK) |
970108c7 BJ |
611 | moff += len; |
612 | else { | |
2557c1fc | 613 | m->m_data += len; |
970108c7 BJ |
614 | m->m_len -= len; |
615 | so->so_rcv.sb_cc -= len; | |
616 | } | |
ce9d8eb4 | 617 | } |
dd1ca18d MK |
618 | if (so->so_oobmark) { |
619 | if ((flags & MSG_PEEK) == 0) { | |
620 | so->so_oobmark -= len; | |
621 | if (so->so_oobmark == 0) { | |
622 | so->so_state |= SS_RCVATMARK; | |
623 | break; | |
624 | } | |
625 | } else | |
626 | offset += len; | |
970108c7 | 627 | } |
261a8548 | 628 | } |
2557c1fc MK |
629 | if (m && (flags & MSG_EOR)) { |
630 | flags &= ~MSG_EOR; | |
631 | if ((flags & MSG_PEEK) == 0) | |
632 | m->m_flags |= M_EOR; | |
633 | } | |
261a8548 | 634 | if ((flags & MSG_PEEK) == 0) { |
491e9020 | 635 | if (m == 0) |
261a8548 | 636 | so->so_rcv.sb_mb = nextrecord; |
2557c1fc MK |
637 | else if (pr->pr_flags & PR_ATOMIC) { |
638 | flags |= MSG_TRUNC; | |
6ff43975 | 639 | (void) sbdroprecord(&so->so_rcv); |
2557c1fc | 640 | } |
261a8548 MK |
641 | if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) |
642 | (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, | |
a2aebb63 KS |
643 | (struct mbuf *)flags, (struct mbuf *)0, |
644 | (struct mbuf *)0); | |
c34d38f4 MK |
645 | if (error == 0 && rightsp && *rightsp && |
646 | pr->pr_domain->dom_externalize) | |
647 | error = (*pr->pr_domain->dom_externalize)(*rightsp); | |
261a8548 | 648 | } |
2557c1fc MK |
649 | if (flagsp) |
650 | *flagsp |= flags; | |
cc15ab5d | 651 | release: |
ae921915 | 652 | sbunlock(&so->so_rcv); |
cc15ab5d | 653 | splx(s); |
ae921915 | 654 | return (error); |
92a533e6 BJ |
655 | } |
656 | ||
98422daa | 657 | soshutdown(so, how) |
88a7a62a SL |
658 | register struct socket *so; |
659 | register int how; | |
98422daa | 660 | { |
88a7a62a | 661 | register struct protosw *pr = so->so_proto; |
98422daa SL |
662 | |
663 | how++; | |
88a7a62a SL |
664 | if (how & FREAD) |
665 | sorflush(so); | |
98422daa | 666 | if (how & FWRITE) |
88a7a62a SL |
667 | return ((*pr->pr_usrreq)(so, PRU_SHUTDOWN, |
668 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); | |
98422daa SL |
669 | return (0); |
670 | } | |
671 | ||
88a7a62a SL |
672 | sorflush(so) |
673 | register struct socket *so; | |
674 | { | |
675 | register struct sockbuf *sb = &so->so_rcv; | |
676 | register struct protosw *pr = so->so_proto; | |
677 | register int s; | |
678 | struct sockbuf asb; | |
679 | ||
680 | sblock(sb); | |
681 | s = splimp(); | |
682 | socantrcvmore(so); | |
683 | sbunlock(sb); | |
684 | asb = *sb; | |
685 | bzero((caddr_t)sb, sizeof (*sb)); | |
686 | splx(s); | |
261a8548 MK |
687 | if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose) |
688 | (*pr->pr_domain->dom_dispose)(asb.sb_mb); | |
88a7a62a SL |
689 | sbrelease(&asb); |
690 | } | |
691 | ||
bc2f5859 | 692 | sosetopt(so, level, optname, m0) |
88a7a62a | 693 | register struct socket *so; |
98422daa | 694 | int level, optname; |
bc2f5859 | 695 | struct mbuf *m0; |
98422daa | 696 | { |
61ec2127 | 697 | int error = 0; |
bc2f5859 | 698 | register struct mbuf *m = m0; |
98422daa | 699 | |
61ec2127 | 700 | if (level != SOL_SOCKET) { |
cbe54390 MK |
701 | if (so->so_proto && so->so_proto->pr_ctloutput) |
702 | return ((*so->so_proto->pr_ctloutput) | |
bc2f5859 | 703 | (PRCO_SETOPT, so, level, optname, &m0)); |
cbe54390 MK |
704 | error = ENOPROTOOPT; |
705 | } else { | |
706 | switch (optname) { | |
98422daa | 707 | |
cbe54390 MK |
708 | case SO_LINGER: |
709 | if (m == NULL || m->m_len != sizeof (struct linger)) { | |
710 | error = EINVAL; | |
711 | goto bad; | |
712 | } | |
713 | so->so_linger = mtod(m, struct linger *)->l_linger; | |
714 | /* fall thru... */ | |
715 | ||
716 | case SO_DEBUG: | |
717 | case SO_KEEPALIVE: | |
718 | case SO_DONTROUTE: | |
719 | case SO_USELOOPBACK: | |
720 | case SO_BROADCAST: | |
721 | case SO_REUSEADDR: | |
97c8f6a8 | 722 | case SO_OOBINLINE: |
cbe54390 MK |
723 | if (m == NULL || m->m_len < sizeof (int)) { |
724 | error = EINVAL; | |
725 | goto bad; | |
726 | } | |
727 | if (*mtod(m, int *)) | |
728 | so->so_options |= optname; | |
729 | else | |
730 | so->so_options &= ~optname; | |
731 | break; | |
98422daa | 732 | |
cbe54390 MK |
733 | case SO_SNDBUF: |
734 | case SO_RCVBUF: | |
735 | case SO_SNDLOWAT: | |
736 | case SO_RCVLOWAT: | |
737 | case SO_SNDTIMEO: | |
738 | case SO_RCVTIMEO: | |
739 | if (m == NULL || m->m_len < sizeof (int)) { | |
740 | error = EINVAL; | |
741 | goto bad; | |
742 | } | |
743 | switch (optname) { | |
744 | ||
745 | case SO_SNDBUF: | |
746 | case SO_RCVBUF: | |
3f36033f MK |
747 | if (sbreserve(optname == SO_SNDBUF ? |
748 | &so->so_snd : &so->so_rcv, | |
749 | (u_long) *mtod(m, int *)) == 0) { | |
cbe54390 MK |
750 | error = ENOBUFS; |
751 | goto bad; | |
752 | } | |
753 | break; | |
754 | ||
755 | case SO_SNDLOWAT: | |
756 | so->so_snd.sb_lowat = *mtod(m, int *); | |
757 | break; | |
758 | case SO_RCVLOWAT: | |
759 | so->so_rcv.sb_lowat = *mtod(m, int *); | |
760 | break; | |
761 | case SO_SNDTIMEO: | |
762 | so->so_snd.sb_timeo = *mtod(m, int *); | |
763 | break; | |
764 | case SO_RCVTIMEO: | |
765 | so->so_rcv.sb_timeo = *mtod(m, int *); | |
766 | break; | |
767 | } | |
768 | break; | |
769 | ||
770 | default: | |
771 | error = ENOPROTOOPT; | |
772 | break; | |
773 | } | |
98422daa | 774 | } |
61ec2127 SL |
775 | bad: |
776 | if (m) | |
777 | (void) m_free(m); | |
778 | return (error); | |
98422daa SL |
779 | } |
780 | ||
61ec2127 | 781 | sogetopt(so, level, optname, mp) |
88a7a62a | 782 | register struct socket *so; |
98422daa | 783 | int level, optname; |
61ec2127 | 784 | struct mbuf **mp; |
98422daa | 785 | { |
61ec2127 | 786 | register struct mbuf *m; |
98422daa | 787 | |
cbe54390 MK |
788 | if (level != SOL_SOCKET) { |
789 | if (so->so_proto && so->so_proto->pr_ctloutput) { | |
790 | return ((*so->so_proto->pr_ctloutput) | |
791 | (PRCO_GETOPT, so, level, optname, mp)); | |
792 | } else | |
793 | return (ENOPROTOOPT); | |
794 | } else { | |
61ec2127 | 795 | m = m_get(M_WAIT, MT_SOOPTS); |
d6e6eea8 MK |
796 | m->m_len = sizeof (int); |
797 | ||
cbe54390 MK |
798 | switch (optname) { |
799 | ||
800 | case SO_LINGER: | |
801 | m->m_len = sizeof (struct linger); | |
802 | mtod(m, struct linger *)->l_onoff = | |
803 | so->so_options & SO_LINGER; | |
804 | mtod(m, struct linger *)->l_linger = so->so_linger; | |
805 | break; | |
806 | ||
807 | case SO_USELOOPBACK: | |
808 | case SO_DONTROUTE: | |
809 | case SO_DEBUG: | |
810 | case SO_KEEPALIVE: | |
811 | case SO_REUSEADDR: | |
812 | case SO_BROADCAST: | |
97c8f6a8 | 813 | case SO_OOBINLINE: |
cbe54390 MK |
814 | *mtod(m, int *) = so->so_options & optname; |
815 | break; | |
816 | ||
d6e6eea8 MK |
817 | case SO_TYPE: |
818 | *mtod(m, int *) = so->so_type; | |
819 | break; | |
820 | ||
de2c74a5 MK |
821 | case SO_ERROR: |
822 | *mtod(m, int *) = so->so_error; | |
823 | so->so_error = 0; | |
824 | break; | |
825 | ||
cbe54390 MK |
826 | case SO_SNDBUF: |
827 | *mtod(m, int *) = so->so_snd.sb_hiwat; | |
828 | break; | |
98422daa | 829 | |
cbe54390 MK |
830 | case SO_RCVBUF: |
831 | *mtod(m, int *) = so->so_rcv.sb_hiwat; | |
832 | break; | |
833 | ||
834 | case SO_SNDLOWAT: | |
835 | *mtod(m, int *) = so->so_snd.sb_lowat; | |
836 | break; | |
837 | ||
838 | case SO_RCVLOWAT: | |
839 | *mtod(m, int *) = so->so_rcv.sb_lowat; | |
840 | break; | |
841 | ||
842 | case SO_SNDTIMEO: | |
843 | *mtod(m, int *) = so->so_snd.sb_timeo; | |
844 | break; | |
845 | ||
846 | case SO_RCVTIMEO: | |
847 | *mtod(m, int *) = so->so_rcv.sb_timeo; | |
848 | break; | |
849 | ||
850 | default: | |
8011f5df | 851 | (void)m_free(m); |
cbe54390 MK |
852 | return (ENOPROTOOPT); |
853 | } | |
854 | *mp = m; | |
855 | return (0); | |
98422daa | 856 | } |
98422daa SL |
857 | } |
858 | ||
edebca28 | 859 | sohasoutofband(so) |
88a7a62a | 860 | register struct socket *so; |
edebca28 | 861 | { |
3d190e86 | 862 | struct proc *p; |
edebca28 | 863 | |
a2aebb63 KS |
864 | if (so->so_pgid < 0) |
865 | gsignal(-so->so_pgid, SIGURG); | |
866 | else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) | |
3d190e86 | 867 | psignal(p, SIGURG); |
de2c74a5 MK |
868 | if (so->so_rcv.sb_sel) { |
869 | selwakeup(so->so_rcv.sb_sel, so->so_rcv.sb_flags & SB_COLL); | |
870 | so->so_rcv.sb_sel = 0; | |
871 | so->so_rcv.sb_flags &= ~SB_COLL; | |
872 | } | |
edebca28 | 873 | } |