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