/** Send using su_vsend(). Map IPv4 addresses as IPv6 addresses, if needed. */ ssize_t tport_send_dgram(tport_t const *self, msg_t *msg, msg_iovec_t iov[], size_t iovused) { su_sockaddr_t su[1]; socklen_t sulen = sizeof su; if (tport_is_connection_oriented(self)) return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0); msg_get_address(msg, su, &sulen); #if SU_HAVE_IN6 && defined(IN6_INADDR_TO_V4MAPPED) if (su->su_family == AF_INET && self->tp_addrinfo->ai_family == AF_INET6) { su_sockaddr_t su0[1]; memset(su0, 0, sizeof su0); su0->su_family = self->tp_addrinfo->ai_family; su0->su_port = su->su_port; IN6_INADDR_TO_V4MAPPED(&su->su_sin.sin_addr, &su0->su_sin6.sin6_addr); memcpy(su, su0, sulen = sizeof(su0->su_sin6)); } #endif su_soerror(self->tp_socket); /* XXX - we *still* have a race condition */ return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, su, sulen); }
/* * Get clients IP address. * don't use gethostbyname, which would invoke yellow pages * Remains only for backward compatibility reasons. * Used mainly by the portmapper so that it can register * with itself. Also used by pmap*() routines */ void get_myaddress_ipv6(char *fmly, struct sockaddr *addr) { if (fmly != 0 && strcmp(fmly, NC_INET6) == 0) { /* LINTED pointer cast */ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; (void) memset(sin6, 0, sizeof (*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(PMAPPORT); if (__can_use_af(AF_INET6)) { /* Local copy of in6addr_any to avoid -lsocket */ struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; sin6->sin6_addr = in6addr_any; } else { struct in_addr in4; in4.s_addr = INADDR_ANY; IN6_INADDR_TO_V4MAPPED(&in4, &sin6->sin6_addr); } } else { /* LINTED pointer cast */ struct sockaddr_in *sin = (struct sockaddr_in *)addr; (void) memset(sin, 0, sizeof (*sin)); sin->sin_family = AF_INET; sin->sin_port = htons(PMAPPORT); sin->sin_addr.s_addr = INADDR_ANY; } }
static boolean_t addrmatch(struct addrinfo *ai, struct sockaddr_in6 *sin6from) { /* * Note: on a GC query, the ds->addr port numbers are * the GC port, and our from addr has the LDAP port. * Just compare the IP addresses. */ if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6p = (void *)ai->ai_addr; if (!memcmp(&sin6from->sin6_addr, &sin6p->sin6_addr, sizeof (struct in6_addr))) return (B_TRUE); } if (ai->ai_family == AF_INET) { struct in6_addr in6; struct sockaddr_in *sin4p = (void *)ai->ai_addr; IN6_INADDR_TO_V4MAPPED(&sin4p->sin_addr, &in6); if (!memcmp(&sin6from->sin6_addr, &in6, sizeof (struct in6_addr))) return (B_TRUE); } return (B_FALSE); }
/* * Attempt a send of the LDAP request to all known addresses * for this candidate server. */ static void send_to_cds(ad_disc_cds_t *send_cds, char *ber_buf, size_t be_len, int fd) { struct sockaddr_in6 addr6; struct addrinfo *ai; int err; if (DBG(DISC, 2)) { logger(LOG_DEBUG, "send to: %s", send_cds->cds_ds.host); } for (ai = send_cds->cds_ai; ai != NULL; ai = ai->ai_next) { /* * Build the "to" address. */ (void) memset(&addr6, 0, sizeof (addr6)); if (ai->ai_family == AF_INET6) { (void) memcpy(&addr6, ai->ai_addr, sizeof (addr6)); } else if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (void *)ai->ai_addr; addr6.sin6_family = AF_INET6; IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &addr6.sin6_addr); } else { continue; } addr6.sin6_port = htons(LDAP_PORT); /* * Send the "ping" to this address. */ err = sendto(fd, ber_buf, be_len, 0, (struct sockaddr *)&addr6, sizeof (addr6)); err = (err < 0) ? errno : 0; if (DBG(DISC, 2)) { char abuf[INET6_ADDRSTRLEN]; const char *pa; pa = inet_ntop(AF_INET6, &addr6.sin6_addr, abuf, sizeof (abuf)); logger(LOG_ERR, " > %s rc=%d", pa ? pa : "?", err); } } }
/* * Returns 0 if the check failed and the restart should be refused, * 1 if the check succeeded. */ int sctp_secure_restart_check(mblk_t *pkt, sctp_chunk_hdr_t *ich, uint32_t ports, int sleep, sctp_stack_t *sctps, ip_recv_attr_t *ira) { sctp_faddr_t *fp, *fphead = NULL; sctp_parm_hdr_t *ph; ssize_t remaining; int isv4; ipha_t *iph; ip6_t *ip6h; in6_addr_t hdraddr[1]; int retval = 0; sctp_tf_t *tf; sctp_t *sctp; int compres; sctp_init_chunk_t *init; int nadded = 0; /* extract the address from the IP header */ isv4 = (IPH_HDR_VERSION(pkt->b_rptr) == IPV4_VERSION); if (isv4) { iph = (ipha_t *)pkt->b_rptr; IN6_IPADDR_TO_V4MAPPED(iph->ipha_src, hdraddr); } else { ip6h = (ip6_t *)pkt->b_rptr; hdraddr[0] = ip6h->ip6_src; } /* Walk the params in the INIT [ACK], pulling out addr params */ remaining = ntohs(ich->sch_len) - sizeof (*ich) - sizeof (sctp_init_chunk_t); if (remaining < sizeof (*ph)) { /* no parameters; restart OK */ return (1); } init = (sctp_init_chunk_t *)(ich + 1); ph = (sctp_parm_hdr_t *)(init + 1); while (ph != NULL) { sctp_faddr_t *fpa = NULL; /* params will have already been byteordered when validating */ if (ph->sph_type == htons(PARM_ADDR4)) { if (remaining >= PARM_ADDR4_LEN) { in6_addr_t addr; IN6_INADDR_TO_V4MAPPED((struct in_addr *) (ph + 1), &addr); fpa = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fpa == NULL) { goto done; } bzero(fpa, sizeof (*fpa)); fpa->faddr = addr; fpa->next = NULL; } } else if (ph->sph_type == htons(PARM_ADDR6)) { if (remaining >= PARM_ADDR6_LEN) { fpa = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fpa == NULL) { goto done; } bzero(fpa, sizeof (*fpa)); bcopy(ph + 1, &fpa->faddr, sizeof (fpa->faddr)); fpa->next = NULL; } } /* link in the new addr, if it was an addr param */ if (fpa != NULL) { if (fphead == NULL) { fphead = fpa; } else { fpa->next = fphead; fphead = fpa; } } ph = sctp_next_parm(ph, &remaining); } if (fphead == NULL) { /* no addr parameters; restart OK */ return (1); } /* * got at least one; make sure the header's addr is * in the list */ fp = sctp_lookup_faddr_nosctp(fphead, hdraddr); if (fp == NULL) { /* not included; add it now */ fp = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep); if (fp == NULL) { goto done; } bzero(fp, sizeof (*fp)); fp->faddr = *hdraddr; fp->next = fphead; fphead = fp; } /* * Now, we can finally do the check: For each sctp instance * on the hash line for ports, compare its faddr set against * the new one. If the new one is a strict subset of any * existing sctp's faddrs, the restart is OK. However, if there * is an overlap, this could be an attack, so return failure. * If all sctp's faddrs are disjoint, this is a legitimate new * association. */ tf = &(sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, ports)]); mutex_enter(&tf->tf_lock); for (sctp = tf->tf_sctp; sctp; sctp = sctp->sctp_conn_hash_next) { if (ports != sctp->sctp_connp->conn_ports) { continue; } compres = sctp_compare_faddrsets(fphead, sctp->sctp_faddrs); if (compres <= SCTP_ADDR_SUBSET) { retval = 1; mutex_exit(&tf->tf_lock); goto done; } if (compres == SCTP_ADDR_OVERLAP) { dprint(1, ("new assoc from %x:%x:%x:%x overlaps with %p\n", SCTP_PRINTADDR(*hdraddr), (void *)sctp)); /* * While we still hold the lock, we need to * figure out which addresses have been * added so we can include them in the abort * we will send back. Since these faddrs will * never be used, we overload the rto field * here, setting it to 0 if the address was * not added, 1 if it was added. */ for (fp = fphead; fp; fp = fp->next) { if (sctp_lookup_faddr(sctp, &fp->faddr)) { fp->rto = 0; } else { fp->rto = 1; nadded++; } } mutex_exit(&tf->tf_lock); goto done; } } mutex_exit(&tf->tf_lock); /* All faddrs are disjoint; legit new association */ retval = 1; done: /* If are attempted adds, send back an abort listing the addrs */ if (nadded > 0) { void *dtail; size_t dlen; dtail = kmem_alloc(PARM_ADDR6_LEN * nadded, KM_NOSLEEP); if (dtail == NULL) { goto cleanup; } ph = dtail; dlen = 0; for (fp = fphead; fp; fp = fp->next) { if (fp->rto == 0) { continue; } if (IN6_IS_ADDR_V4MAPPED(&fp->faddr)) { ipaddr_t addr4; ph->sph_type = htons(PARM_ADDR4); ph->sph_len = htons(PARM_ADDR4_LEN); IN6_V4MAPPED_TO_IPADDR(&fp->faddr, addr4); ph++; bcopy(&addr4, ph, sizeof (addr4)); ph = (sctp_parm_hdr_t *) ((char *)ph + sizeof (addr4)); dlen += PARM_ADDR4_LEN; } else { ph->sph_type = htons(PARM_ADDR6); ph->sph_len = htons(PARM_ADDR6_LEN); ph++; bcopy(&fp->faddr, ph, sizeof (fp->faddr)); ph = (sctp_parm_hdr_t *) ((char *)ph + sizeof (fp->faddr)); dlen += PARM_ADDR6_LEN; } } /* Send off the abort */ sctp_send_abort(sctp, sctp_init2vtag(ich), SCTP_ERR_RESTART_NEW_ADDRS, dtail, dlen, pkt, 0, B_TRUE, ira); kmem_free(dtail, PARM_ADDR6_LEN * nadded); } cleanup: /* Clean up */ if (fphead) { sctp_faddr_t *fpn; for (fp = fphead; fp; fp = fpn) { fpn = fp->next; if (fp->ixa != NULL) { ixa_refrele(fp->ixa); fp->ixa = NULL; } kmem_cache_free(sctp_kmem_faddr_cache, fp); } } return (retval); }
/* * Sets the address parameters given in the INIT chunk into sctp's * faddrs; if psctp is non-NULL, copies psctp's saddrs. If there are * no address parameters in the INIT chunk, a single faddr is created * from the ip hdr at the beginning of pkt. * If there already are existing addresses hanging from sctp, merge * them in, if the old info contains addresses which are not present * in this new info, get rid of them, and clean the pointers if there's * messages which have this as their target address. * * We also re-adjust the source address list here since the list may * contain more than what is actually part of the association. If * we get here from sctp_send_cookie_echo(), we are on the active * side and psctp will be NULL and ich will be the INIT-ACK chunk. * If we get here from sctp_accept_comm(), ich will be the INIT chunk * and psctp will the listening endpoint. * * INIT processing: When processing the INIT we inherit the src address * list from the listener. For a loopback or linklocal association, we * delete the list and just take the address from the IP header (since * that's how we created the INIT-ACK). Additionally, for loopback we * ignore the address params in the INIT. For determining which address * types were sent in the INIT-ACK we follow the same logic as in * creating the INIT-ACK. We delete addresses of the type that are not * supported by the peer. * * INIT-ACK processing: When processing the INIT-ACK since we had not * included addr params for loopback or linklocal addresses when creating * the INIT, we just use the address from the IP header. Further, for * loopback we ignore the addr param list. We mark addresses of the * type not supported by the peer as unconfirmed. * * In case of INIT processing we look for supported address types in the * supported address param, if present. In both cases the address type in * the IP header is supported as well as types for addresses in the param * list, if any. * * Once we have the supported address types sctp_check_saddr() runs through * the source address list and deletes or marks as unconfirmed address of * types not supported by the peer. * * Returns 0 on success, sys errno on failure */ int sctp_get_addrparams(sctp_t *sctp, sctp_t *psctp, mblk_t *pkt, sctp_chunk_hdr_t *ich, uint_t *sctp_options) { sctp_init_chunk_t *init; ipha_t *iph; ip6_t *ip6h; in6_addr_t hdrsaddr[1]; in6_addr_t hdrdaddr[1]; sctp_parm_hdr_t *ph; ssize_t remaining; int isv4; int err; sctp_faddr_t *fp; int supp_af = 0; boolean_t check_saddr = B_TRUE; in6_addr_t curaddr; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; if (sctp_options != NULL) *sctp_options = 0; /* extract the address from the IP header */ isv4 = (IPH_HDR_VERSION(pkt->b_rptr) == IPV4_VERSION); if (isv4) { iph = (ipha_t *)pkt->b_rptr; IN6_IPADDR_TO_V4MAPPED(iph->ipha_src, hdrsaddr); IN6_IPADDR_TO_V4MAPPED(iph->ipha_dst, hdrdaddr); supp_af |= PARM_SUPP_V4; } else { ip6h = (ip6_t *)pkt->b_rptr; hdrsaddr[0] = ip6h->ip6_src; hdrdaddr[0] = ip6h->ip6_dst; supp_af |= PARM_SUPP_V6; } /* * Unfortunately, we can't delay this because adding an faddr * looks for the presence of the source address (from the ire * for the faddr) in the source address list. We could have * delayed this if, say, this was a loopback/linklocal connection. * Now, we just end up nuking this list and taking the addr from * the IP header for loopback/linklocal. */ if (psctp != NULL && psctp->sctp_nsaddrs > 0) { ASSERT(sctp->sctp_nsaddrs == 0); err = sctp_dup_saddrs(psctp, sctp, KM_NOSLEEP); if (err != 0) return (err); } /* * We will add the faddr before parsing the address list as this * might be a loopback connection and we would not have to * go through the list. * * Make sure the header's addr is in the list */ fp = sctp_lookup_faddr(sctp, hdrsaddr); if (fp == NULL) { /* not included; add it now */ err = sctp_add_faddr(sctp, hdrsaddr, KM_NOSLEEP, B_TRUE); if (err != 0) return (err); /* sctp_faddrs will be the hdr addr */ fp = sctp->sctp_faddrs; } /* make the header addr the primary */ if (cl_sctp_assoc_change != NULL && psctp == NULL) curaddr = sctp->sctp_current->faddr; sctp->sctp_primary = fp; sctp->sctp_current = fp; sctp->sctp_mss = fp->sfa_pmss; /* For loopback connections & linklocal get address from the header */ if (sctp->sctp_loopback || sctp->sctp_linklocal) { if (sctp->sctp_nsaddrs != 0) sctp_free_saddrs(sctp); if ((err = sctp_saddr_add_addr(sctp, hdrdaddr, 0)) != 0) return (err); /* For loopback ignore address list */ if (sctp->sctp_loopback) return (0); check_saddr = B_FALSE; } /* Walk the params in the INIT [ACK], pulling out addr params */ remaining = ntohs(ich->sch_len) - sizeof (*ich) - sizeof (sctp_init_chunk_t); if (remaining < sizeof (*ph)) { if (check_saddr) { sctp_check_saddr(sctp, supp_af, psctp == NULL ? B_FALSE : B_TRUE, hdrdaddr); } ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL); return (0); } init = (sctp_init_chunk_t *)(ich + 1); ph = (sctp_parm_hdr_t *)(init + 1); /* params will have already been byteordered when validating */ while (ph != NULL) { if (ph->sph_type == htons(PARM_SUPP_ADDRS)) { int plen; uint16_t *p; uint16_t addrtype; ASSERT(psctp != NULL); plen = ntohs(ph->sph_len); p = (uint16_t *)(ph + 1); while (plen > 0) { addrtype = ntohs(*p); switch (addrtype) { case PARM_ADDR6: supp_af |= PARM_SUPP_V6; break; case PARM_ADDR4: supp_af |= PARM_SUPP_V4; break; default: break; } p++; plen -= sizeof (*p); } } else if (ph->sph_type == htons(PARM_ADDR4)) { if (remaining >= PARM_ADDR4_LEN) { in6_addr_t addr; ipaddr_t ta; supp_af |= PARM_SUPP_V4; /* * Screen out broad/multicasts & loopback. * If the endpoint only accepts v6 address, * go to the next one. * * Subnet broadcast check is done in * sctp_add_faddr(). If the address is * a broadcast address, it won't be added. */ bcopy(ph + 1, &ta, sizeof (ta)); if (ta == 0 || ta == INADDR_BROADCAST || ta == htonl(INADDR_LOOPBACK) || CLASSD(ta) || connp->conn_ipv6_v6only) { goto next; } IN6_INADDR_TO_V4MAPPED((struct in_addr *) (ph + 1), &addr); /* Check for duplicate. */ if (sctp_lookup_faddr(sctp, &addr) != NULL) goto next; /* OK, add it to the faddr set */ err = sctp_add_faddr(sctp, &addr, KM_NOSLEEP, B_FALSE); /* Something is wrong... Try the next one. */ if (err != 0) goto next; } } else if (ph->sph_type == htons(PARM_ADDR6) && connp->conn_family == AF_INET6) { /* An v4 socket should not take v6 addresses. */ if (remaining >= PARM_ADDR6_LEN) { in6_addr_t *addr6; supp_af |= PARM_SUPP_V6; addr6 = (in6_addr_t *)(ph + 1); /* * Screen out link locals, mcast, loopback * and bogus v6 address. */ if (IN6_IS_ADDR_LINKLOCAL(addr6) || IN6_IS_ADDR_MULTICAST(addr6) || IN6_IS_ADDR_LOOPBACK(addr6) || IN6_IS_ADDR_V4MAPPED(addr6)) { goto next; } /* Check for duplicate. */ if (sctp_lookup_faddr(sctp, addr6) != NULL) goto next; err = sctp_add_faddr(sctp, (in6_addr_t *)(ph + 1), KM_NOSLEEP, B_FALSE); /* Something is wrong... Try the next one. */ if (err != 0) goto next; } } else if (ph->sph_type == htons(PARM_FORWARD_TSN)) { if (sctp_options != NULL) *sctp_options |= SCTP_PRSCTP_OPTION; } /* else; skip */ next: ph = sctp_next_parm(ph, &remaining); } if (check_saddr) { sctp_check_saddr(sctp, supp_af, psctp == NULL ? B_FALSE : B_TRUE, hdrdaddr); } ASSERT(sctp_saddr_lookup(sctp, hdrdaddr, 0) != NULL); /* * We have the right address list now, update clustering's * knowledge because when we sent the INIT we had just added * the address the INIT was sent to. */ if (psctp == NULL && cl_sctp_assoc_change != NULL) { uchar_t *alist; size_t asize; uchar_t *dlist; size_t dsize; asize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; alist = kmem_alloc(asize, KM_NOSLEEP); if (alist == NULL) { SCTP_KSTAT(sctps, sctp_cl_assoc_change); return (ENOMEM); } /* * Just include the address the INIT was sent to in the * delete list and send the entire faddr list. We could * do it differently (i.e include all the addresses in the * add list even if it contains the original address OR * remove the original address from the add list etc.), but * this seems reasonable enough. */ dsize = sizeof (in6_addr_t); dlist = kmem_alloc(dsize, KM_NOSLEEP); if (dlist == NULL) { kmem_free(alist, asize); SCTP_KSTAT(sctps, sctp_cl_assoc_change); return (ENOMEM); } bcopy(&curaddr, dlist, sizeof (curaddr)); sctp_get_faddr_list(sctp, alist, asize); (*cl_sctp_assoc_change)(connp->conn_family, alist, asize, sctp->sctp_nfaddrs, dlist, dsize, 1, SCTP_CL_PADDR, (cl_sctp_handle_t)sctp); /* alist and dlist will be freed by the clustering module */ } return (0); }
int sctp_del_ip(sctp_t *sctp, const void *addrs, uint32_t cnt, uchar_t *ulist, size_t usize) { struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; mblk_t *mp; int error = 0; int i; int addrcnt = 0; sctp_addip4_t *ad4; sctp_addip6_t *ad6; sctp_asconf_t asc[1]; sctp_saddr_ipif_t *nsp; uint16_t type = htons(PARM_DEL_IP); boolean_t v4mapped = B_FALSE; in6_addr_t addr; boolean_t asconf = B_TRUE; uint_t ifindex; sctp_cl_ainfo_t *ainfo = NULL; uchar_t *p = ulist; boolean_t check_lport = B_FALSE; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; /* Does the peer understand ASCONF and Add-IP? */ if (sctp->sctp_state <= SCTPS_LISTEN || !sctps->sctps_addip_enabled || !sctp->sctp_understands_asconf || !sctp->sctp_understands_addip) { asconf = B_FALSE; } if (sctp->sctp_state > SCTPS_BOUND) check_lport = B_TRUE; if (asconf) { /* * On a clustered node, we need to pass this list when * we get an ASCONF-ACK. We only pre-allocate memory for the * list, but fill in the addresses when it is processed * successfully after we get an ASCONF-ACK. */ if (cl_sctp_assoc_change != NULL) { ainfo = kmem_alloc(sizeof (*ainfo), KM_SLEEP); ainfo->sctp_cl_dsize = sizeof (in6_addr_t) * cnt; ainfo->sctp_cl_dlist = kmem_alloc(ainfo->sctp_cl_dsize, KM_SLEEP); } sctp_asconf_init(asc); } /* * Screen addresses: * If adding: * o Must not already be a part of the association * o Must be AF_INET or AF_INET6 * o XXX Must be valid source address for this node * o Must be unicast * o XXX Must fit scoping rules * If deleting: * o Must be part of the association */ for (i = 0; i < cnt; i++) { ifindex = 0; switch (connp->conn_family) { case AF_INET: sin4 = (struct sockaddr_in *)addrs + i; if (check_lport && sin4->sin_port != connp->conn_lport) { error = EINVAL; goto fail; } v4mapped = B_TRUE; IN6_IPADDR_TO_V4MAPPED(sin4->sin_addr.s_addr, &addr); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addrs + i; if (check_lport && sin6->sin6_port != connp->conn_lport) { error = EINVAL; goto fail; } addr = sin6->sin6_addr; ifindex = sin6->sin6_scope_id; break; } nsp = sctp_saddr_lookup(sctp, &addr, ifindex); if (nsp == NULL) { error = EADDRNOTAVAIL; goto fail; } /* Collect the list of addresses, if required */ if (usize >= sizeof (addr)) { bcopy(&addr, p, sizeof (addr)); p += sizeof (addr); usize -= sizeof (addr); } if (!asconf) continue; nsp->saddr_ipif_delete_pending = 1; nsp->saddr_ipif_dontsrc = 1; addrcnt++; if (v4mapped) { mp = allocb(sizeof (*ad4), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad4); ad4 = (sctp_addip4_t *)mp->b_rptr; ad4->sad4_addip_ph.sph_type = type; ad4->sad4_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR4_LEN + sizeof (ad4->asconf_req_cid)); ad4->sad4_addr4_ph.sph_type = htons(PARM_ADDR4); ad4->sad4_addr4_ph.sph_len = htons(PARM_ADDR4_LEN); ad4->sad4_addr = sin4->sin_addr.s_addr; } else { mp = allocb(sizeof (*ad6), BPRI_MED); if (mp == NULL) { error = ENOMEM; goto fail; } mp->b_wptr += sizeof (*ad6); ad6 = (sctp_addip6_t *)mp->b_rptr; ad6->sad6_addip_ph.sph_type = type; ad6->sad6_addip_ph.sph_len = htons(sizeof (sctp_parm_hdr_t) + PARM_ADDR6_LEN + sizeof (ad6->asconf_req_cid)); ad6->sad6_addr6_ph.sph_type = htons(PARM_ADDR6); ad6->sad6_addr6_ph.sph_len = htons(PARM_ADDR6_LEN); ad6->sad6_addr = addr; } error = sctp_asconf_add(asc, mp); if (error != 0) goto fail; } if (!asconf) { sctp_del_saddr_list(sctp, addrs, cnt, B_FALSE); return (0); } error = sctp_asconf_send(sctp, asc, sctp->sctp_current, ainfo); if (error != 0) goto fail; sctp_redo_faddr_srcs(sctp); return (0); fail: if (ainfo != NULL) { kmem_free(ainfo->sctp_cl_dlist, ainfo->sctp_cl_dsize); ainfo->sctp_cl_dsize = 0; kmem_free(ainfo, sizeof (*ainfo)); } if (!asconf) return (error); for (i = 0; i < addrcnt; i++) { ifindex = 0; switch (connp->conn_family) { case AF_INET: sin4 = (struct sockaddr_in *)addrs + i; IN6_INADDR_TO_V4MAPPED(&(sin4->sin_addr), &addr); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addrs + i; addr = sin6->sin6_addr; ifindex = sin6->sin6_scope_id; break; } nsp = sctp_saddr_lookup(sctp, &addr, ifindex); ASSERT(nsp != NULL); nsp->saddr_ipif_delete_pending = 0; nsp->saddr_ipif_dontsrc = 0; } sctp_asconf_destroy(asc); return (error); }
int rcmd_af(char **ahost, unsigned short rport, const char *locuser, const char *remuser, const char *cmd, int *fd2p, int af) { int s, timo = 1; ssize_t retval; pid_t pid; struct sockaddr_storage caddr, faddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; struct addrinfo hints; struct addrinfo *res, *resp; size_t addrlen; int rc; #define MAX_SHORTSTRLEN 6 char aport[MAX_SHORTSTRLEN]; char c; int lport = 0; #ifdef SYSV sigset_t oldmask; sigset_t newmask; struct sigaction oldaction; struct sigaction newaction; #else int oldmask; #endif /* SYSV */ fd_set fdset; int selret; char *addr; static char hostname[MAXHOSTNAMELEN]; socklen_t len; char abuf[INET6_ADDRSTRLEN]; if (!(af == AF_INET || af == AF_INET6 || af == AF_UNSPEC)) { errno = EAFNOSUPPORT; return (-1); } pid = getpid(); memset(&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if (af == AF_INET6) { hints.ai_flags |= AI_V4MAPPED; hints.ai_family = AF_UNSPEC; } else { hints.ai_family = af; } (void) snprintf(aport, MAX_SHORTSTRLEN, "%u", ntohs(rport)); rc = getaddrinfo(*ahost, aport, &hints, &res); if (rc != 0) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "%s: unknown host%s\n"), *ahost, rc == EAI_AGAIN ? " (try again later)" : ""); return (-1); } resp = res; (void) strlcpy(hostname, res->ai_canonname, MAXHOSTNAMELEN); *ahost = hostname; #ifdef SYSV /* ignore SIGPIPE */ bzero((char *)&newaction, sizeof (newaction)); newaction.sa_handler = SIG_IGN; (void) _sigaction(SIGPIPE, &newaction, &oldaction); /* block SIGURG */ bzero((char *)&newmask, sizeof (newmask)); (void) _sigaddset(&newmask, SIGURG); (void) _sigprocmask(SIG_BLOCK, &newmask, &oldmask); #else oldmask = _sigblock(sigmask(SIGURG)); #endif /* SYSV */ for (;;) { s = rresvport_af(&lport, res->ai_family); if (s < 0) { int af = res->ai_family; /* * See if we have any addresses of a different type * to try. */ while (res != NULL && res->ai_family == af) res = res->ai_next; if (res != NULL) continue; if (errno == EAGAIN) (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: All ports in use\n")); else perror("rcmd: socket"); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (-1); } bzero((char *)&caddr, sizeof (caddr)); bcopy(res->ai_addr, &caddr, res->ai_addrlen); addrlen = res->ai_addrlen; if (af == AF_INET6 && res->ai_addr->sa_family == AF_INET) { struct in6_addr ia6; struct sockaddr_in6 *in6addr; IN6_INADDR_TO_V4MAPPED(&((struct sockaddr_in *) res->ai_addr)->sin_addr, &ia6); in6addr = (struct sockaddr_in6 *)&caddr; in6addr->sin6_addr = ia6; in6addr->sin6_family = AF_INET6; addrlen = sizeof (struct sockaddr_in6); } (void) _fcntl(s, F_SETOWN, pid); if (connect(s, (struct sockaddr *)&caddr, addrlen) >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport = 0; continue; } if (errno == ECONNREFUSED && timo <= 16) { (void) sleep(timo); timo *= 2; continue; } if (res->ai_next != NULL) { int oerrno = errno; if (res->ai_addr->sa_family == AF_INET6) addr = (char *)&((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; else addr = (char *)&((struct sockaddr_in *) res->ai_addr)->sin_addr; (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "connect to address %s: "), inet_ntop(res->ai_addr->sa_family, addr, abuf, sizeof (abuf))); errno = oerrno; perror(0); res = res->ai_next; if (res->ai_addr->sa_family == AF_INET6) addr = (char *)&((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; else addr = (char *)&((struct sockaddr_in *) res->ai_addr)->sin_addr; (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Trying %s...\n"), inet_ntop(res->ai_addr->sa_family, addr, abuf, sizeof (abuf))); continue; } perror(*ahost); freeaddrinfo(resp); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ return (-1); } lport = 0; if (fd2p == 0) { (void) write(s, "", 1); } else { int s2 = rresvport_af(&lport, res->ai_family), s3; len = (socklen_t)sizeof (faddr); if (s2 < 0) goto bad; (void) listen(s2, 1); (void) snprintf(aport, MAX_SHORTSTRLEN, "%d", lport); if (write(s, aport, strlen(aport)+1) != strlen(aport)+1) { perror(_dgettext(TEXT_DOMAIN, "write: setting up stderr")); (void) close(s2); goto bad; } FD_ZERO(&fdset); FD_SET(s, &fdset); FD_SET(s2, &fdset); while ((selret = select(FD_SETSIZE, &fdset, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) > 0) { if (FD_ISSET(s, &fdset)) { /* * Something's wrong: we should get no * data on this connection at this point, * so we assume that the connection has * gone away. */ (void) close(s2); goto bad; } if (FD_ISSET(s2, &fdset)) { /* * We assume this is an incoming connect * request and proceed normally. */ s3 = accept(s2, (struct sockaddr *)&faddr, &len); FD_CLR(s2, &fdset); (void) close(s2); if (s3 < 0) { perror("accept"); lport = 0; goto bad; } else break; } } if (selret == -1) { /* * This should not happen, and we treat it as * a fatal error. */ (void) close(s2); goto bad; } *fd2p = s3; switch (faddr.ss_family) { case AF_INET: sin = (struct sockaddr_in *)&faddr; if (ntohs(sin->sin_port) >= IPPORT_RESERVED) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit " "setup.\n")); goto bad2; } break; case AF_INET6: sin6 = (struct sockaddr_in6 *)&faddr; if (ntohs(sin6->sin6_port) >= IPPORT_RESERVED) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit " "setup.\n")); goto bad2; } break; default: (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit setup.\n")); goto bad2; } } (void) write(s, locuser, strlen(locuser)+1); (void) write(s, remuser, strlen(remuser)+1); (void) write(s, cmd, strlen(cmd)+1); retval = read(s, &c, 1); if (retval != 1) { if (retval == 0) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Protocol error, %s closed connection\n"), *ahost); } else if (retval < 0) { perror(*ahost); } else { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Protocol error, %s sent %d bytes\n"), *ahost, retval); } goto bad2; } if (c != 0) { while (read(s, &c, 1) == 1) { (void) write(2, &c, 1); if (c == '\n') break; } goto bad2; } #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (-1); }
/* * Connect to a peer - this function inserts the sctp in the * bind and conn fanouts, sends the INIT, and replies to the client * with an OK ack. */ int sctp_connect(sctp_t *sctp, const struct sockaddr *dst, uint32_t addrlen, cred_t *cr, pid_t pid) { sin_t *sin; sin6_t *sin6; in6_addr_t dstaddr; in_port_t dstport; mblk_t *initmp; sctp_tf_t *tbf; sctp_t *lsctp; char buf[INET6_ADDRSTRLEN]; int sleep = sctp->sctp_cansleep ? KM_SLEEP : KM_NOSLEEP; int err; sctp_faddr_t *cur_fp; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; uint_t scope_id = 0; ip_xmit_attr_t *ixa; /* * Determine packet type based on type of address passed in * the request should contain an IPv4 or IPv6 address. * Make sure that address family matches the type of * family of the address passed down. */ if (addrlen < sizeof (sin_t)) { return (EINVAL); } switch (dst->sa_family) { case AF_INET: sin = (sin_t *)dst; /* Check for attempt to connect to non-unicast */ if (CLASSD(sin->sin_addr.s_addr) || (sin->sin_addr.s_addr == INADDR_BROADCAST)) { ip0dbg(("sctp_connect: non-unicast\n")); return (EINVAL); } if (connp->conn_ipv6_v6only) return (EAFNOSUPPORT); /* convert to v6 mapped */ /* Check for attempt to connect to INADDR_ANY */ if (sin->sin_addr.s_addr == INADDR_ANY) { struct in_addr v4_addr; /* * SunOS 4.x and 4.3 BSD allow an application * to connect a TCP socket to INADDR_ANY. * When they do this, the kernel picks the * address of one interface and uses it * instead. The kernel usually ends up * picking the address of the loopback * interface. This is an undocumented feature. * However, we provide the same thing here * in case any TCP apps that use this feature * are being ported to SCTP... */ v4_addr.s_addr = htonl(INADDR_LOOPBACK); IN6_INADDR_TO_V4MAPPED(&v4_addr, &dstaddr); } else { IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &dstaddr); } dstport = sin->sin_port; break; case AF_INET6: sin6 = (sin6_t *)dst; /* Check for attempt to connect to non-unicast. */ if ((addrlen < sizeof (sin6_t)) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { ip0dbg(("sctp_connect: non-unicast\n")); return (EINVAL); } if (connp->conn_ipv6_v6only && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { return (EAFNOSUPPORT); } /* check for attempt to connect to unspec */ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { dstaddr = ipv6_loopback; } else { dstaddr = sin6->sin6_addr; if (IN6_IS_ADDR_LINKLOCAL(&dstaddr)) { sctp->sctp_linklocal = 1; scope_id = sin6->sin6_scope_id; } } dstport = sin6->sin6_port; connp->conn_flowinfo = sin6->sin6_flowinfo; break; default: dprint(1, ("sctp_connect: unknown family %d\n", dst->sa_family)); return (EAFNOSUPPORT); } (void) inet_ntop(AF_INET6, &dstaddr, buf, sizeof (buf)); dprint(1, ("sctp_connect: attempting connect to %s...\n", buf)); RUN_SCTP(sctp); if (connp->conn_family != dst->sa_family || (connp->conn_state_flags & CONN_CLOSING)) { WAKE_SCTP(sctp); return (EINVAL); } /* We update our cred/cpid based on the caller of connect */ if (connp->conn_cred != cr) { crhold(cr); crfree(connp->conn_cred); connp->conn_cred = cr; } connp->conn_cpid = pid; /* Cache things in conn_ixa without any refhold */ ixa = connp->conn_ixa; ixa->ixa_cred = cr; ixa->ixa_cpid = pid; if (is_system_labeled()) { /* We need to restart with a label based on the cred */ ip_xmit_attr_restore_tsl(ixa, ixa->ixa_cred); } switch (sctp->sctp_state) { case SCTPS_IDLE: { struct sockaddr_storage ss; /* * We support a quick connect capability here, allowing * clients to transition directly from IDLE to COOKIE_WAIT. * sctp_bindi will pick an unused port, insert the connection * in the bind hash and transition to BOUND state. SCTP * picks and uses what it considers the optimal local address * set (just like specifiying INADDR_ANY to bind()). */ dprint(1, ("sctp_connect: idle, attempting bind...\n")); ASSERT(sctp->sctp_nsaddrs == 0); bzero(&ss, sizeof (ss)); ss.ss_family = connp->conn_family; WAKE_SCTP(sctp); if ((err = sctp_bind(sctp, (struct sockaddr *)&ss, sizeof (ss))) != 0) { return (err); } RUN_SCTP(sctp); /* FALLTHRU */ } case SCTPS_BOUND: ASSERT(sctp->sctp_nsaddrs > 0); /* do the connect */ /* XXX check for attempt to connect to self */ connp->conn_fport = dstport; ASSERT(sctp->sctp_iphc); ASSERT(sctp->sctp_iphc6); /* * Don't allow this connection to completely duplicate * an existing connection. * * Ensure that the duplicate check and insertion is atomic. */ sctp_conn_hash_remove(sctp); tbf = &sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, connp->conn_ports)]; mutex_enter(&tbf->tf_lock); lsctp = sctp_lookup(sctp, &dstaddr, tbf, &connp->conn_ports, SCTPS_COOKIE_WAIT); if (lsctp != NULL) { /* found a duplicate connection */ mutex_exit(&tbf->tf_lock); SCTP_REFRELE(lsctp); WAKE_SCTP(sctp); return (EADDRINUSE); } /* * OK; set up the peer addr (this may grow after we get * the INIT ACK from the peer with additional addresses). */ if ((err = sctp_add_faddr(sctp, &dstaddr, sleep, B_FALSE)) != 0) { mutex_exit(&tbf->tf_lock); WAKE_SCTP(sctp); return (err); } cur_fp = sctp->sctp_faddrs; ASSERT(cur_fp->ixa != NULL); /* No valid src addr, return. */ if (cur_fp->state == SCTP_FADDRS_UNREACH) { mutex_exit(&tbf->tf_lock); WAKE_SCTP(sctp); return (EADDRNOTAVAIL); } sctp->sctp_primary = cur_fp; sctp->sctp_current = cur_fp; sctp->sctp_mss = cur_fp->sfa_pmss; sctp_conn_hash_insert(tbf, sctp, 1); mutex_exit(&tbf->tf_lock); ixa = cur_fp->ixa; ASSERT(ixa->ixa_cred != NULL); if (scope_id != 0) { ixa->ixa_flags |= IXAF_SCOPEID_SET; ixa->ixa_scopeid = scope_id; } else { ixa->ixa_flags &= ~IXAF_SCOPEID_SET; } /* initialize composite headers */ if ((err = sctp_set_hdraddrs(sctp)) != 0) { sctp_conn_hash_remove(sctp); WAKE_SCTP(sctp); return (err); } if ((err = sctp_build_hdrs(sctp, KM_SLEEP)) != 0) { sctp_conn_hash_remove(sctp); WAKE_SCTP(sctp); return (err); } /* * Turn off the don't fragment bit on the (only) faddr, * so that if one of the messages exchanged during the * initialization sequence exceeds the path mtu, it * at least has a chance to get there. SCTP does no * fragmentation of initialization messages. The DF bit * will be turned on again in sctp_send_cookie_echo() * (but the cookie echo will still be sent with the df bit * off). */ cur_fp->df = B_FALSE; /* Mark this address as alive */ cur_fp->state = SCTP_FADDRS_ALIVE; /* Send the INIT to the peer */ SCTP_FADDR_TIMER_RESTART(sctp, cur_fp, cur_fp->rto); sctp->sctp_state = SCTPS_COOKIE_WAIT; /* * sctp_init_mp() could result in modifying the source * address list, so take the hash lock. */ mutex_enter(&tbf->tf_lock); initmp = sctp_init_mp(sctp, cur_fp); if (initmp == NULL) { mutex_exit(&tbf->tf_lock); /* * It may happen that all the source addresses * (loopback/link local) are removed. In that case, * faile the connect. */ if (sctp->sctp_nsaddrs == 0) { sctp_conn_hash_remove(sctp); SCTP_FADDR_TIMER_STOP(cur_fp); WAKE_SCTP(sctp); return (EADDRNOTAVAIL); } /* Otherwise, let the retransmission timer retry */ WAKE_SCTP(sctp); goto notify_ulp; } mutex_exit(&tbf->tf_lock); /* * On a clustered note send this notification to the clustering * subsystem. */ if (cl_sctp_connect != NULL) { uchar_t *slist; uchar_t *flist; size_t ssize; size_t fsize; fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs; slist = kmem_alloc(ssize, KM_SLEEP); flist = kmem_alloc(fsize, KM_SLEEP); /* The clustering module frees the lists */ sctp_get_saddr_list(sctp, slist, ssize); sctp_get_faddr_list(sctp, flist, fsize); (*cl_sctp_connect)(connp->conn_family, slist, sctp->sctp_nsaddrs, connp->conn_lport, flist, sctp->sctp_nfaddrs, connp->conn_fport, B_TRUE, (cl_sctp_handle_t)sctp); } ASSERT(ixa->ixa_cred != NULL); ASSERT(ixa->ixa_ire != NULL); (void) conn_ip_output(initmp, ixa); BUMP_LOCAL(sctp->sctp_opkts); WAKE_SCTP(sctp); notify_ulp: sctp_set_ulp_prop(sctp); return (0); default: ip0dbg(("sctp_connect: invalid state. %d\n", sctp->sctp_state)); WAKE_SCTP(sctp); return (EINVAL); } }
/* * This is the IPv6 interface for "gethostbyaddr". */ struct hostent * getipnodebyaddr(const void *src, size_t len, int type, int *error_num) { struct in6_addr *addr6 = 0; struct in_addr *addr4 = 0; nss_XbyY_buf_t *buf = 0; nss_XbyY_buf_t *res = 0; struct netconfig *nconf; struct hostent *hp = 0; struct nss_netdirbyaddr_in nssin; union nss_netdirbyaddr_out nssout; int neterr; char tmpbuf[64]; if (type == AF_INET6) { if ((addr6 = (struct in6_addr *)src) == NULL) { *error_num = HOST_NOT_FOUND; return (NULL); } } else if (type == AF_INET) { if ((addr4 = (struct in_addr *)src) == NULL) { *error_num = HOST_NOT_FOUND; return (NULL); } } else { *error_num = HOST_NOT_FOUND; return (NULL); } /* * Specific case: query for "::" */ if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) { *error_num = HOST_NOT_FOUND; return (NULL); } /* * Step 1: IPv4-mapped address or IPv4 Compat */ if ((type == AF_INET6 && len == 16) && ((IN6_IS_ADDR_V4MAPPED(addr6)) || (IN6_IS_ADDR_V4COMPAT(addr6)))) { if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { *error_num = NO_RECOVERY; return (NULL); } if ((nconf = __rpc_getconfip("udp")) == NULL && (nconf = __rpc_getconfip("tcp")) == NULL) { *error_num = NO_RECOVERY; __IPv6_cleanup(buf); return (NULL); } nssin.op_t = NSS_HOST6; if (IN6_IS_ADDR_V4COMPAT(addr6)) { (void) memcpy(tmpbuf, addr6, sizeof (*addr6)); tmpbuf[10] = 0xffU; tmpbuf[11] = 0xffU; nssin.arg.nss.host.addr = (const char *)tmpbuf; } else { nssin.arg.nss.host.addr = (const char *)addr6; } nssin.arg.nss.host.len = sizeof (struct in6_addr); nssin.arg.nss.host.type = AF_INET6; nssin.arg.nss.host.buf = buf->buffer; nssin.arg.nss.host.buflen = buf->buflen; nssout.nss.host.hent = buf->result; nssout.nss.host.herrno_p = error_num; /* * We pass in nconf and let the implementation of the * long-named func decide whether to use the switch based on * nc_nlookups. */ neterr = _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); (void) freenetconfigent(nconf); if (neterr != ND_OK) { /* Failover case, try hosts db for v4 address */ if (!gethostbyaddr_r(((char *)addr6) + 12, sizeof (in_addr_t), AF_INET, buf->result, buf->buffer, buf->buflen, error_num)) { __IPv6_cleanup(buf); return (NULL); } /* Found one, now format it into mapped/compat addr */ if ((res = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { __IPv6_cleanup(buf); *error_num = NO_RECOVERY; return (NULL); } /* Convert IPv4 to mapped/compat address w/name */ hp = res->result; (void) __mapv4tov6(buf->result, 0, res, IN6_IS_ADDR_V4MAPPED(addr6)); __IPv6_cleanup(buf); free(res); return (hp); } /* * At this point, we'll have a v4mapped hostent. If that's * what was passed in, just return. If the request was a compat, * twiggle the two bytes to make the mapped address a compat. */ hp = buf->result; if (IN6_IS_ADDR_V4COMPAT(addr6)) { /* LINTED pointer cast */ addr6 = (struct in6_addr *)hp->h_addr_list[0]; addr6->s6_addr[10] = 0; addr6->s6_addr[11] = 0; } free(buf); return (hp); } /* * Step 2: AF_INET, v4 lookup. Since we're going to search the * ipnodes (v6) path first, we need to treat this as a v4mapped * address. nscd(1m) caches v4 from ipnodes as mapped v6's. The * switch backend knows to lookup v4's (not v4mapped) from the * name services. */ if (type == AF_INET) { struct in6_addr v4mapbuf; addr6 = &v4mapbuf; IN6_INADDR_TO_V4MAPPED(addr4, addr6); if ((nconf = __rpc_getconfip("udp")) == NULL && (nconf = __rpc_getconfip("tcp")) == NULL) { *error_num = NO_RECOVERY; return (NULL); } if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { *error_num = NO_RECOVERY; freenetconfigent(nconf); return (NULL); } nssin.op_t = NSS_HOST6; nssin.arg.nss.host.addr = (const char *)addr6; nssin.arg.nss.host.len = sizeof (struct in6_addr); nssin.arg.nss.host.type = AF_INET6; nssin.arg.nss.host.buf = buf->buffer; nssin.arg.nss.host.buflen = buf->buflen; nssout.nss.host.hent = buf->result; nssout.nss.host.herrno_p = error_num; /* * We pass in nconf and let the implementation of the * long-named func decide whether to use the switch based on * nc_nlookups. */ neterr = _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); (void) freenetconfigent(nconf); if (neterr != ND_OK) { /* Failover case, try hosts db for v4 address */ hp = buf->result; if (!gethostbyaddr_r(src, len, type, buf->result, buf->buffer, buf->buflen, error_num)) { __IPv6_cleanup(buf); return (NULL); } free(buf); return (hp); } if ((hp = __mappedtov4(buf->result, error_num)) == NULL) { __IPv6_cleanup(buf); return (NULL); } __IPv6_cleanup(buf); return (hp); } /* * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call. */ if (type == AF_INET6) { if ((nconf = __rpc_getconfip("udp")) == NULL && (nconf = __rpc_getconfip("tcp")) == NULL) { *error_num = NO_RECOVERY; return (NULL); } if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { *error_num = NO_RECOVERY; freenetconfigent(nconf); return (NULL); } nssin.op_t = NSS_HOST6; nssin.arg.nss.host.addr = (const char *)addr6; nssin.arg.nss.host.len = len; nssin.arg.nss.host.type = type; nssin.arg.nss.host.buf = buf->buffer; nssin.arg.nss.host.buflen = buf->buflen; nssout.nss.host.hent = buf->result; nssout.nss.host.herrno_p = error_num; /* * We pass in nconf and let the implementation of the * long-named func decide whether to use the switch based on * nc_nlookups. */ neterr = _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); (void) freenetconfigent(nconf); if (neterr != ND_OK) { __IPv6_cleanup(buf); return (NULL); } free(buf); return (nssout.nss.host.hent); } /* * If we got here, unknown type. */ *error_num = HOST_NOT_FOUND; return (NULL); }
static NSS_STATUS parse_response(int af, nss_XbyY_args_t* argp, struct winbindd_response *response) { struct hostent *he = (struct hostent *)argp->buf.result; char *buffer = argp->buf.buffer; int buflen = argp->buf.buflen; NSS_STATUS ret; char *p, *data; int addrcount = 0; int len = 0; struct in_addr *addrp; #if defined(AF_INET6) struct in6_addr *addrp6; #endif int i; /* response is tab separated list of ip addresses with hostname and newline at the end. so at first we will strip newline then construct list of addresses for hostent. */ p = strchr(response->data.winsresp, '\n'); if(p) *p = '\0'; else {/* it must be broken */ argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } for(; p != response->data.winsresp; p--) { if(*p == '\t') addrcount++; } if(addrcount == 0) {/* it must be broken */ argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } /* allocate space for addresses and h_addr_list */ he->h_addrtype = af; if( he->h_addrtype == AF_INET) { he->h_length = sizeof(struct in_addr); addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen, sizeof(struct in_addr)); addrp -= addrcount; he->h_addr_list = (char **)ROUND_DOWN(addrp, sizeof (char*)); he->h_addr_list -= addrcount+1; } #if defined(AF_INET6) else { he->h_length = sizeof(struct in6_addr); addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen, sizeof(struct in6_addr)); addrp6 -= addrcount; he->h_addr_list = (char **)ROUND_DOWN(addrp6, sizeof (char*)); he->h_addr_list -= addrcount+1; } #endif /* buffer too small?! */ if((char *)he->h_addr_list < buffer ) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } data = response->data.winsresp; for( i = 0; i < addrcount; i++) { p = strchr(data, '\t'); if(p == NULL) break; /* just in case... */ *p = '\0'; /* terminate the string */ if(he->h_addrtype == AF_INET) { he->h_addr_list[i] = (char *)&addrp[i]; if ((addrp[i].s_addr = inet_addr(data)) == -1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } } #if defined(AF_INET6) else { he->h_addr_list[i] = (char *)&addrp6[i]; if (strchr(data, ':') != 0) { if (inet_pton(AF_INET6, data, &addrp6[i]) != 1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } } else { struct in_addr in4; if ((in4.s_addr = inet_addr(data)) == -1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]); } } #endif data = p+1; } he->h_addr_list[i] = (char *)NULL; len = strlen(data); if(len > he->h_addr_list - (char**)argp->buf.buffer) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } /* this is a bit overkill to use _nss_netdb_aliases here since there seems to be no aliases but it will create all data for us */ he->h_aliases = _nss_netdb_aliases(data, len, buffer, ((char*) he->h_addr_list) - buffer); if(he->h_aliases == NULL) { argp->erange = 1; ret = NSS_STR_PARSE_ERANGE; } else { he->h_name = he->h_aliases[0]; he->h_aliases++; ret = NSS_STR_PARSE_SUCCESS; } argp->returnval = (void*)he; return ret; }
dladm_status_t do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd) { dladm_status_t status; int prefix_max, prefix_len = 0; char *prefix_str, *endp = NULL; flow_mask_t mask; in6_addr_t *addr; uchar_t *netmask; struct in_addr v4addr; struct in6_addr v6addr; int family; if ((prefix_str = strchr(addr_str, '/')) != NULL) { *prefix_str++ = '\0'; errno = 0; prefix_len = (int)strtol(prefix_str, &endp, 10); if (errno != 0 || prefix_len == 0 || *endp != '\0') return (DLADM_STATUS_INVALID_PREFIXLEN); } if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) { family = AF_INET; } else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) { family = AF_INET6; } else { return (DLADM_STATUS_INVALID_IP); } mask = FLOW_IP_VERSION; if (local) { mask |= FLOW_IP_LOCAL; addr = &fd->fd_local_addr; netmask = (uchar_t *)&fd->fd_local_netmask; } else { mask |= FLOW_IP_REMOTE; addr = &fd->fd_remote_addr; netmask = (uchar_t *)&fd->fd_remote_netmask; } if (family == AF_INET) { IN6_INADDR_TO_V4MAPPED(&v4addr, addr); prefix_max = IP_ABITS; fd->fd_ipversion = IPV4_VERSION; netmask = (uchar_t *) &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask)))); } else { *addr = v6addr; prefix_max = IPV6_ABITS; fd->fd_ipversion = IPV6_VERSION; } if (prefix_len == 0) prefix_len = prefix_max; status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask); if (status != DLADM_STATUS_OK) { return (DLADM_STATUS_INVALID_PREFIXLEN); } fd->fd_mask |= mask; return (DLADM_STATUS_OK); }