]>
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)) { |
6d2010ae | 240 | MALLOC(sa, struct sockaddr *, to->sa_len, M_SONAME, M_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) { |
39236c6e | 253 | FREE(sa, M_SONAME); |
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)) { |
6d2010ae | 273 | MALLOC(sa, struct sockaddr *, to->sa_len, M_SONAME, |
39236c6e | 274 | (flags & MSG_DONTWAIT) ? M_NOWAIT : M_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) { |
6d2010ae | 326 | FREE(sa, M_SONAME); |
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; | |
478 | } else if (error != 0 && *psa != NULL) { | |
479 | FREE(*psa, M_SONAME); | |
480 | *psa = NULL; | |
481 | } | |
0a7de745 | 482 | return error; |
2d21ac55 A |
483 | } |
484 | ||
b0d623f7 A |
485 | errno_t |
486 | sock_getaddr(socket_t sock, struct sockaddr **psa, int peer) | |
487 | { | |
488 | int error; | |
489 | ||
0a7de745 A |
490 | if (sock == NULL || psa == NULL) { |
491 | return EINVAL; | |
492 | } | |
b0d623f7 A |
493 | |
494 | socket_lock(sock, 1); | |
495 | error = sogetaddr_locked(sock, psa, peer); | |
496 | socket_unlock(sock, 1); | |
39236c6e | 497 | |
0a7de745 | 498 | return error; |
b0d623f7 A |
499 | } |
500 | ||
2d21ac55 A |
501 | void |
502 | sock_freeaddr(struct sockaddr *sa) | |
503 | { | |
0a7de745 | 504 | if (sa != NULL) { |
2d21ac55 | 505 | FREE(sa, M_SONAME); |
0a7de745 | 506 | } |
91447636 A |
507 | } |
508 | ||
509 | errno_t | |
39236c6e | 510 | sock_getsockopt(socket_t sock, int level, int optname, void *optval, |
0a7de745 | 511 | int *optlen) |
91447636 | 512 | { |
39236c6e | 513 | int error = 0; |
0a7de745 | 514 | struct sockopt sopt; |
39236c6e | 515 | |
0a7de745 A |
516 | if (sock == NULL || optval == NULL || optlen == NULL) { |
517 | return EINVAL; | |
518 | } | |
39236c6e | 519 | |
91447636 A |
520 | sopt.sopt_dir = SOPT_GET; |
521 | sopt.sopt_level = level; | |
522 | sopt.sopt_name = optname; | |
39236c6e | 523 | sopt.sopt_val = CAST_USER_ADDR_T(optval); |
91447636 | 524 | sopt.sopt_valsize = *optlen; |
b0d623f7 | 525 | sopt.sopt_p = kernproc; |
0a7de745 A |
526 | error = sogetoptlock(sock, &sopt, 1); /* will lock socket */ |
527 | if (error == 0) { | |
f427ee49 | 528 | *optlen = (uint32_t)sopt.sopt_valsize; |
0a7de745 A |
529 | } |
530 | return error; | |
91447636 A |
531 | } |
532 | ||
533 | errno_t | |
39236c6e | 534 | sock_ioctl(socket_t sock, unsigned long request, void *argp) |
91447636 | 535 | { |
0a7de745 | 536 | return soioctl(sock, request, argp, kernproc); /* will lock socket */ |
91447636 A |
537 | } |
538 | ||
539 | errno_t | |
39236c6e | 540 | sock_setsockopt(socket_t sock, int level, int optname, const void *optval, |
0a7de745 | 541 | int optlen) |
91447636 | 542 | { |
0a7de745 | 543 | struct sockopt sopt; |
39236c6e | 544 | |
0a7de745 A |
545 | if (sock == NULL || optval == NULL) { |
546 | return EINVAL; | |
547 | } | |
39236c6e | 548 | |
91447636 A |
549 | sopt.sopt_dir = SOPT_SET; |
550 | sopt.sopt_level = level; | |
551 | sopt.sopt_name = optname; | |
552 | sopt.sopt_val = CAST_USER_ADDR_T(optval); | |
553 | sopt.sopt_valsize = optlen; | |
b0d623f7 | 554 | sopt.sopt_p = kernproc; |
0a7de745 | 555 | return sosetoptlock(sock, &sopt, 1); /* will lock socket */ |
91447636 A |
556 | } |
557 | ||
6d2010ae | 558 | /* |
39236c6e A |
559 | * This follows the recommended mappings between DSCP code points |
560 | * and WMM access classes. | |
6d2010ae | 561 | */ |
f427ee49 A |
562 | static uint32_t |
563 | so_tc_from_dscp(uint8_t dscp) | |
6d2010ae | 564 | { |
f427ee49 | 565 | uint32_t tc; |
6d2010ae | 566 | |
0a7de745 | 567 | if (dscp >= 0x30 && dscp <= 0x3f) { |
6d2010ae | 568 | tc = SO_TC_VO; |
0a7de745 | 569 | } else if (dscp >= 0x20 && dscp <= 0x2f) { |
6d2010ae | 570 | tc = SO_TC_VI; |
0a7de745 | 571 | } else if (dscp >= 0x08 && dscp <= 0x17) { |
39037602 | 572 | tc = SO_TC_BK_SYS; |
0a7de745 | 573 | } else { |
6d2010ae | 574 | tc = SO_TC_BE; |
0a7de745 | 575 | } |
6d2010ae | 576 | |
0a7de745 | 577 | return tc; |
6d2010ae A |
578 | } |
579 | ||
b0d623f7 | 580 | errno_t |
39236c6e A |
581 | sock_settclassopt(socket_t sock, const void *optval, size_t optlen) |
582 | { | |
b0d623f7 A |
583 | errno_t error = 0; |
584 | struct sockopt sopt; | |
6d2010ae | 585 | int sotc; |
b0d623f7 | 586 | |
0a7de745 A |
587 | if (sock == NULL || optval == NULL || optlen != sizeof(int)) { |
588 | return EINVAL; | |
589 | } | |
b0d623f7 A |
590 | |
591 | socket_lock(sock, 1); | |
592 | if (!(sock->so_state & SS_ISCONNECTED)) { | |
39236c6e A |
593 | /* |
594 | * If the socket is not connected then we don't know | |
b0d623f7 A |
595 | * if the destination is on LAN or not. Skip |
596 | * setting traffic class in this case | |
597 | */ | |
598 | error = ENOTCONN; | |
599 | goto out; | |
600 | } | |
601 | ||
39236c6e A |
602 | if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL || |
603 | sock->so_pcb == NULL) { | |
b0d623f7 A |
604 | error = EINVAL; |
605 | goto out; | |
606 | } | |
607 | ||
6d2010ae A |
608 | /* |
609 | * Set the socket traffic class based on the passed DSCP code point | |
610 | * regardless of the scope of the destination | |
611 | */ | |
f427ee49 | 612 | sotc = so_tc_from_dscp((uint8_t)((*(const int *)optval) >> 2)); |
6d2010ae A |
613 | |
614 | sopt.sopt_dir = SOPT_SET; | |
615 | sopt.sopt_val = CAST_USER_ADDR_T(&sotc); | |
0a7de745 | 616 | sopt.sopt_valsize = sizeof(sotc); |
6d2010ae A |
617 | sopt.sopt_p = kernproc; |
618 | sopt.sopt_level = SOL_SOCKET; | |
619 | sopt.sopt_name = SO_TRAFFIC_CLASS; | |
620 | ||
0a7de745 | 621 | error = sosetoptlock(sock, &sopt, 0); /* already locked */ |
6d2010ae A |
622 | |
623 | if (error != 0) { | |
39236c6e A |
624 | printf("%s: sosetopt SO_TRAFFIC_CLASS failed %d\n", |
625 | __func__, error); | |
6d2010ae A |
626 | goto out; |
627 | } | |
628 | ||
39236c6e A |
629 | /* |
630 | * Check if the destination address is LAN or link local address. | |
b0d623f7 | 631 | * We do not want to set traffic class bits if the destination |
39236c6e A |
632 | * is not local. |
633 | */ | |
0a7de745 | 634 | if (!so_isdstlocal(sock)) { |
b0d623f7 | 635 | goto out; |
0a7de745 | 636 | } |
b0d623f7 | 637 | |
6d2010ae A |
638 | sopt.sopt_dir = SOPT_SET; |
639 | sopt.sopt_val = CAST_USER_ADDR_T(optval); | |
640 | sopt.sopt_valsize = optlen; | |
641 | sopt.sopt_p = kernproc; | |
642 | ||
39236c6e A |
643 | switch (SOCK_DOM(sock)) { |
644 | case PF_INET: | |
b0d623f7 A |
645 | sopt.sopt_level = IPPROTO_IP; |
646 | sopt.sopt_name = IP_TOS; | |
647 | break; | |
39236c6e | 648 | case PF_INET6: |
b0d623f7 A |
649 | sopt.sopt_level = IPPROTO_IPV6; |
650 | sopt.sopt_name = IPV6_TCLASS; | |
651 | break; | |
652 | default: | |
653 | error = EINVAL; | |
654 | goto out; | |
655 | } | |
39236c6e | 656 | |
0a7de745 | 657 | error = sosetoptlock(sock, &sopt, 0); /* already locked */ |
b0d623f7 | 658 | socket_unlock(sock, 1); |
0a7de745 | 659 | return error; |
b0d623f7 A |
660 | out: |
661 | socket_unlock(sock, 1); | |
0a7de745 | 662 | return error; |
b0d623f7 A |
663 | } |
664 | ||
665 | errno_t | |
39236c6e A |
666 | sock_gettclassopt(socket_t sock, void *optval, size_t *optlen) |
667 | { | |
668 | errno_t error = 0; | |
669 | struct sockopt sopt; | |
b0d623f7 | 670 | |
0a7de745 A |
671 | if (sock == NULL || optval == NULL || optlen == NULL) { |
672 | return EINVAL; | |
673 | } | |
b0d623f7 A |
674 | |
675 | sopt.sopt_dir = SOPT_GET; | |
39236c6e | 676 | sopt.sopt_val = CAST_USER_ADDR_T(optval); |
b0d623f7 A |
677 | sopt.sopt_valsize = *optlen; |
678 | sopt.sopt_p = kernproc; | |
679 | ||
680 | socket_lock(sock, 1); | |
681 | if (sock->so_proto == NULL || sock->so_proto->pr_domain == NULL) { | |
682 | socket_unlock(sock, 1); | |
0a7de745 | 683 | return EINVAL; |
b0d623f7 A |
684 | } |
685 | ||
39236c6e A |
686 | switch (SOCK_DOM(sock)) { |
687 | case PF_INET: | |
b0d623f7 A |
688 | sopt.sopt_level = IPPROTO_IP; |
689 | sopt.sopt_name = IP_TOS; | |
690 | break; | |
39236c6e | 691 | case PF_INET6: |
b0d623f7 A |
692 | sopt.sopt_level = IPPROTO_IPV6; |
693 | sopt.sopt_name = IPV6_TCLASS; | |
694 | break; | |
695 | default: | |
696 | socket_unlock(sock, 1); | |
0a7de745 | 697 | return EINVAL; |
b0d623f7 | 698 | } |
0a7de745 | 699 | error = sogetoptlock(sock, &sopt, 0); /* already locked */ |
b0d623f7 | 700 | socket_unlock(sock, 1); |
0a7de745 | 701 | if (error == 0) { |
39236c6e | 702 | *optlen = sopt.sopt_valsize; |
0a7de745 A |
703 | } |
704 | return error; | |
b0d623f7 A |
705 | } |
706 | ||
91447636 | 707 | errno_t |
39236c6e | 708 | sock_listen(socket_t sock, int backlog) |
91447636 | 709 | { |
0a7de745 A |
710 | if (sock == NULL) { |
711 | return EINVAL; | |
712 | } | |
39236c6e | 713 | |
0a7de745 | 714 | return solisten(sock, backlog); /* will lock socket */ |
91447636 A |
715 | } |
716 | ||
39236c6e A |
717 | errno_t |
718 | sock_receive_internal(socket_t sock, struct msghdr *msg, mbuf_t *data, | |
719 | int flags, size_t *recvdlen) | |
91447636 | 720 | { |
39236c6e A |
721 | uio_t auio; |
722 | struct mbuf *control = NULL; | |
723 | int error = 0; | |
f427ee49 | 724 | user_ssize_t length = 0; |
0a7de745 A |
725 | struct sockaddr *fromsa = NULL; |
726 | char uio_buf[UIO_SIZEOF((msg != NULL) ? msg->msg_iovlen : 0)]; | |
39236c6e | 727 | |
0a7de745 A |
728 | if (sock == NULL) { |
729 | return EINVAL; | |
730 | } | |
39236c6e A |
731 | |
732 | auio = uio_createwithbuffer(((msg != NULL) ? msg->msg_iovlen : 0), | |
0a7de745 | 733 | 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf)); |
39236c6e | 734 | if (msg != NULL && data == NULL) { |
91447636 | 735 | int i; |
b0d623f7 | 736 | struct iovec *tempp = msg->msg_iov; |
39236c6e | 737 | |
91447636 | 738 | for (i = 0; i < msg->msg_iovlen; i++) { |
39236c6e A |
739 | uio_addiov(auio, |
740 | CAST_USER_ADDR_T((tempp + i)->iov_base), | |
741 | (tempp + i)->iov_len); | |
91447636 | 742 | } |
0a7de745 A |
743 | if (uio_resid(auio) < 0) { |
744 | return EINVAL; | |
745 | } | |
39236c6e | 746 | } else if (recvdlen != NULL) { |
91447636 A |
747 | uio_setresid(auio, (uio_resid(auio) + *recvdlen)); |
748 | } | |
749 | length = uio_resid(auio); | |
39236c6e | 750 | |
0a7de745 | 751 | if (recvdlen != NULL) { |
91447636 | 752 | *recvdlen = 0; |
0a7de745 | 753 | } |
91447636 | 754 | |
39236c6e | 755 | /* let pru_soreceive handle the socket locking */ |
91447636 | 756 | error = sock->so_proto->pr_usrreqs->pru_soreceive(sock, &fromsa, auio, |
b0d623f7 | 757 | data, (msg && msg->msg_control) ? &control : NULL, &flags); |
0a7de745 | 758 | if (error != 0) { |
39236c6e | 759 | goto cleanup; |
0a7de745 | 760 | } |
39236c6e | 761 | |
0a7de745 | 762 | if (recvdlen != NULL) { |
91447636 | 763 | *recvdlen = length - uio_resid(auio); |
0a7de745 | 764 | } |
39236c6e | 765 | if (msg != NULL) { |
91447636 | 766 | msg->msg_flags = flags; |
39236c6e A |
767 | |
768 | if (msg->msg_name != NULL) { | |
91447636 A |
769 | int salen; |
770 | salen = msg->msg_namelen; | |
39236c6e | 771 | if (msg->msg_namelen > 0 && fromsa != NULL) { |
91447636 A |
772 | salen = MIN(salen, fromsa->sa_len); |
773 | memcpy(msg->msg_name, fromsa, | |
39236c6e A |
774 | msg->msg_namelen > fromsa->sa_len ? |
775 | fromsa->sa_len : msg->msg_namelen); | |
91447636 A |
776 | } |
777 | } | |
39236c6e A |
778 | |
779 | if (msg->msg_control != NULL) { | |
780 | struct mbuf *m = control; | |
781 | u_char *ctlbuf = msg->msg_control; | |
782 | int clen = msg->msg_controllen; | |
783 | ||
91447636 | 784 | msg->msg_controllen = 0; |
39236c6e A |
785 | |
786 | while (m != NULL && clen > 0) { | |
91447636 | 787 | unsigned int tocopy; |
39236c6e A |
788 | |
789 | if (clen >= m->m_len) { | |
91447636 | 790 | tocopy = m->m_len; |
39236c6e | 791 | } else { |
91447636 A |
792 | msg->msg_flags |= MSG_CTRUNC; |
793 | tocopy = clen; | |
794 | } | |
795 | memcpy(ctlbuf, mtod(m, caddr_t), tocopy); | |
796 | ctlbuf += tocopy; | |
797 | clen -= tocopy; | |
798 | m = m->m_next; | |
799 | } | |
39236c6e | 800 | msg->msg_controllen = |
f427ee49 | 801 | (socklen_t)((uintptr_t)ctlbuf - (uintptr_t)msg->msg_control); |
91447636 A |
802 | } |
803 | } | |
804 | ||
805 | cleanup: | |
0a7de745 | 806 | if (control != NULL) { |
39236c6e | 807 | m_freem(control); |
0a7de745 A |
808 | } |
809 | if (fromsa != NULL) { | |
39236c6e | 810 | FREE(fromsa, M_SONAME); |
0a7de745 A |
811 | } |
812 | return error; | |
91447636 A |
813 | } |
814 | ||
815 | errno_t | |
39236c6e | 816 | sock_receive(socket_t sock, struct msghdr *msg, int flags, size_t *recvdlen) |
91447636 | 817 | { |
39236c6e A |
818 | if ((msg == NULL) || (msg->msg_iovlen < 1) || |
819 | (msg->msg_iov[0].iov_len == 0) || | |
0a7de745 A |
820 | (msg->msg_iov[0].iov_base == NULL)) { |
821 | return EINVAL; | |
822 | } | |
39236c6e | 823 | |
0a7de745 | 824 | return sock_receive_internal(sock, msg, NULL, flags, recvdlen); |
91447636 A |
825 | } |
826 | ||
827 | errno_t | |
39236c6e A |
828 | sock_receivembuf(socket_t sock, struct msghdr *msg, mbuf_t *data, int flags, |
829 | size_t *recvlen) | |
91447636 | 830 | { |
39236c6e | 831 | if (data == NULL || recvlen == 0 || *recvlen <= 0 || (msg != NULL && |
0a7de745 A |
832 | (msg->msg_iov != NULL || msg->msg_iovlen != 0))) { |
833 | return EINVAL; | |
834 | } | |
39236c6e | 835 | |
0a7de745 | 836 | return sock_receive_internal(sock, msg, data, flags, recvlen); |
91447636 A |
837 | } |
838 | ||
839 | errno_t | |
39236c6e A |
840 | sock_send_internal(socket_t sock, const struct msghdr *msg, mbuf_t data, |
841 | int flags, size_t *sentlen) | |
91447636 | 842 | { |
39236c6e A |
843 | uio_t auio = NULL; |
844 | struct mbuf *control = NULL; | |
845 | int error = 0; | |
f427ee49 | 846 | user_ssize_t datalen = 0; |
0a7de745 | 847 | char uio_buf[UIO_SIZEOF((msg != NULL ? msg->msg_iovlen : 1))]; |
39236c6e | 848 | |
91447636 A |
849 | if (sock == NULL) { |
850 | error = EINVAL; | |
851 | goto errorout; | |
852 | } | |
39236c6e A |
853 | |
854 | if (data == NULL && msg != NULL) { | |
b0d623f7 | 855 | struct iovec *tempp = msg->msg_iov; |
91447636 | 856 | |
39236c6e | 857 | auio = uio_createwithbuffer(msg->msg_iovlen, 0, |
0a7de745 | 858 | UIO_SYSSPACE, UIO_WRITE, &uio_buf[0], sizeof(uio_buf)); |
39236c6e | 859 | if (tempp != NULL) { |
91447636 | 860 | int i; |
39236c6e | 861 | |
91447636 | 862 | for (i = 0; i < msg->msg_iovlen; i++) { |
39236c6e A |
863 | uio_addiov(auio, |
864 | CAST_USER_ADDR_T((tempp + i)->iov_base), | |
865 | (tempp + i)->iov_len); | |
91447636 | 866 | } |
39236c6e | 867 | |
91447636 A |
868 | if (uio_resid(auio) < 0) { |
869 | error = EINVAL; | |
870 | goto errorout; | |
871 | } | |
872 | } | |
873 | } | |
39236c6e | 874 | |
0a7de745 | 875 | if (sentlen != NULL) { |
91447636 | 876 | *sentlen = 0; |
0a7de745 | 877 | } |
39236c6e | 878 | |
0a7de745 | 879 | if (auio != NULL) { |
91447636 | 880 | datalen = uio_resid(auio); |
0a7de745 | 881 | } else { |
91447636 | 882 | datalen = data->m_pkthdr.len; |
0a7de745 | 883 | } |
39236c6e A |
884 | |
885 | if (msg != NULL && msg->msg_control) { | |
0a7de745 | 886 | if ((size_t)msg->msg_controllen < sizeof(struct cmsghdr)) { |
39236c6e A |
887 | error = EINVAL; |
888 | goto errorout; | |
889 | } | |
890 | ||
891 | if ((size_t)msg->msg_controllen > MLEN) { | |
892 | error = EINVAL; | |
893 | goto errorout; | |
894 | } | |
895 | ||
91447636 A |
896 | control = m_get(M_NOWAIT, MT_CONTROL); |
897 | if (control == NULL) { | |
898 | error = ENOMEM; | |
899 | goto errorout; | |
900 | } | |
39236c6e A |
901 | memcpy(mtod(control, caddr_t), msg->msg_control, |
902 | msg->msg_controllen); | |
91447636 A |
903 | control->m_len = msg->msg_controllen; |
904 | } | |
39236c6e | 905 | |
8f6c56a5 | 906 | error = sock->so_proto->pr_usrreqs->pru_sosend(sock, msg != NULL ? |
39236c6e A |
907 | (struct sockaddr *)msg->msg_name : NULL, auio, data, |
908 | control, flags); | |
8f6c56a5 A |
909 | |
910 | /* | |
911 | * Residual data is possible in the case of IO vectors but not | |
912 | * in the mbuf case since the latter is treated as atomic send. | |
913 | * If pru_sosend() consumed a portion of the iovecs data and | |
914 | * the error returned is transient, treat it as success; this | |
915 | * is consistent with sendit() behavior. | |
916 | */ | |
917 | if (auio != NULL && uio_resid(auio) != datalen && | |
0a7de745 | 918 | (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) { |
8f6c56a5 | 919 | error = 0; |
0a7de745 | 920 | } |
8f6c56a5 A |
921 | |
922 | if (error == 0 && sentlen != NULL) { | |
0a7de745 | 923 | if (auio != NULL) { |
91447636 | 924 | *sentlen = datalen - uio_resid(auio); |
0a7de745 | 925 | } else { |
91447636 | 926 | *sentlen = datalen; |
0a7de745 | 927 | } |
91447636 | 928 | } |
39236c6e | 929 | |
0a7de745 | 930 | return error; |
91447636 A |
931 | |
932 | /* | |
933 | * In cases where we detect an error before returning, we need to | |
934 | * free the mbuf chain if there is one. sosend (and pru_sosend) will | |
935 | * free the mbuf chain if they encounter an error. | |
936 | */ | |
937 | errorout: | |
0a7de745 | 938 | if (control) { |
91447636 | 939 | m_freem(control); |
0a7de745 A |
940 | } |
941 | if (data) { | |
91447636 | 942 | m_freem(data); |
0a7de745 A |
943 | } |
944 | if (sentlen) { | |
91447636 | 945 | *sentlen = 0; |
0a7de745 A |
946 | } |
947 | return error; | |
91447636 A |
948 | } |
949 | ||
950 | errno_t | |
39236c6e | 951 | sock_send(socket_t sock, const struct msghdr *msg, int flags, size_t *sentlen) |
91447636 | 952 | { |
0a7de745 A |
953 | if (msg == NULL || msg->msg_iov == NULL || msg->msg_iovlen < 1) { |
954 | return EINVAL; | |
955 | } | |
39236c6e | 956 | |
0a7de745 | 957 | return sock_send_internal(sock, msg, NULL, flags, sentlen); |
91447636 A |
958 | } |
959 | ||
960 | errno_t | |
39236c6e | 961 | sock_sendmbuf(socket_t sock, const struct msghdr *msg, mbuf_t data, |
0a7de745 | 962 | int flags, size_t *sentlen) |
91447636 | 963 | { |
39236c6e A |
964 | if (data == NULL || (msg != NULL && (msg->msg_iov != NULL || |
965 | msg->msg_iovlen != 0))) { | |
0a7de745 | 966 | if (data != NULL) { |
91447636 | 967 | m_freem(data); |
0a7de745 A |
968 | } |
969 | return EINVAL; | |
91447636 | 970 | } |
0a7de745 | 971 | return sock_send_internal(sock, msg, data, flags, sentlen); |
91447636 A |
972 | } |
973 | ||
974 | errno_t | |
39236c6e | 975 | sock_shutdown(socket_t sock, int how) |
91447636 | 976 | { |
0a7de745 A |
977 | if (sock == NULL) { |
978 | return EINVAL; | |
979 | } | |
39236c6e | 980 | |
0a7de745 | 981 | return soshutdown(sock, how); |
91447636 A |
982 | } |
983 | ||
91447636 | 984 | errno_t |
5ba3f43e A |
985 | sock_socket_common(int domain, int type, int protocol, sock_upcall callback, |
986 | void *context, socket_t *new_so, bool is_internal) | |
91447636 | 987 | { |
39236c6e A |
988 | int error = 0; |
989 | ||
0a7de745 A |
990 | if (new_so == NULL) { |
991 | return EINVAL; | |
992 | } | |
39236c6e | 993 | |
91447636 A |
994 | /* socreate will create an initial so_count */ |
995 | error = socreate(domain, new_so, type, protocol); | |
39236c6e | 996 | if (error == 0) { |
5ba3f43e A |
997 | /* |
998 | * This is an in-kernel socket | |
999 | */ | |
1000 | (*new_so)->so_flags1 |= SOF1_IN_KERNEL_SOCKET; | |
1001 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_total); | |
1002 | if (is_internal) { | |
1003 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_in_kernel_os_total); | |
1004 | } | |
1005 | ||
39236c6e A |
1006 | /* see comments in sock_setupcall() */ |
1007 | if (callback != NULL) { | |
5ba3f43e | 1008 | sock_setupcall(*new_so, callback, context); |
39236c6e | 1009 | } |
0a7de745 | 1010 | /* |
39236c6e A |
1011 | * last_pid and last_upid should be zero for sockets |
1012 | * created using sock_socket | |
1013 | */ | |
316670eb A |
1014 | (*new_so)->last_pid = 0; |
1015 | (*new_so)->last_upid = 0; | |
91447636 | 1016 | } |
0a7de745 | 1017 | return error; |
91447636 A |
1018 | } |
1019 | ||
5ba3f43e A |
1020 | errno_t |
1021 | sock_socket_internal(int domain, int type, int protocol, sock_upcall callback, | |
1022 | void *context, socket_t *new_so) | |
1023 | { | |
0a7de745 A |
1024 | return sock_socket_common(domain, type, protocol, callback, |
1025 | context, new_so, true); | |
5ba3f43e A |
1026 | } |
1027 | ||
1028 | errno_t | |
1029 | sock_socket(int domain, int type, int protocol, sock_upcall callback, | |
1030 | void *context, socket_t *new_so) | |
1031 | { | |
0a7de745 A |
1032 | return sock_socket_common(domain, type, protocol, callback, |
1033 | context, new_so, false); | |
5ba3f43e A |
1034 | } |
1035 | ||
91447636 | 1036 | void |
39236c6e | 1037 | sock_close(socket_t sock) |
91447636 | 1038 | { |
0a7de745 | 1039 | if (sock == NULL) { |
39236c6e | 1040 | return; |
0a7de745 | 1041 | } |
39236c6e | 1042 | |
91447636 A |
1043 | soclose(sock); |
1044 | } | |
1045 | ||
39236c6e | 1046 | /* Do we want this to be APPLE_PRIVATE API?: YES (LD 12/23/04) */ |
91447636 | 1047 | void |
39236c6e | 1048 | sock_retain(socket_t sock) |
91447636 | 1049 | { |
0a7de745 | 1050 | if (sock == NULL) { |
39236c6e | 1051 | return; |
0a7de745 | 1052 | } |
39236c6e | 1053 | |
91447636 A |
1054 | socket_lock(sock, 1); |
1055 | sock->so_retaincnt++; | |
0a7de745 | 1056 | sock->so_usecount++; /* add extra reference for holding the socket */ |
91447636 A |
1057 | socket_unlock(sock, 1); |
1058 | } | |
1059 | ||
1060 | /* Do we want this to be APPLE_PRIVATE API? */ | |
1061 | void | |
2d21ac55 | 1062 | sock_release(socket_t sock) |
91447636 | 1063 | { |
0a7de745 | 1064 | if (sock == NULL) { |
2d21ac55 | 1065 | return; |
0a7de745 | 1066 | } |
2d21ac55 | 1067 | |
39236c6e | 1068 | socket_lock(sock, 1); |
0a7de745 | 1069 | if (sock->so_upcallusecount > 0) { |
2d21ac55 | 1070 | soclose_wait_locked(sock); |
0a7de745 | 1071 | } |
2d21ac55 | 1072 | |
91447636 | 1073 | sock->so_retaincnt--; |
39236c6e A |
1074 | if (sock->so_retaincnt < 0) { |
1075 | panic("%s: negative retain count (%d) for sock=%p\n", | |
1076 | __func__, sock->so_retaincnt, sock); | |
1077 | /* NOTREACHED */ | |
1078 | } | |
490019cf A |
1079 | /* |
1080 | * Check SS_NOFDREF in case a close happened as sock_retain() | |
1081 | * was grabbing the lock | |
1082 | */ | |
1083 | if ((sock->so_retaincnt == 0) && (sock->so_usecount == 2) && | |
1084 | (!(sock->so_state & SS_NOFDREF) || | |
1085 | (sock->so_flags & SOF_MP_SUBFLOW))) { | |
2d21ac55 A |
1086 | /* close socket only if the FD is not holding it */ |
1087 | soclose_locked(sock); | |
1088 | } else { | |
1089 | /* remove extra reference holding the socket */ | |
d190cdc3 | 1090 | VERIFY(sock->so_usecount > 1); |
2d21ac55 A |
1091 | sock->so_usecount--; |
1092 | } | |
91447636 A |
1093 | socket_unlock(sock, 1); |
1094 | } | |
1095 | ||
1096 | errno_t | |
39236c6e | 1097 | sock_setpriv(socket_t sock, int on) |
91447636 | 1098 | { |
0a7de745 A |
1099 | if (sock == NULL) { |
1100 | return EINVAL; | |
1101 | } | |
39236c6e | 1102 | |
91447636 | 1103 | socket_lock(sock, 1); |
0a7de745 | 1104 | if (on) { |
91447636 | 1105 | sock->so_state |= SS_PRIV; |
0a7de745 | 1106 | } else { |
91447636 | 1107 | sock->so_state &= ~SS_PRIV; |
0a7de745 | 1108 | } |
91447636 | 1109 | socket_unlock(sock, 1); |
0a7de745 | 1110 | return 0; |
91447636 A |
1111 | } |
1112 | ||
1113 | int | |
39236c6e | 1114 | sock_isconnected(socket_t sock) |
91447636 A |
1115 | { |
1116 | int retval; | |
39236c6e | 1117 | |
91447636 | 1118 | socket_lock(sock, 1); |
39236c6e | 1119 | retval = ((sock->so_state & SS_ISCONNECTED) ? 1 : 0); |
91447636 | 1120 | socket_unlock(sock, 1); |
0a7de745 | 1121 | return retval; |
91447636 A |
1122 | } |
1123 | ||
1124 | int | |
39236c6e | 1125 | sock_isnonblocking(socket_t sock) |
91447636 A |
1126 | { |
1127 | int retval; | |
39236c6e | 1128 | |
91447636 | 1129 | socket_lock(sock, 1); |
39236c6e | 1130 | retval = ((sock->so_state & SS_NBIO) ? 1 : 0); |
91447636 | 1131 | socket_unlock(sock, 1); |
0a7de745 | 1132 | return retval; |
91447636 A |
1133 | } |
1134 | ||
1135 | errno_t | |
39236c6e | 1136 | sock_gettype(socket_t sock, int *outDomain, int *outType, int *outProtocol) |
91447636 A |
1137 | { |
1138 | socket_lock(sock, 1); | |
0a7de745 | 1139 | if (outDomain != NULL) { |
39236c6e | 1140 | *outDomain = SOCK_DOM(sock); |
0a7de745 A |
1141 | } |
1142 | if (outType != NULL) { | |
91447636 | 1143 | *outType = sock->so_type; |
0a7de745 A |
1144 | } |
1145 | if (outProtocol != NULL) { | |
39236c6e | 1146 | *outProtocol = SOCK_PROTO(sock); |
0a7de745 | 1147 | } |
91447636 | 1148 | socket_unlock(sock, 1); |
0a7de745 | 1149 | return 0; |
91447636 | 1150 | } |
2d21ac55 A |
1151 | |
1152 | /* | |
1153 | * Return the listening socket of a pre-accepted socket. It returns the | |
1154 | * listener (so_head) value of a given socket. This is intended to be | |
1155 | * called by a socket filter during a filter attach (sf_attach) callback. | |
1156 | * The value returned by this routine is safe to be used only in the | |
1157 | * context of that callback, because we hold the listener's lock across | |
1158 | * the sflt_initsock() call. | |
1159 | */ | |
1160 | socket_t | |
1161 | sock_getlistener(socket_t sock) | |
1162 | { | |
0a7de745 | 1163 | return sock->so_head; |
2d21ac55 | 1164 | } |
d1ecb069 | 1165 | |
6d2010ae A |
1166 | static inline void |
1167 | sock_set_tcp_stream_priority(socket_t sock) | |
1168 | { | |
39236c6e A |
1169 | if ((SOCK_DOM(sock) == PF_INET || SOCK_DOM(sock) == PF_INET6) && |
1170 | SOCK_TYPE(sock) == SOCK_STREAM) { | |
6d2010ae | 1171 | set_tcp_stream_priority(sock); |
6d2010ae A |
1172 | } |
1173 | } | |
1174 | ||
d1ecb069 A |
1175 | /* |
1176 | * Caller must have ensured socket is valid and won't be going away. | |
1177 | */ | |
1178 | void | |
3e170ce0 | 1179 | socket_set_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags) |
d1ecb069 | 1180 | { |
39037602 | 1181 | u_int32_t soflags1 = 0; |
0a7de745 A |
1182 | |
1183 | if ((flags & TRAFFIC_MGT_SO_BACKGROUND)) { | |
39037602 | 1184 | soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND; |
0a7de745 A |
1185 | } |
1186 | if ((flags & TRAFFIC_MGT_TCP_RECVBG)) { | |
39037602 | 1187 | soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG; |
0a7de745 A |
1188 | } |
1189 | ||
39037602 A |
1190 | (void) OSBitOrAtomic(soflags1, &sock->so_flags1); |
1191 | ||
6d2010ae A |
1192 | sock_set_tcp_stream_priority(sock); |
1193 | } | |
1194 | ||
1195 | void | |
3e170ce0 | 1196 | socket_set_traffic_mgt_flags(socket_t sock, u_int8_t flags) |
6d2010ae A |
1197 | { |
1198 | socket_lock(sock, 1); | |
1199 | socket_set_traffic_mgt_flags_locked(sock, flags); | |
1200 | socket_unlock(sock, 1); | |
d1ecb069 A |
1201 | } |
1202 | ||
1203 | /* | |
1204 | * Caller must have ensured socket is valid and won't be going away. | |
1205 | */ | |
1206 | void | |
3e170ce0 | 1207 | socket_clear_traffic_mgt_flags_locked(socket_t sock, u_int8_t flags) |
d1ecb069 | 1208 | { |
39037602 A |
1209 | u_int32_t soflags1 = 0; |
1210 | ||
0a7de745 | 1211 | if ((flags & TRAFFIC_MGT_SO_BACKGROUND)) { |
39037602 | 1212 | soflags1 |= SOF1_TRAFFIC_MGT_SO_BACKGROUND; |
0a7de745 A |
1213 | } |
1214 | if ((flags & TRAFFIC_MGT_TCP_RECVBG)) { | |
39037602 | 1215 | soflags1 |= SOF1_TRAFFIC_MGT_TCP_RECVBG; |
0a7de745 A |
1216 | } |
1217 | ||
39037602 A |
1218 | (void) OSBitAndAtomic(~soflags1, &sock->so_flags1); |
1219 | ||
6d2010ae | 1220 | sock_set_tcp_stream_priority(sock); |
d1ecb069 | 1221 | } |
d41d1dae | 1222 | |
6d2010ae | 1223 | void |
3e170ce0 | 1224 | socket_clear_traffic_mgt_flags(socket_t sock, u_int8_t flags) |
d41d1dae | 1225 | { |
6d2010ae A |
1226 | socket_lock(sock, 1); |
1227 | socket_clear_traffic_mgt_flags_locked(sock, flags); | |
1228 | socket_unlock(sock, 1); | |
1229 | } | |
1230 | ||
d41d1dae | 1231 | |
6d2010ae A |
1232 | /* |
1233 | * Caller must have ensured socket is valid and won't be going away. | |
1234 | */ | |
1235 | errno_t | |
1236 | socket_defunct(struct proc *p, socket_t so, int level) | |
1237 | { | |
1238 | errno_t retval; | |
1239 | ||
1240 | if (level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC && | |
0a7de745 A |
1241 | level != SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL) { |
1242 | return EINVAL; | |
1243 | } | |
6d2010ae A |
1244 | |
1245 | socket_lock(so, 1); | |
1246 | /* | |
1247 | * SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC level is meant to tear down | |
1248 | * all of mDNSResponder IPC sockets, currently those of AF_UNIX; note | |
1249 | * that this is an implementation artifact of mDNSResponder. We do | |
1250 | * a quick test against the socket buffers for SB_UNIX, since that | |
1251 | * would have been set by unp_attach() at socket creation time. | |
1252 | */ | |
1253 | if (level == SHUTDOWN_SOCKET_LEVEL_DISCONNECT_SVC && | |
1254 | (so->so_rcv.sb_flags & so->so_snd.sb_flags & SB_UNIX) != SB_UNIX) { | |
1255 | socket_unlock(so, 1); | |
0a7de745 | 1256 | return EOPNOTSUPP; |
6d2010ae A |
1257 | } |
1258 | retval = sosetdefunct(p, so, level, TRUE); | |
0a7de745 | 1259 | if (retval == 0) { |
6d2010ae | 1260 | retval = sodefunct(p, so, level); |
0a7de745 | 1261 | } |
6d2010ae | 1262 | socket_unlock(so, 1); |
0a7de745 | 1263 | return retval; |
6d2010ae A |
1264 | } |
1265 | ||
5ba3f43e A |
1266 | void |
1267 | sock_setupcalls_locked(socket_t sock, sock_upcall rcallback, void *rcontext, | |
1268 | sock_upcall wcallback, void *wcontext, int locked) | |
39236c6e A |
1269 | { |
1270 | if (rcallback != NULL) { | |
1271 | sock->so_rcv.sb_flags |= SB_UPCALL; | |
0a7de745 | 1272 | if (locked) { |
5ba3f43e | 1273 | sock->so_rcv.sb_flags |= SB_UPCALL_LOCK; |
0a7de745 | 1274 | } |
39236c6e A |
1275 | sock->so_rcv.sb_upcall = rcallback; |
1276 | sock->so_rcv.sb_upcallarg = rcontext; | |
1277 | } else { | |
5ba3f43e | 1278 | sock->so_rcv.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK); |
39236c6e A |
1279 | sock->so_rcv.sb_upcall = NULL; |
1280 | sock->so_rcv.sb_upcallarg = NULL; | |
1281 | } | |
1282 | ||
1283 | if (wcallback != NULL) { | |
1284 | sock->so_snd.sb_flags |= SB_UPCALL; | |
0a7de745 | 1285 | if (locked) { |
5ba3f43e | 1286 | sock->so_snd.sb_flags |= SB_UPCALL_LOCK; |
0a7de745 | 1287 | } |
39236c6e A |
1288 | sock->so_snd.sb_upcall = wcallback; |
1289 | sock->so_snd.sb_upcallarg = wcontext; | |
1290 | } else { | |
5ba3f43e | 1291 | sock->so_snd.sb_flags &= ~(SB_UPCALL | SB_UPCALL_LOCK); |
39236c6e A |
1292 | sock->so_snd.sb_upcall = NULL; |
1293 | sock->so_snd.sb_upcallarg = NULL; | |
1294 | } | |
1295 | } | |
1296 | ||
6d2010ae | 1297 | errno_t |
39236c6e | 1298 | sock_setupcall(socket_t sock, sock_upcall callback, void *context) |
6d2010ae | 1299 | { |
0a7de745 A |
1300 | if (sock == NULL) { |
1301 | return EINVAL; | |
1302 | } | |
6d2010ae A |
1303 | |
1304 | /* | |
1305 | * Note that we don't wait for any in progress upcall to complete. | |
39236c6e A |
1306 | * On embedded, sock_setupcall() causes both read and write |
1307 | * callbacks to be set; on desktop, only read callback is set | |
1308 | * to maintain legacy KPI behavior. | |
1309 | * | |
1310 | * The newer sock_setupcalls() KPI should be used instead to set | |
1311 | * the read and write callbacks and their respective parameters. | |
6d2010ae A |
1312 | */ |
1313 | socket_lock(sock, 1); | |
f427ee49 | 1314 | #if (defined(__arm__) || defined(__arm64__)) |
5ba3f43e | 1315 | sock_setupcalls_locked(sock, callback, context, callback, context, 0); |
f427ee49 | 1316 | #else /* (defined(__arm__) || defined(__arm64__)) */ |
5ba3f43e | 1317 | sock_setupcalls_locked(sock, callback, context, NULL, NULL, 0); |
f427ee49 | 1318 | #endif /* (defined(__arm__) || defined(__arm64__)) */ |
39236c6e | 1319 | socket_unlock(sock, 1); |
6d2010ae | 1320 | |
0a7de745 | 1321 | return 0; |
39236c6e A |
1322 | } |
1323 | ||
1324 | errno_t | |
1325 | sock_setupcalls(socket_t sock, sock_upcall rcallback, void *rcontext, | |
1326 | sock_upcall wcallback, void *wcontext) | |
1327 | { | |
0a7de745 A |
1328 | if (sock == NULL) { |
1329 | return EINVAL; | |
1330 | } | |
39236c6e A |
1331 | |
1332 | /* | |
1333 | * Note that we don't wait for any in progress upcall to complete. | |
1334 | */ | |
1335 | socket_lock(sock, 1); | |
5ba3f43e | 1336 | sock_setupcalls_locked(sock, rcallback, rcontext, wcallback, wcontext, 0); |
39236c6e A |
1337 | socket_unlock(sock, 1); |
1338 | ||
0a7de745 | 1339 | return 0; |
39236c6e A |
1340 | } |
1341 | ||
5ba3f43e A |
1342 | void |
1343 | sock_catchevents_locked(socket_t sock, sock_evupcall ecallback, void *econtext, | |
f427ee49 | 1344 | long emask) |
39236c6e | 1345 | { |
5ba3f43e | 1346 | socket_lock_assert_owned(sock); |
39236c6e A |
1347 | |
1348 | /* | |
1349 | * Note that we don't wait for any in progress upcall to complete. | |
1350 | */ | |
39236c6e A |
1351 | if (ecallback != NULL) { |
1352 | sock->so_event = ecallback; | |
1353 | sock->so_eventarg = econtext; | |
f427ee49 | 1354 | sock->so_eventmask = (uint32_t)emask; |
d41d1dae | 1355 | } else { |
fe8ab488 | 1356 | sock->so_event = sonullevent; |
39236c6e A |
1357 | sock->so_eventarg = NULL; |
1358 | sock->so_eventmask = 0; | |
d41d1dae | 1359 | } |
5ba3f43e A |
1360 | } |
1361 | ||
1362 | errno_t | |
1363 | sock_catchevents(socket_t sock, sock_evupcall ecallback, void *econtext, | |
f427ee49 | 1364 | long emask) |
5ba3f43e | 1365 | { |
0a7de745 A |
1366 | if (sock == NULL) { |
1367 | return EINVAL; | |
1368 | } | |
5ba3f43e A |
1369 | |
1370 | socket_lock(sock, 1); | |
1371 | sock_catchevents_locked(sock, ecallback, econtext, emask); | |
6d2010ae A |
1372 | socket_unlock(sock, 1); |
1373 | ||
0a7de745 | 1374 | return 0; |
d41d1dae | 1375 | } |
fe8ab488 A |
1376 | |
1377 | /* | |
1378 | * Returns true whether or not a socket belongs to the kernel. | |
1379 | */ | |
1380 | int | |
1381 | sock_iskernel(socket_t so) | |
1382 | { | |
0a7de745 | 1383 | return so && so->last_pid == 0; |
fe8ab488 | 1384 | } |