Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
7c4ec3aa | 2 | * Copyright (c) 1982, 1986, 1988, 1990 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 | * |
7c4ec3aa | 17 | * @(#)uipc_socket.c 7.18 (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 | ||
7c4ec3aa MK |
86 | error = |
87 | (*so->so_proto->pr_usrreq)(so, PRU_BIND, | |
88a7a62a | 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 | } | |
629e51da | 106 | if (so->so_q == 0) |
cf012934 | 107 | so->so_options |= SO_ACCEPTCONN; |
cf012934 BJ |
108 | if (backlog < 0) |
109 | backlog = 0; | |
2557c1fc | 110 | so->so_qlimit = min(backlog, SOMAXCONN); |
9e87be97 | 111 | splx(s); |
cf012934 BJ |
112 | return (0); |
113 | } | |
114 | ||
ae921915 | 115 | sofree(so) |
88a7a62a | 116 | register struct socket *so; |
ae921915 BJ |
117 | { |
118 | ||
bb73a14e MK |
119 | if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0) |
120 | return; | |
90aaea96 BJ |
121 | if (so->so_head) { |
122 | if (!soqremque(so, 0) && !soqremque(so, 1)) | |
123 | panic("sofree dq"); | |
124 | so->so_head = 0; | |
125 | } | |
4ad99bae | 126 | sbrelease(&so->so_snd); |
88a7a62a | 127 | sorflush(so); |
a2aebb63 | 128 | FREE(so, M_SOCKET); |
ae921915 BJ |
129 | } |
130 | ||
92a533e6 | 131 | /* |
cc15ab5d BJ |
132 | * Close a socket on last file table reference removal. |
133 | * Initiate disconnect if connected. | |
134 | * Free socket when disconnect complete. | |
92a533e6 | 135 | */ |
88a7a62a | 136 | soclose(so) |
92a533e6 | 137 | register struct socket *so; |
92a533e6 | 138 | { |
cc15ab5d | 139 | int s = splnet(); /* conservative */ |
e58562f2 | 140 | int error = 0; |
cc15ab5d | 141 | |
90aaea96 | 142 | if (so->so_options & SO_ACCEPTCONN) { |
629e51da | 143 | while (so->so_q0) |
26225f25 | 144 | (void) soabort(so->so_q0); |
629e51da | 145 | while (so->so_q) |
26225f25 | 146 | (void) soabort(so->so_q); |
90aaea96 | 147 | } |
cc15ab5d BJ |
148 | if (so->so_pcb == 0) |
149 | goto discard; | |
150 | if (so->so_state & SS_ISCONNECTED) { | |
151 | if ((so->so_state & SS_ISDISCONNECTING) == 0) { | |
dedd6629 | 152 | error = sodisconnect(so); |
88a7a62a SL |
153 | if (error) |
154 | goto drop; | |
cc15ab5d | 155 | } |
98422daa | 156 | if (so->so_options & SO_LINGER) { |
b8acc34d | 157 | if ((so->so_state & SS_ISDISCONNECTING) && |
88a7a62a SL |
158 | (so->so_state & SS_NBIO)) |
159 | goto drop; | |
b8acc34d | 160 | while (so->so_state & SS_ISCONNECTED) |
83866636 MK |
161 | if (error = tsleep((caddr_t)&so->so_timeo, |
162 | PSOCK | PCATCH, netcls, so->so_linger)) | |
163 | break; | |
72857acf | 164 | } |
cc15ab5d | 165 | } |
89900a09 | 166 | drop: |
37c0974a | 167 | if (so->so_pcb) { |
88a7a62a SL |
168 | int error2 = |
169 | (*so->so_proto->pr_usrreq)(so, PRU_DETACH, | |
170 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); | |
171 | if (error == 0) | |
172 | error = error2; | |
37c0974a | 173 | } |
cc15ab5d | 174 | discard: |
26225f25 SL |
175 | if (so->so_state & SS_NOFDREF) |
176 | panic("soclose: NOFDREF"); | |
90aaea96 | 177 | so->so_state |= SS_NOFDREF; |
4ad99bae | 178 | sofree(so); |
cc15ab5d | 179 | splx(s); |
88a7a62a | 180 | return (error); |
92a533e6 BJ |
181 | } |
182 | ||
26225f25 SL |
183 | /* |
184 | * Must be called at splnet... | |
185 | */ | |
186 | soabort(so) | |
187 | struct socket *so; | |
188 | { | |
26225f25 | 189 | |
88a7a62a SL |
190 | return ( |
191 | (*so->so_proto->pr_usrreq)(so, PRU_ABORT, | |
192 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); | |
92a533e6 BJ |
193 | } |
194 | ||
98422daa | 195 | soaccept(so, nam) |
88a7a62a | 196 | register struct socket *so; |
cf012934 | 197 | struct mbuf *nam; |
2b4b57cd BJ |
198 | { |
199 | int s = splnet(); | |
200 | int error; | |
201 | ||
26225f25 SL |
202 | if ((so->so_state & SS_NOFDREF) == 0) |
203 | panic("soaccept: !NOFDREF"); | |
98422daa | 204 | so->so_state &= ~SS_NOFDREF; |
cf012934 | 205 | error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, |
88a7a62a | 206 | (struct mbuf *)0, nam, (struct mbuf *)0); |
2b4b57cd BJ |
207 | splx(s); |
208 | return (error); | |
209 | } | |
210 | ||
98422daa | 211 | soconnect(so, nam) |
88a7a62a | 212 | register struct socket *so; |
cf012934 | 213 | struct mbuf *nam; |
ce9d8eb4 | 214 | { |
7bcf9d13 | 215 | int s; |
cc15ab5d | 216 | int error; |
ce9d8eb4 | 217 | |
7bcf9d13 MK |
218 | if (so->so_options & SO_ACCEPTCONN) |
219 | return (EOPNOTSUPP); | |
220 | s = splnet(); | |
de2c74a5 MK |
221 | /* |
222 | * If protocol is connection-based, can only connect once. | |
223 | * Otherwise, if connected, try to disconnect first. | |
224 | * This allows user to disconnect by connecting to, e.g., | |
225 | * a null address. | |
226 | */ | |
227 | if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) && | |
228 | ((so->so_proto->pr_flags & PR_CONNREQUIRED) || | |
229 | (error = sodisconnect(so)))) | |
cc15ab5d | 230 | error = EISCONN; |
de2c74a5 MK |
231 | else |
232 | error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, | |
233 | (struct mbuf *)0, nam, (struct mbuf *)0); | |
cc15ab5d BJ |
234 | splx(s); |
235 | return (error); | |
ce9d8eb4 BJ |
236 | } |
237 | ||
88a7a62a SL |
238 | soconnect2(so1, so2) |
239 | register struct socket *so1; | |
240 | struct socket *so2; | |
241 | { | |
242 | int s = splnet(); | |
243 | int error; | |
244 | ||
5a48956d SL |
245 | error = (*so1->so_proto->pr_usrreq)(so1, PRU_CONNECT2, |
246 | (struct mbuf *)0, (struct mbuf *)so2, (struct mbuf *)0); | |
88a7a62a SL |
247 | splx(s); |
248 | return (error); | |
249 | } | |
88a7a62a | 250 | |
dedd6629 | 251 | sodisconnect(so) |
88a7a62a | 252 | register struct socket *so; |
ce9d8eb4 | 253 | { |
cc15ab5d BJ |
254 | int s = splnet(); |
255 | int error; | |
ce9d8eb4 | 256 | |
cc15ab5d BJ |
257 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
258 | error = ENOTCONN; | |
259 | goto bad; | |
ce9d8eb4 | 260 | } |
cc15ab5d BJ |
261 | if (so->so_state & SS_ISDISCONNECTING) { |
262 | error = EALREADY; | |
263 | goto bad; | |
ce9d8eb4 | 264 | } |
cf012934 | 265 | error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, |
dedd6629 | 266 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0); |
cc15ab5d BJ |
267 | bad: |
268 | splx(s); | |
269 | return (error); | |
ce9d8eb4 BJ |
270 | } |
271 | ||
cc15ab5d BJ |
272 | /* |
273 | * Send on a socket. | |
274 | * If send must go all at once and message is larger than | |
275 | * send buffering, then hard error. | |
276 | * Lock against other senders. | |
277 | * If must go all at once and not enough room now, then | |
278 | * inform user that this would block and do nothing. | |
8250a099 | 279 | * Otherwise, if nonblocking, send as much as possible. |
7c4ec3aa MK |
280 | * The data to be sent is described by "uio" if nonzero, |
281 | * otherwise by the mbuf chain "top" (which must be null | |
282 | * if uio is not). Data provided in mbuf chain must be small | |
283 | * enough to send all at once. | |
284 | * | |
285 | * Returns nonzero on error, timeout or signal; callers | |
286 | * must check for short counts if EINTR/ERESTART are returned. | |
287 | * Data and control buffers are freed on return. | |
cc15ab5d | 288 | */ |
7c4ec3aa | 289 | sosend(so, addr, uio, top, control, flags/*, sbwait_func, sbwait_arg*/) |
ce9d8eb4 | 290 | register struct socket *so; |
7c4ec3aa MK |
291 | struct mbuf *addr; |
292 | struct uio *uio; | |
293 | struct mbuf *top; | |
2967f28e | 294 | struct mbuf *control; |
7c4ec3aa MK |
295 | int flags; |
296 | /* | |
297 | int (*sbwait_func)(); | |
298 | caddr_t sbwait_arg; | |
299 | */ | |
ce9d8eb4 | 300 | { |
7c4ec3aa | 301 | struct mbuf **mp; |
2557c1fc | 302 | register struct mbuf *m; |
7c4ec3aa MK |
303 | register long space, len, resid; |
304 | int clen = 0, error, s, dontroute, mlen; | |
305 | int atomic = sosendallatonce(so) || top; | |
ce9d8eb4 | 306 | |
7c4ec3aa MK |
307 | if (uio) |
308 | resid = uio->uio_resid; | |
309 | else | |
310 | resid = top->m_pkthdr.len; | |
88a7a62a SL |
311 | dontroute = |
312 | (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && | |
313 | (so->so_proto->pr_flags & PR_ATOMIC); | |
8250a099 | 314 | u.u_ru.ru_msgsnd++; |
2967f28e | 315 | if (control) |
7c4ec3aa | 316 | clen = control->m_len; |
cc15ab5d BJ |
317 | #define snderr(errno) { error = errno; splx(s); goto release; } |
318 | ||
8250a099 | 319 | restart: |
83866636 | 320 | if (error = sblock(&so->so_snd)) |
7c4ec3aa | 321 | goto out; |
8250a099 MK |
322 | do { |
323 | s = splnet(); | |
af9c562f | 324 | if (so->so_state & SS_CANTSENDMORE) |
8250a099 | 325 | snderr(EPIPE); |
a2aebb63 KS |
326 | if (so->so_error) |
327 | snderr(so->so_error); | |
8250a099 | 328 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
a2aebb63 | 329 | if (so->so_proto->pr_flags & PR_CONNREQUIRED) { |
629e51da | 330 | if ((so->so_state & SS_ISCONFIRMING) == 0) |
a2aebb63 | 331 | snderr(ENOTCONN); |
7c4ec3aa | 332 | } else if (addr == 0) |
8250a099 MK |
333 | snderr(EDESTADDRREQ); |
334 | } | |
7c4ec3aa | 335 | space = sbspace(&so->so_snd); |
8250a099 | 336 | if (flags & MSG_OOB) |
7c4ec3aa MK |
337 | space += 1024; |
338 | if (space < resid + clen && | |
339 | (atomic || space < so->so_snd.sb_lowat || space < clen)) { | |
340 | if (atomic && resid > so->so_snd.sb_hiwat || | |
341 | clen > so->so_snd.sb_hiwat) | |
342 | snderr(EMSGSIZE); | |
343 | if (so->so_state & SS_NBIO) | |
344 | snderr(EWOULDBLOCK); | |
345 | sbunlock(&so->so_snd); | |
346 | /* | |
347 | if (sbwait_func) | |
348 | error = (*sbwait_func)(&so->so_snd, sbwait_arg); | |
349 | else | |
350 | */ | |
351 | error = sbwait(&so->so_snd); | |
352 | splx(s); | |
353 | if (error) | |
354 | goto out; | |
355 | goto restart; | |
8250a099 | 356 | } |
4c078bb2 | 357 | splx(s); |
8250a099 | 358 | mp = ⊤ |
7c4ec3aa MK |
359 | space -= clen; |
360 | if (uio == NULL) { | |
361 | /* | |
362 | * Data is prepackaged in "top". | |
363 | */ | |
364 | resid = 0; | |
365 | if (flags & MSG_EOR) | |
366 | top->m_flags |= M_EOR; | |
367 | } else do { | |
2557c1fc MK |
368 | do { |
369 | if (top == 0) { | |
370 | MGETHDR(m, M_WAIT, MT_DATA); | |
371 | mlen = MHLEN; | |
372 | m->m_pkthdr.len = 0; | |
373 | m->m_pkthdr.rcvif = (struct ifnet *)0; | |
374 | } else { | |
375 | MGET(m, M_WAIT, MT_DATA); | |
376 | mlen = MLEN; | |
377 | } | |
7c4ec3aa | 378 | if (resid >= MINCLSIZE && space >= MCLBYTES) { |
2557c1fc MK |
379 | MCLGET(m, M_WAIT); |
380 | if ((m->m_flags & M_EXT) == 0) | |
8250a099 | 381 | goto nopages; |
2557c1fc MK |
382 | mlen = MCLBYTES; |
383 | #ifdef MAPPED_MBUFS | |
7c4ec3aa | 384 | len = min(MCLBYTES, resid); |
2557c1fc | 385 | #else |
7c4ec3aa MK |
386 | if (top == 0) { |
387 | len = min(MCLBYTES - max_hdr, resid); | |
388 | m->m_data += max_hdr; | |
389 | } | |
2557c1fc MK |
390 | #endif |
391 | space -= MCLBYTES; | |
8250a099 MK |
392 | } else { |
393 | nopages: | |
7c4ec3aa | 394 | len = min(min(mlen, resid), space); |
8c0650b0 | 395 | space -= len; |
2557c1fc MK |
396 | /* |
397 | * For datagram protocols, leave room | |
398 | * for protocol headers in first mbuf. | |
399 | */ | |
84efcd38 | 400 | if (atomic && top == 0 && len < mlen) |
2557c1fc | 401 | MH_ALIGN(m, len); |
8250a099 | 402 | } |
c4ec2128 | 403 | error = uiomove(mtod(m, caddr_t), len, uio); |
7c4ec3aa | 404 | resid = uio->uio_resid; |
8250a099 MK |
405 | m->m_len = len; |
406 | *mp = m; | |
2557c1fc | 407 | top->m_pkthdr.len += len; |
8250a099 MK |
408 | if (error) |
409 | goto release; | |
410 | mp = &m->m_next; | |
7c4ec3aa MK |
411 | if (resid <= 0) { |
412 | if (flags & MSG_EOR) | |
2557c1fc | 413 | top->m_flags |= M_EOR; |
af9c562f | 414 | break; |
2557c1fc MK |
415 | } |
416 | } while (space > 0 && atomic); | |
417 | if (dontroute) | |
418 | so->so_options |= SO_DONTROUTE; | |
419 | s = splnet(); /* XXX */ | |
420 | error = (*so->so_proto->pr_usrreq)(so, | |
421 | (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND, | |
7c4ec3aa | 422 | top, addr, control); |
2557c1fc MK |
423 | splx(s); |
424 | if (dontroute) | |
425 | so->so_options &= ~SO_DONTROUTE; | |
7c4ec3aa MK |
426 | clen = 0; |
427 | control = 0; | |
2557c1fc MK |
428 | top = 0; |
429 | mp = ⊤ | |
2557c1fc MK |
430 | if (error) |
431 | goto release; | |
7c4ec3aa MK |
432 | } while (resid && space > 0); |
433 | } while (resid); | |
cc15ab5d | 434 | |
ce9d8eb4 | 435 | release: |
cc15ab5d | 436 | sbunlock(&so->so_snd); |
7c4ec3aa | 437 | out: |
0f90f987 BJ |
438 | if (top) |
439 | m_freem(top); | |
7c4ec3aa MK |
440 | if (control) |
441 | m_freem(control); | |
ce9d8eb4 BJ |
442 | return (error); |
443 | } | |
444 | ||
c34d38f4 MK |
445 | /* |
446 | * Implement receive operations on a socket. | |
447 | * We depend on the way that records are added to the sockbuf | |
448 | * by sbappend*. In particular, each record (mbufs linked through m_next) | |
449 | * must begin with an address if the protocol so specifies, | |
7c4ec3aa MK |
450 | * followed by an optional mbuf or mbufs containing ancillary data, |
451 | * and then zero or more mbufs of data. | |
c34d38f4 MK |
452 | * In order to avoid blocking network interrupts for the entire time here, |
453 | * we splx() while doing the actual copy to user space. | |
454 | * Although the sockbuf is locked, new data may still be appended, | |
455 | * and thus we must maintain consistency of the sockbuf during that time. | |
7c4ec3aa MK |
456 | * |
457 | * The caller may receive the data as a single mbuf chain by supplying | |
458 | * an mbuf **mp for use in returning the chain. The uio is then used | |
459 | * only for the count in uio_resid. | |
c34d38f4 | 460 | */ |
7c4ec3aa | 461 | soreceive(so, paddr, uio, mp, controlp, flagsp/*, sbwait_func, sbwait_arg*/) |
ce9d8eb4 | 462 | register struct socket *so; |
7c4ec3aa MK |
463 | struct mbuf **paddr; |
464 | struct uio *uio; | |
465 | struct mbuf **mp; | |
466 | struct mbuf **controlp; | |
2557c1fc | 467 | int *flagsp; |
7c4ec3aa MK |
468 | /* |
469 | int (*sbwait_func)(); | |
470 | caddr_t sbwait_arg; | |
471 | */ | |
ce9d8eb4 | 472 | { |
6ff43975 | 473 | register struct mbuf *m; |
7c4ec3aa | 474 | register int resid, flags, len, error, s, offset; |
88a7a62a | 475 | struct protosw *pr = so->so_proto; |
7c4ec3aa MK |
476 | struct mbuf *nextrecord; |
477 | int moff, type; | |
88a7a62a | 478 | |
7c4ec3aa MK |
479 | if (paddr) |
480 | *paddr = 0; | |
2557c1fc MK |
481 | if (controlp) |
482 | *controlp = 0; | |
483 | if (flagsp) | |
484 | flags = *flagsp &~ MSG_EOR; | |
485 | else | |
486 | flags = 0; | |
88a7a62a | 487 | if (flags & MSG_OOB) { |
cce93e4b | 488 | m = m_get(M_WAIT, MT_DATA); |
88a7a62a | 489 | error = (*pr->pr_usrreq)(so, PRU_RCVOOB, |
de2c74a5 | 490 | m, (struct mbuf *)(flags & MSG_PEEK), (struct mbuf *)0); |
a8d3bf7f | 491 | if (error) |
5fe6f9d1 | 492 | goto bad; |
970108c7 | 493 | do { |
7c4ec3aa MK |
494 | error = uiomove(mtod(m, caddr_t), |
495 | (int) min(uio->uio_resid, m->m_len), uio); | |
970108c7 | 496 | m = m_free(m); |
a8d3bf7f | 497 | } while (uio->uio_resid && error == 0 && m); |
5fe6f9d1 | 498 | bad: |
970108c7 | 499 | if (m) |
39d536e6 | 500 | m_freem(m); |
a8d3bf7f | 501 | return (error); |
970108c7 | 502 | } |
7c4ec3aa MK |
503 | if (mp) |
504 | *mp = (struct mbuf *)0; | |
505 | resid = uio->uio_resid; | |
506 | if (so->so_state & SS_ISCONFIRMING && resid) | |
2557c1fc MK |
507 | (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, |
508 | (struct mbuf *)0, (struct mbuf *)0); | |
ce9d8eb4 | 509 | |
cc15ab5d | 510 | restart: |
83866636 MK |
511 | if (error = sblock(&so->so_rcv)) |
512 | return (error); | |
cc15ab5d BJ |
513 | s = splnet(); |
514 | ||
a2aebb63 | 515 | m = so->so_rcv.sb_mb; |
7c4ec3aa MK |
516 | if (m == 0 || (so->so_rcv.sb_cc < resid && |
517 | so->so_rcv.sb_cc < so->so_rcv.sb_lowat)) { | |
518 | #ifdef DIAGNOSTIC | |
519 | if (m == 0 && so->so_rcv.sb_cc) | |
a2aebb63 | 520 | panic("receive 1"); |
7c4ec3aa | 521 | #endif |
4c078bb2 BJ |
522 | if (so->so_error) { |
523 | error = so->so_error; | |
524 | so->so_error = 0; | |
4c078bb2 BJ |
525 | goto release; |
526 | } | |
f02d4eaa | 527 | if (so->so_state & SS_CANTRCVMORE) |
cc15ab5d | 528 | goto release; |
629e51da | 529 | if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 && |
f02d4eaa KB |
530 | (so->so_proto->pr_flags & PR_CONNREQUIRED)) { |
531 | error = ENOTCONN; | |
532 | goto release; | |
533 | } | |
7c4ec3aa | 534 | if (resid == 0) |
c34d38f4 | 535 | goto release; |
f02d4eaa KB |
536 | if (so->so_state & SS_NBIO) { |
537 | error = EWOULDBLOCK; | |
538 | goto release; | |
539 | } | |
cc15ab5d | 540 | sbunlock(&so->so_rcv); |
7c4ec3aa MK |
541 | /* |
542 | if (sbwait_func) | |
543 | error = (*sbwait_func)(&so->so_rcv, sbwait_arg); | |
544 | else | |
545 | */ | |
546 | error = sbwait(&so->so_rcv); | |
a4f6d93d | 547 | splx(s); |
7c4ec3aa MK |
548 | if (error) |
549 | return (error); | |
cc15ab5d | 550 | goto restart; |
ce9d8eb4 | 551 | } |
c7256358 | 552 | u.u_ru.ru_msgrcv++; |
7c4ec3aa | 553 | #ifdef DIAGNOSTIC |
2557c1fc MK |
554 | if (m->m_type == 0) |
555 | panic("receive 3a"); | |
7c4ec3aa | 556 | #endif |
2557c1fc | 557 | nextrecord = m->m_nextpkt; |
88a7a62a | 558 | if (pr->pr_flags & PR_ADDR) { |
7c4ec3aa | 559 | #ifdef DIAGNOSTIC |
c34d38f4 | 560 | if (m->m_type != MT_SONAME) |
261a8548 | 561 | panic("receive 1a"); |
7c4ec3aa | 562 | #endif |
261a8548 | 563 | if (flags & MSG_PEEK) { |
7c4ec3aa MK |
564 | if (paddr) |
565 | *paddr = m_copy(m, 0, m->m_len); | |
c34d38f4 | 566 | m = m->m_next; |
261a8548 | 567 | } else { |
c34d38f4 | 568 | sbfree(&so->so_rcv, m); |
7c4ec3aa MK |
569 | if (paddr) { |
570 | *paddr = m; | |
6ff43975 | 571 | so->so_rcv.sb_mb = m->m_next; |
c34d38f4 | 572 | m->m_next = 0; |
6ff43975 | 573 | m = so->so_rcv.sb_mb; |
c34d38f4 | 574 | } else { |
6ff43975 MK |
575 | MFREE(m, so->so_rcv.sb_mb); |
576 | m = so->so_rcv.sb_mb; | |
c34d38f4 | 577 | } |
88a7a62a | 578 | } |
cc15ab5d | 579 | } |
7c4ec3aa | 580 | while (m && m->m_type == MT_CONTROL && error == 0) { |
2557c1fc MK |
581 | if (flags & MSG_PEEK) { |
582 | if (controlp) | |
583 | *controlp = m_copy(m, 0, m->m_len); | |
584 | m = m->m_next; | |
585 | } else { | |
586 | sbfree(&so->so_rcv, m); | |
587 | if (controlp) { | |
7c4ec3aa MK |
588 | if (mtod(m, struct cmsghdr *)->cmsg_type == |
589 | SCM_RIGHTS && | |
590 | pr->pr_domain->dom_externalize) | |
591 | error = (*pr->pr_domain->dom_externalize)(m); | |
2557c1fc MK |
592 | *controlp = m; |
593 | so->so_rcv.sb_mb = m->m_next; | |
594 | m->m_next = 0; | |
595 | m = so->so_rcv.sb_mb; | |
596 | } else { | |
597 | MFREE(m, so->so_rcv.sb_mb); | |
598 | m = so->so_rcv.sb_mb; | |
599 | } | |
600 | } | |
7c4ec3aa MK |
601 | if (controlp) |
602 | controlp = &(*controlp)->m_next; | |
2557c1fc | 603 | } |
7c4ec3aa | 604 | if (m) { |
2557c1fc | 605 | m->m_nextpkt = nextrecord; |
7c4ec3aa MK |
606 | type = m->m_type; |
607 | } | |
970108c7 | 608 | moff = 0; |
dd1ca18d | 609 | offset = 0; |
7c4ec3aa | 610 | while (m && m->m_type == type && resid > 0 && error == 0) { |
2557c1fc MK |
611 | if (m->m_type == MT_OOBDATA) |
612 | flags |= MSG_OOB; | |
7c4ec3aa | 613 | #ifdef DIAGNOSTIC |
2557c1fc | 614 | else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) |
c34d38f4 | 615 | panic("receive 3"); |
7c4ec3aa MK |
616 | #endif |
617 | type = m->m_type; | |
32a43ee2 | 618 | so->so_state &= ~SS_RCVATMARK; |
7c4ec3aa | 619 | len = resid; |
dd1ca18d MK |
620 | if (so->so_oobmark && len > so->so_oobmark - offset) |
621 | len = so->so_oobmark - offset; | |
8c0650b0 | 622 | if (len > m->m_len - moff) |
970108c7 | 623 | len = m->m_len - moff; |
7c4ec3aa MK |
624 | /* |
625 | * If mp is set, just pass back the mbufs. | |
626 | * Otherwise copy them out via the uio, then free. | |
627 | * Sockbuf must be consistent here (points to current mbuf, | |
628 | * it points to next record) when we drop priority; | |
629 | * we must note any additions to the sockbuf when we | |
630 | * block interrupts again. | |
631 | */ | |
632 | if (mp == 0) { | |
633 | splx(s); | |
634 | error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); | |
635 | resid = uio->uio_resid; | |
636 | s = splnet(); | |
637 | } | |
8c0650b0 | 638 | if (len == m->m_len - moff) { |
7c4ec3aa MK |
639 | if (m->m_flags & M_EOR) |
640 | flags |= MSG_EOR; | |
c34d38f4 MK |
641 | if (flags & MSG_PEEK) { |
642 | m = m->m_next; | |
643 | moff = 0; | |
644 | } else { | |
2557c1fc | 645 | nextrecord = m->m_nextpkt; |
6ff43975 | 646 | sbfree(&so->so_rcv, m); |
7c4ec3aa MK |
647 | if (mp) { |
648 | *mp = m; | |
649 | mp = &m->m_next; | |
650 | m = m->m_next; | |
651 | } else { | |
652 | MFREE(m, so->so_rcv.sb_mb); | |
653 | m = so->so_rcv.sb_mb; | |
654 | } | |
6ff43975 | 655 | if (m) |
2557c1fc | 656 | m->m_nextpkt = nextrecord; |
c34d38f4 | 657 | } |
ce9d8eb4 | 658 | } else { |
88a7a62a | 659 | if (flags & MSG_PEEK) |
970108c7 BJ |
660 | moff += len; |
661 | else { | |
2557c1fc | 662 | m->m_data += len; |
970108c7 BJ |
663 | m->m_len -= len; |
664 | so->so_rcv.sb_cc -= len; | |
665 | } | |
ce9d8eb4 | 666 | } |
dd1ca18d MK |
667 | if (so->so_oobmark) { |
668 | if ((flags & MSG_PEEK) == 0) { | |
669 | so->so_oobmark -= len; | |
670 | if (so->so_oobmark == 0) { | |
671 | so->so_state |= SS_RCVATMARK; | |
672 | break; | |
673 | } | |
674 | } else | |
675 | offset += len; | |
970108c7 | 676 | } |
7c4ec3aa | 677 | if (flags & MSG_EOR) |
2967f28e | 678 | break; |
7c4ec3aa MK |
679 | /* |
680 | * If the MSG_WAITALL flag is set (for non-atomic socket), | |
681 | * we must not quit until "resid == 0" or an error | |
682 | * termination. If a signal/timeout occurs, return | |
683 | * prematurely but without error. | |
684 | * Keep sockbuf locked against other readers. | |
685 | */ | |
686 | while (flags & MSG_WAITALL && m == 0 && resid > 0 && | |
687 | !sosendallatonce(so)) { | |
688 | error = sbwait(&so->so_rcv); | |
689 | if (error) { | |
690 | sbunlock(&so->so_rcv); | |
691 | splx(s); | |
692 | if (mp) | |
693 | *mp = (struct mbuf *)0; | |
694 | return (0); | |
695 | } | |
696 | if (m = so->so_rcv.sb_mb) | |
697 | nextrecord = m->m_nextpkt; | |
698 | if (so->so_error || so->so_state & SS_CANTRCVMORE) | |
699 | break; | |
700 | continue; | |
701 | } | |
261a8548 | 702 | } |
7c4ec3aa MK |
703 | if (mp) |
704 | *mp = (struct mbuf *)0; | |
261a8548 | 705 | if ((flags & MSG_PEEK) == 0) { |
491e9020 | 706 | if (m == 0) |
261a8548 | 707 | so->so_rcv.sb_mb = nextrecord; |
2557c1fc MK |
708 | else if (pr->pr_flags & PR_ATOMIC) { |
709 | flags |= MSG_TRUNC; | |
6ff43975 | 710 | (void) sbdroprecord(&so->so_rcv); |
2557c1fc | 711 | } |
261a8548 MK |
712 | if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) |
713 | (*pr->pr_usrreq)(so, PRU_RCVD, (struct mbuf *)0, | |
a2aebb63 KS |
714 | (struct mbuf *)flags, (struct mbuf *)0, |
715 | (struct mbuf *)0); | |
261a8548 | 716 | } |
2557c1fc MK |
717 | if (flagsp) |
718 | *flagsp |= flags; | |
cc15ab5d | 719 | release: |
ae921915 | 720 | sbunlock(&so->so_rcv); |
cc15ab5d | 721 | splx(s); |
ae921915 | 722 | return (error); |
92a533e6 BJ |
723 | } |
724 | ||
98422daa | 725 | soshutdown(so, how) |
88a7a62a SL |
726 | register struct socket *so; |
727 | register int how; | |
98422daa | 728 | { |
88a7a62a | 729 | register struct protosw *pr = so->so_proto; |
98422daa SL |
730 | |
731 | how++; | |
88a7a62a SL |
732 | if (how & FREAD) |
733 | sorflush(so); | |
98422daa | 734 | if (how & FWRITE) |
88a7a62a SL |
735 | return ((*pr->pr_usrreq)(so, PRU_SHUTDOWN, |
736 | (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)); | |
98422daa SL |
737 | return (0); |
738 | } | |
739 | ||
88a7a62a SL |
740 | sorflush(so) |
741 | register struct socket *so; | |
742 | { | |
743 | register struct sockbuf *sb = &so->so_rcv; | |
744 | register struct protosw *pr = so->so_proto; | |
745 | register int s; | |
746 | struct sockbuf asb; | |
747 | ||
83866636 MK |
748 | sb->sb_flags |= SB_NOINTR; |
749 | (void) sblock(sb); | |
88a7a62a SL |
750 | s = splimp(); |
751 | socantrcvmore(so); | |
752 | sbunlock(sb); | |
753 | asb = *sb; | |
754 | bzero((caddr_t)sb, sizeof (*sb)); | |
755 | splx(s); | |
261a8548 MK |
756 | if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose) |
757 | (*pr->pr_domain->dom_dispose)(asb.sb_mb); | |
88a7a62a SL |
758 | sbrelease(&asb); |
759 | } | |
760 | ||
bc2f5859 | 761 | sosetopt(so, level, optname, m0) |
88a7a62a | 762 | register struct socket *so; |
98422daa | 763 | int level, optname; |
bc2f5859 | 764 | struct mbuf *m0; |
98422daa | 765 | { |
61ec2127 | 766 | int error = 0; |
bc2f5859 | 767 | register struct mbuf *m = m0; |
98422daa | 768 | |
61ec2127 | 769 | if (level != SOL_SOCKET) { |
cbe54390 MK |
770 | if (so->so_proto && so->so_proto->pr_ctloutput) |
771 | return ((*so->so_proto->pr_ctloutput) | |
bc2f5859 | 772 | (PRCO_SETOPT, so, level, optname, &m0)); |
cbe54390 MK |
773 | error = ENOPROTOOPT; |
774 | } else { | |
775 | switch (optname) { | |
98422daa | 776 | |
cbe54390 MK |
777 | case SO_LINGER: |
778 | if (m == NULL || m->m_len != sizeof (struct linger)) { | |
779 | error = EINVAL; | |
780 | goto bad; | |
781 | } | |
782 | so->so_linger = mtod(m, struct linger *)->l_linger; | |
783 | /* fall thru... */ | |
784 | ||
785 | case SO_DEBUG: | |
786 | case SO_KEEPALIVE: | |
787 | case SO_DONTROUTE: | |
788 | case SO_USELOOPBACK: | |
789 | case SO_BROADCAST: | |
790 | case SO_REUSEADDR: | |
97c8f6a8 | 791 | case SO_OOBINLINE: |
cbe54390 MK |
792 | if (m == NULL || m->m_len < sizeof (int)) { |
793 | error = EINVAL; | |
794 | goto bad; | |
795 | } | |
796 | if (*mtod(m, int *)) | |
797 | so->so_options |= optname; | |
798 | else | |
799 | so->so_options &= ~optname; | |
800 | break; | |
98422daa | 801 | |
cbe54390 | 802 | case SO_SNDBUF: |
83866636 | 803 | case SO_RCVBUF: |
7c4ec3aa | 804 | case SO_SNDLOWAT: |
83866636 | 805 | case SO_RCVLOWAT: |
7c4ec3aa | 806 | case SO_SNDTIMEO: |
cbe54390 MK |
807 | case SO_RCVTIMEO: |
808 | if (m == NULL || m->m_len < sizeof (int)) { | |
809 | error = EINVAL; | |
810 | goto bad; | |
811 | } | |
812 | switch (optname) { | |
813 | ||
814 | case SO_SNDBUF: | |
815 | case SO_RCVBUF: | |
7c4ec3aa MK |
816 | if (sbreserve(optname == SO_SNDBUF ? |
817 | &so->so_snd : &so->so_rcv, | |
818 | (u_long) *mtod(m, int *)) == 0) { | |
cbe54390 MK |
819 | error = ENOBUFS; |
820 | goto bad; | |
821 | } | |
822 | break; | |
823 | ||
824 | case SO_SNDLOWAT: | |
7c4ec3aa MK |
825 | so->so_snd.sb_lowat = *mtod(m, int *); |
826 | break; | |
cbe54390 | 827 | case SO_RCVLOWAT: |
7c4ec3aa | 828 | so->so_rcv.sb_lowat = *mtod(m, int *); |
cbe54390 MK |
829 | break; |
830 | case SO_SNDTIMEO: | |
7c4ec3aa MK |
831 | so->so_snd.sb_timeo = *mtod(m, int *); |
832 | break; | |
cbe54390 | 833 | case SO_RCVTIMEO: |
7c4ec3aa | 834 | so->so_rcv.sb_timeo = *mtod(m, int *); |
cbe54390 MK |
835 | break; |
836 | } | |
837 | break; | |
838 | ||
839 | default: | |
840 | error = ENOPROTOOPT; | |
841 | break; | |
842 | } | |
98422daa | 843 | } |
61ec2127 SL |
844 | bad: |
845 | if (m) | |
846 | (void) m_free(m); | |
847 | return (error); | |
98422daa SL |
848 | } |
849 | ||
61ec2127 | 850 | sogetopt(so, level, optname, mp) |
88a7a62a | 851 | register struct socket *so; |
98422daa | 852 | int level, optname; |
61ec2127 | 853 | struct mbuf **mp; |
98422daa | 854 | { |
61ec2127 | 855 | register struct mbuf *m; |
98422daa | 856 | |
cbe54390 MK |
857 | if (level != SOL_SOCKET) { |
858 | if (so->so_proto && so->so_proto->pr_ctloutput) { | |
859 | return ((*so->so_proto->pr_ctloutput) | |
860 | (PRCO_GETOPT, so, level, optname, mp)); | |
861 | } else | |
862 | return (ENOPROTOOPT); | |
863 | } else { | |
61ec2127 | 864 | m = m_get(M_WAIT, MT_SOOPTS); |
d6e6eea8 MK |
865 | m->m_len = sizeof (int); |
866 | ||
cbe54390 MK |
867 | switch (optname) { |
868 | ||
869 | case SO_LINGER: | |
870 | m->m_len = sizeof (struct linger); | |
871 | mtod(m, struct linger *)->l_onoff = | |
872 | so->so_options & SO_LINGER; | |
873 | mtod(m, struct linger *)->l_linger = so->so_linger; | |
874 | break; | |
875 | ||
876 | case SO_USELOOPBACK: | |
877 | case SO_DONTROUTE: | |
878 | case SO_DEBUG: | |
879 | case SO_KEEPALIVE: | |
880 | case SO_REUSEADDR: | |
881 | case SO_BROADCAST: | |
97c8f6a8 | 882 | case SO_OOBINLINE: |
cbe54390 MK |
883 | *mtod(m, int *) = so->so_options & optname; |
884 | break; | |
885 | ||
d6e6eea8 MK |
886 | case SO_TYPE: |
887 | *mtod(m, int *) = so->so_type; | |
888 | break; | |
889 | ||
de2c74a5 MK |
890 | case SO_ERROR: |
891 | *mtod(m, int *) = so->so_error; | |
892 | so->so_error = 0; | |
893 | break; | |
894 | ||
cbe54390 MK |
895 | case SO_SNDBUF: |
896 | *mtod(m, int *) = so->so_snd.sb_hiwat; | |
897 | break; | |
98422daa | 898 | |
cbe54390 MK |
899 | case SO_RCVBUF: |
900 | *mtod(m, int *) = so->so_rcv.sb_hiwat; | |
901 | break; | |
902 | ||
903 | case SO_SNDLOWAT: | |
904 | *mtod(m, int *) = so->so_snd.sb_lowat; | |
905 | break; | |
906 | ||
907 | case SO_RCVLOWAT: | |
908 | *mtod(m, int *) = so->so_rcv.sb_lowat; | |
909 | break; | |
910 | ||
911 | case SO_SNDTIMEO: | |
912 | *mtod(m, int *) = so->so_snd.sb_timeo; | |
913 | break; | |
914 | ||
915 | case SO_RCVTIMEO: | |
916 | *mtod(m, int *) = so->so_rcv.sb_timeo; | |
917 | break; | |
918 | ||
919 | default: | |
8011f5df | 920 | (void)m_free(m); |
cbe54390 MK |
921 | return (ENOPROTOOPT); |
922 | } | |
923 | *mp = m; | |
924 | return (0); | |
98422daa | 925 | } |
98422daa SL |
926 | } |
927 | ||
edebca28 | 928 | sohasoutofband(so) |
88a7a62a | 929 | register struct socket *so; |
edebca28 | 930 | { |
3d190e86 | 931 | struct proc *p; |
edebca28 | 932 | |
a2aebb63 KS |
933 | if (so->so_pgid < 0) |
934 | gsignal(-so->so_pgid, SIGURG); | |
935 | else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) | |
3d190e86 | 936 | psignal(p, SIGURG); |
de2c74a5 MK |
937 | if (so->so_rcv.sb_sel) { |
938 | selwakeup(so->so_rcv.sb_sel, so->so_rcv.sb_flags & SB_COLL); | |
939 | so->so_rcv.sb_sel = 0; | |
940 | so->so_rcv.sb_flags &= ~SB_COLL; | |
941 | } | |
edebca28 | 942 | } |