xref: /illumos-gate/usr/src/lib/libsmbfs/smb/nbns_rq.c (revision f0243e0a614849de4aa0ea9de2c50de8532a346a)
1 /*
2  * Copyright (c) 2000, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <ctype.h>
41 #include <netdb.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <strings.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <libintl.h>
49 
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #include <tsol/label.h>
53 
54 #define	NB_NEEDRESOLVER
55 #include <netsmb/netbios.h>
56 #include <netsmb/smb_lib.h>
57 #include <netsmb/nb_lib.h>
58 #include <netsmb/mchain.h>
59 
60 static int  nbns_rq_create(int opcode, struct nb_ctx *ctx,
61     struct nbns_rq **rqpp);
62 static void nbns_rq_done(struct nbns_rq *rqp);
63 static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
64 static int  nbns_rq_prepare(struct nbns_rq *rqp);
65 static int  nbns_rq(struct nbns_rq *rqp);
66 
67 static struct nb_ifdesc *nb_iflist = NULL;
68 
69 int
70 nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
71 {
72 	struct nbns_rq *rqp;
73 	struct nb_name nn;
74 	struct nbns_rr rr;
75 	struct sockaddr_in *dest;
76 	int error, rdrcount, len;
77 
78 	if (strlen(name) > NB_NAMELEN)
79 		return (NBERROR(NBERR_NAMETOOLONG));
80 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
81 	if (error)
82 		return (error);
83 	/*
84 	 * Pad the name with blanks, but
85 	 * leave the "type" byte NULL.
86 	 * nb_name_encode adds the type.
87 	 */
88 	bzero(&nn, sizeof (nn));
89 	snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
90 	nn.nn_type = NBT_SERVER;
91 	nn.nn_scope = ctx->nb_scope;
92 	rqp->nr_nmflags = NBNS_NMFLAG_RD;
93 	rqp->nr_qdname = &nn;
94 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
95 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
96 	rqp->nr_qdcount = 1;
97 	rqp->nr_maxretry = 5;
98 
99 	error = nbns_rq_prepare(rqp);
100 	if (error) {
101 		nbns_rq_done(rqp);
102 		return (error);
103 	}
104 	rdrcount = NBNS_MAXREDIRECTS;
105 	for (;;) {
106 		error = nbns_rq(rqp);
107 		if (error)
108 			break;
109 		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
110 			/*
111 			 * Not an authoritative answer.  Query again
112 			 * using the NS address in the 2nd record.
113 			 */
114 			if (rdrcount-- == 0) {
115 				error = NBERROR(NBERR_TOOMANYREDIRECTS);
116 				break;
117 			}
118 			error = nbns_rq_getrr(rqp, &rr);
119 			if (error)
120 				break;
121 			error = nbns_rq_getrr(rqp, &rr);
122 			if (error)
123 				break;
124 			bcopy(rr.rr_data, &rqp->nr_dest, 4);
125 			continue;
126 		}
127 		if (rqp->nr_rpancount == 0) {
128 			error = NBERROR(NBERR_HOSTNOTFOUND);
129 			break;
130 		}
131 		error = nbns_rq_getrr(rqp, &rr);
132 		if (error)
133 			break;
134 		len = sizeof (struct sockaddr_in);
135 		dest = malloc(len);
136 		if (dest == NULL)
137 			return (ENOMEM);
138 		bzero(dest, len);
139 		/*
140 		 * Solaris sockaddr_in doesn't have this field.
141 		 * dest->sin_len = len;
142 		 */
143 		dest->sin_family = AF_INET;
144 		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
145 		dest->sin_port = htons(SMB_TCP_PORT);
146 		*adpp = (struct sockaddr *)dest;
147 		ctx->nb_lastns = rqp->nr_sender;
148 		break;
149 	}
150 	nbns_rq_done(rqp);
151 	return (error);
152 }
153 
154 static char *
155 smb_optstrncpy(char *d, char *s, unsigned maxlen)
156 {
157 	if (d && s) {
158 		strncpy(d, s, maxlen);
159 		d[maxlen] = (char)0;
160 	}
161 	return (d);
162 }
163 
164 
165 int
166 nbns_getnodestatus(struct sockaddr *targethost,
167     struct nb_ctx *ctx, char *system, char *workgroup)
168 {
169 	struct nbns_rq *rqp;
170 	struct nbns_rr rr;
171 	struct nb_name nn;
172 	struct nbns_nr *nrp;
173 	char nrtype;
174 	char *cp, *retname = NULL;
175 	struct sockaddr_in *dest;
176 	unsigned char nrcount;
177 	int error, rdrcount, i, foundserver = 0, foundgroup = 0;
178 
179 	if (targethost->sa_family != AF_INET)
180 		return (EINVAL);
181 	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
182 	if (error)
183 		return (error);
184 	bzero(&nn, sizeof (nn));
185 	strcpy((char *)nn.nn_name, "*");
186 	nn.nn_scope = ctx->nb_scope;
187 	nn.nn_type = NBT_WKSTA;
188 	rqp->nr_nmflags = 0;
189 	rqp->nr_qdname = &nn;
190 	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
191 	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
192 	rqp->nr_qdcount = 1;
193 	rqp->nr_maxretry = 2;
194 
195 	/* LINTED */
196 	dest = (struct sockaddr_in *)targethost;
197 	rqp->nr_dest = dest->sin_addr;
198 
199 	error = nbns_rq_prepare(rqp);
200 	if (error) {
201 		nbns_rq_done(rqp);
202 		return (error);
203 	}
204 
205 	/*
206 	 * Darwin had a loop here, allowing redirect, etc.
207 	 * but we only handle point-to-point for node status.
208 	 */
209 	error = nbns_rq(rqp);
210 	if (error)
211 		goto out;
212 	if (rqp->nr_rpancount == 0) {
213 		error = NBERROR(NBERR_HOSTNOTFOUND);
214 		goto out;
215 	}
216 	error = nbns_rq_getrr(rqp, &rr);
217 	if (error)
218 		goto out;
219 
220 	/* Compiler didn't like cast on lvalue++ */
221 	nrcount = *((unsigned char *)rr.rr_data);
222 	rr.rr_data++;
223 	/* LINTED */
224 	for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
225 	    i <= nrcount; ++i, ++nrp) {
226 		nrtype = nrp->ns_name[NB_NAMELEN-1];
227 		/* Terminate the string: */
228 		nrp->ns_name[NB_NAMELEN-1] = (char)0;
229 		/* Strip off trailing spaces */
230 		for (cp = &nrp->ns_name[NB_NAMELEN-2];
231 		    cp >= nrp->ns_name; --cp) {
232 			if (*cp != (char)0x20)
233 				break;
234 			*cp = (char)0;
235 		}
236 		nrp->ns_flags = ntohs(nrp->ns_flags);
237 		if (nrp->ns_flags & NBNS_GROUPFLG) {
238 			if (!foundgroup ||
239 			    (foundgroup != NBT_WKSTA+1 &&
240 			    nrtype == NBT_WKSTA)) {
241 				smb_optstrncpy(workgroup, nrp->ns_name,
242 				    SMB_MAXUSERNAMELEN);
243 				foundgroup = nrtype+1;
244 			}
245 		} else {
246 			/*
247 			 * Track at least ONE name, in case
248 			 * no server name is found
249 			 */
250 			retname = nrp->ns_name;
251 		}
252 		if (nrtype == NBT_SERVER) {
253 			smb_optstrncpy(system, nrp->ns_name,
254 			    SMB_MAXSRVNAMELEN);
255 			foundserver = 1;
256 		}
257 	}
258 	if (!foundserver)
259 		smb_optstrncpy(system, retname, SMB_MAXSRVNAMELEN);
260 	ctx->nb_lastns = rqp->nr_sender;
261 
262 out:
263 	nbns_rq_done(rqp);
264 	return (error);
265 }
266 
267 int
268 nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
269 {
270 	struct nbns_rq *rqp;
271 	static uint16_t trnid;
272 	int error;
273 
274 	if (trnid == 0)
275 		trnid = getpid();
276 	rqp = malloc(sizeof (*rqp));
277 	if (rqp == NULL)
278 		return (ENOMEM);
279 	bzero(rqp, sizeof (*rqp));
280 	error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE);
281 	if (error) {
282 		free(rqp);
283 		return (error);
284 	}
285 	rqp->nr_opcode = opcode;
286 	rqp->nr_nbd = ctx;
287 	rqp->nr_trnid = trnid++;
288 	*rqpp = rqp;
289 	return (0);
290 }
291 
292 void
293 nbns_rq_done(struct nbns_rq *rqp)
294 {
295 	if (rqp == NULL)
296 		return;
297 	if (rqp->nr_fd >= 0)
298 		close(rqp->nr_fd);
299 	mb_done(&rqp->nr_rq);
300 	mb_done(&rqp->nr_rp);
301 	if (rqp->nr_if)
302 		free(rqp->nr_if);
303 	free(rqp);
304 }
305 
306 /*
307  * Extract resource record from the packet. Assume that there is only
308  * one mbuf.
309  */
310 int
311 nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
312 {
313 	struct mbdata *mbp = &rqp->nr_rp;
314 	uchar_t *cp;
315 	int error, len;
316 
317 	bzero(rrp, sizeof (*rrp));
318 	cp = (uchar_t *)mbp->mb_pos;
319 	len = nb_encname_len(cp);
320 	if (len < 1)
321 		return (NBERROR(NBERR_INVALIDRESPONSE));
322 	rrp->rr_name = cp;
323 	error = mb_get_mem(mbp, NULL, len);
324 	if (error)
325 		return (error);
326 	mb_get_uint16be(mbp, &rrp->rr_type);
327 	mb_get_uint16be(mbp, &rrp->rr_class);
328 	mb_get_uint32be(mbp, &rrp->rr_ttl);
329 	mb_get_uint16be(mbp, &rrp->rr_rdlength);
330 	rrp->rr_data = (uchar_t *)mbp->mb_pos;
331 	error = mb_get_mem(mbp, NULL, rrp->rr_rdlength);
332 	return (error);
333 }
334 
335 int
336 nbns_rq_prepare(struct nbns_rq *rqp)
337 {
338 	struct nb_ctx *ctx = rqp->nr_nbd;
339 	struct mbdata *mbp = &rqp->nr_rq;
340 	uint16_t ofr; /* opcode, flags, rcode */
341 	uchar_t *cp;
342 	int len, error;
343 
344 	/*
345 	 * Replacing with one argument.
346 	 * error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE);
347 	 */
348 	error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE);
349 	if (error)
350 		return (error);
351 
352 	/*
353 	 * When looked into the ethereal trace, 'nmblookup' command sets this
354 	 * flag. We will also set.
355 	 */
356 	mb_put_uint16be(mbp, rqp->nr_trnid);
357 	ofr = ((rqp->nr_opcode & 0x1F) << 11) |
358 	    ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
359 	mb_put_uint16be(mbp, ofr);
360 	mb_put_uint16be(mbp, rqp->nr_qdcount);
361 	mb_put_uint16be(mbp, rqp->nr_ancount);
362 	mb_put_uint16be(mbp, rqp->nr_nscount);
363 	mb_put_uint16be(mbp, rqp->nr_arcount);
364 	if (rqp->nr_qdcount) {
365 		if (rqp->nr_qdcount > 1)
366 			return (EINVAL);
367 		len = nb_name_len(rqp->nr_qdname);
368 		error = mb_fit(mbp, len, (char **)&cp);
369 		if (error)
370 			return (error);
371 		nb_name_encode(rqp->nr_qdname, cp);
372 		mb_put_uint16be(mbp, rqp->nr_qdtype);
373 		mb_put_uint16be(mbp, rqp->nr_qdclass);
374 	}
375 	m_lineup(mbp->mb_top, &mbp->mb_top);
376 	if (ctx->nb_timo == 0)
377 		ctx->nb_timo = 1;	/* by default 1 second */
378 	return (0);
379 }
380 
381 static int
382 nbns_rq_recv(struct nbns_rq *rqp)
383 {
384 	struct mbdata *mbp = &rqp->nr_rp;
385 	void *rpdata = mtod(mbp->mb_top, void *);
386 	fd_set rd, wr, ex;
387 	struct timeval tv;
388 	struct sockaddr_in sender;
389 	int s = rqp->nr_fd;
390 	int n, len;
391 
392 	FD_ZERO(&rd);
393 	FD_ZERO(&wr);
394 	FD_ZERO(&ex);
395 	FD_SET(s, &rd);
396 
397 	tv.tv_sec = rqp->nr_nbd->nb_timo;
398 	tv.tv_usec = 0;
399 
400 	n = select(s + 1, &rd, &wr, &ex, &tv);
401 	if (n == -1)
402 		return (-1);
403 	if (n == 0)
404 		return (ETIMEDOUT);
405 	if (FD_ISSET(s, &rd) == 0)
406 		return (ETIMEDOUT);
407 	len = sizeof (sender);
408 	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
409 	    (struct sockaddr *)&sender, &len);
410 	if (n < 0)
411 		return (errno);
412 	mbp->mb_top->m_len = mbp->mb_count = n;
413 	rqp->nr_sender = sender;
414 	return (0);
415 }
416 
417 static int
418 nbns_rq_opensocket(struct nbns_rq *rqp)
419 {
420 	struct sockaddr_in locaddr;
421 	int opt = 1, s;
422 	struct nb_ctx *ctx = rqp->nr_nbd;
423 
424 	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
425 	if (s < 0)
426 		return (errno);
427 	if (ctx->nb_flags & NBCF_BC_ENABLE) {
428 		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
429 		    sizeof (opt)) < 0)
430 			return (errno);
431 	}
432 	if (is_system_labeled())
433 		(void) setsockopt(s, SOL_SOCKET, SO_MAC_EXEMPT, &opt,
434 		    sizeof (opt));
435 	bzero(&locaddr, sizeof (locaddr));
436 	locaddr.sin_family = AF_INET;
437 	/* locaddr.sin_len = sizeof (locaddr); */
438 	if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
439 		return (errno);
440 	return (0);
441 }
442 
443 static int
444 nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
445 {
446 	struct sockaddr_in dest;
447 	struct mbdata *mbp = &rqp->nr_rq;
448 	int s = rqp->nr_fd;
449 	uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
450 	uint16_t *datap;
451 	uint8_t nmflags;
452 	int rc;
453 
454 	bzero(&dest, sizeof (dest));
455 	dest.sin_family = AF_INET;
456 	dest.sin_port = htons(NBNS_UDP_PORT);
457 	dest.sin_addr.s_addr = ina;
458 
459 	if (ina == INADDR_BROADCAST) {
460 		/* Turn on the broadcast bit. */
461 		nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
462 		/*LINTED*/
463 		datap = mtod(mbp->mb_top, uint16_t *);
464 		ofr = ((rqp->nr_opcode & 0x1F) << 11) |
465 		    ((nmflags & 0x7F) << 4); /* rcode=0 */
466 		ofr_save = datap[1];
467 		datap[1] = htons(ofr);
468 	}
469 
470 	rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
471 	    (struct sockaddr *)&dest, sizeof (dest));
472 
473 	if (ina == INADDR_BROADCAST) {
474 		/* Turn the broadcast bit back off. */
475 		datap[1] = ofr_save;
476 	}
477 
478 
479 	if (rc < 0)
480 		return (errno);
481 
482 	return (0);
483 }
484 
485 int
486 nbns_rq(struct nbns_rq *rqp)
487 {
488 	struct nb_ctx *ctx = rqp->nr_nbd;
489 	struct mbdata *mbp = &rqp->nr_rq;
490 	uint16_t ofr, rpid;
491 	uint8_t nmflags;
492 	int error, tries, maxretry;
493 
494 	error = nbns_rq_opensocket(rqp);
495 	if (error)
496 		return (error);
497 
498 	maxretry = rqp->nr_maxretry;
499 	for (tries = 0; tries < maxretry; tries++) {
500 
501 		/*
502 		 * Minor hack: If nr_dest is set, send there only.
503 		 * Used by _getnodestatus, _resolvname redirects.
504 		 */
505 		if (rqp->nr_dest.s_addr) {
506 			error = nbns_rq_send(rqp, rqp->nr_dest.s_addr);
507 			if (error) {
508 				smb_error(dgettext(TEXT_DOMAIN,
509 				    "nbns error %d sending to %s"),
510 				    0, error, inet_ntoa(rqp->nr_dest));
511 			}
512 			goto do_recv;
513 		}
514 
515 		if (ctx->nb_wins1) {
516 			error = nbns_rq_send(rqp, ctx->nb_wins1);
517 			if (error) {
518 				smb_error(dgettext(TEXT_DOMAIN,
519 				    "nbns error %d sending to wins1"),
520 				    0, error);
521 			}
522 		}
523 
524 		if (ctx->nb_wins2 && (tries > 0)) {
525 			error = nbns_rq_send(rqp, ctx->nb_wins2);
526 			if (error) {
527 				smb_error(dgettext(TEXT_DOMAIN,
528 				    "nbns error %d sending to wins2"),
529 				    0, error);
530 			}
531 		}
532 
533 		/*
534 		 * If broadcast is enabled, start broadcasting
535 		 * only after wins servers fail to respond, or
536 		 * immediately if no WINS servers configured.
537 		 */
538 		if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
539 		    ((tries > 1) || (ctx->nb_wins1 == 0))) {
540 			error = nbns_rq_send(rqp, INADDR_BROADCAST);
541 			if (error) {
542 				smb_error(dgettext(TEXT_DOMAIN,
543 				    "nbns error %d sending broadcast"),
544 				    0, error);
545 			}
546 		}
547 
548 		/*
549 		 * Wait for responses from ANY of the above.
550 		 */
551 do_recv:
552 		error = nbns_rq_recv(rqp);
553 		if (error == ETIMEDOUT)
554 			continue;
555 		if (error) {
556 			smb_error(dgettext(TEXT_DOMAIN,
557 			    "nbns recv error %d"),
558 			    0, error);
559 			return (error);
560 		}
561 
562 		mbp = &rqp->nr_rp;
563 		if (mbp->mb_count < 12)
564 			return (NBERROR(NBERR_INVALIDRESPONSE));
565 		mb_get_uint16be(mbp, &rpid);
566 		if (rpid != rqp->nr_trnid)
567 			return (NBERROR(NBERR_INVALIDRESPONSE));
568 		break;
569 	}
570 
571 	mb_get_uint16be(mbp, &ofr);
572 	rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
573 	rqp->nr_rprcode = ofr & 0xf;
574 	if (rqp->nr_rprcode)
575 		return (NBERROR(rqp->nr_rprcode));
576 	mb_get_uint16be(mbp, &rpid);	/* QDCOUNT */
577 	mb_get_uint16be(mbp, &rqp->nr_rpancount);
578 	mb_get_uint16be(mbp, &rqp->nr_rpnscount);
579 	mb_get_uint16be(mbp, &rqp->nr_rparcount);
580 	return (0);
581 }
582