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