xref: /onnv-gate/usr/src/lib/libsmbfs/smb/nbns_rq.c (revision 12556:b7f7250e66fe)
16007Sthurlow /*
26007Sthurlow  * Copyright (c) 2000, Boris Popov
36007Sthurlow  * All rights reserved.
46007Sthurlow  *
56007Sthurlow  * Redistribution and use in source and binary forms, with or without
66007Sthurlow  * modification, are permitted provided that the following conditions
76007Sthurlow  * are met:
86007Sthurlow  * 1. Redistributions of source code must retain the above copyright
96007Sthurlow  *    notice, this list of conditions and the following disclaimer.
106007Sthurlow  * 2. Redistributions in binary form must reproduce the above copyright
116007Sthurlow  *    notice, this list of conditions and the following disclaimer in the
126007Sthurlow  *    documentation and/or other materials provided with the distribution.
136007Sthurlow  * 3. All advertising materials mentioning features or use of this software
146007Sthurlow  *    must display the following acknowledgement:
156007Sthurlow  *    This product includes software developed by Boris Popov.
166007Sthurlow  * 4. Neither the name of the author nor the names of any co-contributors
176007Sthurlow  *    may be used to endorse or promote products derived from this software
186007Sthurlow  *    without specific prior written permission.
196007Sthurlow  *
206007Sthurlow  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
216007Sthurlow  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226007Sthurlow  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
236007Sthurlow  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
246007Sthurlow  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
256007Sthurlow  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
266007Sthurlow  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
276007Sthurlow  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
286007Sthurlow  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
296007Sthurlow  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
306007Sthurlow  * SUCH DAMAGE.
316007Sthurlow  *
326007Sthurlow  * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
336007Sthurlow  */
346007Sthurlow 
35*12556SGordon.Ross@Sun.COM /*
36*12556SGordon.Ross@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
37*12556SGordon.Ross@Sun.COM  */
38*12556SGordon.Ross@Sun.COM 
396007Sthurlow #include <sys/param.h>
406007Sthurlow #include <sys/socket.h>
416007Sthurlow #include <sys/time.h>
426007Sthurlow #include <ctype.h>
436007Sthurlow #include <netdb.h>
446007Sthurlow #include <errno.h>
456007Sthurlow #include <stdlib.h>
466007Sthurlow #include <string.h>
476007Sthurlow #include <strings.h>
486007Sthurlow #include <stdio.h>
496007Sthurlow #include <unistd.h>
506007Sthurlow #include <libintl.h>
516007Sthurlow 
526007Sthurlow #include <netinet/in.h>
536007Sthurlow #include <arpa/inet.h>
546007Sthurlow #include <tsol/label.h>
556007Sthurlow 
566007Sthurlow #define	NB_NEEDRESOLVER
576007Sthurlow #include <netsmb/netbios.h>
586007Sthurlow #include <netsmb/smb_lib.h>
596007Sthurlow #include <netsmb/nb_lib.h>
606007Sthurlow #include <netsmb/mchain.h>
616007Sthurlow 
6210023SGordon.Ross@Sun.COM #include "charsets.h"
638271SGordon.Ross@Sun.COM #include "private.h"
648271SGordon.Ross@Sun.COM 
658271SGordon.Ross@Sun.COM /*
668271SGordon.Ross@Sun.COM  * nbns request
678271SGordon.Ross@Sun.COM  */
688271SGordon.Ross@Sun.COM struct nbns_rq {
698271SGordon.Ross@Sun.COM 	int		nr_opcode;
708271SGordon.Ross@Sun.COM 	int		nr_nmflags;
718271SGordon.Ross@Sun.COM 	int		nr_rcode;
728271SGordon.Ross@Sun.COM 	int		nr_qdcount;
738271SGordon.Ross@Sun.COM 	int		nr_ancount;
748271SGordon.Ross@Sun.COM 	int		nr_nscount;
758271SGordon.Ross@Sun.COM 	int		nr_arcount;
768271SGordon.Ross@Sun.COM 	struct nb_name	*nr_qdname;
778271SGordon.Ross@Sun.COM 	uint16_t	nr_qdtype;
788271SGordon.Ross@Sun.COM 	uint16_t	nr_qdclass;
798271SGordon.Ross@Sun.COM 	struct in_addr	nr_dest;	/* receiver of query */
808271SGordon.Ross@Sun.COM 	struct sockaddr_in nr_sender;	/* sender of response */
818271SGordon.Ross@Sun.COM 	int		nr_rpnmflags;
828271SGordon.Ross@Sun.COM 	int		nr_rprcode;
838271SGordon.Ross@Sun.COM 	uint16_t	nr_rpancount;
848271SGordon.Ross@Sun.COM 	uint16_t	nr_rpnscount;
858271SGordon.Ross@Sun.COM 	uint16_t	nr_rparcount;
868271SGordon.Ross@Sun.COM 	uint16_t	nr_trnid;
878271SGordon.Ross@Sun.COM 	struct nb_ctx	*nr_nbd;
888271SGordon.Ross@Sun.COM 	struct mbdata	nr_rq;
898271SGordon.Ross@Sun.COM 	struct mbdata	nr_rp;
908271SGordon.Ross@Sun.COM 	struct nb_ifdesc *nr_if;
918271SGordon.Ross@Sun.COM 	int		nr_flags;
928271SGordon.Ross@Sun.COM 	int		nr_fd;
938271SGordon.Ross@Sun.COM 	int		nr_maxretry;
948271SGordon.Ross@Sun.COM };
958271SGordon.Ross@Sun.COM typedef struct nbns_rq nbns_rq_t;
968271SGordon.Ross@Sun.COM 
976007Sthurlow static int  nbns_rq_create(int opcode, struct nb_ctx *ctx,
986007Sthurlow     struct nbns_rq **rqpp);
996007Sthurlow static void nbns_rq_done(struct nbns_rq *rqp);
1006007Sthurlow static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
1016007Sthurlow static int  nbns_rq_prepare(struct nbns_rq *rqp);
1026007Sthurlow static int  nbns_rq(struct nbns_rq *rqp);
1036007Sthurlow 
10410023SGordon.Ross@Sun.COM /*
10510023SGordon.Ross@Sun.COM  * Call NetBIOS name lookup and return a result in the
10610023SGordon.Ross@Sun.COM  * same form as getaddrinfo(3) returns.  Return code is
10710023SGordon.Ross@Sun.COM  * zero or one of the EAI_xxx codes like getaddrinfo.
10810023SGordon.Ross@Sun.COM  */
10910023SGordon.Ross@Sun.COM int
nbns_getaddrinfo(const char * name,struct nb_ctx * nbc,struct addrinfo ** res)11010023SGordon.Ross@Sun.COM nbns_getaddrinfo(const char *name, struct nb_ctx *nbc, struct addrinfo **res)
11110023SGordon.Ross@Sun.COM {
11210023SGordon.Ross@Sun.COM 	struct addrinfo *nai = NULL;
11310023SGordon.Ross@Sun.COM 	struct sockaddr *sap = NULL;
11410023SGordon.Ross@Sun.COM 	char *ucname = NULL;
11510023SGordon.Ross@Sun.COM 	int err;
11610023SGordon.Ross@Sun.COM 
11710023SGordon.Ross@Sun.COM 	/*
11810023SGordon.Ross@Sun.COM 	 * Try NetBIOS name lookup.
11910023SGordon.Ross@Sun.COM 	 */
12010023SGordon.Ross@Sun.COM 	if (strlen(name) >= NB_NAMELEN) {
12110023SGordon.Ross@Sun.COM 		err = EAI_OVERFLOW;
12210023SGordon.Ross@Sun.COM 		goto out;
12310023SGordon.Ross@Sun.COM 	}
12410023SGordon.Ross@Sun.COM 	ucname = utf8_str_toupper(name);
12510023SGordon.Ross@Sun.COM 	if (ucname == NULL)
12610023SGordon.Ross@Sun.COM 		goto nomem;
12710023SGordon.Ross@Sun.COM 
12810023SGordon.Ross@Sun.COM 	/* Note: this returns an NBERROR value. */
12910023SGordon.Ross@Sun.COM 	err = nbns_resolvename(ucname, nbc, &sap);
13010023SGordon.Ross@Sun.COM 	if (err) {
13110023SGordon.Ross@Sun.COM 		if (smb_verbose)
13210023SGordon.Ross@Sun.COM 			smb_error(dgettext(TEXT_DOMAIN,
13310023SGordon.Ross@Sun.COM 			    "nbns_resolvename: %s"),
13410023SGordon.Ross@Sun.COM 			    err, name);
13510023SGordon.Ross@Sun.COM 		err = EAI_NODATA;
13610023SGordon.Ross@Sun.COM 		goto out;
13710023SGordon.Ross@Sun.COM 	}
13810023SGordon.Ross@Sun.COM 	/* Note: sap allocated */
13910023SGordon.Ross@Sun.COM 
14010023SGordon.Ross@Sun.COM 	/*
14110023SGordon.Ross@Sun.COM 	 * Build the addrinfo struct to return.
14210023SGordon.Ross@Sun.COM 	 */
14310023SGordon.Ross@Sun.COM 	nai = malloc(sizeof (*nai));
14410023SGordon.Ross@Sun.COM 	if (nai == NULL)
14510023SGordon.Ross@Sun.COM 		goto nomem;
14610023SGordon.Ross@Sun.COM 	bzero(nai, sizeof (*nai));
14710023SGordon.Ross@Sun.COM 
14810023SGordon.Ross@Sun.COM 	nai->ai_flags = AI_CANONNAME;
14910023SGordon.Ross@Sun.COM 	nai->ai_family = sap->sa_family;
15010023SGordon.Ross@Sun.COM 	nai->ai_socktype = SOCK_STREAM;
15110023SGordon.Ross@Sun.COM 	nai->ai_canonname = ucname;
15210023SGordon.Ross@Sun.COM 	ucname = NULL;
15310023SGordon.Ross@Sun.COM 
15410023SGordon.Ross@Sun.COM 	/*
15510023SGordon.Ross@Sun.COM 	 * The type of this is really sockaddr_in,
15610023SGordon.Ross@Sun.COM 	 * but is returned in the generic form.
15710023SGordon.Ross@Sun.COM 	 * See nbns_resolvename.
15810023SGordon.Ross@Sun.COM 	 */
15910023SGordon.Ross@Sun.COM 	nai->ai_addrlen = sizeof (struct sockaddr_in);
16010023SGordon.Ross@Sun.COM 	nai->ai_addr = sap;
16110023SGordon.Ross@Sun.COM 
16210023SGordon.Ross@Sun.COM 	*res = nai;
16310023SGordon.Ross@Sun.COM 	return (0);
16410023SGordon.Ross@Sun.COM 
16510023SGordon.Ross@Sun.COM nomem:
16610023SGordon.Ross@Sun.COM 	err = EAI_MEMORY;
16710023SGordon.Ross@Sun.COM out:
16810023SGordon.Ross@Sun.COM 	if (nai != NULL)
16910023SGordon.Ross@Sun.COM 		free(nai);
17010023SGordon.Ross@Sun.COM 	if (sap)
17110023SGordon.Ross@Sun.COM 		free(sap);
17210023SGordon.Ross@Sun.COM 	if (ucname)
17310023SGordon.Ross@Sun.COM 		free(ucname);
17410023SGordon.Ross@Sun.COM 	*res = NULL;
17510023SGordon.Ross@Sun.COM 
17610023SGordon.Ross@Sun.COM 	return (err);
17710023SGordon.Ross@Sun.COM }
1786007Sthurlow 
1796007Sthurlow int
nbns_resolvename(const char * name,struct nb_ctx * ctx,struct sockaddr ** adpp)1806007Sthurlow nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
1816007Sthurlow {
1826007Sthurlow 	struct nbns_rq *rqp;
1836007Sthurlow 	struct nb_name nn;
1846007Sthurlow 	struct nbns_rr rr;
1856007Sthurlow 	struct sockaddr_in *dest;
1866007Sthurlow 	int error, rdrcount, len;
1876007Sthurlow 
18810023SGordon.Ross@Sun.COM 	if (strlen(name) >= NB_NAMELEN)
1896007Sthurlow 		return (NBERROR(NBERR_NAMETOOLONG));
1906007Sthurlow 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
1916007Sthurlow 	if (error)
1926007Sthurlow 		return (error);
1936007Sthurlow 	/*
1946007Sthurlow 	 * Pad the name with blanks, but
1956007Sthurlow 	 * leave the "type" byte NULL.
1966007Sthurlow 	 * nb_name_encode adds the type.
1976007Sthurlow 	 */
1986007Sthurlow 	bzero(&nn, sizeof (nn));
1996007Sthurlow 	snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
2006007Sthurlow 	nn.nn_type = NBT_SERVER;
2016007Sthurlow 	nn.nn_scope = ctx->nb_scope;
2026007Sthurlow 	rqp->nr_nmflags = NBNS_NMFLAG_RD;
2036007Sthurlow 	rqp->nr_qdname = &nn;
2046007Sthurlow 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
2056007Sthurlow 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2066007Sthurlow 	rqp->nr_qdcount = 1;
2076007Sthurlow 	rqp->nr_maxretry = 5;
2086007Sthurlow 
2096007Sthurlow 	error = nbns_rq_prepare(rqp);
2106007Sthurlow 	if (error) {
2116007Sthurlow 		nbns_rq_done(rqp);
2126007Sthurlow 		return (error);
2136007Sthurlow 	}
2146007Sthurlow 	rdrcount = NBNS_MAXREDIRECTS;
2156007Sthurlow 	for (;;) {
2166007Sthurlow 		error = nbns_rq(rqp);
2176007Sthurlow 		if (error)
2186007Sthurlow 			break;
2196007Sthurlow 		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
2206007Sthurlow 			/*
2216007Sthurlow 			 * Not an authoritative answer.  Query again
2226007Sthurlow 			 * using the NS address in the 2nd record.
2236007Sthurlow 			 */
2246007Sthurlow 			if (rdrcount-- == 0) {
2256007Sthurlow 				error = NBERROR(NBERR_TOOMANYREDIRECTS);
2266007Sthurlow 				break;
2276007Sthurlow 			}
2286007Sthurlow 			error = nbns_rq_getrr(rqp, &rr);
2296007Sthurlow 			if (error)
2306007Sthurlow 				break;
2316007Sthurlow 			error = nbns_rq_getrr(rqp, &rr);
2326007Sthurlow 			if (error)
2336007Sthurlow 				break;
2346007Sthurlow 			bcopy(rr.rr_data, &rqp->nr_dest, 4);
2356007Sthurlow 			continue;
2366007Sthurlow 		}
2376007Sthurlow 		if (rqp->nr_rpancount == 0) {
2386007Sthurlow 			error = NBERROR(NBERR_HOSTNOTFOUND);
2396007Sthurlow 			break;
2406007Sthurlow 		}
2416007Sthurlow 		error = nbns_rq_getrr(rqp, &rr);
2426007Sthurlow 		if (error)
2436007Sthurlow 			break;
2446007Sthurlow 		len = sizeof (struct sockaddr_in);
2456007Sthurlow 		dest = malloc(len);
2466007Sthurlow 		if (dest == NULL)
2476007Sthurlow 			return (ENOMEM);
2486007Sthurlow 		bzero(dest, len);
2496007Sthurlow 		/*
25010023SGordon.Ross@Sun.COM 		 * Solaris sockaddr_in doesn't a sin_len field.
2516007Sthurlow 		 * dest->sin_len = len;
2526007Sthurlow 		 */
25310023SGordon.Ross@Sun.COM 		dest->sin_family = AF_NETBIOS;	/* nb_lib.h */
2546007Sthurlow 		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
2556007Sthurlow 		*adpp = (struct sockaddr *)dest;
2566007Sthurlow 		ctx->nb_lastns = rqp->nr_sender;
2576007Sthurlow 		break;
2586007Sthurlow 	}
2596007Sthurlow 	nbns_rq_done(rqp);
2606007Sthurlow 	return (error);
2616007Sthurlow }
2626007Sthurlow 
26310023SGordon.Ross@Sun.COM /*
26410023SGordon.Ross@Sun.COM  * NB: system, workgroup are both NB_NAMELEN
26510023SGordon.Ross@Sun.COM  */
2666007Sthurlow int
nbns_getnodestatus(struct nb_ctx * ctx,struct in_addr * targethost,char * system,char * workgroup)26710023SGordon.Ross@Sun.COM nbns_getnodestatus(struct nb_ctx *ctx,
26810023SGordon.Ross@Sun.COM     struct in_addr *targethost, char *system, char *workgroup)
2696007Sthurlow {
2706007Sthurlow 	struct nbns_rq *rqp;
2716007Sthurlow 	struct nbns_rr rr;
2726007Sthurlow 	struct nb_name nn;
2736007Sthurlow 	struct nbns_nr *nrp;
2746007Sthurlow 	char nrtype;
2756007Sthurlow 	char *cp, *retname = NULL;
2766007Sthurlow 	unsigned char nrcount;
27710023SGordon.Ross@Sun.COM 	int error, i, foundserver = 0, foundgroup = 0;
2786007Sthurlow 
2796007Sthurlow 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
2806007Sthurlow 	if (error)
2816007Sthurlow 		return (error);
2826007Sthurlow 	bzero(&nn, sizeof (nn));
2836007Sthurlow 	strcpy((char *)nn.nn_name, "*");
2846007Sthurlow 	nn.nn_scope = ctx->nb_scope;
2856007Sthurlow 	nn.nn_type = NBT_WKSTA;
2866007Sthurlow 	rqp->nr_nmflags = 0;
2876007Sthurlow 	rqp->nr_qdname = &nn;
2886007Sthurlow 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
2896007Sthurlow 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2906007Sthurlow 	rqp->nr_qdcount = 1;
2916007Sthurlow 	rqp->nr_maxretry = 2;
2926007Sthurlow 
29310023SGordon.Ross@Sun.COM 	rqp->nr_dest = *targethost;
2946007Sthurlow 	error = nbns_rq_prepare(rqp);
2956007Sthurlow 	if (error) {
2966007Sthurlow 		nbns_rq_done(rqp);
2976007Sthurlow 		return (error);
2986007Sthurlow 	}
2996007Sthurlow 
3006007Sthurlow 	/*
3016007Sthurlow 	 * Darwin had a loop here, allowing redirect, etc.
3026007Sthurlow 	 * but we only handle point-to-point for node status.
3036007Sthurlow 	 */
3046007Sthurlow 	error = nbns_rq(rqp);
3056007Sthurlow 	if (error)
3066007Sthurlow 		goto out;
3076007Sthurlow 	if (rqp->nr_rpancount == 0) {
3086007Sthurlow 		error = NBERROR(NBERR_HOSTNOTFOUND);
3096007Sthurlow 		goto out;
3106007Sthurlow 	}
3116007Sthurlow 	error = nbns_rq_getrr(rqp, &rr);
3126007Sthurlow 	if (error)
3136007Sthurlow 		goto out;
3146007Sthurlow 
3156007Sthurlow 	/* Compiler didn't like cast on lvalue++ */
3166007Sthurlow 	nrcount = *((unsigned char *)rr.rr_data);
3176007Sthurlow 	rr.rr_data++;
3186007Sthurlow 	/* LINTED */
3196007Sthurlow 	for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
3206007Sthurlow 	    i <= nrcount; ++i, ++nrp) {
3216007Sthurlow 		nrtype = nrp->ns_name[NB_NAMELEN-1];
3226007Sthurlow 		/* Terminate the string: */
3236007Sthurlow 		nrp->ns_name[NB_NAMELEN-1] = (char)0;
3246007Sthurlow 		/* Strip off trailing spaces */
3256007Sthurlow 		for (cp = &nrp->ns_name[NB_NAMELEN-2];
3266007Sthurlow 		    cp >= nrp->ns_name; --cp) {
3276007Sthurlow 			if (*cp != (char)0x20)
3286007Sthurlow 				break;
3296007Sthurlow 			*cp = (char)0;
3306007Sthurlow 		}
3316007Sthurlow 		nrp->ns_flags = ntohs(nrp->ns_flags);
33210023SGordon.Ross@Sun.COM 		DPRINT(" %s[%02x] Flags 0x%x",
33310023SGordon.Ross@Sun.COM 		    nrp->ns_name, nrtype, nrp->ns_flags);
3346007Sthurlow 		if (nrp->ns_flags & NBNS_GROUPFLG) {
3356007Sthurlow 			if (!foundgroup ||
3366007Sthurlow 			    (foundgroup != NBT_WKSTA+1 &&
3376007Sthurlow 			    nrtype == NBT_WKSTA)) {
33810023SGordon.Ross@Sun.COM 				strlcpy(workgroup, nrp->ns_name,
33910023SGordon.Ross@Sun.COM 				    NB_NAMELEN);
3406007Sthurlow 				foundgroup = nrtype+1;
3416007Sthurlow 			}
3426007Sthurlow 		} else {
3436007Sthurlow 			/*
3446007Sthurlow 			 * Track at least ONE name, in case
3456007Sthurlow 			 * no server name is found
3466007Sthurlow 			 */
3476007Sthurlow 			retname = nrp->ns_name;
3486007Sthurlow 		}
34910023SGordon.Ross@Sun.COM 		/*
35010023SGordon.Ross@Sun.COM 		 * Keep the first NBT_SERVER name.
35110023SGordon.Ross@Sun.COM 		 */
35210023SGordon.Ross@Sun.COM 		if (nrtype == NBT_SERVER && foundserver == 0) {
35310023SGordon.Ross@Sun.COM 			strlcpy(system, nrp->ns_name,
35410023SGordon.Ross@Sun.COM 			    NB_NAMELEN);
3556007Sthurlow 			foundserver = 1;
3566007Sthurlow 		}
3576007Sthurlow 	}
358*12556SGordon.Ross@Sun.COM 	if (foundserver == 0 && retname != NULL)
35910023SGordon.Ross@Sun.COM 		strlcpy(system, retname, NB_NAMELEN);
3606007Sthurlow 	ctx->nb_lastns = rqp->nr_sender;
3616007Sthurlow 
3626007Sthurlow out:
3636007Sthurlow 	nbns_rq_done(rqp);
3646007Sthurlow 	return (error);
3656007Sthurlow }
3666007Sthurlow 
3676007Sthurlow int
nbns_rq_create(int opcode,struct nb_ctx * ctx,struct nbns_rq ** rqpp)3686007Sthurlow nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
3696007Sthurlow {
3706007Sthurlow 	struct nbns_rq *rqp;
3716007Sthurlow 	static uint16_t trnid;
3726007Sthurlow 	int error;
3736007Sthurlow 
3746007Sthurlow 	if (trnid == 0)
3756007Sthurlow 		trnid = getpid();
3766007Sthurlow 	rqp = malloc(sizeof (*rqp));
3776007Sthurlow 	if (rqp == NULL)
3786007Sthurlow 		return (ENOMEM);
3796007Sthurlow 	bzero(rqp, sizeof (*rqp));
38011332SGordon.Ross@Sun.COM 	error = mb_init_sz(&rqp->nr_rq, NBDG_MAXSIZE);
3816007Sthurlow 	if (error) {
3826007Sthurlow 		free(rqp);
3836007Sthurlow 		return (error);
3846007Sthurlow 	}
3856007Sthurlow 	rqp->nr_opcode = opcode;
3866007Sthurlow 	rqp->nr_nbd = ctx;
3876007Sthurlow 	rqp->nr_trnid = trnid++;
3886007Sthurlow 	*rqpp = rqp;
3896007Sthurlow 	return (0);
3906007Sthurlow }
3916007Sthurlow 
3926007Sthurlow void
nbns_rq_done(struct nbns_rq * rqp)3936007Sthurlow nbns_rq_done(struct nbns_rq *rqp)
3946007Sthurlow {
3956007Sthurlow 	if (rqp == NULL)
3966007Sthurlow 		return;
3976007Sthurlow 	if (rqp->nr_fd >= 0)
3986007Sthurlow 		close(rqp->nr_fd);
3996007Sthurlow 	mb_done(&rqp->nr_rq);
4006007Sthurlow 	mb_done(&rqp->nr_rp);
4016007Sthurlow 	if (rqp->nr_if)
4026007Sthurlow 		free(rqp->nr_if);
4036007Sthurlow 	free(rqp);
4046007Sthurlow }
4056007Sthurlow 
4066007Sthurlow /*
4076007Sthurlow  * Extract resource record from the packet. Assume that there is only
4086007Sthurlow  * one mbuf.
4096007Sthurlow  */
4106007Sthurlow int
nbns_rq_getrr(struct nbns_rq * rqp,struct nbns_rr * rrp)4116007Sthurlow nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
4126007Sthurlow {
4136007Sthurlow 	struct mbdata *mbp = &rqp->nr_rp;
4146007Sthurlow 	uchar_t *cp;
4156007Sthurlow 	int error, len;
4166007Sthurlow 
4176007Sthurlow 	bzero(rrp, sizeof (*rrp));
4186007Sthurlow 	cp = (uchar_t *)mbp->mb_pos;
4196007Sthurlow 	len = nb_encname_len(cp);
4206007Sthurlow 	if (len < 1)
4216007Sthurlow 		return (NBERROR(NBERR_INVALIDRESPONSE));
4226007Sthurlow 	rrp->rr_name = cp;
42311332SGordon.Ross@Sun.COM 	error = md_get_mem(mbp, NULL, len, MB_MSYSTEM);
4246007Sthurlow 	if (error)
4256007Sthurlow 		return (error);
42611332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rrp->rr_type);
42711332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rrp->rr_class);
42811332SGordon.Ross@Sun.COM 	md_get_uint32be(mbp, &rrp->rr_ttl);
42911332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rrp->rr_rdlength);
4306007Sthurlow 	rrp->rr_data = (uchar_t *)mbp->mb_pos;
43111332SGordon.Ross@Sun.COM 	error = md_get_mem(mbp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
4326007Sthurlow 	return (error);
4336007Sthurlow }
4346007Sthurlow 
4356007Sthurlow int
nbns_rq_prepare(struct nbns_rq * rqp)4366007Sthurlow nbns_rq_prepare(struct nbns_rq *rqp)
4376007Sthurlow {
4386007Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
4396007Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
4406007Sthurlow 	uint16_t ofr; /* opcode, flags, rcode */
44110023SGordon.Ross@Sun.COM 	int error;
4426007Sthurlow 
44311332SGordon.Ross@Sun.COM 	error = mb_init_sz(&rqp->nr_rp, NBDG_MAXSIZE);
4446007Sthurlow 	if (error)
4456007Sthurlow 		return (error);
4466007Sthurlow 
4476007Sthurlow 	/*
4486007Sthurlow 	 * When looked into the ethereal trace, 'nmblookup' command sets this
4496007Sthurlow 	 * flag. We will also set.
4506007Sthurlow 	 */
4516007Sthurlow 	mb_put_uint16be(mbp, rqp->nr_trnid);
4526007Sthurlow 	ofr = ((rqp->nr_opcode & 0x1F) << 11) |
4536007Sthurlow 	    ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
4546007Sthurlow 	mb_put_uint16be(mbp, ofr);
4556007Sthurlow 	mb_put_uint16be(mbp, rqp->nr_qdcount);
4566007Sthurlow 	mb_put_uint16be(mbp, rqp->nr_ancount);
4576007Sthurlow 	mb_put_uint16be(mbp, rqp->nr_nscount);
45811332SGordon.Ross@Sun.COM 	error = mb_put_uint16be(mbp, rqp->nr_arcount);
4596007Sthurlow 	if (rqp->nr_qdcount) {
4606007Sthurlow 		if (rqp->nr_qdcount > 1)
4616007Sthurlow 			return (EINVAL);
46211332SGordon.Ross@Sun.COM 		(void) nb_name_encode(mbp, rqp->nr_qdname);
4636007Sthurlow 		mb_put_uint16be(mbp, rqp->nr_qdtype);
46411332SGordon.Ross@Sun.COM 		error = mb_put_uint16be(mbp, rqp->nr_qdclass);
4656007Sthurlow 	}
46611332SGordon.Ross@Sun.COM 	if (error)
46711332SGordon.Ross@Sun.COM 		return (error);
46811332SGordon.Ross@Sun.COM 	error = m_lineup(mbp->mb_top, &mbp->mb_top);
46911332SGordon.Ross@Sun.COM 	if (error)
47011332SGordon.Ross@Sun.COM 		return (error);
4716007Sthurlow 	if (ctx->nb_timo == 0)
4726007Sthurlow 		ctx->nb_timo = 1;	/* by default 1 second */
4736007Sthurlow 	return (0);
4746007Sthurlow }
4756007Sthurlow 
4766007Sthurlow static int
nbns_rq_recv(struct nbns_rq * rqp)4776007Sthurlow nbns_rq_recv(struct nbns_rq *rqp)
4786007Sthurlow {
4796007Sthurlow 	struct mbdata *mbp = &rqp->nr_rp;
4806007Sthurlow 	void *rpdata = mtod(mbp->mb_top, void *);
4816007Sthurlow 	fd_set rd, wr, ex;
4826007Sthurlow 	struct timeval tv;
4836007Sthurlow 	struct sockaddr_in sender;
4846007Sthurlow 	int s = rqp->nr_fd;
4856007Sthurlow 	int n, len;
4866007Sthurlow 
4876007Sthurlow 	FD_ZERO(&rd);
4886007Sthurlow 	FD_ZERO(&wr);
4896007Sthurlow 	FD_ZERO(&ex);
4906007Sthurlow 	FD_SET(s, &rd);
4916007Sthurlow 
4926007Sthurlow 	tv.tv_sec = rqp->nr_nbd->nb_timo;
4936007Sthurlow 	tv.tv_usec = 0;
4946007Sthurlow 
4956007Sthurlow 	n = select(s + 1, &rd, &wr, &ex, &tv);
4966007Sthurlow 	if (n == -1)
4976007Sthurlow 		return (-1);
4986007Sthurlow 	if (n == 0)
4996007Sthurlow 		return (ETIMEDOUT);
5006007Sthurlow 	if (FD_ISSET(s, &rd) == 0)
5016007Sthurlow 		return (ETIMEDOUT);
5026007Sthurlow 	len = sizeof (sender);
5036007Sthurlow 	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
5046007Sthurlow 	    (struct sockaddr *)&sender, &len);
5056007Sthurlow 	if (n < 0)
5066007Sthurlow 		return (errno);
5076007Sthurlow 	mbp->mb_top->m_len = mbp->mb_count = n;
5086007Sthurlow 	rqp->nr_sender = sender;
5096007Sthurlow 	return (0);
5106007Sthurlow }
5116007Sthurlow 
5126007Sthurlow static int
nbns_rq_opensocket(struct nbns_rq * rqp)5136007Sthurlow nbns_rq_opensocket(struct nbns_rq *rqp)
5146007Sthurlow {
5156007Sthurlow 	struct sockaddr_in locaddr;
5166007Sthurlow 	int opt = 1, s;
5176007Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
5186007Sthurlow 
5196007Sthurlow 	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
5206007Sthurlow 	if (s < 0)
5216007Sthurlow 		return (errno);
5226007Sthurlow 	if (ctx->nb_flags & NBCF_BC_ENABLE) {
5236007Sthurlow 		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
5246007Sthurlow 		    sizeof (opt)) < 0)
5256007Sthurlow 			return (errno);
5266007Sthurlow 	}
5276007Sthurlow 	if (is_system_labeled())
5286007Sthurlow 		(void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt,
5296007Sthurlow 		    sizeof (opt));
5306007Sthurlow 	bzero(&locaddr, sizeof (locaddr));
5316007Sthurlow 	locaddr.sin_family = AF_INET;
5326007Sthurlow 	/* locaddr.sin_len = sizeof (locaddr); */
5336007Sthurlow 	if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
5346007Sthurlow 		return (errno);
5356007Sthurlow 	return (0);
5366007Sthurlow }
5376007Sthurlow 
5386007Sthurlow static int
nbns_rq_send(struct nbns_rq * rqp,in_addr_t ina)5396007Sthurlow nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
5406007Sthurlow {
5416007Sthurlow 	struct sockaddr_in dest;
5426007Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
5436007Sthurlow 	int s = rqp->nr_fd;
5446007Sthurlow 	uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
5456007Sthurlow 	uint16_t *datap;
5466007Sthurlow 	uint8_t nmflags;
5476007Sthurlow 	int rc;
5486007Sthurlow 
5496007Sthurlow 	bzero(&dest, sizeof (dest));
5506007Sthurlow 	dest.sin_family = AF_INET;
55110023SGordon.Ross@Sun.COM 	dest.sin_port = htons(IPPORT_NETBIOS_NS);
5526007Sthurlow 	dest.sin_addr.s_addr = ina;
5536007Sthurlow 
5546007Sthurlow 	if (ina == INADDR_BROADCAST) {
5556007Sthurlow 		/* Turn on the broadcast bit. */
5566007Sthurlow 		nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
5576007Sthurlow 		/*LINTED*/
5586007Sthurlow 		datap = mtod(mbp->mb_top, uint16_t *);
5596007Sthurlow 		ofr = ((rqp->nr_opcode & 0x1F) << 11) |
5606007Sthurlow 		    ((nmflags & 0x7F) << 4); /* rcode=0 */
5616007Sthurlow 		ofr_save = datap[1];
5626007Sthurlow 		datap[1] = htons(ofr);
5636007Sthurlow 	}
5646007Sthurlow 
5656007Sthurlow 	rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
5666007Sthurlow 	    (struct sockaddr *)&dest, sizeof (dest));
5676007Sthurlow 
5686007Sthurlow 	if (ina == INADDR_BROADCAST) {
5696007Sthurlow 		/* Turn the broadcast bit back off. */
5706007Sthurlow 		datap[1] = ofr_save;
5716007Sthurlow 	}
5726007Sthurlow 
5736007Sthurlow 
5746007Sthurlow 	if (rc < 0)
5756007Sthurlow 		return (errno);
5766007Sthurlow 
5776007Sthurlow 	return (0);
5786007Sthurlow }
5796007Sthurlow 
5806007Sthurlow int
nbns_rq(struct nbns_rq * rqp)5816007Sthurlow nbns_rq(struct nbns_rq *rqp)
5826007Sthurlow {
5836007Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
5846007Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
5856007Sthurlow 	uint16_t ofr, rpid;
5866007Sthurlow 	int error, tries, maxretry;
5876007Sthurlow 
5886007Sthurlow 	error = nbns_rq_opensocket(rqp);
5896007Sthurlow 	if (error)
5906007Sthurlow 		return (error);
5916007Sthurlow 
5926007Sthurlow 	maxretry = rqp->nr_maxretry;
5936007Sthurlow 	for (tries = 0; tries < maxretry; tries++) {
5946007Sthurlow 
5956007Sthurlow 		/*
5966007Sthurlow 		 * Minor hack: If nr_dest is set, send there only.
5976007Sthurlow 		 * Used by _getnodestatus, _resolvname redirects.
5986007Sthurlow 		 */
5996007Sthurlow 		if (rqp->nr_dest.s_addr) {
6006007Sthurlow 			error = nbns_rq_send(rqp, rqp->nr_dest.s_addr);
6016007Sthurlow 			if (error) {
6026007Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6036007Sthurlow 				    "nbns error %d sending to %s"),
6046007Sthurlow 				    0, error, inet_ntoa(rqp->nr_dest));
6056007Sthurlow 			}
6066007Sthurlow 			goto do_recv;
6076007Sthurlow 		}
6086007Sthurlow 
6096007Sthurlow 		if (ctx->nb_wins1) {
6106007Sthurlow 			error = nbns_rq_send(rqp, ctx->nb_wins1);
6116007Sthurlow 			if (error) {
6126007Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6136007Sthurlow 				    "nbns error %d sending to wins1"),
6146007Sthurlow 				    0, error);
6156007Sthurlow 			}
6166007Sthurlow 		}
6176007Sthurlow 
6186007Sthurlow 		if (ctx->nb_wins2 && (tries > 0)) {
6196007Sthurlow 			error = nbns_rq_send(rqp, ctx->nb_wins2);
6206007Sthurlow 			if (error) {
6216007Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6226007Sthurlow 				    "nbns error %d sending to wins2"),
6236007Sthurlow 				    0, error);
6246007Sthurlow 			}
6256007Sthurlow 		}
6266007Sthurlow 
6276007Sthurlow 		/*
6286007Sthurlow 		 * If broadcast is enabled, start broadcasting
6296007Sthurlow 		 * only after wins servers fail to respond, or
6306007Sthurlow 		 * immediately if no WINS servers configured.
6316007Sthurlow 		 */
6326007Sthurlow 		if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
6336007Sthurlow 		    ((tries > 1) || (ctx->nb_wins1 == 0))) {
6346007Sthurlow 			error = nbns_rq_send(rqp, INADDR_BROADCAST);
6356007Sthurlow 			if (error) {
6366007Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6376007Sthurlow 				    "nbns error %d sending broadcast"),
6386007Sthurlow 				    0, error);
6396007Sthurlow 			}
6406007Sthurlow 		}
6416007Sthurlow 
6426007Sthurlow 		/*
6436007Sthurlow 		 * Wait for responses from ANY of the above.
6446007Sthurlow 		 */
6456007Sthurlow do_recv:
6466007Sthurlow 		error = nbns_rq_recv(rqp);
6476007Sthurlow 		if (error == ETIMEDOUT)
6486007Sthurlow 			continue;
6496007Sthurlow 		if (error) {
6506007Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
6516007Sthurlow 			    "nbns recv error %d"),
6526007Sthurlow 			    0, error);
6536007Sthurlow 			return (error);
6546007Sthurlow 		}
6556007Sthurlow 
6566007Sthurlow 		mbp = &rqp->nr_rp;
6576007Sthurlow 		if (mbp->mb_count < 12)
6586007Sthurlow 			return (NBERROR(NBERR_INVALIDRESPONSE));
65911332SGordon.Ross@Sun.COM 		md_get_uint16be(mbp, &rpid);
6606007Sthurlow 		if (rpid != rqp->nr_trnid)
6616007Sthurlow 			return (NBERROR(NBERR_INVALIDRESPONSE));
6626007Sthurlow 		break;
6636007Sthurlow 	}
66410023SGordon.Ross@Sun.COM 	if (tries == maxretry)
66510023SGordon.Ross@Sun.COM 		return (NBERROR(NBERR_HOSTNOTFOUND));
6666007Sthurlow 
66711332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &ofr);
6686007Sthurlow 	rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
6696007Sthurlow 	rqp->nr_rprcode = ofr & 0xf;
6706007Sthurlow 	if (rqp->nr_rprcode)
6716007Sthurlow 		return (NBERROR(rqp->nr_rprcode));
67211332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rpid);	/* QDCOUNT */
67311332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rqp->nr_rpancount);
67411332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rqp->nr_rpnscount);
67511332SGordon.Ross@Sun.COM 	md_get_uint16be(mbp, &rqp->nr_rparcount);
6766007Sthurlow 	return (0);
6776007Sthurlow }
678