2N/A/*
2N/A * Copyright (c) 2000, Boris Popov
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by Boris Popov.
2N/A * 4. Neither the name of the author nor the names of any co-contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A *
2N/A * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/param.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/time.h>
2N/A#include <ctype.h>
2N/A#include <netdb.h>
2N/A#include <errno.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <libintl.h>
2N/A
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <tsol/label.h>
2N/A
2N/A#define NB_NEEDRESOLVER
2N/A#include <netsmb/netbios.h>
2N/A#include "smbfs_lib.h"
2N/A#include "smbfs_nb_lib.h"
2N/A#include <netsmb/mchain.h>
2N/A
2N/A#include "smbfs_charsets.h"
2N/A#include "smbfs_private.h"
2N/A
2N/A/*
2N/A * nbns request
2N/A */
2N/Astruct nbns_rq {
2N/A int nr_opcode;
2N/A int nr_nmflags;
2N/A int nr_rcode;
2N/A int nr_qdcount;
2N/A int nr_ancount;
2N/A int nr_nscount;
2N/A int nr_arcount;
2N/A struct nb_name *nr_qdname;
2N/A uint16_t nr_qdtype;
2N/A uint16_t nr_qdclass;
2N/A struct in_addr nr_dest; /* receiver of query */
2N/A struct sockaddr_in nr_sender; /* sender of response */
2N/A int nr_rpnmflags;
2N/A int nr_rprcode;
2N/A uint16_t nr_rpancount;
2N/A uint16_t nr_rpnscount;
2N/A uint16_t nr_rparcount;
2N/A uint16_t nr_trnid;
2N/A struct nb_ctx *nr_nbd;
2N/A struct mbdata nr_rq;
2N/A struct mbdata nr_rp;
2N/A struct nb_ifdesc *nr_if;
2N/A int nr_flags;
2N/A int nr_fd;
2N/A int nr_maxretry;
2N/A};
2N/Atypedef struct nbns_rq nbns_rq_t;
2N/A
2N/Astatic int smbfs_nbns_rq_create(int, struct nb_ctx *, struct nbns_rq **);
2N/Astatic void smbfs_nbns_rq_done(struct nbns_rq *);
2N/Astatic int smbfs_nbns_rq_getrr(struct nbns_rq *, struct nbns_rr *);
2N/Astatic int smbfs_nbns_rq_prepare(struct nbns_rq *);
2N/Astatic int smbfs_nbns_rq(struct nbns_rq *);
2N/A
2N/A/*
2N/A * Call NetBIOS name lookup and return a result in the
2N/A * same form as getaddrinfo(3) returns. Return code is
2N/A * zero or one of the EAI_xxx codes like getaddrinfo.
2N/A */
2N/Aint
2N/Asmbfs_nbns_getaddrinfo(const char *name, struct nb_ctx *nbc,
2N/A struct addrinfo **res)
2N/A{
2N/A struct addrinfo *nai = NULL;
2N/A struct sockaddr *sap = NULL;
2N/A char *ucname = NULL;
2N/A int err;
2N/A
2N/A /*
2N/A * Try NetBIOS name lookup.
2N/A */
2N/A if (strlen(name) >= NB_NAMELEN) {
2N/A err = EAI_OVERFLOW;
2N/A goto out;
2N/A }
2N/A ucname = smbfs_utf8_str_toupper(name);
2N/A if (ucname == NULL)
2N/A goto nomem;
2N/A
2N/A /* Note: this returns an NBERROR value. */
2N/A err = smbfs_nbns_resolvename(ucname, nbc, &sap);
2N/A if (err) {
2N/A if (smbfs_debug)
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns_resolvename: %s"),
2N/A err, name);
2N/A err = EAI_NODATA;
2N/A goto out;
2N/A }
2N/A /* Note: sap allocated */
2N/A
2N/A /*
2N/A * Build the addrinfo struct to return.
2N/A */
2N/A nai = malloc(sizeof (*nai));
2N/A if (nai == NULL)
2N/A goto nomem;
2N/A bzero(nai, sizeof (*nai));
2N/A
2N/A nai->ai_flags = AI_CANONNAME;
2N/A nai->ai_family = sap->sa_family;
2N/A nai->ai_socktype = SOCK_STREAM;
2N/A nai->ai_canonname = ucname;
2N/A ucname = NULL;
2N/A
2N/A /*
2N/A * The type of this is really sockaddr_in,
2N/A * but is returned in the generic form.
2N/A * See nbns_resolvename.
2N/A */
2N/A nai->ai_addrlen = sizeof (struct sockaddr_in);
2N/A nai->ai_addr = sap;
2N/A
2N/A *res = nai;
2N/A return (0);
2N/A
2N/Anomem:
2N/A err = EAI_MEMORY;
2N/Aout:
2N/A if (nai != NULL)
2N/A free(nai);
2N/A if (sap)
2N/A free(sap);
2N/A if (ucname)
2N/A free(ucname);
2N/A *res = NULL;
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_nbns_resolvename(const char *name, struct nb_ctx *ctx,
2N/A struct sockaddr **adpp)
2N/A{
2N/A struct nbns_rq *rqp;
2N/A struct nb_name nn;
2N/A struct nbns_rr rr;
2N/A struct sockaddr_in *dest;
2N/A int error, rdrcount, len;
2N/A
2N/A if (strlen(name) >= NB_NAMELEN)
2N/A return (NBERROR(NBERR_NAMETOOLONG));
2N/A error = smbfs_nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
2N/A if (error)
2N/A return (error);
2N/A /*
2N/A * Pad the name with blanks, but
2N/A * leave the "type" byte NULL.
2N/A * smbfs_nb_name_encode adds the type.
2N/A */
2N/A bzero(&nn, sizeof (nn));
2N/A (void) snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
2N/A nn.nn_type = NBT_SERVER;
2N/A nn.nn_scope = ctx->nb_scope;
2N/A rqp->nr_nmflags = NBNS_NMFLAG_RD;
2N/A rqp->nr_qdname = &nn;
2N/A rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
2N/A rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2N/A rqp->nr_qdcount = 1;
2N/A rqp->nr_maxretry = 5;
2N/A
2N/A error = smbfs_nbns_rq_prepare(rqp);
2N/A if (error) {
2N/A smbfs_nbns_rq_done(rqp);
2N/A return (error);
2N/A }
2N/A rdrcount = NBNS_MAXREDIRECTS;
2N/A for (;;) {
2N/A error = smbfs_nbns_rq(rqp);
2N/A if (error)
2N/A break;
2N/A if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
2N/A /*
2N/A * Not an authoritative answer. Query again
2N/A * using the NS address in the 2nd record.
2N/A */
2N/A if (rdrcount-- == 0) {
2N/A error = NBERROR(NBERR_TOOMANYREDIRECTS);
2N/A break;
2N/A }
2N/A error = smbfs_nbns_rq_getrr(rqp, &rr);
2N/A if (error)
2N/A break;
2N/A error = smbfs_nbns_rq_getrr(rqp, &rr);
2N/A if (error)
2N/A break;
2N/A bcopy(rr.rr_data, &rqp->nr_dest, 4);
2N/A continue;
2N/A }
2N/A if (rqp->nr_rpancount == 0) {
2N/A error = NBERROR(NBERR_HOSTNOTFOUND);
2N/A break;
2N/A }
2N/A error = smbfs_nbns_rq_getrr(rqp, &rr);
2N/A if (error)
2N/A break;
2N/A len = sizeof (struct sockaddr_in);
2N/A dest = malloc(len);
2N/A if (dest == NULL) {
2N/A error = ENOMEM;
2N/A break;
2N/A }
2N/A
2N/A bzero(dest, len);
2N/A /*
2N/A * Solaris sockaddr_in doesn't a sin_len field.
2N/A * dest->sin_len = len;
2N/A */
2N/A dest->sin_family = AF_NETBIOS; /* nb_lib.h */
2N/A bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
2N/A *adpp = (struct sockaddr *)dest;
2N/A ctx->nb_lastns = rqp->nr_sender;
2N/A break;
2N/A }
2N/A smbfs_nbns_rq_done(rqp);
2N/A return (error);
2N/A}
2N/A
2N/A/*
2N/A * NB: system, workgroup are both NB_NAMELEN
2N/A */
2N/Aint
2N/Asmbfs_nbns_getnodestatus(struct nb_ctx *ctx,
2N/A struct in_addr *targethost, char *system, char *workgroup)
2N/A{
2N/A struct nbns_rq *rqp;
2N/A struct nbns_rr rr;
2N/A struct nb_name nn;
2N/A struct nbns_nr *nrp;
2N/A char nrtype;
2N/A char *cp, *retname = NULL;
2N/A unsigned char nrcount;
2N/A int error, i, foundserver = 0, foundgroup = 0;
2N/A
2N/A error = smbfs_nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
2N/A if (error)
2N/A return (error);
2N/A bzero(&nn, sizeof (nn));
2N/A (void) strcpy((char *)nn.nn_name, "*");
2N/A nn.nn_scope = ctx->nb_scope;
2N/A nn.nn_type = NBT_WKSTA;
2N/A rqp->nr_nmflags = 0;
2N/A rqp->nr_qdname = &nn;
2N/A rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
2N/A rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2N/A rqp->nr_qdcount = 1;
2N/A rqp->nr_maxretry = 2;
2N/A
2N/A rqp->nr_dest = *targethost;
2N/A error = smbfs_nbns_rq_prepare(rqp);
2N/A if (error) {
2N/A smbfs_nbns_rq_done(rqp);
2N/A return (error);
2N/A }
2N/A
2N/A /*
2N/A * Darwin had a loop here, allowing redirect, etc.
2N/A * but we only handle point-to-point for node status.
2N/A */
2N/A error = smbfs_nbns_rq(rqp);
2N/A if (error)
2N/A goto out;
2N/A if (rqp->nr_rpancount == 0) {
2N/A error = NBERROR(NBERR_HOSTNOTFOUND);
2N/A goto out;
2N/A }
2N/A error = smbfs_nbns_rq_getrr(rqp, &rr);
2N/A if (error)
2N/A goto out;
2N/A
2N/A /* Compiler didn't like cast on lvalue++ */
2N/A nrcount = *((unsigned char *)rr.rr_data);
2N/A rr.rr_data++;
2N/A /* LINTED */
2N/A for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
2N/A i <= nrcount; ++i, ++nrp) {
2N/A nrtype = nrp->ns_name[NB_NAMELEN-1];
2N/A /* Terminate the string: */
2N/A nrp->ns_name[NB_NAMELEN-1] = (char)0;
2N/A /* Strip off trailing spaces */
2N/A for (cp = &nrp->ns_name[NB_NAMELEN-2];
2N/A cp >= nrp->ns_name; --cp) {
2N/A if (*cp != (char)0x20)
2N/A break;
2N/A *cp = (char)0;
2N/A }
2N/A nrp->ns_flags = ntohs(nrp->ns_flags);
2N/A DPRINT(" %s[%02x] Flags 0x%x",
2N/A nrp->ns_name, nrtype, nrp->ns_flags);
2N/A if (nrp->ns_flags & NBNS_GROUPFLG) {
2N/A if (!foundgroup ||
2N/A (foundgroup != NBT_WKSTA+1 &&
2N/A nrtype == NBT_WKSTA)) {
2N/A (void) strlcpy(workgroup, nrp->ns_name,
2N/A NB_NAMELEN);
2N/A foundgroup = nrtype+1;
2N/A }
2N/A } else {
2N/A /*
2N/A * Track at least ONE name, in case
2N/A * no server name is found
2N/A */
2N/A retname = nrp->ns_name;
2N/A }
2N/A /*
2N/A * Keep the first NBT_SERVER name.
2N/A */
2N/A if (nrtype == NBT_SERVER && foundserver == 0) {
2N/A (void) strlcpy(system, nrp->ns_name,
2N/A NB_NAMELEN);
2N/A foundserver = 1;
2N/A }
2N/A }
2N/A if (foundserver == 0 && retname != NULL)
2N/A (void) strlcpy(system, retname, NB_NAMELEN);
2N/A ctx->nb_lastns = rqp->nr_sender;
2N/A
2N/Aout:
2N/A smbfs_nbns_rq_done(rqp);
2N/A return (error);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
2N/A{
2N/A struct nbns_rq *rqp;
2N/A static uint16_t trnid;
2N/A int error;
2N/A
2N/A if (trnid == 0)
2N/A trnid = getpid();
2N/A rqp = malloc(sizeof (*rqp));
2N/A if (rqp == NULL)
2N/A return (ENOMEM);
2N/A bzero(rqp, sizeof (*rqp));
2N/A error = smbfs_mb_init_sz(&rqp->nr_rq, NBDG_MAXSIZE);
2N/A if (error) {
2N/A free(rqp);
2N/A return (error);
2N/A }
2N/A rqp->nr_opcode = opcode;
2N/A rqp->nr_nbd = ctx;
2N/A rqp->nr_trnid = trnid++;
2N/A *rqpp = rqp;
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Asmbfs_nbns_rq_done(struct nbns_rq *rqp)
2N/A{
2N/A if (rqp == NULL)
2N/A return;
2N/A if (rqp->nr_fd >= 0)
2N/A (void) close(rqp->nr_fd);
2N/A smbfs_mb_done(&rqp->nr_rq);
2N/A smbfs_mb_done(&rqp->nr_rp);
2N/A if (rqp->nr_if)
2N/A free(rqp->nr_if);
2N/A free(rqp);
2N/A}
2N/A
2N/A/*
2N/A * Extract resource record from the packet. Assume that there is only
2N/A * one mbuf.
2N/A */
2N/Astatic int
2N/Asmbfs_nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
2N/A{
2N/A struct mbdata *mbp = &rqp->nr_rp;
2N/A uchar_t *cp;
2N/A int error, len;
2N/A
2N/A bzero(rrp, sizeof (*rrp));
2N/A cp = (uchar_t *)mbp->mb_pos;
2N/A len = smbfs_nb_name_enc_len(cp);
2N/A if (len < 1)
2N/A return (NBERROR(NBERR_INVALIDRESPONSE));
2N/A rrp->rr_name = cp;
2N/A error = smbfs_md_get_mem(mbp, NULL, len, MB_MSYSTEM);
2N/A if (error)
2N/A return (error);
2N/A smbfs_md_get_uint16be(mbp, &rrp->rr_type);
2N/A smbfs_md_get_uint16be(mbp, &rrp->rr_class);
2N/A smbfs_md_get_uint32be(mbp, &rrp->rr_ttl);
2N/A smbfs_md_get_uint16be(mbp, &rrp->rr_rdlength);
2N/A rrp->rr_data = (uchar_t *)mbp->mb_pos;
2N/A error = smbfs_md_get_mem(mbp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
2N/A return (error);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq_prepare(struct nbns_rq *rqp)
2N/A{
2N/A struct nb_ctx *ctx = rqp->nr_nbd;
2N/A struct mbdata *mbp = &rqp->nr_rq;
2N/A uint16_t ofr; /* opcode, flags, rcode */
2N/A int error;
2N/A
2N/A error = smbfs_mb_init_sz(&rqp->nr_rp, NBDG_MAXSIZE);
2N/A if (error)
2N/A return (error);
2N/A
2N/A /*
2N/A * When looked into the ethereal trace, 'nmblookup' command sets this
2N/A * flag. We will also set.
2N/A */
2N/A smbfs_mb_put_uint16be(mbp, rqp->nr_trnid);
2N/A ofr = ((rqp->nr_opcode & 0x1F) << 11) |
2N/A ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
2N/A smbfs_mb_put_uint16be(mbp, ofr);
2N/A smbfs_mb_put_uint16be(mbp, rqp->nr_qdcount);
2N/A smbfs_mb_put_uint16be(mbp, rqp->nr_ancount);
2N/A smbfs_mb_put_uint16be(mbp, rqp->nr_nscount);
2N/A error = smbfs_mb_put_uint16be(mbp, rqp->nr_arcount);
2N/A if (rqp->nr_qdcount) {
2N/A if (rqp->nr_qdcount > 1)
2N/A return (EINVAL);
2N/A (void) smbfs_nb_name_encode(mbp, rqp->nr_qdname);
2N/A smbfs_mb_put_uint16be(mbp, rqp->nr_qdtype);
2N/A error = smbfs_mb_put_uint16be(mbp, rqp->nr_qdclass);
2N/A }
2N/A if (error)
2N/A return (error);
2N/A error = smbfs_m_lineup(mbp->mb_top, &mbp->mb_top);
2N/A if (error)
2N/A return (error);
2N/A if (ctx->nb_timo == 0)
2N/A ctx->nb_timo = 1; /* by default 1 second */
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq_recv(struct nbns_rq *rqp)
2N/A{
2N/A struct mbdata *mbp = &rqp->nr_rp;
2N/A void *rpdata = mtod(mbp->mb_top, void *);
2N/A fd_set rd, wr, ex;
2N/A struct timeval tv;
2N/A struct sockaddr_in sender;
2N/A int s = rqp->nr_fd;
2N/A int n;
2N/A socklen_t len;
2N/A
2N/A FD_ZERO(&rd);
2N/A FD_ZERO(&wr);
2N/A FD_ZERO(&ex);
2N/A FD_SET(s, &rd);
2N/A
2N/A tv.tv_sec = rqp->nr_nbd->nb_timo;
2N/A tv.tv_usec = 0;
2N/A
2N/A n = select(s + 1, &rd, &wr, &ex, &tv);
2N/A if (n == -1)
2N/A return (-1);
2N/A if (n == 0)
2N/A return (ETIMEDOUT);
2N/A if (FD_ISSET(s, &rd) == 0)
2N/A return (ETIMEDOUT);
2N/A len = sizeof (sender);
2N/A n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
2N/A (struct sockaddr *)&sender, &len);
2N/A if (n < 0)
2N/A return (errno);
2N/A mbp->mb_top->m_len = mbp->mb_count = n;
2N/A rqp->nr_sender = sender;
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq_opensocket(struct nbns_rq *rqp)
2N/A{
2N/A struct sockaddr_in locaddr;
2N/A int opt = 1, s;
2N/A struct nb_ctx *ctx = rqp->nr_nbd;
2N/A
2N/A s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
2N/A if (s < 0)
2N/A return (errno);
2N/A if (ctx->nb_flags & NBCF_BC_ENABLE) {
2N/A if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
2N/A sizeof (opt)) < 0)
2N/A return (errno);
2N/A }
2N/A if (is_system_labeled())
2N/A (void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt,
2N/A sizeof (opt));
2N/A bzero(&locaddr, sizeof (locaddr));
2N/A locaddr.sin_family = AF_INET;
2N/A /* locaddr.sin_len = sizeof (locaddr); */
2N/A if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
2N/A return (errno);
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
2N/A{
2N/A struct sockaddr_in dest;
2N/A struct mbdata *mbp = &rqp->nr_rq;
2N/A int s = rqp->nr_fd;
2N/A uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
2N/A uint16_t *datap;
2N/A uint8_t nmflags;
2N/A int rc;
2N/A
2N/A bzero(&dest, sizeof (dest));
2N/A dest.sin_family = AF_INET;
2N/A dest.sin_port = htons(IPPORT_NETBIOS_NS);
2N/A dest.sin_addr.s_addr = ina;
2N/A
2N/A if (ina == INADDR_BROADCAST) {
2N/A /* Turn on the broadcast bit. */
2N/A nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
2N/A /*LINTED*/
2N/A datap = mtod(mbp->mb_top, uint16_t *);
2N/A ofr = ((rqp->nr_opcode & 0x1F) << 11) |
2N/A ((nmflags & 0x7F) << 4); /* rcode=0 */
2N/A ofr_save = datap[1];
2N/A datap[1] = htons(ofr);
2N/A }
2N/A
2N/A rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
2N/A (struct sockaddr *)&dest, sizeof (dest));
2N/A
2N/A if (ina == INADDR_BROADCAST) {
2N/A /* Turn the broadcast bit back off. */
2N/A datap[1] = ofr_save;
2N/A }
2N/A
2N/A
2N/A if (rc < 0)
2N/A return (errno);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Asmbfs_nbns_rq(struct nbns_rq *rqp)
2N/A{
2N/A struct nb_ctx *ctx = rqp->nr_nbd;
2N/A struct mbdata *mbp = &rqp->nr_rq;
2N/A uint16_t ofr, rpid;
2N/A int error, tries, maxretry;
2N/A
2N/A error = smbfs_nbns_rq_opensocket(rqp);
2N/A if (error)
2N/A return (error);
2N/A
2N/A maxretry = rqp->nr_maxretry;
2N/A for (tries = 0; tries < maxretry; tries++) {
2N/A
2N/A /*
2N/A * Minor hack: If nr_dest is set, send there only.
2N/A * Used by _getnodestatus, _resolvname redirects.
2N/A */
2N/A if (rqp->nr_dest.s_addr) {
2N/A error = smbfs_nbns_rq_send(rqp, rqp->nr_dest.s_addr);
2N/A if (error) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns error %d sending to %s"),
2N/A 0, error, inet_ntoa(rqp->nr_dest));
2N/A }
2N/A goto do_recv;
2N/A }
2N/A
2N/A if (ctx->nb_wins1) {
2N/A error = smbfs_nbns_rq_send(rqp, ctx->nb_wins1);
2N/A if (error) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns error %d sending to wins1"),
2N/A 0, error);
2N/A }
2N/A }
2N/A
2N/A if (ctx->nb_wins2 && (tries > 0)) {
2N/A error = smbfs_nbns_rq_send(rqp, ctx->nb_wins2);
2N/A if (error) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns error %d sending to wins2"),
2N/A 0, error);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If broadcast is enabled, start broadcasting
2N/A * only after wins servers fail to respond, or
2N/A * immediately if no WINS servers configured.
2N/A */
2N/A if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
2N/A ((tries > 1) || (ctx->nb_wins1 == 0))) {
2N/A error = smbfs_nbns_rq_send(rqp, INADDR_BROADCAST);
2N/A if (error) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns error %d sending broadcast"),
2N/A 0, error);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Wait for responses from ANY of the above.
2N/A */
2N/Ado_recv:
2N/A error = smbfs_nbns_rq_recv(rqp);
2N/A if (error == ETIMEDOUT)
2N/A continue;
2N/A if (error) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "nbns recv error %d"),
2N/A 0, error);
2N/A return (error);
2N/A }
2N/A
2N/A mbp = &rqp->nr_rp;
2N/A if (mbp->mb_count < 12)
2N/A return (NBERROR(NBERR_INVALIDRESPONSE));
2N/A smbfs_md_get_uint16be(mbp, &rpid);
2N/A if (rpid != rqp->nr_trnid)
2N/A return (NBERROR(NBERR_INVALIDRESPONSE));
2N/A break;
2N/A }
2N/A if (tries == maxretry)
2N/A return (NBERROR(NBERR_HOSTNOTFOUND));
2N/A
2N/A smbfs_md_get_uint16be(mbp, &ofr);
2N/A rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
2N/A rqp->nr_rprcode = ofr & 0xf;
2N/A if (rqp->nr_rprcode)
2N/A return (NBERROR(rqp->nr_rprcode));
2N/A smbfs_md_get_uint16be(mbp, &rpid); /* QDCOUNT */
2N/A smbfs_md_get_uint16be(mbp, &rqp->nr_rpancount);
2N/A smbfs_md_get_uint16be(mbp, &rqp->nr_rpnscount);
2N/A smbfs_md_get_uint16be(mbp, &rqp->nr_rparcount);
2N/A return (0);
2N/A}