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