int in6_pcbbind( struct inpcb *inp, struct sockaddr *nam, struct proc *p) { struct socket *so = inp->inp_socket; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); if (!in6_ifaddrs) /* XXX broken! */ return (EADDRNOTAVAIL); if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) return(EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; socket_unlock(so, 0); /* keep reference */ lck_rw_lock_exclusive(pcbinfo->mtx); if (nam) { sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof(*sin6)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EINVAL); } /* * family check. */ if (nam->sa_family != AF_INET6) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EAFNOSUPPORT); } /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return EINVAL; } /* this must be cleared for ifa_ifwithaddr() */ sin6->sin6_scope_id = 0; lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* yech... */ if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRNOTAVAIL); } /* * XXX: bind to an anycast address might accidentally * cause sending a packet with anycast source address. * We should allow to bind to a deprecated address, since * the application dare to use it. */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { ifafree(ia); lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRNOTAVAIL); } ifafree(ia); ia = NULL; } if (lport) { struct inpcb *t; /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && p && ((so->so_state & SS_PRIV) == 0)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EACCES); } if (so->so_uid && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { t = in6_pcblookup_local_and_cleanup(pcbinfo, &sin6->sin6_addr, lport, INPLOOKUP_WILDCARD); if (t && (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || (t->inp_socket->so_options & SO_REUSEPORT) == 0) && (so->so_uid != t->inp_socket->so_uid) && ((t->inp_socket->so_flags & SOF_REUSESHAREUID) == 0)) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local_and_cleanup(pcbinfo, sin.sin_addr, lport, INPLOOKUP_WILDCARD); if (t && (t->inp_socket->so_options & SO_REUSEPORT) == 0 && (so->so_uid != t->inp_socket->so_uid) && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } } } t = in6_pcblookup_local_and_cleanup(pcbinfo, &sin6->sin6_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return(EADDRINUSE); } if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local_and_cleanup(pcbinfo, sin.sin_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0 && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) { lck_rw_done(pcbinfo->mtx); socket_lock(so, 0); return (EADDRINUSE); } } } inp->in6p_laddr = sin6->sin6_addr; } socket_lock(so, 0); if (lport == 0) { int e; if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, p, 1)) != 0) { lck_rw_done(pcbinfo->mtx); return(e); } } else { inp->inp_lport = lport; if (in_pcbinshash(inp, 1) != 0) { inp->in6p_laddr = in6addr_any; inp->inp_lport = 0; lck_rw_done(pcbinfo->mtx); return (EAGAIN); } } lck_rw_done(pcbinfo->mtx); sflt_notify(so, sock_evt_bound, NULL); return(0); }
static int udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) { struct inpcb *inp; struct inpcbinfo *pcbinfo; struct sockaddr_in6 *sin6; int error; pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); sin6 = (struct sockaddr_in6 *)nam; KASSERT(inp != NULL, ("udp6_connect: inp == NULL")); /* * XXXRW: Need to clarify locking of v4/v6 flags. */ INP_WLOCK(inp); #ifdef INET if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct sockaddr_in sin; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { error = EINVAL; goto out; } if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; goto out; } in6_sin6_2_sin(&sin, sin6); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; INP_HASH_WLOCK(pcbinfo); error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); INP_HASH_WUNLOCK(pcbinfo); if (error == 0) soisconnected(so); goto out; } #endif if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { error = EISCONN; goto out; } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; INP_HASH_WLOCK(pcbinfo); error = in6_pcbconnect(inp, nam, td->td_ucred); INP_HASH_WUNLOCK(pcbinfo); if (error == 0) soisconnected(so); out: INP_WUNLOCK(inp); return (error); }
int sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt, sctp_assoc_t * id) { char buf[SCTP_STACK_BUF_SIZE]; int i, ret, cnt, *aa; char *cpto; const struct sockaddr *at; sctp_assoc_t *p_id; size_t len = sizeof(int); /* validate the address count and list */ if ((addrs == NULL) || (addrcnt <= 0)) { errno = EINVAL; return (-1); } at = addrs; cnt = 0; cpto = ((caddr_t)buf + sizeof(int)); /* validate all the addresses and get the size */ for (i = 0; i < addrcnt; i++) { if (at->sa_family == AF_INET) { if (at->sa_len != sizeof(struct sockaddr_in)) { errno = EINVAL; return (-1); } memcpy(cpto, at, at->sa_len); cpto = ((caddr_t)cpto + at->sa_len); len += at->sa_len; } else if (at->sa_family == AF_INET6) { if (at->sa_len != sizeof(struct sockaddr_in6)) { errno = EINVAL; return (-1); } if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) { len += sizeof(struct sockaddr_in); in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at); cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in)); len += sizeof(struct sockaddr_in); } else { memcpy(cpto, at, at->sa_len); cpto = ((caddr_t)cpto + at->sa_len); len += at->sa_len; } } else { errno = EINVAL; return (-1); } if (len > (sizeof(buf) - sizeof(int))) { /* Never enough memory */ errno = E2BIG; return (-1); } at = (struct sockaddr *)((caddr_t)at + at->sa_len); cnt++; } /* do we have any? */ if (cnt == 0) { errno = EINVAL; return (-1); } aa = (int *)buf; *aa = cnt; ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf, (socklen_t) len); if ((ret == 0) && id) { p_id = (sctp_assoc_t *) buf; *id = *p_id; } return (ret); }
int sctp_connectx(int sd, const struct sockaddr *addrs, int addrcnt, sctp_assoc_t * id) { char *buf; int i, ret, *aa; char *cpto; const struct sockaddr *at; size_t len; /* validate the address count and list */ if ((addrs == NULL) || (addrcnt <= 0)) { errno = EINVAL; return (-1); } if ((buf = malloc(sizeof(int) + (size_t)addrcnt * sizeof(struct sockaddr_in6))) == NULL) { errno = E2BIG; return (-1); } len = sizeof(int); at = addrs; cpto = buf + sizeof(int); /* validate all the addresses and get the size */ for (i = 0; i < addrcnt; i++) { switch (at->sa_family) { case AF_INET: if (at->sa_len != sizeof(struct sockaddr_in)) { free(buf); errno = EINVAL; return (-1); } memcpy(cpto, at, sizeof(struct sockaddr_in)); cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in)); len += sizeof(struct sockaddr_in); break; case AF_INET6: if (at->sa_len != sizeof(struct sockaddr_in6)) { free(buf); errno = EINVAL; return (-1); } if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)at)->sin6_addr)) { in6_sin6_2_sin((struct sockaddr_in *)cpto, (struct sockaddr_in6 *)at); cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in)); len += sizeof(struct sockaddr_in); } else { memcpy(cpto, at, sizeof(struct sockaddr_in6)); cpto = ((caddr_t)cpto + sizeof(struct sockaddr_in6)); len += sizeof(struct sockaddr_in6); } break; default: free(buf); errno = EINVAL; return (-1); } at = (struct sockaddr *)((caddr_t)at + at->sa_len); } aa = (int *)buf; *aa = addrcnt; ret = setsockopt(sd, IPPROTO_SCTP, SCTP_CONNECT_X, (void *)buf, (socklen_t) len); if ((ret == 0) && (id != NULL)) { *id = *(sctp_assoc_t *) buf; } free(buf); return (ret); }