Commit | Line | Data |
---|---|---|
2156d53c | 1 | /* uipc_socket.c 4.22 82/01/07 */ |
ce9d8eb4 BJ |
2 | |
3 | #include "../h/param.h" | |
92a533e6 | 4 | #include "../h/systm.h" |
ce9d8eb4 BJ |
5 | #include "../h/dir.h" |
6 | #include "../h/user.h" | |
92a533e6 BJ |
7 | #include "../h/proc.h" |
8 | #include "../h/file.h" | |
ce9d8eb4 | 9 | #include "../h/inode.h" |
92a533e6 | 10 | #include "../h/buf.h" |
ce9d8eb4 | 11 | #include "../h/mbuf.h" |
92a533e6 BJ |
12 | #include "../h/protosw.h" |
13 | #include "../h/socket.h" | |
14 | #include "../h/socketvar.h" | |
ae921915 | 15 | #include "../h/stat.h" |
b8acc34d | 16 | #include "../h/ioctl.h" |
c5ff2e32 BJ |
17 | #include "../net/in.h" |
18 | #include "../net/in_systm.h" | |
ce9d8eb4 | 19 | |
ce9d8eb4 | 20 | /* |
cc15ab5d BJ |
21 | * Socket support routines. |
22 | * | |
23 | * DEAL WITH INTERRUPT NOTIFICATION. | |
ce9d8eb4 | 24 | */ |
ce9d8eb4 BJ |
25 | |
26 | /* | |
27 | * Create a socket. | |
28 | */ | |
2b4b57cd | 29 | socreate(aso, type, asp, asa, options) |
ce9d8eb4 BJ |
30 | struct socket **aso; |
31 | int type; | |
2b4b57cd BJ |
32 | struct sockproto *asp; |
33 | struct sockaddr *asa; | |
92a533e6 | 34 | int options; |
ce9d8eb4 BJ |
35 | { |
36 | register struct protosw *prp; | |
37 | register struct socket *so; | |
38 | struct mbuf *m; | |
cc15ab5d | 39 | int pf, proto, error; |
2b4b57cd | 40 | COUNT(SOCREATE); |
ce9d8eb4 BJ |
41 | |
42 | /* | |
cc15ab5d BJ |
43 | * Use process standard protocol/protocol family if none |
44 | * specified by address argument. | |
ce9d8eb4 | 45 | */ |
2b4b57cd | 46 | if (asp == 0) { |
cc15ab5d | 47 | pf = PF_INET; /* should be u.u_protof */ |
ce9d8eb4 BJ |
48 | proto = 0; |
49 | } else { | |
2b4b57cd BJ |
50 | pf = asp->sp_family; |
51 | proto = asp->sp_protocol; | |
ce9d8eb4 | 52 | } |
cc15ab5d BJ |
53 | |
54 | /* | |
55 | * If protocol specified, look for it, otherwise | |
56 | * for a protocol of the correct type in the right family. | |
57 | */ | |
58 | if (proto) | |
59 | prp = pffindproto(pf, proto); | |
60 | else | |
61 | prp = pffindtype(pf, type); | |
62 | if (prp == 0) | |
63 | return (EPROTONOSUPPORT); | |
ce9d8eb4 BJ |
64 | |
65 | /* | |
66 | * Get a socket structure. | |
67 | */ | |
cc15ab5d | 68 | m = m_getclr(M_WAIT); |
ce9d8eb4 BJ |
69 | if (m == 0) |
70 | return (ENOBUFS); | |
ce9d8eb4 | 71 | so = mtod(m, struct socket *); |
92a533e6 | 72 | so->so_options = options; |
ce9d8eb4 BJ |
73 | |
74 | /* | |
cc15ab5d BJ |
75 | * Attach protocol to socket, initializing |
76 | * and reserving resources. | |
ce9d8eb4 BJ |
77 | */ |
78 | so->so_proto = prp; | |
b91acce4 BJ |
79 | error = (*prp->pr_usrreq)(so, PRU_ATTACH, 0, asa); |
80 | if (error) { | |
2752c877 | 81 | (void) m_free(dtom(so)); |
cc15ab5d | 82 | return (error); |
ce9d8eb4 BJ |
83 | } |
84 | *aso = so; | |
85 | return (0); | |
86 | } | |
87 | ||
ae921915 BJ |
88 | sofree(so) |
89 | struct socket *so; | |
90 | { | |
91 | ||
2b4b57cd | 92 | COUNT(SOFREE); |
4ad99bae BJ |
93 | if (so->so_pcb || (so->so_state & SS_USERGONE) == 0) |
94 | return; | |
95 | sbrelease(&so->so_snd); | |
96 | sbrelease(&so->so_rcv); | |
2752c877 | 97 | (void) m_free(dtom(so)); |
ae921915 BJ |
98 | } |
99 | ||
92a533e6 | 100 | /* |
cc15ab5d BJ |
101 | * Close a socket on last file table reference removal. |
102 | * Initiate disconnect if connected. | |
103 | * Free socket when disconnect complete. | |
92a533e6 | 104 | */ |
cc15ab5d | 105 | soclose(so) |
92a533e6 | 106 | register struct socket *so; |
92a533e6 | 107 | { |
cc15ab5d BJ |
108 | int s = splnet(); /* conservative */ |
109 | ||
2b4b57cd | 110 | COUNT(SOCLOSE); |
cc15ab5d BJ |
111 | if (so->so_pcb == 0) |
112 | goto discard; | |
113 | if (so->so_state & SS_ISCONNECTED) { | |
114 | if ((so->so_state & SS_ISDISCONNECTING) == 0) { | |
2b4b57cd | 115 | u.u_error = sodisconnect(so, (struct sockaddr *)0); |
cc15ab5d BJ |
116 | if (u.u_error) { |
117 | splx(s); | |
118 | return; | |
119 | } | |
120 | } | |
b8acc34d BJ |
121 | if (so->so_options & SO_LETDATADRAIN) { |
122 | if ((so->so_state & SS_ISDISCONNECTING) && | |
123 | (so->so_options & SO_NBIO)) { | |
124 | u.u_error = EINPROGRESS; | |
125 | splx(s); | |
126 | return; | |
127 | } | |
128 | while (so->so_state & SS_ISCONNECTED) | |
129 | sleep((caddr_t)&so->so_timeo, PZERO+1); | |
72857acf | 130 | } |
cc15ab5d BJ |
131 | } |
132 | u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DETACH, 0, 0); | |
133 | discard: | |
4ad99bae BJ |
134 | so->so_state |= SS_USERGONE; |
135 | sofree(so); | |
cc15ab5d | 136 | splx(s); |
92a533e6 BJ |
137 | } |
138 | ||
2b4b57cd BJ |
139 | sosplice(pso, so) |
140 | struct socket *pso, *so; | |
141 | { | |
142 | ||
143 | COUNT(SOSPLICE); | |
4c078bb2 | 144 | if (pso->so_proto->pr_family != PF_UNIX) { |
2b4b57cd BJ |
145 | struct socket *tso; |
146 | tso = pso; pso = so; so = tso; | |
147 | } | |
4c078bb2 | 148 | if (pso->so_proto->pr_family != PF_UNIX) |
2b4b57cd BJ |
149 | return (EOPNOTSUPP); |
150 | /* check types and buffer space */ | |
151 | /* merge buffers */ | |
152 | return (0); | |
153 | } | |
154 | ||
ae921915 | 155 | /*ARGSUSED*/ |
cc15ab5d | 156 | sostat(so, sb) |
92a533e6 | 157 | struct socket *so; |
cc15ab5d | 158 | struct stat *sb; |
92a533e6 BJ |
159 | { |
160 | ||
2b4b57cd | 161 | COUNT(SOSTAT); |
5e35cca3 BJ |
162 | bzero((caddr_t)sb, sizeof (*sb)); /* XXX */ |
163 | return (0); /* XXX */ | |
92a533e6 BJ |
164 | } |
165 | ||
2b4b57cd BJ |
166 | /* |
167 | * Accept connection on a socket. | |
168 | */ | |
169 | soaccept(so, asa) | |
170 | struct socket *so; | |
171 | struct sockaddr *asa; | |
172 | { | |
173 | int s = splnet(); | |
174 | int error; | |
175 | ||
176 | COUNT(SOACCEPT); | |
2b4b57cd BJ |
177 | if ((so->so_options & SO_ACCEPTCONN) == 0) { |
178 | error = EINVAL; /* XXX */ | |
179 | goto bad; | |
180 | } | |
92d06f69 BJ |
181 | if ((so->so_state & SS_CONNAWAITING) == 0) { |
182 | error = ENOTCONN; | |
183 | goto bad; | |
184 | } | |
185 | so->so_state &= ~SS_CONNAWAITING; | |
2b4b57cd BJ |
186 | error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, 0, (caddr_t)asa); |
187 | bad: | |
188 | splx(s); | |
189 | return (error); | |
190 | } | |
191 | ||
ce9d8eb4 | 192 | /* |
cc15ab5d BJ |
193 | * Connect socket to a specified address. |
194 | * If already connected or connecting, then avoid | |
195 | * the protocol entry, to keep its job simpler. | |
ce9d8eb4 | 196 | */ |
2b4b57cd | 197 | soconnect(so, asa) |
ce9d8eb4 | 198 | struct socket *so; |
2b4b57cd | 199 | struct sockaddr *asa; |
ce9d8eb4 | 200 | { |
cc15ab5d BJ |
201 | int s = splnet(); |
202 | int error; | |
ce9d8eb4 | 203 | |
2b4b57cd | 204 | COUNT(SOCONNECT); |
cc15ab5d BJ |
205 | if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) { |
206 | error = EISCONN; | |
207 | goto bad; | |
208 | } | |
2b4b57cd | 209 | error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT, 0, (caddr_t)asa); |
cc15ab5d BJ |
210 | bad: |
211 | splx(s); | |
212 | return (error); | |
ce9d8eb4 BJ |
213 | } |
214 | ||
215 | /* | |
cc15ab5d BJ |
216 | * Disconnect from a socket. |
217 | * Address parameter is from system call for later multicast | |
218 | * protocols. Check to make sure that connected and no disconnect | |
219 | * in progress (for protocol's sake), and then invoke protocol. | |
ce9d8eb4 | 220 | */ |
2b4b57cd | 221 | sodisconnect(so, asa) |
ce9d8eb4 | 222 | struct socket *so; |
2b4b57cd | 223 | struct sockaddr *asa; |
ce9d8eb4 | 224 | { |
cc15ab5d BJ |
225 | int s = splnet(); |
226 | int error; | |
ce9d8eb4 | 227 | |
2b4b57cd | 228 | COUNT(SODISCONNECT); |
cc15ab5d BJ |
229 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
230 | error = ENOTCONN; | |
231 | goto bad; | |
ce9d8eb4 | 232 | } |
cc15ab5d BJ |
233 | if (so->so_state & SS_ISDISCONNECTING) { |
234 | error = EALREADY; | |
235 | goto bad; | |
ce9d8eb4 | 236 | } |
2b4b57cd | 237 | error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, 0, asa); |
cc15ab5d BJ |
238 | bad: |
239 | splx(s); | |
240 | return (error); | |
ce9d8eb4 BJ |
241 | } |
242 | ||
cc15ab5d BJ |
243 | /* |
244 | * Send on a socket. | |
245 | * If send must go all at once and message is larger than | |
246 | * send buffering, then hard error. | |
247 | * Lock against other senders. | |
248 | * If must go all at once and not enough room now, then | |
249 | * inform user that this would block and do nothing. | |
250 | */ | |
2b4b57cd | 251 | sosend(so, asa) |
ce9d8eb4 | 252 | register struct socket *so; |
2b4b57cd | 253 | struct sockaddr *asa; |
ce9d8eb4 | 254 | { |
cc15ab5d BJ |
255 | struct mbuf *top = 0; |
256 | register struct mbuf *m, **mp = ⊤ | |
ae921915 BJ |
257 | register u_int len; |
258 | int error = 0, space, s; | |
ce9d8eb4 | 259 | |
2b4b57cd | 260 | COUNT(SOSEND); |
cc15ab5d BJ |
261 | if (so->so_state & SS_CANTSENDMORE) |
262 | return (EPIPE); | |
263 | if (sosendallatonce(so) && u.u_count > so->so_snd.sb_hiwat) | |
264 | return (EMSGSIZE); | |
265 | if ((so->so_snd.sb_flags & SB_LOCK) && (so->so_options & SO_NBIO)) | |
266 | return (EWOULDBLOCK); | |
267 | sblock(&so->so_snd); | |
268 | #define snderr(errno) { error = errno; splx(s); goto release; } | |
269 | ||
270 | s = splnet(); | |
271 | again: | |
4c078bb2 BJ |
272 | if (so->so_error) { |
273 | error = so->so_error; | |
274 | so->so_error = 0; | |
275 | splx(s); | |
276 | goto release; | |
277 | } | |
cc15ab5d BJ |
278 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
279 | if (so->so_proto->pr_flags & PR_CONNREQUIRED) | |
280 | snderr(ENOTCONN); | |
2b4b57cd | 281 | if (asa == 0) |
cc15ab5d BJ |
282 | snderr(EDESTADDRREQ); |
283 | } | |
cc15ab5d | 284 | if (top) { |
2b4b57cd | 285 | error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, top, asa); |
cc15ab5d BJ |
286 | if (error) { |
287 | splx(s); | |
ce9d8eb4 BJ |
288 | goto release; |
289 | } | |
cc15ab5d BJ |
290 | top = 0; |
291 | mp = ⊤ | |
ce9d8eb4 | 292 | } |
b91acce4 BJ |
293 | if (u.u_count == 0) { |
294 | splx(s); | |
295 | goto release; | |
296 | } | |
50fc8df6 BJ |
297 | space = sbspace(&so->so_snd); |
298 | if (space == 0 || sosendallatonce(so) && space < u.u_count) { | |
cc15ab5d BJ |
299 | if (so->so_options & SO_NBIO) |
300 | snderr(EWOULDBLOCK); | |
301 | sbunlock(&so->so_snd); | |
302 | sbwait(&so->so_snd); | |
303 | splx(s); | |
ce9d8eb4 BJ |
304 | goto again; |
305 | } | |
cc15ab5d | 306 | splx(s); |
50fc8df6 | 307 | while (u.u_count && space > 0) { |
cc15ab5d BJ |
308 | MGET(m, 1); |
309 | if (m == NULL) { | |
310 | error = ENOBUFS; | |
311 | m_freem(top); | |
312 | goto release; | |
ce9d8eb4 | 313 | } |
c5ff2e32 | 314 | if (u.u_count >= CLBYTES && space >= CLBYTES) { |
cc15ab5d | 315 | register struct mbuf *p; |
c5ff2e32 | 316 | MCLGET(p, 1); |
cc15ab5d BJ |
317 | if (p == 0) |
318 | goto nopages; | |
319 | m->m_off = (int)p - (int)m; | |
c5ff2e32 | 320 | len = CLBYTES; |
cc15ab5d | 321 | } else { |
ce9d8eb4 | 322 | nopages: |
cc15ab5d BJ |
323 | m->m_off = MMINOFF; |
324 | len = MIN(MLEN, u.u_count); | |
ce9d8eb4 | 325 | } |
cc15ab5d BJ |
326 | iomove(mtod(m, caddr_t), len, B_WRITE); |
327 | m->m_len = len; | |
328 | *mp = m; | |
329 | mp = &m->m_next; | |
50fc8df6 | 330 | space = sbspace(&so->so_snd); |
ce9d8eb4 | 331 | } |
cc15ab5d BJ |
332 | s = splnet(); |
333 | goto again; | |
334 | ||
ce9d8eb4 | 335 | release: |
cc15ab5d | 336 | sbunlock(&so->so_snd); |
ce9d8eb4 BJ |
337 | return (error); |
338 | } | |
339 | ||
2b4b57cd | 340 | soreceive(so, asa) |
ce9d8eb4 | 341 | register struct socket *so; |
2b4b57cd | 342 | struct sockaddr *asa; |
ce9d8eb4 BJ |
343 | { |
344 | register struct mbuf *m, *n; | |
ae921915 | 345 | u_int len; |
cc15ab5d | 346 | int eor, s, error = 0; |
ce9d8eb4 | 347 | |
2b4b57cd | 348 | COUNT(SORECEIVE); |
cc15ab5d BJ |
349 | restart: |
350 | sblock(&so->so_rcv); | |
351 | s = splnet(); | |
352 | ||
353 | #define rcverr(errno) { error = errno; splx(s); goto release; } | |
ce9d8eb4 | 354 | if (so->so_rcv.sb_cc == 0) { |
4c078bb2 BJ |
355 | if (so->so_error) { |
356 | error = so->so_error; | |
357 | so->so_error = 0; | |
358 | splx(s); | |
359 | goto release; | |
360 | } | |
cc15ab5d BJ |
361 | if (so->so_state & SS_CANTRCVMORE) { |
362 | splx(s); | |
363 | goto release; | |
364 | } | |
196d84fd BJ |
365 | if ((so->so_state & SS_ISCONNECTED) == 0 && |
366 | (so->so_proto->pr_flags & PR_CONNREQUIRED)) | |
367 | rcverr(ENOTCONN); | |
cc15ab5d | 368 | if (so->so_options & SO_NBIO) |
4c078bb2 | 369 | rcverr(EWOULDBLOCK); |
cc15ab5d | 370 | sbunlock(&so->so_rcv); |
2752c877 | 371 | sbwait(&so->so_rcv); |
a4f6d93d | 372 | splx(s); |
cc15ab5d | 373 | goto restart; |
ce9d8eb4 | 374 | } |
92a533e6 | 375 | m = so->so_rcv.sb_mb; |
ce9d8eb4 BJ |
376 | if (m == 0) |
377 | panic("receive"); | |
b031e888 | 378 | if (so->so_proto->pr_flags & PR_ADDR) { |
b031e888 BJ |
379 | if (m->m_len != sizeof (struct sockaddr)) |
380 | panic("soreceive addr"); | |
381 | if (asa) | |
382 | bcopy(mtod(m, caddr_t), (caddr_t)asa, sizeof (*asa)); | |
b031e888 BJ |
383 | so->so_rcv.sb_cc -= m->m_len; |
384 | so->so_rcv.sb_mbcnt -= MSIZE; | |
50fc8df6 | 385 | m = m_free(m); |
cc15ab5d BJ |
386 | if (m == 0) |
387 | panic("receive 2"); | |
50fc8df6 | 388 | so->so_rcv.sb_mb = m; |
cc15ab5d | 389 | } |
ce9d8eb4 BJ |
390 | eor = 0; |
391 | do { | |
392 | len = MIN(m->m_len, u.u_count); | |
393 | if (len == m->m_len) { | |
394 | eor = (int)m->m_act; | |
cc15ab5d | 395 | sbfree(&so->so_rcv, m); |
2156d53c | 396 | so->so_rcv.sb_mb = m->m_next; |
ce9d8eb4 BJ |
397 | } |
398 | splx(s); | |
399 | iomove(mtod(m, caddr_t), len, B_READ); | |
400 | s = splnet(); | |
401 | if (len == m->m_len) { | |
402 | MFREE(m, n); | |
403 | } else { | |
404 | m->m_off += len; | |
405 | m->m_len -= len; | |
92a533e6 | 406 | so->so_rcv.sb_cc -= len; |
ce9d8eb4 | 407 | } |
92a533e6 | 408 | } while ((m = so->so_rcv.sb_mb) && u.u_count && !eor); |
ce9d8eb4 BJ |
409 | if ((so->so_proto->pr_flags & PR_ATOMIC) && eor == 0) |
410 | do { | |
ce9d8eb4 | 411 | if (m == 0) |
cc15ab5d BJ |
412 | panic("receive 3"); |
413 | sbfree(&so->so_rcv, m); | |
ce9d8eb4 BJ |
414 | eor = (int)m->m_act; |
415 | so->so_rcv.sb_mb = m->m_next; | |
416 | MFREE(m, n); | |
cc15ab5d | 417 | m = n; |
ce9d8eb4 | 418 | } while (eor == 0); |
cc15ab5d BJ |
419 | if ((so->so_proto->pr_flags & PR_WANTRCVD) && so->so_pcb) |
420 | (*so->so_proto->pr_usrreq)(so, PRU_RCVD, 0, 0); | |
cc15ab5d | 421 | release: |
ae921915 | 422 | sbunlock(&so->so_rcv); |
cc15ab5d | 423 | splx(s); |
ae921915 | 424 | return (error); |
92a533e6 BJ |
425 | } |
426 | ||
ae921915 BJ |
427 | /*ARGSUSED*/ |
428 | soioctl(so, cmd, cmdp) | |
92a533e6 BJ |
429 | register struct socket *so; |
430 | int cmd; | |
431 | register caddr_t cmdp; | |
432 | { | |
433 | ||
2b4b57cd | 434 | COUNT(SOIOCTL); |
2156d53c | 435 | switch (cmd) { |
92a533e6 | 436 | |
b8acc34d BJ |
437 | case SIOCDONE: { |
438 | int flags; | |
439 | if (copyin(cmdp, (caddr_t)&flags, sizeof (flags))) { | |
440 | u.u_error = EFAULT; | |
441 | return; | |
442 | } | |
443 | if (flags & FREAD) { | |
444 | int s = splimp(); | |
445 | socantrcvmore(so); | |
446 | sbflush(&so->so_rcv); | |
447 | } | |
448 | if (flags & FWRITE) | |
2156d53c | 449 | u.u_error = (*so->so_proto->pr_usrreq)(so, PRU_DISCONNECT, (struct mbuf *)0, 0); |
b8acc34d BJ |
450 | return; |
451 | } | |
452 | ||
92a533e6 BJ |
453 | } |
454 | switch (so->so_type) { | |
455 | ||
456 | case SOCK_STREAM: | |
457 | break; | |
458 | ||
459 | case SOCK_DGRAM: | |
460 | break; | |
461 | ||
462 | case SOCK_RDM: | |
463 | break; | |
464 | ||
465 | case SOCK_RAW: | |
466 | break; | |
467 | ||
468 | } | |
ce9d8eb4 | 469 | } |