+ /*
+ * Ask getsockaddr{_s} to not translate AF_UNSPEC to AF_INET
+ * if this is a datagram socket; translate for other types.
+ */
+ dgram = (so->so_type == SOCK_DGRAM);
+
+ /* Get socket address now before we obtain socket lock */
+ if (uap->namelen > sizeof (ss)) {
+ error = getsockaddr(so, &sa, uap->name, uap->namelen, !dgram);
+ } else {
+ error = getsockaddr_s(so, &ss, uap->name, uap->namelen, !dgram);
+ if (error == 0)
+ sa = (struct sockaddr *)&ss;
+ }
+ if (error != 0)
+ goto out;
+
+ error = connectit(so, sa);
+
+ if (sa != NULL && sa != SA(&ss))
+ FREE(sa, M_SONAME);
+ if (error == ERESTART)
+ error = EINTR;
+out:
+ file_drop(fd);
+ return (error);
+}
+
+static int
+connectx_nocancel(struct proc *p, struct connectx_args *uap, int *retval)
+{
+#pragma unused(p, retval)
+ struct sockaddr_list *src_sl = NULL, *dst_sl = NULL;
+ struct socket *so;
+ int error, fd = uap->s;
+ boolean_t dgram;
+ connid_t cid = CONNID_ANY;
+
+ AUDIT_ARG(fd, uap->s);
+ error = file_socket(fd, &so);
+ if (error != 0)
+ return (error);
+ if (so == NULL) {
+ error = EBADF;
+ goto out;
+ }
+
+ /*
+ * Ask getsockaddr{_s} to not translate AF_UNSPEC to AF_INET
+ * if this is a datagram socket; translate for other types.
+ */
+ dgram = (so->so_type == SOCK_DGRAM);
+
+ /*
+ * Get socket address(es) now before we obtain socket lock; use
+ * sockaddr_list for src address for convenience, if present,
+ * even though it won't hold more than one.
+ */
+ if (uap->src != USER_ADDR_NULL && (error = getsockaddrlist(so,
+ &src_sl, uap->src, uap->srclen, dgram)) != 0)
+ goto out;
+
+ error = getsockaddrlist(so, &dst_sl, uap->dsts, uap->dstlen, dgram);
+ if (error != 0)
+ goto out;
+
+ VERIFY(dst_sl != NULL &&
+ !TAILQ_EMPTY(&dst_sl->sl_head) && dst_sl->sl_cnt > 0);
+
+ error = connectitx(so, &src_sl, &dst_sl, p, uap->ifscope,
+ uap->aid, &cid);
+ if (error == ERESTART)
+ error = EINTR;
+
+ if (uap->cid != USER_ADDR_NULL)
+ (void) copyout(&cid, uap->cid, sizeof (cid));
+
+out:
+ file_drop(fd);
+ if (src_sl != NULL)
+ sockaddrlist_free(src_sl);
+ if (dst_sl != NULL)
+ sockaddrlist_free(dst_sl);
+ return (error);
+}
+
+int
+connectx(struct proc *p, struct connectx_args *uap, int *retval)
+{
+ /*
+ * Due to similiarity with a POSIX interface, define as
+ * an unofficial cancellation point.
+ */
+ __pthread_testcancel(1);
+ return (connectx_nocancel(p, uap, retval));
+}
+
+static int
+connectit(struct socket *so, struct sockaddr *sa)
+{
+ int error;
+
+ AUDIT_ARG(sockaddr, vfs_context_cwd(vfs_context_current()), sa);
+#if CONFIG_MACF_SOCKET_SUBSET
+ if ((error = mac_socket_check_connect(kauth_cred_get(), so, sa)) != 0)
+ return (error);
+#endif /* MAC_SOCKET_SUBSET */