]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2003-2020 Apple Inc. All rights reserved. |
91447636 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
39236c6e | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
39236c6e | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
39236c6e | 17 | * |
2d21ac55 A |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
39236c6e | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
91447636 A |
27 | */ |
28 | ||
0a7de745 | 29 | #define __KPI__ |
2d21ac55 | 30 | #include <sys/systm.h> |
91447636 A |
31 | #include <sys/kernel.h> |
32 | #include <sys/types.h> | |
33 | #include <sys/socket.h> | |
34 | #include <sys/socketvar.h> | |
35 | #include <sys/param.h> | |
36 | #include <sys/proc.h> | |
37 | #include <sys/errno.h> | |
38 | #include <sys/malloc.h> | |
39 | #include <sys/protosw.h> | |
40 | #include <sys/domain.h> | |
41 | #include <sys/mbuf.h> | |
d190cdc3 | 42 | #include <sys/mcache.h> |
91447636 A |
43 | #include <sys/fcntl.h> |
44 | #include <sys/filio.h> | |
45 | #include <sys/uio_internal.h> | |
fe8ab488 | 46 | #include <kern/locks.h> |
5ba3f43e | 47 | #include <net/net_api_stats.h> |
b0d623f7 | 48 | #include <netinet/in.h> |
d1ecb069 | 49 | #include <libkern/OSAtomic.h> |
5ba3f43e | 50 | #include <stdbool.h> |
91447636 | 51 | |
0a7de745 A |
52 | static errno_t sock_send_internal(socket_t, const struct msghdr *, |
53 | mbuf_t, int, size_t *); | |
5ba3f43e A |
54 | |
55 | #undef sock_accept | |
56 | #undef sock_socket | |
57 | errno_t sock_accept(socket_t so, struct sockaddr *from, int fromlen, | |
58 | int flags, sock_upcall callback, void *cookie, socket_t *new_so); | |
59 | errno_t sock_socket(int domain, int type, int protocol, sock_upcall callback, | |
60 | void *context, socket_t *new_so); | |
61 | ||
62 | static errno_t sock_accept_common(socket_t sock, struct sockaddr *from, | |
63 | int fromlen, int flags, sock_upcall callback, void *cookie, | |
64 | socket_t *new_sock, bool is_internal); | |
65 | static errno_t sock_socket_common(int domain, int type, int protocol, | |
66 | sock_upcall callback, void *context, socket_t *new_so, bool is_internal); | |
91447636 A |
67 | |
68 | errno_t | |
5ba3f43e A |
69 | sock_accept_common(socket_t sock, struct sockaddr *from, int fromlen, int flags, |
70 | sock_upcall callback, void *cookie, socket_t *new_sock, bool is_internal) | |
91447636 A |
71 | { |
72 | struct sockaddr *sa; | |
73 | struct socket *new_so; | |
74 | lck_mtx_t *mutex_held; | |
39236c6e | 75 | int dosocklock; |
0a7de745 | 76 | errno_t error = 0; |
39236c6e | 77 | |
0a7de745 A |
78 | if (sock == NULL || new_sock == NULL) { |
79 | return EINVAL; | |
80 | } | |
39236c6e | 81 | |
91447636 A |
82 | socket_lock(sock, 1); |
83 | if ((sock->so_options & SO_ACCEPTCONN) == 0) { | |
84 | socket_unlock(sock, 1); | |
0a7de745 | 85 | return EINVAL; |
91447636 A |
86 | } |
87 | if ((flags & ~(MSG_DONTWAIT)) != 0) { | |
88 | socket_unlock(sock, 1); | |
0a7de745 | 89 | return ENOTSUP; |
91447636 | 90 | } |
813fb2f6 | 91 | check_again: |
91447636 | 92 | if (((flags & MSG_DONTWAIT) != 0 || (sock->so_state & SS_NBIO) != 0) && |
39236c6e | 93 | sock->so_comp.tqh_first == NULL) { |
91447636 | 94 | socket_unlock(sock, 1); |
0a7de745 | 95 | return EWOULDBLOCK; |
91447636 A |
96 | } |
97 | ||
0a7de745 | 98 | if (sock->so_proto->pr_getlock != NULL) { |
5ba3f43e | 99 | mutex_held = (*sock->so_proto->pr_getlock)(sock, PR_F_WILLUNLOCK); |
91447636 | 100 | dosocklock = 1; |
39236c6e | 101 | } else { |
91447636 A |
102 | mutex_held = sock->so_proto->pr_domain->dom_mtx; |
103 | dosocklock = 0; | |
104 | } | |
39236c6e | 105 | |
91447636 A |
106 | while (TAILQ_EMPTY(&sock->so_comp) && sock->so_error == 0) { |
107 | if (sock->so_state & SS_CANTRCVMORE) { | |
108 | sock->so_error = ECONNABORTED; | |
109 | break; | |
110 | } | |
39236c6e A |
111 | error = msleep((caddr_t)&sock->so_timeo, mutex_held, |
112 | PSOCK | PCATCH, "sock_accept", NULL); | |
113 | if (error != 0) { | |
91447636 | 114 | socket_unlock(sock, 1); |
0a7de745 | 115 | return error; |
91447636 A |
116 | } |
117 | } | |
39236c6e | 118 | if (sock->so_error != 0) { |
91447636 A |
119 | error = sock->so_error; |
120 | sock->so_error = 0; | |
121 | socket_unlock(sock, 1); | |
0a7de745 | 122 | return error; |
91447636 | 123 | } |
39236c6e | 124 | |
813fb2f6 A |
125 | so_acquire_accept_list(sock, NULL); |
126 | if (TAILQ_EMPTY(&sock->so_comp)) { | |
127 | so_release_accept_list(sock); | |
128 | goto check_again; | |
129 | } | |
91447636 A |
130 | new_so = TAILQ_FIRST(&sock->so_comp); |
131 | TAILQ_REMOVE(&sock->so_comp, new_so, so_list); | |
d190cdc3 A |
132 | new_so->so_state &= ~SS_COMP; |
133 | new_so->so_head = NULL; | |
91447636 | 134 | sock->so_qlen--; |
2d21ac55 | 135 | |
813fb2f6 A |
136 | so_release_accept_list(sock); |
137 | ||
5ba3f43e A |
138 | /* |
139 | * Count the accepted socket as an in-kernel socket | |
140 | */ | |
141 | new_so->so_flags1 |= SOF1_IN_KERNEL_SOCKET; | |
142 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_total); | |
143 | if (is_internal) { | |
144 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_os_total); | |
145 | } | |
146 | ||
2d21ac55 A |
147 | /* |
148 | * Pass the pre-accepted socket to any interested socket filter(s). | |
149 | * Upon failure, the socket would have been closed by the callee. | |
150 | */ | |
151 | if (new_so->so_filt != NULL) { | |
152 | /* | |
153 | * Temporarily drop the listening socket's lock before we | |
154 | * hand off control over to the socket filter(s), but keep | |
155 | * a reference so that it won't go away. We'll grab it | |
156 | * again once we're done with the filter(s). | |
157 | */ | |
158 | socket_unlock(sock, 0); | |
d190cdc3 | 159 | if ((error = soacceptfilter(new_so, sock)) != 0) { |
2d21ac55 A |
160 | /* Drop reference on listening socket */ |
161 | sodereference(sock); | |
0a7de745 | 162 | return error; |
2d21ac55 A |
163 | } |
164 | socket_lock(sock, 0); | |
165 | } | |
91447636 | 166 | |
0a7de745 | 167 | if (dosocklock) { |
5ba3f43e | 168 | LCK_MTX_ASSERT(new_so->so_proto->pr_getlock(new_so, 0), |
39236c6e | 169 | LCK_MTX_ASSERT_NOTOWNED); |
91447636 A |
170 | socket_lock(new_so, 1); |
171 | } | |
39236c6e | 172 | |
2d21ac55 | 173 | (void) soacceptlock(new_so, &sa, 0); |
39236c6e | 174 | |
0a7de745 | 175 | socket_unlock(sock, 1); /* release the head */ |
2d21ac55 | 176 | |
39236c6e A |
177 | /* see comments in sock_setupcall() */ |
178 | if (callback != NULL) { | |
f427ee49 | 179 | #if (defined(__arm__) || defined(__arm64__)) |
5ba3f43e | 180 | sock_setupcalls_locked(new_so, callback, cookie, callback, cookie, 0); |
f427ee49 | 181 | #else /* (defined(__arm__) || defined(__arm64__)) */ |
5ba3f43e | 182 | sock_setupcalls_locked(new_so, callback, cookie, NULL, NULL, 0); |
f427ee49 | 183 | #endif /* (defined(__arm__) || defined(__arm64__)) */ |
91447636 | 184 | } |
39236c6e A |
185 | |
186 | if (sa != NULL && from != NULL) { | |
0a7de745 | 187 | if (fromlen > sa->sa_len) { |
39236c6e | 188 | fromlen = sa->sa_len; |
0a7de745 | 189 | } |
91447636 A |
190 | memcpy(from, sa, fromlen); |
191 | } | |
0a7de745 | 192 | if (sa != NULL) { |
39236c6e | 193 | FREE(sa, M_SONAME); |
0a7de745 | 194 | } |
2d21ac55 A |
195 | |
196 | /* | |
6d2010ae A |
197 | * If the socket has been marked as inactive by sosetdefunct(), |
198 | * disallow further operations on it. | |
2d21ac55 A |
199 | */ |
200 | if (new_so->so_flags & SOF_DEFUNCT) { | |
6d2010ae A |
201 | (void) sodefunct(current_proc(), new_so, |
202 | SHUTDOWN_SOCKET_LEVEL_DISCONNECT_INTERNAL); | |
2d21ac55 | 203 | } |
91447636 | 204 | *new_sock = new_so; |
0a7de745 | 205 | if (dosocklock) { |
91447636 | 206 | socket_unlock(new_so, 1); |
0a7de745 A |
207 | } |
208 | return error; | |
91447636 A |
209 | } |
210 | ||
5ba3f43e A |
211 | errno_t |
212 | sock_accept(socket_t sock, struct sockaddr *from, int fromlen, int flags, | |
213 | sock_upcall callback, void *cookie, socket_t *new_sock) | |
214 | { | |
0a7de745 A |
215 | return sock_accept_common(sock, from, fromlen, flags, |
216 | callback, cookie, new_sock, false); | |
5ba3f43e A |
217 | } |
218 | ||
219 | errno_t | |
220 | sock_accept_internal(socket_t sock, struct sockaddr *from, int fromlen, int flags, | |
221 | sock_upcall callback, void *cookie, socket_t *new_sock) | |
222 | { | |
0a7de745 A |
223 | return sock_accept_common(sock, from, fromlen, flags, |
224 | callback, cookie, new_sock, true); | |
5ba3f43e A |
225 | } |
226 | ||
91447636 | 227 | errno_t |
39236c6e | 228 | sock_bind(socket_t sock, const struct sockaddr *to) |
91447636 | 229 | { |
39236c6e | 230 | int error = 0; |
6d2010ae A |
231 | struct sockaddr *sa = NULL; |
232 | struct sockaddr_storage ss; | |
233 | boolean_t want_free = TRUE; | |
234 | ||
0a7de745 A |
235 | if (sock == NULL || to == NULL) { |
236 | return EINVAL; | |
237 | } | |
39236c6e | 238 | |
0a7de745 | 239 | if (to->sa_len > sizeof(ss)) { |
c3c9b80d | 240 | sa = kheap_alloc(KHEAP_TEMP, to->sa_len, Z_WAITOK); |
0a7de745 A |
241 | if (sa == NULL) { |
242 | return ENOBUFS; | |
243 | } | |
6d2010ae A |
244 | } else { |
245 | sa = (struct sockaddr *)&ss; | |
246 | want_free = FALSE; | |
247 | } | |
248 | memcpy(sa, to, to->sa_len); | |
249 | ||
0a7de745 | 250 | error = sobindlock(sock, sa, 1); /* will lock socket */ |
39236c6e | 251 | |
0a7de745 | 252 | if (sa != NULL && want_free == TRUE) { |
c3c9b80d | 253 | kheap_free(KHEAP_TEMP, sa, sa->sa_len); |
0a7de745 | 254 | } |
6d2010ae | 255 | |
0a7de745 | 256 | return error; |
91447636 A |
257 | } |
258 | ||
259 | errno_t | |
39236c6e | 260 | sock_connect(socket_t sock, const struct sockaddr *to, int flags) |
91447636 | 261 | { |
39236c6e | 262 | int error = 0; |
91447636 | 263 | lck_mtx_t *mutex_held; |
6d2010ae A |
264 | struct sockaddr *sa = NULL; |
265 | struct sockaddr_storage ss; | |
266 | boolean_t want_free = TRUE; | |
39236c6e | 267 | |
0a7de745 A |
268 | if (sock == NULL || to == NULL) { |
269 | return EINVAL; | |
270 | } | |
39236c6e | 271 | |
0a7de745 | 272 | if (to->sa_len > sizeof(ss)) { |
c3c9b80d A |
273 | sa = kheap_alloc(KHEAP_TEMP, to->sa_len, |
274 | (flags & MSG_DONTWAIT) ? Z_NOWAIT : Z_WAITOK); | |
0a7de745 A |
275 | if (sa == NULL) { |
276 | return ENOBUFS; | |
277 | } | |
6d2010ae A |
278 | } else { |
279 | sa = (struct sockaddr *)&ss; | |
280 | want_free = FALSE; | |
281 | } | |
282 | memcpy(sa, to, to->sa_len); | |
91447636 A |
283 | |
284 | socket_lock(sock, 1); | |
285 | ||
286 | if ((sock->so_state & SS_ISCONNECTING) && | |
39236c6e | 287 | ((sock->so_state & SS_NBIO) != 0 || (flags & MSG_DONTWAIT) != 0)) { |
6d2010ae A |
288 | error = EALREADY; |
289 | goto out; | |
91447636 | 290 | } |
6d2010ae | 291 | error = soconnectlock(sock, sa, 0); |
91447636 A |
292 | if (!error) { |
293 | if ((sock->so_state & SS_ISCONNECTING) && | |
39236c6e A |
294 | ((sock->so_state & SS_NBIO) != 0 || |
295 | (flags & MSG_DONTWAIT) != 0)) { | |
6d2010ae A |
296 | error = EINPROGRESS; |
297 | goto out; | |
91447636 | 298 | } |
39236c6e | 299 | |
0a7de745 | 300 | if (sock->so_proto->pr_getlock != NULL) { |
5ba3f43e | 301 | mutex_held = (*sock->so_proto->pr_getlock)(sock, PR_F_WILLUNLOCK); |
0a7de745 | 302 | } else { |
39236c6e | 303 | mutex_held = sock->so_proto->pr_domain->dom_mtx; |
0a7de745 | 304 | } |
91447636 | 305 | |
39236c6e A |
306 | while ((sock->so_state & SS_ISCONNECTING) && |
307 | sock->so_error == 0) { | |
308 | error = msleep((caddr_t)&sock->so_timeo, | |
309 | mutex_held, PSOCK | PCATCH, "sock_connect", NULL); | |
0a7de745 | 310 | if (error != 0) { |
91447636 | 311 | break; |
0a7de745 | 312 | } |
91447636 | 313 | } |
39236c6e | 314 | |
91447636 A |
315 | if (error == 0) { |
316 | error = sock->so_error; | |
317 | sock->so_error = 0; | |
318 | } | |
39236c6e | 319 | } else { |
91447636 A |
320 | sock->so_state &= ~SS_ISCONNECTING; |
321 | } | |
6d2010ae | 322 | out: |
91447636 | 323 | socket_unlock(sock, 1); |
6d2010ae | 324 | |
0a7de745 | 325 | if (sa != NULL && want_free == TRUE) { |
c3c9b80d | 326 | kheap_free(KHEAP_TEMP, sa, sa->sa_len); |
0a7de745 | 327 | } |
39236c6e | 328 | |
0a7de745 | 329 | return error; |
91447636 A |
330 | } |
331 | ||
332 | errno_t | |
39236c6e | 333 | sock_connectwait(socket_t sock, const struct timeval *tv) |
91447636 | 334 | { |
39236c6e | 335 | lck_mtx_t *mutex_held; |
0a7de745 | 336 | errno_t retval = 0; |
91447636 | 337 | struct timespec ts; |
39236c6e | 338 | |
91447636 | 339 | socket_lock(sock, 1); |
39236c6e A |
340 | |
341 | /* Check if we're already connected or if we've already errored out */ | |
342 | if ((sock->so_state & SS_ISCONNECTING) == 0 || sock->so_error != 0) { | |
343 | if (sock->so_error != 0) { | |
91447636 A |
344 | retval = sock->so_error; |
345 | sock->so_error = 0; | |
39236c6e | 346 | } else { |
0a7de745 | 347 | if ((sock->so_state & SS_ISCONNECTED) != 0) { |
91447636 | 348 | retval = 0; |
0a7de745 | 349 | } else { |
91447636 | 350 | retval = EINVAL; |
0a7de745 | 351 | } |
91447636 A |
352 | } |
353 | goto done; | |
354 | } | |
39236c6e A |
355 | |
356 | /* copied translation from timeval to hertz from SO_RCVTIMEO handling */ | |
91447636 | 357 | if (tv->tv_sec < 0 || tv->tv_sec > SHRT_MAX / hz || |
39236c6e | 358 | tv->tv_usec < 0 || tv->tv_usec >= 1000000) { |
91447636 A |
359 | retval = EDOM; |
360 | goto done; | |
361 | } | |
39236c6e | 362 | |
91447636 | 363 | ts.tv_sec = tv->tv_sec; |
39236c6e | 364 | ts.tv_nsec = (tv->tv_usec * (integer_t)NSEC_PER_USEC); |
0a7de745 | 365 | if ((ts.tv_sec + (ts.tv_nsec / (long)NSEC_PER_SEC)) / 100 > SHRT_MAX) { |
91447636 A |
366 | retval = EDOM; |
367 | goto done; | |
368 | } | |
39236c6e | 369 | |
0a7de745 | 370 | if (sock->so_proto->pr_getlock != NULL) { |
5ba3f43e | 371 | mutex_held = (*sock->so_proto->pr_getlock)(sock, PR_F_WILLUNLOCK); |
0a7de745 | 372 | } else { |
39236c6e | 373 | mutex_held = sock->so_proto->pr_domain->dom_mtx; |
0a7de745 | 374 | } |
39236c6e A |
375 | |
376 | msleep((caddr_t)&sock->so_timeo, mutex_held, | |
377 | PSOCK, "sock_connectwait", &ts); | |
91447636 | 378 | |
39236c6e | 379 | /* Check if we're still waiting to connect */ |
91447636 A |
380 | if ((sock->so_state & SS_ISCONNECTING) && sock->so_error == 0) { |
381 | retval = EINPROGRESS; | |
382 | goto done; | |
383 | } | |
39236c6e A |
384 | |
385 | if (sock->so_error != 0) { | |
91447636 A |
386 | retval = sock->so_error; |
387 | sock->so_error = 0; | |
388 | } | |
39236c6e | 389 | |
91447636 A |
390 | done: |
391 | socket_unlock(sock, 1); | |
0a7de745 | 392 | return retval; |
91447636 A |
393 | } |
394 | ||
395 | errno_t | |
39236c6e | 396 | sock_nointerrupt(socket_t sock, int on) |
91447636 A |
397 | { |
398 | socket_lock(sock, 1); | |
399 | ||
400 | if (on) { | |
0a7de745 A |
401 | sock->so_rcv.sb_flags |= SB_NOINTR; /* This isn't safe */ |
402 | sock->so_snd.sb_flags |= SB_NOINTR; /* This isn't safe */ | |
39236c6e | 403 | } else { |
0a7de745 A |
404 | sock->so_rcv.sb_flags &= ~SB_NOINTR; /* This isn't safe */ |
405 | sock->so_snd.sb_flags &= ~SB_NOINTR; /* This isn't safe */ | |
91447636 A |
406 | } |
407 | ||
408 | socket_unlock(sock, 1); | |
409 | ||
0a7de745 | 410 | return 0; |
91447636 A |
411 | } |
412 | ||
413 | errno_t | |
0a7de745 | 414 | sock_getpeername(socket_t sock, struct sockaddr *peername, int peernamelen) |
91447636 | 415 | { |
2d21ac55 | 416 | int error; |
0a7de745 | 417 | struct sockaddr *sa = NULL; |
2d21ac55 | 418 | |
0a7de745 A |
419 | if (sock == NULL || peername == NULL || peernamelen < 0) { |
420 | return EINVAL; | |
421 | } | |
2d21ac55 | 422 | |
91447636 | 423 | socket_lock(sock, 1); |
0a7de745 | 424 | if (!(sock->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING))) { |
91447636 | 425 | socket_unlock(sock, 1); |
0a7de745 | 426 | return ENOTCONN; |
91447636 | 427 | } |
b0d623f7 | 428 | error = sogetaddr_locked(sock, &sa, 1); |
2d21ac55 A |
429 | socket_unlock(sock, 1); |
430 | if (error == 0) { | |
0a7de745 | 431 | if (peernamelen > sa->sa_len) { |
2d21ac55 | 432 | peernamelen = sa->sa_len; |
0a7de745 | 433 | } |
91447636 | 434 | memcpy(peername, sa, peernamelen); |
2d21ac55 | 435 | FREE(sa, M_SONAME); |
91447636 | 436 | } |
0a7de745 | 437 | return error; |
91447636 A |
438 | } |
439 | ||
440 | errno_t | |
0a7de745 | 441 | sock_getsockname(socket_t sock, struct sockaddr *sockname, int socknamelen) |
91447636 | 442 | { |
2d21ac55 | 443 | int error; |
0a7de745 | 444 | struct sockaddr *sa = NULL; |
2d21ac55 | 445 | |
0a7de745 A |
446 | if (sock == NULL || sockname == NULL || socknamelen < 0) { |
447 | return EINVAL; | |
448 | } | |
2d21ac55 | 449 | |
91447636 | 450 | socket_lock(sock, 1); |
b0d623f7 | 451 | error = sogetaddr_locked(sock, &sa, 0); |
2d21ac55 A |
452 | socket_unlock(sock, 1); |
453 | if (error == 0) { | |
0a7de745 | 454 | if (socknamelen > sa->sa_len) { |
2d21ac55 | 455 | socknamelen = sa->sa_len; |
0a7de745 | 456 | } |
91447636 | 457 | memcpy(sockname, sa, socknamelen); |
2d21ac55 | 458 | FREE(sa, M_SONAME); |
91447636 | 459 | } |
0a7de745 | 460 | return error; |
2d21ac55 A |
461 | } |
462 | ||
b0d623f7 A |
463 | __private_extern__ int |
464 | sogetaddr_locked(struct socket *so, struct sockaddr **psa, int peer) | |
2d21ac55 A |
465 | { |
466 | int error; | |
467 | ||
0a7de745 A |
468 | if (so == NULL || psa == NULL) { |
469 | return EINVAL; | |
470 | } | |
2d21ac55 A |
471 | |
472 | *psa = NULL; | |
b0d623f7 A |
473 | error = peer ? so->so_proto->pr_usrreqs->pru_peeraddr(so, psa) : |
474 | so->so_proto->pr_usrreqs->pru_sockaddr(so, psa); | |
2d21ac55 A |
475 | |
476 | if (error == 0 && *psa == NULL) { | |
477 | error = ENOMEM; | |
c3c9b80d | 478 | } else if (error != 0) { |
2d21ac55 | 479 | FREE(*psa, M_SONAME); |
2d21ac55 | 480 | } |
0a7de745 | 481 | return error; |
2d21ac55 A |
482 | } |
483 | ||
b0d623f7 A |
484 | errno_t |
485 | sock_getaddr(socket_t sock, struct sockaddr **psa, int peer) | |
486 | { | |
487 | int error; | |
488 | ||
0a7de745 A |
489 | if (sock == NULL || psa == NULL) { |
490 | return EINVAL; | |
491 | } | |
b0d623f7 A |
492 | |
493 | socket_lock(sock, 1); | |
494 | error = sogetaddr_locked(sock, psa, peer); | |
495 | socket_unlock(sock, 1); | |
39236c6e | 496 | |
0a7de745 | 497 | return error; |
b0d623f7 A |
498 | } |
499 | ||
2d21ac55 A |
500 | void |
501 | sock_freeaddr(struct sockaddr *sa) | |
502 | { | |
c3c9b80d | 503 | FREE(sa, M_SONAME); |
91447636 A |
504 | } |
505 | ||
506 | errno_t | |
39236c6e | 507 | sock_getsockopt(socket_t sock, int level, int optname, void *optval, |
0a7de745 | 508 | int *optlen) |
91447636 | 509 | { |
39236c6e | 510 | int error = 0; |
0a7de745 | 511 | struct sockopt sopt; |
39236c6e | 512 | |
0a7de745 A |
513 | if (sock == NULL || optval == NULL || optlen == NULL) { |
514 | return EINVAL; | |
515 | } | |
39236c6e | 516 | |
91447636 A |
517 | sopt.sopt_dir = SOPT_GET; |
518 | sopt.sopt_level = level; | |
519 | sopt.sopt_name = optname; | |
39236c6e | 520 | sopt.sopt_val = CAST_USER_ADDR_T(optval); |
91447636 | 521 | sopt.sopt_valsize = *optlen; |
b0d623f7 | 522 | sopt.sopt_p = kernproc; |
0a7de745 A |
523 | error = sogetoptlock(sock, &sopt, 1); /* will lock socket */ |
524 | if (error == 0) { | |
f427ee49 | 525 | *optlen = (uint32_t)sopt.sopt_valsize; |
0a7de745 A |
526 | } |
527 | return error; | |
91447636 A |
528 | } |
529 | ||
530 | errno_t | |
39236c6e | 531 | sock_ioctl(socket_t sock, unsigned long request, void *argp) |
91447636 | 532 | { |
0a7de745 | 533 | return soioctl(sock, request, argp, kernproc); /* will lock socket */ |
91447636 A |
534 | } |
535 | ||
536 | errno_t | |
39236c6e | 537 | sock_setsockopt(socket_t sock, int level, int optname, const void *optval, |
0a7de745 | 538 | int optlen) |
91447636 | 539 | { |
0a7de745 | 540 | struct sockopt sopt; |
39236c6e | 541 | |
0a7de745 A |
542 | if (sock == NULL || optval == NULL) { |
543 | return EINVAL; | |
544 | } | |
39236c6e | 545 | |
91447636 A |
546 | sopt.sopt_dir = SOPT_SET; |
547 | sopt.sopt_level = level; | |
548 | sopt.sopt_name = optname; | |
549 | sopt.sopt_val = CAST_USER_ADDR_T(optval); | |
550 | sopt.sopt_valsize = optlen; | |
b0d623f7 | 551 | sopt.sopt_p = kernproc; |
0a7de745 | 552 | return sosetoptlock(sock, &sopt, 1); /* will lock socket */ |
91447636 A |
553 | } |
554 | ||
6d2010ae | 555 | /* |
39236c6e A |
556 | * This follows the recommended mappings between DSCP code points |
557 | * and WMM access classes. | |
6d2010ae | 558 | */ |
f427ee49 A |
559 | static uint32_t |
560 | so_tc_from_dscp(uint8_t dscp) | |
6d2010ae | 561 | { |
f427ee49 | 562 | uint32_t tc; |
6d2010ae | 563 | |
0a7de745 | 564 | if (dscp >= 0x30 && dscp <= 0x3f) { |
6d2010ae | 565 | tc = SO_TC_VO; |
0a7de745 | 566 | } else if (dscp >= 0x20 && dscp <= 0x2f) { |
6d2010ae | 567 | tc = SO_TC_VI; |
0a7de745 | 568 | } else if (dscp >= 0x08 && dscp <= 0x17) { |
39037602 | 569 | tc = SO_TC_BK_SYS; |
0a7de745 | 570 | } else { |
6d2010ae | 571 | tc = SO_TC_BE; |
0a7de745 | 572 | } |
6d2010ae | 573 | |
0a7de745 | 574 | return tc; |
6d2010ae A |
575 | } |
576 | ||
b0d623f7 | 577 | errno_t |
39236c6e A |
578 | sock_settclassopt(socket_t sock, const void *optval, size_t optlen) |
579 | { | |
b0d623f7 A |
580 | errno_t error = 0; |
581 | struct sockopt sopt; | |
6d2010ae | 582 | int sotc; |
b0d623f7 | 583 | |
0a7de745 A |
584 | if (sock == NULL || optval == NULL || optlen != sizeof(int)) { |
585 | return EINVAL; | |
586 | } | |
b0d623f7 A |
587 | |
588 | socket_lock(sock, 1); | |
589 | if (!(sock->so_state & SS_ISCONNECTED)) { | |
39236c6e A |
590 | /* |
591 | * If the socket is not connected then we don't know | |
b0d623f7 A |
592 | * if the destination is on LAN or not. Skip |
593 | * setting traffic class in this case | |
594 | */ | |
595 | error = ENOTCONN; | |
596 | goto out; | |
597 | } | |
598 | ||
39236c6e A |
599 | if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL || |
600 | sock->so_pcb == NULL) { | |
b0d623f7 A |
601 | error = EINVAL; |
602 | goto out; | |
603 | } | |
604 | ||
6d2010ae A |
605 | /* |
606 | * Set the socket traffic class based on the passed DSCP code point | |
607 | * regardless of the scope of the destination | |
608 | */ | |
f427ee49 | 609 | sotc = so_tc_from_dscp((uint8_t)((*(const int *)optval) >> 2)); |
6d2010ae A |
610 | |
611 | sopt.sopt_dir = SOPT_SET; | |
612 | sopt.sopt_val = CAST_USER_ADDR_T(&sotc); | |
0a7de745 | 613 | sopt.sopt_valsize = sizeof(sotc); |
6d2010ae A |
614 | sopt.sopt_p = kernproc; |
615 | sopt.sopt_level = SOL_SOCKET; | |
616 | sopt.sopt_name = SO_TRAFFIC_CLASS; | |
617 | ||
0a7de745 | 618 | error = sosetoptlock(sock, &sopt, 0); /* already locked */ |
6d2010ae A |
619 | |
620 | if (error != 0) { | |
39236c6e A |
621 | printf("%s: sosetopt SO_TRAFFIC_CLASS failed %d\n", |
622 | __func__, error); | |
6d2010ae A |
623 | goto out; |
624 | } | |
625 | ||
39236c6e A |
626 | /* |
627 | * Check if the destination address is LAN or link local address. | |
b0d623f7 | 628 | * We do not want to set traffic class bits if the destination |
39236c6e A |
629 | * is not local. |
630 | */ | |
0a7de745 | 631 | if (!so_isdstlocal(sock)) { |
b0d623f7 | 632 | goto out; |
0a7de745 | 633 | } |
b0d623f7 | 634 | |
6d2010ae A |
635 | sopt.sopt_dir = SOPT_SET; |
636 | sopt.sopt_val = CAST_USER_ADDR_T(optval); | |
637 | sopt.sopt_valsize = optlen; | |
638 | sopt.sopt_p = kernproc; | |
639 | ||
39236c6e A |
640 | switch (SOCK_DOM(sock)) { |
641 | case PF_INET: | |
b0d623f7 A |
642 | sopt.sopt_level = IPPROTO_IP; |
643 | sopt.sopt_name = IP_TOS; | |
644 | break; | |
39236c6e | 645 | case PF_INET6: |
b0d623f7 A |
646 | sopt.sopt_level = IPPROTO_IPV6; |
647 | sopt.sopt_name = IPV6_TCLASS; | |
648 | break; | |
649 | default: | |
650 | error = EINVAL; | |
651 | goto out; | |
652 | } | |
39236c6e | 653 | |
0a7de745 | 654 | error = sosetoptlock(sock, &sopt, 0); /* already locked */ |
b0d623f7 | 655 | socket_unlock(sock, 1); |
0a7de745 | 656 | return error; |
b0d623f7 A |
657 | out: |
658 | socket_unlock(sock, 1); | |
0a7de745 | 659 | return error; |
b0d623f7 A |
660 | } |
661 | ||
662 | errno_t | |
39236c6e A |
663 | sock_gettclassopt(socket_t sock, void *optval, size_t *optlen) |
664 | { | |
665 | errno_t error = 0; | |
666 | struct sockopt sopt; | |
b0d623f7 | 667 | |
0a7de745 A |
668 | if (sock == NULL || optval == NULL || optlen == NULL) { |
669 | return EINVAL; | |
670 | } | |
b0d623f7 A |
671 | |
672 | sopt.sopt_dir = SOPT_GET; | |
39236c6e | 673 | sopt.sopt_val = CAST_USER_ADDR_T(optval); |
b0d623f7 A |
674 | sopt.sopt_valsize = *optlen; |
675 | sopt.sopt_p = kernproc; | |
676 | ||
677 | socket_lock(sock, 1); | |
678 | if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL) { | |
679 | socket_unlock(sock, 1); | |
0a7de745 | 680 | return EINVAL; |
b0d623f7 A |
681 | } |
682 | ||
39236c6e A |
683 | switch (SOCK_DOM(sock)) { |
684 | case PF_INET: | |
b0d623f7 A |
685 | sopt.sopt_level = IPPROTO_IP; |
686 | sopt.sopt_name = IP_TOS; | |
687 | break; | |
39236c6e | 688 | case PF_INET6: |
b0d623f7 A |
689 | sopt.sopt_level = IPPROTO_IPV6; |
690 | sopt.sopt_name = IPV6_TCLASS; | |
691 | break; | |
692 | default: | |
693 | socket_unlock(sock, 1); | |
0a7de745 | 694 | return EINVAL; |
b0d623f7 | 695 | } |
0a7de745 | 696 | error = sogetoptlock(sock, &sopt, 0); /* already locked */ |
b0d623f7 | 697 | socket_unlock(sock, 1); |
0a7de745 | 698 | if (error == 0) { |
39236c6e | 699 | *optlen = sopt.sopt_valsize; |
0a7de745 A |
700 | } |
701 | return error; | |
b0d623f7 A |
702 | } |
703 | ||
91447636 | 704 | errno_t |
39236c6e | 705 | sock_listen(socket_t sock, int backlog) |
91447636 | 706 | { |
0a7de745 A |
707 | if (sock == NULL) { |
708 | return EINVAL; | |
709 | } | |
39236c6e | 710 | |
0a7de745 | 711 | return solisten(sock, backlog); /* will lock socket */ |
91447636 A |
712 | } |
713 | ||
39236c6e A |
714 | errno_t |
715 | sock_receive_internal(socket_t sock, struct msghdr *msg, mbuf_t *data, | |
716 | int flags, size_t *recvdlen) | |
91447636 | 717 | { |
39236c6e A |
718 | uio_t auio; |
719 | struct mbuf *control = NULL; | |
720 | int error = 0; | |
f427ee49 | 721 | user_ssize_t length = 0; |
0a7de745 A |
722 | struct sockaddr *fromsa = NULL; |
723 | char uio_buf[UIO_SIZEOF((msg != NULL) ? msg->msg_iovlen : 0)]; | |
39236c6e | 724 | |
0a7de745 A |
725 | if (sock == NULL) { |
726 | return EINVAL; | |
727 | } | |
39236c6e A |
728 | |
729 | auio = uio_createwithbuffer(((msg != NULL) ? msg->msg_iovlen : 0), | |
0a7de745 | 730 | 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf)); |
39236c6e | 731 | if (msg != NULL && data == NULL) { |
91447636 | 732 | int i; |
b0d623f7 | 733 | struct iovec *tempp = msg->msg_iov; |
39236c6e | 734 | |
91447636 | 735 | for (i = 0; i < msg->msg_iovlen; i++) { |
39236c6e A |
736 | uio_addiov(auio, |
737 | CAST_USER_ADDR_T((tempp + i)->iov_base), | |
738 | (tempp + i)->iov_len); | |
91447636 | 739 | } |
0a7de745 A |
740 | if (uio_resid(auio) < 0) { |
741 | return EINVAL; | |
742 | } | |
39236c6e | 743 | } else if (recvdlen != NULL) { |
91447636 A |
744 | uio_setresid(auio, (uio_resid(auio) + *recvdlen)); |
745 | } | |
746 | length = uio_resid(auio); | |
39236c6e | 747 | |
0a7de745 | 748 | if (recvdlen != NULL) { |
91447636 | 749 | *recvdlen = 0; |
0a7de745 | 750 | } |
91447636 | 751 | |
39236c6e | 752 | /* let pru_soreceive handle the socket locking */ |
91447636 | 753 | error = sock->so_proto->pr_usrreqs->pru_soreceive(sock, &fromsa, auio, |
b0d623f7 | 754 | data, (msg && msg->msg_control) ? &control : NULL, &flags); |
0a7de745 | 755 | if (error != 0) { |
39236c6e | 756 | goto cleanup; |
0a7de745 | 757 | } |
39236c6e | 758 | |
0a7de745 | 759 | if (recvdlen != NULL) { |
91447636 | 760 | *recvdlen = length - uio_resid(auio); |
0a7de745 | 761 | } |
39236c6e | 762 | if (msg != NULL) { |
91447636 | 763 | msg->msg_flags = flags; |
39236c6e A |
764 | |
765 | if (msg->msg_name != NULL) { | |
91447636 A |
766 | int salen; |
767 | salen = msg->msg_namelen; | |
39236c6e | 768 | if (msg->msg_namelen > 0 && fromsa != NULL) { |
91447636 A |
769 | salen = MIN(salen, fromsa->sa_len); |
770 | memcpy(msg->msg_name, fromsa, | |
39236c6e A |
771 | msg->msg_namelen > fromsa->sa_len ? |
772 | fromsa->sa_len : msg->msg_namelen); | |
91447636 A |
773 | } |
774 | } | |
39236c6e A |
775 | |
776 | if (msg->msg_control != NULL) { | |
777 | struct mbuf *m = control; | |
778 | u_char *ctlbuf = msg->msg_control; | |
779 | int clen = msg->msg_controllen; | |
780 | ||
91447636 | 781 | msg->msg_controllen = 0; |
39236c6e A |
782 | |
783 | while (m != NULL && clen > 0) { | |
91447636 | 784 | unsigned int tocopy; |
39236c6e A |
785 | |
786 | if (clen >= m->m_len) { | |
91447636 | 787 | tocopy = m->m_len; |
39236c6e | 788 | } else { |
91447636 A |
789 | msg->msg_flags |= MSG_CTRUNC; |
790 | tocopy = clen; | |
791 | } | |
792 | memcpy(ctlbuf, mtod(m, caddr_t), tocopy); | |
793 | ctlbuf += tocopy; | |
794 | clen -= tocopy; | |
795 | m = m->m_next; | |
796 | } | |
39236c6e | 797 | msg->msg_controllen = |
f427ee49 | 798 | (socklen_t)((uintptr_t)ctlbuf - (uintptr_t)msg->msg_control); |
91447636 A |
799 | } |
800 | } | |
801 | ||
802 | cleanup: | |
0a7de745 | 803 | if (control != NULL) { |
39236c6e | 804 | m_freem(control); |
0a7de745 | 805 | } |
c3c9b80d | 806 | FREE(fromsa, M_SONAME); |
0a7de745 | 807 | return error; |
91447636 A |
808 | } |
809 | ||
810 | errno_t | |
39236c6e | 811 | sock_receive(socket_t sock, struct msghdr *msg, int flags, size_t *recvdlen) |
91447636 | 812 | { |
39236c6e A |
813 | if ((msg == NULL) || (msg->msg_iovlen < 1) || |
814 | (msg->msg_iov[0].iov_len == 0) || | |
0a7de745 A |
815 | (msg->msg_iov[0].iov_base == NULL)) { |
816 | return EINVAL; | |
817 | } | |
39236c6e | 818 | |
0a7de745 | 819 | return sock_receive_internal(sock, msg, NULL, flags, recvdlen); |
91447636 A |
820 | } |
821 | ||
822 | errno_t | |
39236c6e A |
823 | sock_receivembuf(socket_t sock, struct msghdr *msg, mbuf_t *data, int flags, |
824 | size_t *recvlen) | |
91447636 | 825 | { |
39236c6e | 826 | if (data == NULL || recvlen == 0 || *recvlen <= 0 || (msg != NULL && |
0a7de745 A |
827 | (msg->msg_iov != NULL || msg->msg_iovlen != 0))) { |
828 | return EINVAL; | |
829 | } | |
39236c6e | 830 | |
0a7de745 | 831 | return sock_receive_internal(sock, msg, data, flags, recvlen); |
91447636 A |
832 | } |
833 | ||
834 | errno_t | |
39236c6e A |
835 | sock_send_internal(socket_t sock, const struct msghdr *msg, mbuf_t data, |
836 | int flags, size_t *sentlen) | |
91447636 | 837 | { |
39236c6e A |
838 | uio_t auio = NULL; |
839 | struct mbuf *control = NULL; | |
840 | int error = 0; | |
f427ee49 | 841 | user_ssize_t datalen = 0; |
0a7de745 | 842 | char uio_buf[UIO_SIZEOF((msg != NULL ? msg->msg_iovlen : 1))]; |
39236c6e | 843 | |
91447636 A |
844 | if (sock == NULL) { |
845 | error = EINVAL; | |
846 | goto errorout; | |
847 | } | |
39236c6e A |
848 | |
849 | if (data == NULL && msg != NULL) { | |
b0d623f7 | 850 | struct iovec *tempp = msg->msg_iov; |
91447636 | 851 | |
39236c6e | 852 | auio = uio_createwithbuffer(msg->msg_iovlen, 0, |
0a7de745 | 853 | UIO_SYSSPACE, UIO_WRITE, &uio_buf[0], sizeof(uio_buf)); |
39236c6e | 854 | if (tempp != NULL) { |
91447636 | 855 | int i; |
39236c6e | 856 | |
91447636 | 857 | for (i = 0; i < msg->msg_iovlen; i++) { |
39236c6e A |
858 | uio_addiov(auio, |
859 | CAST_USER_ADDR_T((tempp + i)->iov_base), | |
860 | (tempp + i)->iov_len); | |
91447636 | 861 | } |
39236c6e | 862 | |
91447636 A |
863 | if (uio_resid(auio) < 0) { |
864 | error = EINVAL; | |
865 | goto errorout; | |
866 | } | |
867 | } | |
868 | } | |
39236c6e | 869 | |
0a7de745 | 870 | if (sentlen != NULL) { |
91447636 | 871 | *sentlen = 0; |
0a7de745 | 872 | } |
39236c6e | 873 | |
0a7de745 | 874 | if (auio != NULL) { |
91447636 | 875 | datalen = uio_resid(auio); |
0a7de745 | 876 | } else { |
91447636 | 877 | datalen = data->m_pkthdr.len; |
0a7de745 | 878 | } |
39236c6e A |
879 | |
880 | if (msg != NULL && msg->msg_control) { | |
0a7de745 | 881 | if ((size_t)msg->msg_controllen < sizeof(struct cmsghdr)) { |
39236c6e A |
882 | error = EINVAL; |
883 | goto errorout; | |
884 | } | |
885 | ||
886 | if ((size_t)msg->msg_controllen > MLEN) { | |
887 | error = EINVAL; | |
888 | goto errorout; | |
889 | } | |
890 | ||
91447636 A |
891 | control = m_get(M_NOWAIT, MT_CONTROL); |
892 | if (control == NULL) { | |
893 | error = ENOMEM; | |
894 | goto errorout; | |
895 | } | |
39236c6e A |
896 | memcpy(mtod(control, caddr_t), msg->msg_control, |
897 | msg->msg_controllen); | |
91447636 A |
898 | control->m_len = msg->msg_controllen; |
899 | } | |
39236c6e | 900 | |
8f6c56a5 | 901 | error = sock->so_proto->pr_usrreqs->pru_sosend(sock, msg != NULL ? |
39236c6e A |
902 | (struct sockaddr *)msg->msg_name : NULL, auio, data, |
903 | control, flags); | |
8f6c56a5 A |
904 | |
905 | /* | |
906 | * Residual data is possible in the case of IO vectors but not | |
907 | * in the mbuf case since the latter is treated as atomic send. | |
908 | * If pru_sosend() consumed a portion of the iovecs data and | |
909 | * the error returned is transient, treat it as success; this | |
910 | * is consistent with sendit() behavior. | |
911 | */ | |
912 | if (auio != NULL && uio_resid(auio) != datalen && | |
0a7de745 | 913 | (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) { |
8f6c56a5 | 914 | error = 0; |
0a7de745 | 915 | } |
8f6c56a5 A |
916 | |
917 | if (error == 0 && sentlen != NULL) { | |
0a7de745 | 918 | if (auio != NULL) { |
91447636 | 919 | *sentlen = datalen - uio_resid(auio); |
0a7de745 | 920 | } else { |
91447636 | 921 | *sentlen = datalen; |
0a7de745 | 922 | } |
91447636 | 923 | } |
39236c6e | 924 | |
0a7de745 | 925 | return error; |
91447636 A |
926 | |
927 | /* | |
928 | * In cases where we detect an error before returning, we need to | |
929 | * free the mbuf chain if there is one. sosend (and pru_sosend) will | |
930 | * free the mbuf chain if they encounter an error. | |
931 | */ | |
932 | errorout: | |
0a7de745 | 933 | if (control) { |
91447636 | 934 | m_freem(control); |
0a7de745 A |
935 | } |
936 | if (data) { | |
91447636 | 937 | m_freem(data); |
0a7de745 A |
938 | } |
939 | if (sentlen) { | |
91447636 | 940 | *sentlen = 0; |
0a7de745 A |
941 | } |
942 | return error; | |
91447636 A |
943 | } |
944 | ||
945 | errno_t | |
39236c6e | 946 | sock_send(socket_t sock, const struct msghdr *msg, int flags, size_t *sentlen) |
91447636 | 947 | { |
0a7de745 A |
948 | if (msg == NULL || msg->msg_iov == NULL || msg->msg_iovlen < 1) { |
949 | return EINVAL; | |
950 | } | |
39236c6e | 951 | |
0a7de745 | 952 | return sock_send_internal(sock, msg, NULL, flags, sentlen); |
91447636 A |
953 | } |
954 | ||
955 | errno_t | |
39236c6e | 956 | sock_sendmbuf(socket_t sock, const struct msghdr *msg, mbuf_t data, |
0a7de745 | 957 | int flags, size_t *sentlen) |
91447636 | 958 | { |
39236c6e A |
959 | if (data == NULL || (msg != NULL && (msg->msg_iov != NULL || |
960 | msg->msg_iovlen != 0))) { | |
0a7de745 | 961 | if (data != NULL) { |
91447636 | 962 | m_freem(data); |
0a7de745 A |
963 | } |
964 | return EINVAL; | |
91447636 | 965 | } |
0a7de745 | 966 | return sock_send_internal(sock, msg, data, flags, sentlen); |
91447636 A |
967 | } |
968 | ||
969 | errno_t | |
39236c6e | 970 | sock_shutdown(socket_t sock, int how) |
91447636 | 971 | { |
0a7de745 A |
972 | if (sock == NULL) { |
973 | return EINVAL; | |
974 | } | |
39236c6e | 975 | |
0a7de745 | 976 | return soshutdown(sock, how); |
91447636 A |
977 | } |
978 | ||
91447636 | 979 | errno_t |
5ba3f43e A |
980 | sock_socket_common(int domain, int type, int protocol, sock_upcall callback, |
981 | void *context, socket_t *new_so, bool is_internal) | |
91447636 | 982 | { |
39236c6e A |
983 | int error = 0; |
984 | ||
0a7de745 A |
985 | if (new_so == NULL) { |
986 | return EINVAL; | |
987 | } | |
39236c6e | 988 | |
91447636 A |
989 | /* socreate will create an initial so_count */ |
990 | error = socreate(domain, new_so, type, protocol); | |
39236c6e | 991 | if (error == 0) { |
5ba3f43e A |
992 | /* |
993 | * This is an in-kernel socket | |
994 | */ | |
995 | (*new_so)->so_flags1 |= SOF1_IN_KERNEL_SOCKET; | |
996 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_total); | |
997 | if (is_internal) { | |
998 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_os_total); | |
999 | } | |
1000 | ||
39236c6e A |
1001 | /* see comments in sock_setupcall() */ |
1002 | if (callback != NULL) { | |
5ba3f43e | 1003 | sock_setupcall(*new_so, callback, context); |
39236c6e | 1004 | } |
0a7de745 | 1005 | /* |
39236c6e A |
1006 | * last_pid and last_upid should be zero for sockets |
1007 | * created using sock_socket | |
1008 | */ | |
316670eb A |
1009 | (*new_so)->last_pid = 0; |
1010 | (*new_so)->last_upid = 0; | |
91447636 | 1011 | } |
0a7de745 | 1012 | return error; |
91447636 A |
1013 | } |
1014 | ||
5ba3f43e A |
1015 | errno_t |
1016 | sock_socket_internal(int domain, int type, int protocol, sock_upcall callback, | |
1017 | void *context, socket_t *new_so) | |
1018 | { | |
0a7de745 A |
1019 | return sock_socket_common(domain, type, protocol, callback, |
1020 | context, new_so, true); | |
5ba3f43e A |
1021 | } |
1022 | ||
1023 | errno_t | |
1024 | sock_socket(int domain, int type, int protocol, sock_upcall callback, | |
1025 | void *context, socket_t *new_so) | |
1026 | { | |
0a7de745 A |
1027 | return sock_socket_common(domain, type, protocol, callback, |
1028 | context, new_so, false); | |
5ba3f43e A |
1029 | } |
1030 | ||
91447636 | 1031 | void |
39236c6e | 1032 | sock_close(socket_t sock) |
91447636 | 1033 | { |
0a7de745 | 1034 | if (sock == NULL) { |
39236c6e | 1035 | return; |
0a7de745 | 1036 | } |
39236c6e | 1037 | |
91447636 A |
1038 | soclose(sock); |
1039 | } | |
1040 | ||
39236c6e | 1041 | /* Do we want this to be APPLE_PRIVATE API?: YES (LD 12/23/04) */ |
91447636 | 1042 | void |
39236c6e | 1043 | sock_retain(socket_t sock) |
91447636 | 1044 | { |
0a7de745 | 1045 | if (sock == NULL) { |
39236c6e | 1046 | return; |
0a7de745 | 1047 | } |
39236c6e | 1048 | |
91447636 A |
1049 | socket_lock(sock, 1); |
1050 | sock->so_retaincnt++; | |
0a7de745 | 1051 | sock->so_usecount++; /* add extra reference for holding the socket */ |
91447636 A |
1052 | socket_unlock(sock, 1); |
1053 | } | |
1054 | ||
1055 | /* Do we want this to be APPLE_PRIVATE API? */ | |
1056 | void | |
2d21ac55 | 1057 | sock_release(socket_t sock) |
91447636 | 1058 | { |
0a7de745 | 1059 | if (sock == NULL) { |
2d21ac55 | 1060 | return; |
0a7de745 | 1061 | } |
2d21ac55 | 1062 | |
39236c6e | 1063 | socket_lock(sock, 1); |
0a7de745 | 1064 | if (sock->so_upcallusecount > 0) { |
2d21ac55 | 1065 | soclose_wait_locked(sock); |
0a7de745 | 1066 | } |
2d21ac55 | 1067 | |
91447636 | 1068 | sock->so_retaincnt--; |
39236c6e A |
1069 | if (sock->so_retaincnt < 0) { |
1070 | panic("%s: negative retain count (%d) for sock=%p\n", | |
1071 | __func__, sock->so_retaincnt, sock); | |
1072 | /* NOTREACHED */ | |
1073 | } | |
490019cf A |
1074 | /* |
1075 | * Check SS_NOFDREF in case a close happened as sock_retain() | |
1076 | * was grabbing the lock | |
1077 | */ | |
1078 | if ((sock->so_retaincnt == 0) && (sock->so_usecount == 2) && | |
1079 | (!(sock->so_state & SS_NOFDREF) || | |
1080 | (sock->so_flags & SOF_MP_SUBFLOW))) { | |
2d21ac55 A |
1081 | /* close socket only if the FD is not holding it */ |
1082 | soclose_locked(sock); | |
1083 | } else { | |
1084 | /* remove extra reference holding the socket */ | |
d190cdc3 | 1085 | VERIFY(sock->so_usecount > 1); |
2d21ac55 A |
1086 | sock->so_usecount--; |
1087 | } | |
91447636 A |
1088 | socket_unlock(sock, 1); |
1089 | } | |
1090 | ||
1091 | errno_t | |
39236c6e | 1092 | sock_setpriv(socket_t sock, int on) |
91447636 | 1093 | { |
0a7de745 A |
1094 | if (sock == NULL) { |
1095 | return EINVAL; | |
1096 | } | |
39236c6e | 1097 | |
91447636 | 1098 | socket_lock(sock, 1); |
0a7de745 | 1099 | if (on) { |
91447636 | 1100 | sock->so_state |= SS_PRIV; |
0a7de745 | 1101 | } else { |
91447636 | 1102 | sock->so_state &= ~SS_PRIV; |
0a7de745 | 1103 | } |
91447636 | 1104 | socket_unlock(sock, 1); |
0a7de745 | 1105 | return 0; |
91447636 A |
1106 | } |
1107 | ||
1108 | int | |
39236c6e | 1109 | sock_isconnected(socket_t sock) |
91447636 A |
1110 | { |
1111 | int retval; | |
39236c6e | 1112 | |
91447636 | 1113 | socket_lock(sock, 1); |
39236c6e | 1114 | retval = ((sock->so_state & SS_ISCONNECTED) ? 1 : 0); |
91447636 | 1115 | socket_unlock(sock, 1); |
0a7de745 | 1116 | return retval; |
91447636 A |
1117 | } |
1118 | ||
1119 | int | |
39236c6e | 1120 | sock_isnonblocking(socket_t sock) |
91447636 A |
1121 | { |
1122 | int retval; | |
39236c6e | 1123 | |
91447636 | 1124 | socket_lock(sock, 1); |
39236c6e | 1125 | retval = ((sock->so_state & SS_NBIO) ? 1 : 0); |
91447636 | 1126 | socket_unlock(sock, 1); |
0a7de745 | 1127 | return retval; |
91447636 A |
1128 | } |
1129 | ||
1130 | errno_t | |
39236c6e | 1131 | sock_gettype(socket_t sock, int *outDomain, int *outType, int *outProtocol) |
91447636 A |
1132 | { |
1133 | socket_lock(sock, 1); | |
0a7de745 | 1134 | if (outDomain != NULL) { |
39236c6e | 1135 | *outDomain = SOCK_DOM(sock); |
0a7de745 A |
1136 | } |
1137 | if (outType != NULL) { | |
91447636 | 1138 | *outType = sock->so_type; |
0a7de745 A |
1139 | } |
1140 | if (outProtocol != NULL) { | |
39236c6e | 1141 | *outProtocol = SOCK_PROTO(sock); |
0a7de745 | 1142 | } |
91447636 | 1143 | socket_unlock(sock, 1); |
0a7de745 | 1144 | return 0; |
91447636 | 1145 | } |
2d21ac55 A |
1146 | |
1147 | /* | |
1148 | * Return the listening socket of a pre-accepted socket. It returns the | |
1149 | * listener (so_head) value of a given socket. This is intended to be | |
1150 | * called by a socket filter during a filter attach (sf_attach) callback. | |
1151 | * The value returned by this routine is safe to be used only in the | |
1152 | * context of that callback, because we hold the listener's lock across | |
1153 | * the sflt_initsock() call. | |
1154 | */ | |
1155 | socket_t | |
1156 | sock_getlistener(socket_t sock) | |
1157 | { | |
0a7de745 | 1158 | return sock->so_head; |
2d21ac55 | 1159 | } |
d1ecb069 | 1160 | |
6d2010ae A |
1161 | static inline void |
1162 | sock_set_tcp_stream_priority(socket_t sock) | |
1163 | { | |
39236c6e A |
1164 | if ((SOCK_DOM(sock) == PF_INET || SOCK_DOM(sock) == PF_INET6) && |
1165 | SOCK_TYPE(sock) == SOCK_STREAM) { | |
6d2010ae | 1166 | set_tcp_stream_priority(sock); |
6d2010ae A |
1167 | } |
1168 | } | |
1169 | ||
d1ecb069 A |
1170 | /* |
1171 | * Caller must have ensured socket is valid and won't be going away. | |
1172 | */ | |
1173 | void | |
3e170ce0 | 1174 | socket_set_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags) |
d1ecb069 | 1175 | { |
39037602 | 1176 | u_int32_t soflags1 = 0; |
0a7de745 A |
1177 | |
1178 | if ((flags & TRAFFIC_MGT_SO_BACKGROUND)) { | |
39037602 | 1179 | soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND; |
0a7de745 A |
1180 | } |
1181 | if ((flags & TRAFFIC_MGT_TCP_RECVBG)) { | |
39037602 | 1182 | soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG; |
0a7de745 A |
1183 | } |
1184 | ||
39037602 A |
1185 | (void) OSBitOrAtomic(soflags1, &sock->so_flags1); |
1186 | ||
6d2010ae A |
1187 | sock_set_tcp_stream_priority(sock); |
1188 | } | |
1189 | ||
1190 | void | |
3e170ce0 | 1191 | socket_set_traffic_mgt_flags(socket_t sock, u_int8_t flags) |
6d2010ae A |
1192 | { |
1193 | socket_lock(sock, 1); | |
1194 | socket_set_traffic_mgt_flags_locked(sock, flags); | |
1195 | socket_unlock(sock, 1); | |
d1ecb069 A |
1196 | } |
1197 | ||
1198 | /* | |
1199 | * Caller must have ensured socket is valid and won't be going away. | |
1200 | */ | |
1201 | void | |
3e170ce0 | 1202 | socket_clear_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags) |
d1ecb069 | 1203 | { |
39037602 A |
1204 | u_int32_t soflags1 = 0; |
1205 | ||
0a7de745 | 1206 | if ((flags & TRAFFIC_MGT_SO_BACKGROUND)) { |
39037602 | 1207 | soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND; |
0a7de745 A |
1208 | } |
1209 | if ((flags & TRAFFIC_MGT_TCP_RECVBG)) { | |
39037602 | 1210 | soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG; |
0a7de745 A |
1211 | } |
1212 | ||
39037602 A |
1213 | (void) OSBitAndAtomic(~soflags1, &sock->so_flags1); |
1214 | ||
6d2010ae | 1215 | sock_set_tcp_stream_priority(sock); |
d1ecb069 | 1216 | } |
d41d1dae | 1217 | |
6d2010ae | 1218 | void |
3e170ce0 | 1219 | socket_clear_traffic_mgt_flags(socket_t sock, u_int8_t flags) |
d41d1dae | 1220 | { |
6d2010ae A |
1221 | socket_lock(sock, 1); |
1222 | socket_clear_traffic_mgt_flags_locked(sock, flags); | |
1223 | socket_unlock(sock, 1); | |
1224 | } | |
1225 | ||
d41d1dae | 1226 | |
6d2010ae A |
1227 | /* |
1228 | * Caller must have ensured socket is valid and won't be going away. | |
1229 | */ | |
1230 | errno_t | |
1231 | socket_defunct(struct proc *p, socket_t so, int level) | |
1232 | { | |
1233 | errno_t retval; | |
1234 | ||
1235 | if (level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC && | |
0a7de745 A |
1236 | level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL) { |
1237 | return EINVAL; | |
1238 | } | |
6d2010ae A |
1239 | |
1240 | socket_lock(so, 1); | |
1241 | /* | |
1242 | * SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC level is meant to tear down | |
1243 | * all of mDNSResponder IPC sockets, currently those of AF_UNIX; note | |
1244 | * that this is an implementation artifact of mDNSResponder. We do | |
1245 | * a quick test against the socket buffers for SB_UNIX, since that | |
1246 | * would have been set by unp_attach() at socket creation time. | |
1247 | */ | |
1248 | if (level == SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC && | |
1249 | (so->so_rcv.sb_flags & so->so_snd.sb_flags & SB_UNIX) != SB_UNIX) { | |
1250 | socket_unlock(so, 1); | |
0a7de745 | 1251 | return EOPNOTSUPP; |
6d2010ae A |
1252 | } |
1253 | retval = sosetdefunct(p, so, level, TRUE); | |
0a7de745 | 1254 | if (retval == 0) { |
6d2010ae | 1255 | retval = sodefunct(p, so, level); |
0a7de745 | 1256 | } |
6d2010ae | 1257 | socket_unlock(so, 1); |
0a7de745 | 1258 | return retval; |
6d2010ae A |
1259 | } |
1260 | ||
5ba3f43e A |
1261 | void |
1262 | sock_setupcalls_locked(socket_t sock, sock_upcall rcallback, void *rcontext, | |
1263 | sock_upcall wcallback, void *wcontext, int locked) | |
39236c6e A |
1264 | { |
1265 | if (rcallback != NULL) { | |
1266 | sock->so_rcv.sb_flags |= SB_UPCALL; | |
0a7de745 | 1267 | if (locked) { |
5ba3f43e | 1268 | sock->so_rcv.sb_flags |= SB_UPCALL_LOCK; |
0a7de745 | 1269 | } |
39236c6e A |
1270 | sock->so_rcv.sb_upcall = rcallback; |
1271 | sock->so_rcv.sb_upcallarg = rcontext; | |
1272 | } else { | |
5ba3f43e | 1273 | sock->so_rcv.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK); |
39236c6e A |
1274 | sock->so_rcv.sb_upcall = NULL; |
1275 | sock->so_rcv.sb_upcallarg = NULL; | |
1276 | } | |
1277 | ||
1278 | if (wcallback != NULL) { | |
1279 | sock->so_snd.sb_flags |= SB_UPCALL; | |
0a7de745 | 1280 | if (locked) { |
5ba3f43e | 1281 | sock->so_snd.sb_flags |= SB_UPCALL_LOCK; |
0a7de745 | 1282 | } |
39236c6e A |
1283 | sock->so_snd.sb_upcall = wcallback; |
1284 | sock->so_snd.sb_upcallarg = wcontext; | |
1285 | } else { | |
5ba3f43e | 1286 | sock->so_snd.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK); |
39236c6e A |
1287 | sock->so_snd.sb_upcall = NULL; |
1288 | sock->so_snd.sb_upcallarg = NULL; | |
1289 | } | |
1290 | } | |
1291 | ||
6d2010ae | 1292 | errno_t |
39236c6e | 1293 | sock_setupcall(socket_t sock, sock_upcall callback, void *context) |
6d2010ae | 1294 | { |
0a7de745 A |
1295 | if (sock == NULL) { |
1296 | return EINVAL; | |
1297 | } | |
6d2010ae A |
1298 | |
1299 | /* | |
1300 | * Note that we don't wait for any in progress upcall to complete. | |
39236c6e A |
1301 | * On embedded, sock_setupcall() causes both read and write |
1302 | * callbacks to be set; on desktop, only read callback is set | |
1303 | * to maintain legacy KPI behavior. | |
1304 | * | |
1305 | * The newer sock_setupcalls() KPI should be used instead to set | |
1306 | * the read and write callbacks and their respective parameters. | |
6d2010ae A |
1307 | */ |
1308 | socket_lock(sock, 1); | |
f427ee49 | 1309 | #if (defined(__arm__) || defined(__arm64__)) |
5ba3f43e | 1310 | sock_setupcalls_locked(sock, callback, context, callback, context, 0); |
f427ee49 | 1311 | #else /* (defined(__arm__) || defined(__arm64__)) */ |
5ba3f43e | 1312 | sock_setupcalls_locked(sock, callback, context, NULL, NULL, 0); |
f427ee49 | 1313 | #endif /* (defined(__arm__) || defined(__arm64__)) */ |
39236c6e | 1314 | socket_unlock(sock, 1); |
6d2010ae | 1315 | |
0a7de745 | 1316 | return 0; |
39236c6e A |
1317 | } |
1318 | ||
1319 | errno_t | |
1320 | sock_setupcalls(socket_t sock, sock_upcall rcallback, void *rcontext, | |
1321 | sock_upcall wcallback, void *wcontext) | |
1322 | { | |
0a7de745 A |
1323 | if (sock == NULL) { |
1324 | return EINVAL; | |
1325 | } | |
39236c6e A |
1326 | |
1327 | /* | |
1328 | * Note that we don't wait for any in progress upcall to complete. | |
1329 | */ | |
1330 | socket_lock(sock, 1); | |
5ba3f43e | 1331 | sock_setupcalls_locked(sock, rcallback, rcontext, wcallback, wcontext, 0); |
39236c6e A |
1332 | socket_unlock(sock, 1); |
1333 | ||
0a7de745 | 1334 | return 0; |
39236c6e A |
1335 | } |
1336 | ||
5ba3f43e A |
1337 | void |
1338 | sock_catchevents_locked(socket_t sock, sock_evupcall ecallback, void *econtext, | |
f427ee49 | 1339 | long emask) |
39236c6e | 1340 | { |
5ba3f43e | 1341 | socket_lock_assert_owned(sock); |
39236c6e A |
1342 | |
1343 | /* | |
1344 | * Note that we don't wait for any in progress upcall to complete. | |
1345 | */ | |
39236c6e A |
1346 | if (ecallback != NULL) { |
1347 | sock->so_event = ecallback; | |
1348 | sock->so_eventarg = econtext; | |
f427ee49 | 1349 | sock->so_eventmask = (uint32_t)emask; |
d41d1dae | 1350 | } else { |
fe8ab488 | 1351 | sock->so_event = sonullevent; |
39236c6e A |
1352 | sock->so_eventarg = NULL; |
1353 | sock->so_eventmask = 0; | |
d41d1dae | 1354 | } |
5ba3f43e A |
1355 | } |
1356 | ||
1357 | errno_t | |
1358 | sock_catchevents(socket_t sock, sock_evupcall ecallback, void *econtext, | |
f427ee49 | 1359 | long emask) |
5ba3f43e | 1360 | { |
0a7de745 A |
1361 | if (sock == NULL) { |
1362 | return EINVAL; | |
1363 | } | |
5ba3f43e A |
1364 | |
1365 | socket_lock(sock, 1); | |
1366 | sock_catchevents_locked(sock, ecallback, econtext, emask); | |
6d2010ae A |
1367 | socket_unlock(sock, 1); |
1368 | ||
0a7de745 | 1369 | return 0; |
d41d1dae | 1370 | } |
fe8ab488 A |
1371 | |
1372 | /* | |
1373 | * Returns true whether or not a socket belongs to the kernel. | |
1374 | */ | |
1375 | int | |
1376 | sock_iskernel(socket_t so) | |
1377 | { | |
0a7de745 | 1378 | return so && so->last_pid == 0; |
fe8ab488 | 1379 | } |