+static int
+tcp_usr_connectx_common(struct socket *so, int af,
+ struct sockaddr *src, struct sockaddr *dst,
+ struct proc *p, uint32_t ifscope, sae_associd_t aid, sae_connid_t *pcid,
+ uint32_t flags, void *arg, uint32_t arglen, struct uio *auio,
+ user_ssize_t *bytes_written)
+{
+#pragma unused(aid, flags, arg, arglen)
+ struct inpcb *inp = sotoinpcb(so);
+ int error = 0;
+ user_ssize_t datalen = 0;
+
+ if (inp == NULL)
+ return (EINVAL);
+
+ VERIFY(dst != NULL);
+
+ ASSERT(!(inp->inp_flags2 & INP2_CONNECT_IN_PROGRESS));
+ inp->inp_flags2 |= INP2_CONNECT_IN_PROGRESS;
+
+#if NECP
+ inp_update_necp_policy(inp, src, dst, ifscope);
+#endif /* NECP */
+
+ if ((so->so_flags1 & SOF1_DATA_IDEMPOTENT) &&
+ (tcp_fastopen & TCP_FASTOPEN_CLIENT))
+ sototcpcb(so)->t_flagsext |= TF_FASTOPEN;
+
+ /* bind socket to the specified interface, if requested */
+ if (ifscope != IFSCOPE_NONE &&
+ (error = inp_bindif(inp, ifscope, NULL)) != 0) {
+ goto done;
+ }
+
+ /* if source address and/or port is specified, bind to it */
+ if (src != NULL) {
+ error = sobindlock(so, src, 0); /* already locked */
+ if (error != 0) {
+ goto done;
+ }
+ }
+
+ switch (af) {
+ case AF_INET:
+ error = tcp_usr_connect(so, dst, p);
+ break;
+#if INET6
+ case AF_INET6:
+ error = tcp6_usr_connect(so, dst, p);
+ break;
+#endif /* INET6 */
+ default:
+ VERIFY(0);
+ /* NOTREACHED */
+ }
+
+ if (error != 0) {
+ goto done;
+ }
+
+ /* if there is data, copy it */
+ if (auio != NULL) {
+ socket_unlock(so, 0);
+
+ VERIFY(bytes_written != NULL);
+
+ datalen = uio_resid(auio);
+ error = so->so_proto->pr_usrreqs->pru_sosend(so, NULL,
+ (uio_t)auio, NULL, NULL, 0);
+ socket_lock(so, 0);
+
+ if (error == 0 || error == EWOULDBLOCK)
+ *bytes_written = datalen - uio_resid(auio);
+
+ /*
+ * sosend returns EWOULDBLOCK if it's a non-blocking
+ * socket or a timeout occured (this allows to return
+ * the amount of queued data through sendit()).
+ *
+ * However, connectx() returns EINPROGRESS in case of a
+ * blocking socket. So we change the return value here.
+ */
+ if (error == EWOULDBLOCK)
+ error = EINPROGRESS;
+ }
+
+ if (error == 0 && pcid != NULL)
+ *pcid = 1; /* there is only one connection in regular TCP */
+
+done:
+ if (error && error != EINPROGRESS)
+ so->so_flags1 &= ~SOF1_PRECONNECT_DATA;
+
+ inp->inp_flags2 &= ~INP2_CONNECT_IN_PROGRESS;
+ return (error);
+}
+
+static int
+tcp_usr_connectx(struct socket *so, struct sockaddr *src,
+ struct sockaddr *dst, struct proc *p, uint32_t ifscope,
+ sae_associd_t aid, sae_connid_t *pcid, uint32_t flags, void *arg,
+ uint32_t arglen, struct uio *uio, user_ssize_t *bytes_written)
+{
+ return (tcp_usr_connectx_common(so, AF_INET, src, dst, p, ifscope, aid,
+ pcid, flags, arg, arglen, uio, bytes_written));
+}
+