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