xref: /illumos-gate/usr/src/lib/libsmbfs/smb/nbns_rq.c (revision a547be5d)
14bff34e3Sthurlow /*
24bff34e3Sthurlow  * Copyright (c) 2000, Boris Popov
34bff34e3Sthurlow  * All rights reserved.
44bff34e3Sthurlow  *
54bff34e3Sthurlow  * Redistribution and use in source and binary forms, with or without
64bff34e3Sthurlow  * modification, are permitted provided that the following conditions
74bff34e3Sthurlow  * are met:
84bff34e3Sthurlow  * 1. Redistributions of source code must retain the above copyright
94bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer.
104bff34e3Sthurlow  * 2. Redistributions in binary form must reproduce the above copyright
114bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer in the
124bff34e3Sthurlow  *    documentation and/or other materials provided with the distribution.
134bff34e3Sthurlow  * 3. All advertising materials mentioning features or use of this software
144bff34e3Sthurlow  *    must display the following acknowledgement:
154bff34e3Sthurlow  *    This product includes software developed by Boris Popov.
164bff34e3Sthurlow  * 4. Neither the name of the author nor the names of any co-contributors
174bff34e3Sthurlow  *    may be used to endorse or promote products derived from this software
184bff34e3Sthurlow  *    without specific prior written permission.
194bff34e3Sthurlow  *
204bff34e3Sthurlow  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
214bff34e3Sthurlow  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224bff34e3Sthurlow  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234bff34e3Sthurlow  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
244bff34e3Sthurlow  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254bff34e3Sthurlow  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264bff34e3Sthurlow  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274bff34e3Sthurlow  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284bff34e3Sthurlow  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294bff34e3Sthurlow  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304bff34e3Sthurlow  * SUCH DAMAGE.
314bff34e3Sthurlow  *
324bff34e3Sthurlow  * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
334bff34e3Sthurlow  */
344bff34e3Sthurlow 
35*a547be5dSGordon Ross /*
36*a547be5dSGordon Ross  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
37*a547be5dSGordon Ross  */
38*a547be5dSGordon Ross 
394bff34e3Sthurlow #include <sys/param.h>
404bff34e3Sthurlow #include <sys/socket.h>
414bff34e3Sthurlow #include <sys/time.h>
424bff34e3Sthurlow #include <ctype.h>
434bff34e3Sthurlow #include <netdb.h>
444bff34e3Sthurlow #include <errno.h>
454bff34e3Sthurlow #include <stdlib.h>
464bff34e3Sthurlow #include <string.h>
474bff34e3Sthurlow #include <strings.h>
484bff34e3Sthurlow #include <stdio.h>
494bff34e3Sthurlow #include <unistd.h>
504bff34e3Sthurlow #include <libintl.h>
514bff34e3Sthurlow 
524bff34e3Sthurlow #include <netinet/in.h>
534bff34e3Sthurlow #include <arpa/inet.h>
544bff34e3Sthurlow #include <tsol/label.h>
554bff34e3Sthurlow 
564bff34e3Sthurlow #define	NB_NEEDRESOLVER
574bff34e3Sthurlow #include <netsmb/netbios.h>
584bff34e3Sthurlow #include <netsmb/smb_lib.h>
594bff34e3Sthurlow #include <netsmb/nb_lib.h>
604bff34e3Sthurlow #include <netsmb/mchain.h>
614bff34e3Sthurlow 
62613a2f6bSGordon Ross #include "charsets.h"
639c9af259SGordon Ross #include "private.h"
649c9af259SGordon Ross 
659c9af259SGordon Ross /*
669c9af259SGordon Ross  * nbns request
679c9af259SGordon Ross  */
689c9af259SGordon Ross struct nbns_rq {
699c9af259SGordon Ross 	int		nr_opcode;
709c9af259SGordon Ross 	int		nr_nmflags;
719c9af259SGordon Ross 	int		nr_rcode;
729c9af259SGordon Ross 	int		nr_qdcount;
739c9af259SGordon Ross 	int		nr_ancount;
749c9af259SGordon Ross 	int		nr_nscount;
759c9af259SGordon Ross 	int		nr_arcount;
769c9af259SGordon Ross 	struct nb_name	*nr_qdname;
779c9af259SGordon Ross 	uint16_t	nr_qdtype;
789c9af259SGordon Ross 	uint16_t	nr_qdclass;
799c9af259SGordon Ross 	struct in_addr	nr_dest;	/* receiver of query */
809c9af259SGordon Ross 	struct sockaddr_in nr_sender;	/* sender of response */
819c9af259SGordon Ross 	int		nr_rpnmflags;
829c9af259SGordon Ross 	int		nr_rprcode;
839c9af259SGordon Ross 	uint16_t	nr_rpancount;
849c9af259SGordon Ross 	uint16_t	nr_rpnscount;
859c9af259SGordon Ross 	uint16_t	nr_rparcount;
869c9af259SGordon Ross 	uint16_t	nr_trnid;
879c9af259SGordon Ross 	struct nb_ctx	*nr_nbd;
889c9af259SGordon Ross 	struct mbdata	nr_rq;
899c9af259SGordon Ross 	struct mbdata	nr_rp;
909c9af259SGordon Ross 	struct nb_ifdesc *nr_if;
919c9af259SGordon Ross 	int		nr_flags;
929c9af259SGordon Ross 	int		nr_fd;
939c9af259SGordon Ross 	int		nr_maxretry;
949c9af259SGordon Ross };
959c9af259SGordon Ross typedef struct nbns_rq nbns_rq_t;
969c9af259SGordon Ross 
974bff34e3Sthurlow static int  nbns_rq_create(int opcode, struct nb_ctx *ctx,
984bff34e3Sthurlow     struct nbns_rq **rqpp);
994bff34e3Sthurlow static void nbns_rq_done(struct nbns_rq *rqp);
1004bff34e3Sthurlow static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
1014bff34e3Sthurlow static int  nbns_rq_prepare(struct nbns_rq *rqp);
1024bff34e3Sthurlow static int  nbns_rq(struct nbns_rq *rqp);
1034bff34e3Sthurlow 
104613a2f6bSGordon Ross /*
105613a2f6bSGordon Ross  * Call NetBIOS name lookup and return a result in the
106613a2f6bSGordon Ross  * same form as getaddrinfo(3) returns.  Return code is
107613a2f6bSGordon Ross  * zero or one of the EAI_xxx codes like getaddrinfo.
108613a2f6bSGordon Ross  */
109613a2f6bSGordon Ross int
nbns_getaddrinfo(const char * name,struct nb_ctx * nbc,struct addrinfo ** res)110613a2f6bSGordon Ross nbns_getaddrinfo(const char *name, struct nb_ctx *nbc, struct addrinfo **res)
111613a2f6bSGordon Ross {
112613a2f6bSGordon Ross 	struct addrinfo *nai = NULL;
113613a2f6bSGordon Ross 	struct sockaddr *sap = NULL;
114613a2f6bSGordon Ross 	char *ucname = NULL;
115613a2f6bSGordon Ross 	int err;
116613a2f6bSGordon Ross 
117613a2f6bSGordon Ross 	/*
118613a2f6bSGordon Ross 	 * Try NetBIOS name lookup.
119613a2f6bSGordon Ross 	 */
120613a2f6bSGordon Ross 	if (strlen(name) >= NB_NAMELEN) {
121613a2f6bSGordon Ross 		err = EAI_OVERFLOW;
122613a2f6bSGordon Ross 		goto out;
123613a2f6bSGordon Ross 	}
124613a2f6bSGordon Ross 	ucname = utf8_str_toupper(name);
125613a2f6bSGordon Ross 	if (ucname == NULL)
126613a2f6bSGordon Ross 		goto nomem;
127613a2f6bSGordon Ross 
128613a2f6bSGordon Ross 	/* Note: this returns an NBERROR value. */
129613a2f6bSGordon Ross 	err = nbns_resolvename(ucname, nbc, &sap);
130613a2f6bSGordon Ross 	if (err) {
131613a2f6bSGordon Ross 		if (smb_verbose)
132613a2f6bSGordon Ross 			smb_error(dgettext(TEXT_DOMAIN,
133613a2f6bSGordon Ross 			    "nbns_resolvename: %s"),
134613a2f6bSGordon Ross 			    err, name);
135613a2f6bSGordon Ross 		err = EAI_NODATA;
136613a2f6bSGordon Ross 		goto out;
137613a2f6bSGordon Ross 	}
138613a2f6bSGordon Ross 	/* Note: sap allocated */
139613a2f6bSGordon Ross 
140613a2f6bSGordon Ross 	/*
141613a2f6bSGordon Ross 	 * Build the addrinfo struct to return.
142613a2f6bSGordon Ross 	 */
143613a2f6bSGordon Ross 	nai = malloc(sizeof (*nai));
144613a2f6bSGordon Ross 	if (nai == NULL)
145613a2f6bSGordon Ross 		goto nomem;
146613a2f6bSGordon Ross 	bzero(nai, sizeof (*nai));
147613a2f6bSGordon Ross 
148613a2f6bSGordon Ross 	nai->ai_flags = AI_CANONNAME;
149613a2f6bSGordon Ross 	nai->ai_family = sap->sa_family;
150613a2f6bSGordon Ross 	nai->ai_socktype = SOCK_STREAM;
151613a2f6bSGordon Ross 	nai->ai_canonname = ucname;
152613a2f6bSGordon Ross 	ucname = NULL;
153613a2f6bSGordon Ross 
154613a2f6bSGordon Ross 	/*
155613a2f6bSGordon Ross 	 * The type of this is really sockaddr_in,
156613a2f6bSGordon Ross 	 * but is returned in the generic form.
157613a2f6bSGordon Ross 	 * See nbns_resolvename.
158613a2f6bSGordon Ross 	 */
159613a2f6bSGordon Ross 	nai->ai_addrlen = sizeof (struct sockaddr_in);
160613a2f6bSGordon Ross 	nai->ai_addr = sap;
161613a2f6bSGordon Ross 
162613a2f6bSGordon Ross 	*res = nai;
163613a2f6bSGordon Ross 	return (0);
164613a2f6bSGordon Ross 
165613a2f6bSGordon Ross nomem:
166613a2f6bSGordon Ross 	err = EAI_MEMORY;
167613a2f6bSGordon Ross out:
168613a2f6bSGordon Ross 	if (nai != NULL)
169613a2f6bSGordon Ross 		free(nai);
170613a2f6bSGordon Ross 	if (sap)
171613a2f6bSGordon Ross 		free(sap);
172613a2f6bSGordon Ross 	if (ucname)
173613a2f6bSGordon Ross 		free(ucname);
174613a2f6bSGordon Ross 	*res = NULL;
175613a2f6bSGordon Ross 
176613a2f6bSGordon Ross 	return (err);
177613a2f6bSGordon Ross }
1784bff34e3Sthurlow 
1794bff34e3Sthurlow int
nbns_resolvename(const char * name,struct nb_ctx * ctx,struct sockaddr ** adpp)1804bff34e3Sthurlow nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
1814bff34e3Sthurlow {
1824bff34e3Sthurlow 	struct nbns_rq *rqp;
1834bff34e3Sthurlow 	struct nb_name nn;
1844bff34e3Sthurlow 	struct nbns_rr rr;
1854bff34e3Sthurlow 	struct sockaddr_in *dest;
1864bff34e3Sthurlow 	int error, rdrcount, len;
1874bff34e3Sthurlow 
188613a2f6bSGordon Ross 	if (strlen(name) >= NB_NAMELEN)
1894bff34e3Sthurlow 		return (NBERROR(NBERR_NAMETOOLONG));
1904bff34e3Sthurlow 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
1914bff34e3Sthurlow 	if (error)
1924bff34e3Sthurlow 		return (error);
1934bff34e3Sthurlow 	/*
1944bff34e3Sthurlow 	 * Pad the name with blanks, but
1954bff34e3Sthurlow 	 * leave the "type" byte NULL.
1964bff34e3Sthurlow 	 * nb_name_encode adds the type.
1974bff34e3Sthurlow 	 */
1984bff34e3Sthurlow 	bzero(&nn, sizeof (nn));
1994bff34e3Sthurlow 	snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
2004bff34e3Sthurlow 	nn.nn_type = NBT_SERVER;
2014bff34e3Sthurlow 	nn.nn_scope = ctx->nb_scope;
2024bff34e3Sthurlow 	rqp->nr_nmflags = NBNS_NMFLAG_RD;
2034bff34e3Sthurlow 	rqp->nr_qdname = &nn;
2044bff34e3Sthurlow 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
2054bff34e3Sthurlow 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2064bff34e3Sthurlow 	rqp->nr_qdcount = 1;
2074bff34e3Sthurlow 	rqp->nr_maxretry = 5;
2084bff34e3Sthurlow 
2094bff34e3Sthurlow 	error = nbns_rq_prepare(rqp);
2104bff34e3Sthurlow 	if (error) {
2114bff34e3Sthurlow 		nbns_rq_done(rqp);
2124bff34e3Sthurlow 		return (error);
2134bff34e3Sthurlow 	}
2144bff34e3Sthurlow 	rdrcount = NBNS_MAXREDIRECTS;
2154bff34e3Sthurlow 	for (;;) {
2164bff34e3Sthurlow 		error = nbns_rq(rqp);
2174bff34e3Sthurlow 		if (error)
2184bff34e3Sthurlow 			break;
2194bff34e3Sthurlow 		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
2204bff34e3Sthurlow 			/*
2214bff34e3Sthurlow 			 * Not an authoritative answer.  Query again
2224bff34e3Sthurlow 			 * using the NS address in the 2nd record.
2234bff34e3Sthurlow 			 */
2244bff34e3Sthurlow 			if (rdrcount-- == 0) {
2254bff34e3Sthurlow 				error = NBERROR(NBERR_TOOMANYREDIRECTS);
2264bff34e3Sthurlow 				break;
2274bff34e3Sthurlow 			}
2284bff34e3Sthurlow 			error = nbns_rq_getrr(rqp, &rr);
2294bff34e3Sthurlow 			if (error)
2304bff34e3Sthurlow 				break;
2314bff34e3Sthurlow 			error = nbns_rq_getrr(rqp, &rr);
2324bff34e3Sthurlow 			if (error)
2334bff34e3Sthurlow 				break;
2344bff34e3Sthurlow 			bcopy(rr.rr_data, &rqp->nr_dest, 4);
2354bff34e3Sthurlow 			continue;
2364bff34e3Sthurlow 		}
2374bff34e3Sthurlow 		if (rqp->nr_rpancount == 0) {
2384bff34e3Sthurlow 			error = NBERROR(NBERR_HOSTNOTFOUND);
2394bff34e3Sthurlow 			break;
2404bff34e3Sthurlow 		}
2414bff34e3Sthurlow 		error = nbns_rq_getrr(rqp, &rr);
2424bff34e3Sthurlow 		if (error)
2434bff34e3Sthurlow 			break;
2444bff34e3Sthurlow 		len = sizeof (struct sockaddr_in);
2454bff34e3Sthurlow 		dest = malloc(len);
2464bff34e3Sthurlow 		if (dest == NULL)
2474bff34e3Sthurlow 			return (ENOMEM);
2484bff34e3Sthurlow 		bzero(dest, len);
2494bff34e3Sthurlow 		/*
250613a2f6bSGordon Ross 		 * Solaris sockaddr_in doesn't a sin_len field.
2514bff34e3Sthurlow 		 * dest->sin_len = len;
2524bff34e3Sthurlow 		 */
253613a2f6bSGordon Ross 		dest->sin_family = AF_NETBIOS;	/* nb_lib.h */
2544bff34e3Sthurlow 		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
2554bff34e3Sthurlow 		*adpp = (struct sockaddr *)dest;
2564bff34e3Sthurlow 		ctx->nb_lastns = rqp->nr_sender;
2574bff34e3Sthurlow 		break;
2584bff34e3Sthurlow 	}
2594bff34e3Sthurlow 	nbns_rq_done(rqp);
2604bff34e3Sthurlow 	return (error);
2614bff34e3Sthurlow }
2624bff34e3Sthurlow 
263613a2f6bSGordon Ross /*
264613a2f6bSGordon Ross  * NB: system, workgroup are both NB_NAMELEN
265613a2f6bSGordon Ross  */
2664bff34e3Sthurlow int
nbns_getnodestatus(struct nb_ctx * ctx,struct in_addr * targethost,char * system,char * workgroup)267613a2f6bSGordon Ross nbns_getnodestatus(struct nb_ctx *ctx,
268613a2f6bSGordon Ross     struct in_addr *targethost, char *system, char *workgroup)
2694bff34e3Sthurlow {
2704bff34e3Sthurlow 	struct nbns_rq *rqp;
2714bff34e3Sthurlow 	struct nbns_rr rr;
2724bff34e3Sthurlow 	struct nb_name nn;
2734bff34e3Sthurlow 	struct nbns_nr *nrp;
2744bff34e3Sthurlow 	char nrtype;
2754bff34e3Sthurlow 	char *cp, *retname = NULL;
2764bff34e3Sthurlow 	unsigned char nrcount;
277613a2f6bSGordon Ross 	int error, i, foundserver = 0, foundgroup = 0;
2784bff34e3Sthurlow 
2794bff34e3Sthurlow 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
2804bff34e3Sthurlow 	if (error)
2814bff34e3Sthurlow 		return (error);
2824bff34e3Sthurlow 	bzero(&nn, sizeof (nn));
2834bff34e3Sthurlow 	strcpy((char *)nn.nn_name, "*");
2844bff34e3Sthurlow 	nn.nn_scope = ctx->nb_scope;
2854bff34e3Sthurlow 	nn.nn_type = NBT_WKSTA;
2864bff34e3Sthurlow 	rqp->nr_nmflags = 0;
2874bff34e3Sthurlow 	rqp->nr_qdname = &nn;
2884bff34e3Sthurlow 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
2894bff34e3Sthurlow 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
2904bff34e3Sthurlow 	rqp->nr_qdcount = 1;
2914bff34e3Sthurlow 	rqp->nr_maxretry = 2;
2924bff34e3Sthurlow 
293613a2f6bSGordon Ross 	rqp->nr_dest = *targethost;
2944bff34e3Sthurlow 	error = nbns_rq_prepare(rqp);
2954bff34e3Sthurlow 	if (error) {
2964bff34e3Sthurlow 		nbns_rq_done(rqp);
2974bff34e3Sthurlow 		return (error);
2984bff34e3Sthurlow 	}
2994bff34e3Sthurlow 
3004bff34e3Sthurlow 	/*
3014bff34e3Sthurlow 	 * Darwin had a loop here, allowing redirect, etc.
3024bff34e3Sthurlow 	 * but we only handle point-to-point for node status.
3034bff34e3Sthurlow 	 */
3044bff34e3Sthurlow 	error = nbns_rq(rqp);
3054bff34e3Sthurlow 	if (error)
3064bff34e3Sthurlow 		goto out;
3074bff34e3Sthurlow 	if (rqp->nr_rpancount == 0) {
3084bff34e3Sthurlow 		error = NBERROR(NBERR_HOSTNOTFOUND);
3094bff34e3Sthurlow 		goto out;
3104bff34e3Sthurlow 	}
3114bff34e3Sthurlow 	error = nbns_rq_getrr(rqp, &rr);
3124bff34e3Sthurlow 	if (error)
3134bff34e3Sthurlow 		goto out;
3144bff34e3Sthurlow 
3154bff34e3Sthurlow 	/* Compiler didn't like cast on lvalue++ */
3164bff34e3Sthurlow 	nrcount = *((unsigned char *)rr.rr_data);
3174bff34e3Sthurlow 	rr.rr_data++;
3184bff34e3Sthurlow 	/* LINTED */
3194bff34e3Sthurlow 	for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
3204bff34e3Sthurlow 	    i <= nrcount; ++i, ++nrp) {
3214bff34e3Sthurlow 		nrtype = nrp->ns_name[NB_NAMELEN-1];
3224bff34e3Sthurlow 		/* Terminate the string: */
3234bff34e3Sthurlow 		nrp->ns_name[NB_NAMELEN-1] = (char)0;
3244bff34e3Sthurlow 		/* Strip off trailing spaces */
3254bff34e3Sthurlow 		for (cp = &nrp->ns_name[NB_NAMELEN-2];
3264bff34e3Sthurlow 		    cp >= nrp->ns_name; --cp) {
3274bff34e3Sthurlow 			if (*cp != (char)0x20)
3284bff34e3Sthurlow 				break;
3294bff34e3Sthurlow 			*cp = (char)0;
3304bff34e3Sthurlow 		}
3314bff34e3Sthurlow 		nrp->ns_flags = ntohs(nrp->ns_flags);
332613a2f6bSGordon Ross 		DPRINT(" %s[%02x] Flags 0x%x",
333613a2f6bSGordon Ross 		    nrp->ns_name, nrtype, nrp->ns_flags);
3344bff34e3Sthurlow 		if (nrp->ns_flags & NBNS_GROUPFLG) {
3354bff34e3Sthurlow 			if (!foundgroup ||
3364bff34e3Sthurlow 			    (foundgroup != NBT_WKSTA+1 &&
3374bff34e3Sthurlow 			    nrtype == NBT_WKSTA)) {
338613a2f6bSGordon Ross 				strlcpy(workgroup, nrp->ns_name,
339613a2f6bSGordon Ross 				    NB_NAMELEN);
3404bff34e3Sthurlow 				foundgroup = nrtype+1;
3414bff34e3Sthurlow 			}
3424bff34e3Sthurlow 		} else {
3434bff34e3Sthurlow 			/*
3444bff34e3Sthurlow 			 * Track at least ONE name, in case
3454bff34e3Sthurlow 			 * no server name is found
3464bff34e3Sthurlow 			 */
3474bff34e3Sthurlow 			retname = nrp->ns_name;
3484bff34e3Sthurlow 		}
349613a2f6bSGordon Ross 		/*
350613a2f6bSGordon Ross 		 * Keep the first NBT_SERVER name.
351613a2f6bSGordon Ross 		 */
352613a2f6bSGordon Ross 		if (nrtype == NBT_SERVER && foundserver == 0) {
353613a2f6bSGordon Ross 			strlcpy(system, nrp->ns_name,
354613a2f6bSGordon Ross 			    NB_NAMELEN);
3554bff34e3Sthurlow 			foundserver = 1;
3564bff34e3Sthurlow 		}
3574bff34e3Sthurlow 	}
358*a547be5dSGordon Ross 	if (foundserver == 0 && retname != NULL)
359613a2f6bSGordon Ross 		strlcpy(system, retname, NB_NAMELEN);
3604bff34e3Sthurlow 	ctx->nb_lastns = rqp->nr_sender;
3614bff34e3Sthurlow 
3624bff34e3Sthurlow out:
3634bff34e3Sthurlow 	nbns_rq_done(rqp);
3644bff34e3Sthurlow 	return (error);
3654bff34e3Sthurlow }
3664bff34e3Sthurlow 
3674bff34e3Sthurlow int
nbns_rq_create(int opcode,struct nb_ctx * ctx,struct nbns_rq ** rqpp)3684bff34e3Sthurlow nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
3694bff34e3Sthurlow {
3704bff34e3Sthurlow 	struct nbns_rq *rqp;
3714bff34e3Sthurlow 	static uint16_t trnid;
3724bff34e3Sthurlow 	int error;
3734bff34e3Sthurlow 
3744bff34e3Sthurlow 	if (trnid == 0)
3754bff34e3Sthurlow 		trnid = getpid();
3764bff34e3Sthurlow 	rqp = malloc(sizeof (*rqp));
3774bff34e3Sthurlow 	if (rqp == NULL)
3784bff34e3Sthurlow 		return (ENOMEM);
3794bff34e3Sthurlow 	bzero(rqp, sizeof (*rqp));
38002d09e03SGordon Ross 	error = mb_init_sz(&rqp->nr_rq, NBDG_MAXSIZE);
3814bff34e3Sthurlow 	if (error) {
3824bff34e3Sthurlow 		free(rqp);
3834bff34e3Sthurlow 		return (error);
3844bff34e3Sthurlow 	}
3854bff34e3Sthurlow 	rqp->nr_opcode = opcode;
3864bff34e3Sthurlow 	rqp->nr_nbd = ctx;
3874bff34e3Sthurlow 	rqp->nr_trnid = trnid++;
3884bff34e3Sthurlow 	*rqpp = rqp;
3894bff34e3Sthurlow 	return (0);
3904bff34e3Sthurlow }
3914bff34e3Sthurlow 
3924bff34e3Sthurlow void
nbns_rq_done(struct nbns_rq * rqp)3934bff34e3Sthurlow nbns_rq_done(struct nbns_rq *rqp)
3944bff34e3Sthurlow {
3954bff34e3Sthurlow 	if (rqp == NULL)
3964bff34e3Sthurlow 		return;
3974bff34e3Sthurlow 	if (rqp->nr_fd >= 0)
3984bff34e3Sthurlow 		close(rqp->nr_fd);
3994bff34e3Sthurlow 	mb_done(&rqp->nr_rq);
4004bff34e3Sthurlow 	mb_done(&rqp->nr_rp);
4014bff34e3Sthurlow 	if (rqp->nr_if)
4024bff34e3Sthurlow 		free(rqp->nr_if);
4034bff34e3Sthurlow 	free(rqp);
4044bff34e3Sthurlow }
4054bff34e3Sthurlow 
4064bff34e3Sthurlow /*
4074bff34e3Sthurlow  * Extract resource record from the packet. Assume that there is only
4084bff34e3Sthurlow  * one mbuf.
4094bff34e3Sthurlow  */
4104bff34e3Sthurlow int
nbns_rq_getrr(struct nbns_rq * rqp,struct nbns_rr * rrp)4114bff34e3Sthurlow nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
4124bff34e3Sthurlow {
4134bff34e3Sthurlow 	struct mbdata *mbp = &rqp->nr_rp;
4144bff34e3Sthurlow 	uchar_t *cp;
4154bff34e3Sthurlow 	int error, len;
4164bff34e3Sthurlow 
4174bff34e3Sthurlow 	bzero(rrp, sizeof (*rrp));
4184bff34e3Sthurlow 	cp = (uchar_t *)mbp->mb_pos;
4194bff34e3Sthurlow 	len = nb_encname_len(cp);
4204bff34e3Sthurlow 	if (len < 1)
4214bff34e3Sthurlow 		return (NBERROR(NBERR_INVALIDRESPONSE));
4224bff34e3Sthurlow 	rrp->rr_name = cp;
42302d09e03SGordon Ross 	error = md_get_mem(mbp, NULL, len, MB_MSYSTEM);
4244bff34e3Sthurlow 	if (error)
4254bff34e3Sthurlow 		return (error);
42602d09e03SGordon Ross 	md_get_uint16be(mbp, &rrp->rr_type);
42702d09e03SGordon Ross 	md_get_uint16be(mbp, &rrp->rr_class);
42802d09e03SGordon Ross 	md_get_uint32be(mbp, &rrp->rr_ttl);
42902d09e03SGordon Ross 	md_get_uint16be(mbp, &rrp->rr_rdlength);
4304bff34e3Sthurlow 	rrp->rr_data = (uchar_t *)mbp->mb_pos;
43102d09e03SGordon Ross 	error = md_get_mem(mbp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
4324bff34e3Sthurlow 	return (error);
4334bff34e3Sthurlow }
4344bff34e3Sthurlow 
4354bff34e3Sthurlow int
nbns_rq_prepare(struct nbns_rq * rqp)4364bff34e3Sthurlow nbns_rq_prepare(struct nbns_rq *rqp)
4374bff34e3Sthurlow {
4384bff34e3Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
4394bff34e3Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
4404bff34e3Sthurlow 	uint16_t ofr; /* opcode, flags, rcode */
441613a2f6bSGordon Ross 	int error;
4424bff34e3Sthurlow 
44302d09e03SGordon Ross 	error = mb_init_sz(&rqp->nr_rp, NBDG_MAXSIZE);
4444bff34e3Sthurlow 	if (error)
4454bff34e3Sthurlow 		return (error);
4464bff34e3Sthurlow 
4474bff34e3Sthurlow 	/*
4484bff34e3Sthurlow 	 * When looked into the ethereal trace, 'nmblookup' command sets this
4494bff34e3Sthurlow 	 * flag. We will also set.
4504bff34e3Sthurlow 	 */
4514bff34e3Sthurlow 	mb_put_uint16be(mbp, rqp->nr_trnid);
4524bff34e3Sthurlow 	ofr = ((rqp->nr_opcode & 0x1F) << 11) |
4534bff34e3Sthurlow 	    ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
4544bff34e3Sthurlow 	mb_put_uint16be(mbp, ofr);
4554bff34e3Sthurlow 	mb_put_uint16be(mbp, rqp->nr_qdcount);
4564bff34e3Sthurlow 	mb_put_uint16be(mbp, rqp->nr_ancount);
4574bff34e3Sthurlow 	mb_put_uint16be(mbp, rqp->nr_nscount);
45802d09e03SGordon Ross 	error = mb_put_uint16be(mbp, rqp->nr_arcount);
4594bff34e3Sthurlow 	if (rqp->nr_qdcount) {
4604bff34e3Sthurlow 		if (rqp->nr_qdcount > 1)
4614bff34e3Sthurlow 			return (EINVAL);
46202d09e03SGordon Ross 		(void) nb_name_encode(mbp, rqp->nr_qdname);
4634bff34e3Sthurlow 		mb_put_uint16be(mbp, rqp->nr_qdtype);
46402d09e03SGordon Ross 		error = mb_put_uint16be(mbp, rqp->nr_qdclass);
4654bff34e3Sthurlow 	}
46602d09e03SGordon Ross 	if (error)
46702d09e03SGordon Ross 		return (error);
46802d09e03SGordon Ross 	error = m_lineup(mbp->mb_top, &mbp->mb_top);
46902d09e03SGordon Ross 	if (error)
47002d09e03SGordon Ross 		return (error);
4714bff34e3Sthurlow 	if (ctx->nb_timo == 0)
4724bff34e3Sthurlow 		ctx->nb_timo = 1;	/* by default 1 second */
4734bff34e3Sthurlow 	return (0);
4744bff34e3Sthurlow }
4754bff34e3Sthurlow 
4764bff34e3Sthurlow static int
nbns_rq_recv(struct nbns_rq * rqp)4774bff34e3Sthurlow nbns_rq_recv(struct nbns_rq *rqp)
4784bff34e3Sthurlow {
4794bff34e3Sthurlow 	struct mbdata *mbp = &rqp->nr_rp;
4804bff34e3Sthurlow 	void *rpdata = mtod(mbp->mb_top, void *);
4814bff34e3Sthurlow 	fd_set rd, wr, ex;
4824bff34e3Sthurlow 	struct timeval tv;
4834bff34e3Sthurlow 	struct sockaddr_in sender;
4844bff34e3Sthurlow 	int s = rqp->nr_fd;
4854bff34e3Sthurlow 	int n, len;
4864bff34e3Sthurlow 
4874bff34e3Sthurlow 	FD_ZERO(&rd);
4884bff34e3Sthurlow 	FD_ZERO(&wr);
4894bff34e3Sthurlow 	FD_ZERO(&ex);
4904bff34e3Sthurlow 	FD_SET(s, &rd);
4914bff34e3Sthurlow 
4924bff34e3Sthurlow 	tv.tv_sec = rqp->nr_nbd->nb_timo;
4934bff34e3Sthurlow 	tv.tv_usec = 0;
4944bff34e3Sthurlow 
4954bff34e3Sthurlow 	n = select(s + 1, &rd, &wr, &ex, &tv);
4964bff34e3Sthurlow 	if (n == -1)
4974bff34e3Sthurlow 		return (-1);
4984bff34e3Sthurlow 	if (n == 0)
4994bff34e3Sthurlow 		return (ETIMEDOUT);
5004bff34e3Sthurlow 	if (FD_ISSET(s, &rd) == 0)
5014bff34e3Sthurlow 		return (ETIMEDOUT);
5024bff34e3Sthurlow 	len = sizeof (sender);
5034bff34e3Sthurlow 	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
5044bff34e3Sthurlow 	    (struct sockaddr *)&sender, &len);
5054bff34e3Sthurlow 	if (n < 0)
5064bff34e3Sthurlow 		return (errno);
5074bff34e3Sthurlow 	mbp->mb_top->m_len = mbp->mb_count = n;
5084bff34e3Sthurlow 	rqp->nr_sender = sender;
5094bff34e3Sthurlow 	return (0);
5104bff34e3Sthurlow }
5114bff34e3Sthurlow 
5124bff34e3Sthurlow static int
nbns_rq_opensocket(struct nbns_rq * rqp)5134bff34e3Sthurlow nbns_rq_opensocket(struct nbns_rq *rqp)
5144bff34e3Sthurlow {
5154bff34e3Sthurlow 	struct sockaddr_in locaddr;
5164bff34e3Sthurlow 	int opt = 1, s;
5174bff34e3Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
5184bff34e3Sthurlow 
5194bff34e3Sthurlow 	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
5204bff34e3Sthurlow 	if (s < 0)
5214bff34e3Sthurlow 		return (errno);
5224bff34e3Sthurlow 	if (ctx->nb_flags & NBCF_BC_ENABLE) {
5234bff34e3Sthurlow 		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
5244bff34e3Sthurlow 		    sizeof (opt)) < 0)
5254bff34e3Sthurlow 			return (errno);
5264bff34e3Sthurlow 	}
5274bff34e3Sthurlow 	if (is_system_labeled())
5284bff34e3Sthurlow 		(void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt,
5294bff34e3Sthurlow 		    sizeof (opt));
5304bff34e3Sthurlow 	bzero(&locaddr, sizeof (locaddr));
5314bff34e3Sthurlow 	locaddr.sin_family = AF_INET;
5324bff34e3Sthurlow 	/* locaddr.sin_len = sizeof (locaddr); */
5334bff34e3Sthurlow 	if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
5344bff34e3Sthurlow 		return (errno);
5354bff34e3Sthurlow 	return (0);
5364bff34e3Sthurlow }
5374bff34e3Sthurlow 
5384bff34e3Sthurlow static int
nbns_rq_send(struct nbns_rq * rqp,in_addr_t ina)5394bff34e3Sthurlow nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
5404bff34e3Sthurlow {
5414bff34e3Sthurlow 	struct sockaddr_in dest;
5424bff34e3Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
5434bff34e3Sthurlow 	int s = rqp->nr_fd;
5444bff34e3Sthurlow 	uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
5454bff34e3Sthurlow 	uint16_t *datap;
5464bff34e3Sthurlow 	uint8_t nmflags;
5474bff34e3Sthurlow 	int rc;
5484bff34e3Sthurlow 
5494bff34e3Sthurlow 	bzero(&dest, sizeof (dest));
5504bff34e3Sthurlow 	dest.sin_family = AF_INET;
551613a2f6bSGordon Ross 	dest.sin_port = htons(IPPORT_NETBIOS_NS);
5524bff34e3Sthurlow 	dest.sin_addr.s_addr = ina;
5534bff34e3Sthurlow 
5544bff34e3Sthurlow 	if (ina == INADDR_BROADCAST) {
5554bff34e3Sthurlow 		/* Turn on the broadcast bit. */
5564bff34e3Sthurlow 		nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
5574bff34e3Sthurlow 		/*LINTED*/
5584bff34e3Sthurlow 		datap = mtod(mbp->mb_top, uint16_t *);
5594bff34e3Sthurlow 		ofr = ((rqp->nr_opcode & 0x1F) << 11) |
5604bff34e3Sthurlow 		    ((nmflags & 0x7F) << 4); /* rcode=0 */
5614bff34e3Sthurlow 		ofr_save = datap[1];
5624bff34e3Sthurlow 		datap[1] = htons(ofr);
5634bff34e3Sthurlow 	}
5644bff34e3Sthurlow 
5654bff34e3Sthurlow 	rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
5664bff34e3Sthurlow 	    (struct sockaddr *)&dest, sizeof (dest));
5674bff34e3Sthurlow 
5684bff34e3Sthurlow 	if (ina == INADDR_BROADCAST) {
5694bff34e3Sthurlow 		/* Turn the broadcast bit back off. */
5704bff34e3Sthurlow 		datap[1] = ofr_save;
5714bff34e3Sthurlow 	}
5724bff34e3Sthurlow 
5734bff34e3Sthurlow 
5744bff34e3Sthurlow 	if (rc < 0)
5754bff34e3Sthurlow 		return (errno);
5764bff34e3Sthurlow 
5774bff34e3Sthurlow 	return (0);
5784bff34e3Sthurlow }
5794bff34e3Sthurlow 
5804bff34e3Sthurlow int
nbns_rq(struct nbns_rq * rqp)5814bff34e3Sthurlow nbns_rq(struct nbns_rq *rqp)
5824bff34e3Sthurlow {
5834bff34e3Sthurlow 	struct nb_ctx *ctx = rqp->nr_nbd;
5844bff34e3Sthurlow 	struct mbdata *mbp = &rqp->nr_rq;
5854bff34e3Sthurlow 	uint16_t ofr, rpid;
5864bff34e3Sthurlow 	int error, tries, maxretry;
5874bff34e3Sthurlow 
5884bff34e3Sthurlow 	error = nbns_rq_opensocket(rqp);
5894bff34e3Sthurlow 	if (error)
5904bff34e3Sthurlow 		return (error);
5914bff34e3Sthurlow 
5924bff34e3Sthurlow 	maxretry = rqp->nr_maxretry;
5934bff34e3Sthurlow 	for (tries = 0; tries < maxretry; tries++) {
5944bff34e3Sthurlow 
5954bff34e3Sthurlow 		/*
5964bff34e3Sthurlow 		 * Minor hack: If nr_dest is set, send there only.
5974bff34e3Sthurlow 		 * Used by _getnodestatus, _resolvname redirects.
5984bff34e3Sthurlow 		 */
5994bff34e3Sthurlow 		if (rqp->nr_dest.s_addr) {
6004bff34e3Sthurlow 			error = nbns_rq_send(rqp, rqp->nr_dest.s_addr);
6014bff34e3Sthurlow 			if (error) {
6024bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6034bff34e3Sthurlow 				    "nbns error %d sending to %s"),
6044bff34e3Sthurlow 				    0, error, inet_ntoa(rqp->nr_dest));
6054bff34e3Sthurlow 			}
6064bff34e3Sthurlow 			goto do_recv;
6074bff34e3Sthurlow 		}
6084bff34e3Sthurlow 
6094bff34e3Sthurlow 		if (ctx->nb_wins1) {
6104bff34e3Sthurlow 			error = nbns_rq_send(rqp, ctx->nb_wins1);
6114bff34e3Sthurlow 			if (error) {
6124bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6134bff34e3Sthurlow 				    "nbns error %d sending to wins1"),
6144bff34e3Sthurlow 				    0, error);
6154bff34e3Sthurlow 			}
6164bff34e3Sthurlow 		}
6174bff34e3Sthurlow 
6184bff34e3Sthurlow 		if (ctx->nb_wins2 && (tries > 0)) {
6194bff34e3Sthurlow 			error = nbns_rq_send(rqp, ctx->nb_wins2);
6204bff34e3Sthurlow 			if (error) {
6214bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6224bff34e3Sthurlow 				    "nbns error %d sending to wins2"),
6234bff34e3Sthurlow 				    0, error);
6244bff34e3Sthurlow 			}
6254bff34e3Sthurlow 		}
6264bff34e3Sthurlow 
6274bff34e3Sthurlow 		/*
6284bff34e3Sthurlow 		 * If broadcast is enabled, start broadcasting
6294bff34e3Sthurlow 		 * only after wins servers fail to respond, or
6304bff34e3Sthurlow 		 * immediately if no WINS servers configured.
6314bff34e3Sthurlow 		 */
6324bff34e3Sthurlow 		if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
6334bff34e3Sthurlow 		    ((tries > 1) || (ctx->nb_wins1 == 0))) {
6344bff34e3Sthurlow 			error = nbns_rq_send(rqp, INADDR_BROADCAST);
6354bff34e3Sthurlow 			if (error) {
6364bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
6374bff34e3Sthurlow 				    "nbns error %d sending broadcast"),
6384bff34e3Sthurlow 				    0, error);
6394bff34e3Sthurlow 			}
6404bff34e3Sthurlow 		}
6414bff34e3Sthurlow 
6424bff34e3Sthurlow 		/*
6434bff34e3Sthurlow 		 * Wait for responses from ANY of the above.
6444bff34e3Sthurlow 		 */
6454bff34e3Sthurlow do_recv:
6464bff34e3Sthurlow 		error = nbns_rq_recv(rqp);
6474bff34e3Sthurlow 		if (error == ETIMEDOUT)
6484bff34e3Sthurlow 			continue;
6494bff34e3Sthurlow 		if (error) {
6504bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
6514bff34e3Sthurlow 			    "nbns recv error %d"),
6524bff34e3Sthurlow 			    0, error);
6534bff34e3Sthurlow 			return (error);
6544bff34e3Sthurlow 		}
6554bff34e3Sthurlow 
6564bff34e3Sthurlow 		mbp = &rqp->nr_rp;
6574bff34e3Sthurlow 		if (mbp->mb_count < 12)
6584bff34e3Sthurlow 			return (NBERROR(NBERR_INVALIDRESPONSE));
65902d09e03SGordon Ross 		md_get_uint16be(mbp, &rpid);
6604bff34e3Sthurlow 		if (rpid != rqp->nr_trnid)
6614bff34e3Sthurlow 			return (NBERROR(NBERR_INVALIDRESPONSE));
6624bff34e3Sthurlow 		break;
6634bff34e3Sthurlow 	}
664613a2f6bSGordon Ross 	if (tries == maxretry)
665613a2f6bSGordon Ross 		return (NBERROR(NBERR_HOSTNOTFOUND));
6664bff34e3Sthurlow 
66702d09e03SGordon Ross 	md_get_uint16be(mbp, &ofr);
6684bff34e3Sthurlow 	rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
6694bff34e3Sthurlow 	rqp->nr_rprcode = ofr & 0xf;
6704bff34e3Sthurlow 	if (rqp->nr_rprcode)
6714bff34e3Sthurlow 		return (NBERROR(rqp->nr_rprcode));
67202d09e03SGordon Ross 	md_get_uint16be(mbp, &rpid);	/* QDCOUNT */
67302d09e03SGordon Ross 	md_get_uint16be(mbp, &rqp->nr_rpancount);
67402d09e03SGordon Ross 	md_get_uint16be(mbp, &rqp->nr_rpnscount);
67502d09e03SGordon Ross 	md_get_uint16be(mbp, &rqp->nr_rparcount);
6764bff34e3Sthurlow 	return (0);
6774bff34e3Sthurlow }
678