1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $Id: nbns_rq.c,v 1.13.140.1 2006/04/14 23:49:37 gcolley Exp $
35 */
36#include <netsmb/netbios.h>
37#include <sys/smb_byte_order.h>
38#include <netsmb/upi_mbuf.h>
39#include <sys/mchain.h>
40#include <netsmb/smb_lib.h>
41#include <netsmb/nb_lib.h>
42#include "preference.h"
43
44/*
45 * nbns request
46 */
47struct nbns_rq {
48	int		nr_opcode;
49	int		nr_nmflags;
50	int		nr_rcode;
51	int		nr_qdcount;
52	int		nr_ancount;
53	int		nr_nscount;
54	int		nr_arcount;
55	struct nb_name*	nr_qdname;
56	uint16_t	nr_qdtype;
57	uint16_t	nr_qdclass;
58	struct sockaddr_in nr_dest;	/* receiver of query */
59	struct sockaddr_in nr_sender;	/* sender of response */
60	int		nr_rpnmflags;
61	int		nr_rprcode;
62	uint16_t	nr_rpancount;
63	uint16_t	nr_rpnscount;
64	uint16_t	nr_rparcount;
65	uint16_t	nr_trnid;
66	struct nb_ctx *	nr_nbd;
67	struct mbchain	nr_rq;
68	struct mdchain	nr_rp;
69	struct nb_ifdesc *nr_if;
70	int		nr_flags; /* endian-ness depends on host */
71	int		nr_fd;
72	int32_t	nr_timo;
73};
74
75static struct nb_ifdesc *nb_iflist;
76
77
78static char * smb_optstrncpy(char *d, char *s, unsigned maxlen)
79{
80	if (d && s) {
81		strncpy(d, s, maxlen);
82		d[maxlen] = (char)0;
83	}
84	return (d);
85}
86
87static int nbns_rq_create(int opcode, struct smb_prefs *prefs,
88						  struct nbns_rq **rqpp)
89{
90	struct nbns_rq *rqp;
91	static uint16_t trnid;
92	int error;
93
94	rqp = malloc(sizeof(*rqp));
95	if (rqp == NULL)
96		return ENOMEM;
97	bzero(rqp, sizeof(*rqp));
98	error = mb_init(&rqp->nr_rq);
99	if (error) {
100		free(rqp);
101		return error;
102	}
103	if (prefs) {
104		rqp->nr_timo = prefs->NetBIOSResolverTimeout;
105	} else {
106		rqp->nr_timo = DefaultNetBIOSResolverTimeout;
107	}
108
109	rqp->nr_opcode = opcode;
110	rqp->nr_trnid = trnid++;
111	*rqpp = rqp;
112	return 0;
113}
114
115static void nbns_rq_done(struct nbns_rq *rqp)
116{
117	if (rqp == NULL)
118		return;
119	if (rqp->nr_fd >= 0)
120		close(rqp->nr_fd);
121	mb_done(&rqp->nr_rq);
122	md_done(&rqp->nr_rp);
123	free(rqp);
124}
125
126/*
127 * Extract resource record from the packet. Assume that there is only
128 * one mbuf.
129 */
130static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
131{
132	mdchain_t mdp = &rqp->nr_rp;
133	u_char *cp;
134	int error, len;
135
136	bzero(rrp, sizeof(*rrp));
137	cp = (u_char *)(mdp->md_pos);
138	len = nb_encname_len((char *)cp);
139	if (len < 1)
140		return EINVAL;
141	rrp->rr_name = cp;
142	error = md_get_mem(mdp, NULL, len, MB_MSYSTEM);
143	if (error)
144		return error;
145	md_get_uint16be(mdp, &rrp->rr_type);
146	md_get_uint16be(mdp, &rrp->rr_class);
147	md_get_uint32be(mdp, &rrp->rr_ttl);
148	md_get_uint16be(mdp, &rrp->rr_rdlength);
149	rrp->rr_data = (u_char *)mdp->md_pos;
150	error = md_get_mem(mdp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
151	return error;
152}
153
154static int nbns_rq_recv(struct nbns_rq *rqp)
155{
156	mdchain_t mdp = &rqp->nr_rp;
157	void *rpdata = mbuf_data(mdp->md_top);
158	fd_set rd, wr, ex;
159	struct timeval tv;
160	struct sockaddr_in sender;
161	int s = rqp->nr_fd;
162	int n;
163	socklen_t len;
164
165	FD_ZERO(&rd);
166	FD_ZERO(&wr);
167	FD_ZERO(&ex);
168	FD_SET(s, &rd);
169
170	tv.tv_sec = 0;
171	tv.tv_usec = 500000; /* We wait half a second for a response */
172
173	n = select(s + 1, &rd, &wr, &ex, &tv);
174	if (n == -1)
175		return -1;
176	if (n == 0)
177		return ETIMEDOUT;
178	if (FD_ISSET(s, &rd) == 0)
179		return ETIMEDOUT;
180	len = (socklen_t)sizeof(sender);
181	n = (int)recvfrom(s, rpdata, mbuf_maxlen(mdp->md_top), 0, (struct sockaddr*)&sender, &len);
182	if (n < 0)
183		return errno;
184	mbuf_setlen(mdp->md_top, n);
185	rqp->nr_sender = sender;
186	return 0;
187}
188
189static int nbns_rq_opensocket(struct nbns_rq *rqp)
190{
191	struct sockaddr_in locaddr;
192	int opt, s;
193
194	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
195	if (s < 0)
196		return errno;
197	if (rqp->nr_flags & NBRQF_BROADCAST) {
198		opt = 1;
199		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, (socklen_t)sizeof(opt)) < 0)
200			return errno;
201		if (rqp->nr_if == NULL)
202			return ENETDOWN;
203		bzero(&locaddr, sizeof(locaddr));
204		locaddr.sin_family = AF_INET;
205		locaddr.sin_len = sizeof(locaddr);
206		locaddr.sin_addr = rqp->nr_if->id_addr;
207		rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr;
208		if (bind(s, (struct sockaddr*)&locaddr, (socklen_t)sizeof(locaddr)) < 0)
209			return errno;
210	}
211	return 0;
212}
213
214static int nbns_rq_send(struct nbns_rq *rqp)
215{
216	mbchain_t mbp = &rqp->nr_rq;
217	int s = rqp->nr_fd;
218
219	if (sendto(s, mbuf_data(mbp->mb_top), mbp->mb_count, 0,
220			   (struct sockaddr*)&rqp->nr_dest, (socklen_t)sizeof(rqp->nr_dest)) < 0)
221		return errno;
222	return 0;
223}
224
225static int nbns_rq_run(struct nbns_rq *rqp, uint16_t *cancel)
226{
227	mdchain_t mdp;
228	uint16_t rpid;
229	uint8_t nmflags;
230	int error, retrycount;
231
232	rqp->nr_if = nb_iflist;
233again:
234	error = nbns_rq_opensocket(rqp);
235	if (error)
236		return error;
237	/*
238	 * The configuration file alwows the user to set the total amount of time
239	 * we will wait for a NetBIOS name lookup to complete. So will always wait
240	 * half a second per attempt. So NetBIOSResolverTimeout is the number of seconds we want to
241	 * wait. So NetBIOSResolverTimeout * 2 is the number of retries we will attmept.
242	 */
243	retrycount = rqp->nr_timo * 2;
244	for (;;) {
245		if (cancel && *cancel)
246			return ECANCELED;
247
248		error = nbns_rq_send(rqp);
249		if (error)
250			return error;
251		error = nbns_rq_recv(rqp);
252		if (error) {
253			if (error != ETIMEDOUT || --retrycount == 0) {
254				if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) &&
255				    rqp->nr_if != NULL &&
256				    rqp->nr_if->id_next != NULL) {
257					rqp->nr_if = rqp->nr_if->id_next;
258					close(rqp->nr_fd);
259					goto again;
260				} else
261					return error;
262			}
263			continue;
264		}
265		mdp = &rqp->nr_rp;
266		if (md_get_uint16be(mdp, &rpid))
267			return EINVAL;
268
269		if (rpid != rqp->nr_trnid)
270			return EINVAL;
271		break;
272	}
273	if (md_get_uint8(mdp, &nmflags))
274		return EINVAL;
275
276	rqp->nr_rpnmflags = (nmflags & 7) << 4;
277	if (md_get_uint8(mdp, &nmflags))
278		return EINVAL;
279	rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4;
280	rqp->nr_rprcode = nmflags & 0xf;
281	if (rqp->nr_rprcode)
282		return nb_error_to_errno(rqp->nr_rprcode);
283
284	if (md_get_uint16be(mdp, &rpid))	/* QDCOUNT */
285		return EINVAL;
286	if (md_get_uint16be(mdp, &rqp->nr_rpancount))
287		return EINVAL;
288	if (md_get_uint16be(mdp, &rqp->nr_rpnscount))
289		return EINVAL;
290	if (md_get_uint16be(mdp, &rqp->nr_rparcount))
291		return EINVAL;
292	return 0;
293}
294
295static int nbns_rq_prepare(struct nbns_rq *rqp)
296{
297	mbchain_t mbp = &rqp->nr_rq;
298	uint8_t nmflags;
299	u_char *cp;
300	int len, error;
301
302	error = md_init_rcvsize(&rqp->nr_rp, NBDG_MAXSIZE);
303	if (error)
304		return error;
305	if (rqp->nr_dest.sin_addr.s_addr == htonl(INADDR_BROADCAST)) {
306		rqp->nr_nmflags |= NBNS_NMFLAG_BCAST;
307		rqp->nr_flags |= NBRQF_BROADCAST;
308		if (nb_iflist == NULL) {
309			error = nb_enum_if(&nb_iflist, 100);
310			if (error)
311				return error;
312		}
313	}
314	mb_put_uint16be(mbp, rqp->nr_trnid);
315	nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4);
316	mb_put_uint8(mbp, nmflags);
317	mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */);
318	mb_put_uint16be(mbp, rqp->nr_qdcount);
319	mb_put_uint16be(mbp, rqp->nr_ancount);
320	mb_put_uint16be(mbp, rqp->nr_nscount);
321	mb_put_uint16be(mbp, rqp->nr_arcount);
322	if (rqp->nr_qdcount) {
323		if (rqp->nr_qdcount > 1)
324			return EINVAL;
325		len = NB_ENCNAMELEN + 2;
326		cp = (u_char *)mb_reserve(mbp, len);
327		if (cp == NULL)
328			return ENOMEM;
329
330		nb_name_encode(rqp->nr_qdname, cp);
331		mb_put_uint16be(mbp, rqp->nr_qdtype);
332		mb_put_uint16be(mbp, rqp->nr_qdclass);
333	}
334	mb_pullup(mbp);
335	return 0;
336}
337
338/*
339 * Resolve a NetBIOS name to an set of address.
340 */
341static int nbns_resolvename_internal(struct nb_ctx *ctx, struct smb_prefs *prefs,
342							  const char *name, uint8_t nodeType,
343							  CFMutableArrayRef *outAddressArray, uint16_t port,
344							  int allowLocalConn, int tryBothPorts, uint16_t *cancel)
345{
346	CFMutableArrayRef addressArray = NULL;
347	CFMutableDataRef addressData;
348	struct connectAddress conn;
349	struct nbns_rq *rqp;
350	struct nb_name nn;
351	struct nbns_rr rr;
352	int error, rdrcount;
353	u_char *current_ip, *end_of_rr;
354
355	/* If we are trying both ports always put port 139 in after port 445 */
356	if (tryBothPorts && (port == NBSS_TCP_PORT_139))
357		port = SMB_TCP_PORT_445;
358
359	if (strlen(name) > NB_NAMELEN)
360		return ENAMETOOLONG;
361
362	addressArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
363	if (!addressArray) {
364		return ENOMEM;
365	}
366
367	error = nbns_rq_create(NBNS_OPCODE_QUERY, prefs, &rqp);
368	if (error) {
369		CFRelease(addressArray);
370		return error;
371	}
372
373	bzero(&nn, sizeof(nn));
374	strlcpy((char *)nn.nn_name, name, sizeof(nn.nn_name));
375	nn.nn_type = nodeType;
376	rqp->nr_nmflags = NBNS_NMFLAG_RD;
377	rqp->nr_qdname = &nn;
378	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
379	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
380	rqp->nr_qdcount = 1;
381	memcpy(&rqp->nr_dest, &ctx->nb_ns, sizeof(rqp->nr_dest));
382	error = nbns_rq_prepare(rqp);
383	if (error) {
384		goto done;
385	}
386	rdrcount = NBNS_MAXREDIRECTS;
387	for (;;) {
388		error = nbns_rq_run(rqp, cancel);
389		if (error)
390			break;
391		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
392			if (rdrcount-- == 0) {
393				error = ETOOMANYREFS;
394				break;
395			}
396			error = nbns_rq_getrr(rqp, &rr);
397			if (! error)
398				error = nbns_rq_getrr(rqp, &rr);
399			if (error)
400				break;
401			bcopy(rr.rr_data, &rqp->nr_dest.sin_addr, 4);
402			rqp->nr_flags &= ~NBRQF_BROADCAST;
403			continue;
404		}
405		if (rqp->nr_rpancount == 0) {
406			error = EHOSTUNREACH;
407			break;
408		}
409		error = nbns_rq_getrr(rqp, &rr);
410		if (error)
411			break;
412
413		/* We have an answer, so store away the address of the server that responded */
414		ctx->nb_sender = rqp->nr_sender;
415
416		end_of_rr = rr.rr_data + rr.rr_rdlength;
417		for(current_ip = rr.rr_data + 2; current_ip < end_of_rr; current_ip += 6) {
418			bzero(&conn, sizeof(conn));
419			conn.in4.sin_len = (int)sizeof(struct sockaddr_in);
420			conn.in4.sin_family = AF_INET;
421			conn.in4.sin_port = htons(port);
422			memcpy(&conn.in4.sin_addr, current_ip, 4);
423			/* Check to make sure we are not connecting to ourself */
424			if (isLocalIPAddress((struct sockaddr *)&conn.addr, port, allowLocalConn)) {
425				smb_log_info("The address for `%s' is a loopback address, not allowed!",
426							 ASL_LEVEL_DEBUG, name);
427				error = ELOOP;	/* AFP returns ELOOP, so we will do the same */
428				goto done;
429			}
430			addressData = CFDataCreateMutable(NULL, 0);
431			if (addressData) {
432				/* The name is the netbios name, we need a netbios sockaddr */
433				if (port == NBSS_TCP_PORT_139)
434					convertToNetBIOSaddr(&conn.storage, name);
435
436				CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn));
437				CFArrayAppendValue(addressArray, addressData);
438				CFRelease(addressData);
439			}
440			/* We only try both ports with IPv4 */
441			if (tryBothPorts) {
442				conn.in4.sin_port = htons(NBSS_TCP_PORT_139);
443				/* The name is the netbios name, we need a netbios sockaddr */
444				convertToNetBIOSaddr(&conn.storage, name);
445
446				addressData = CFDataCreateMutable(NULL, 0);
447				if (addressData) {
448					CFDataAppendBytes(addressData, (const UInt8 *)&conn, (CFIndex)sizeof(conn));
449					CFArrayAppendValue(addressArray, addressData);
450					CFRelease(addressData);
451				}
452			}
453		}
454		break;
455	} /* end big for loop */
456
457
458done:
459	if (CFArrayGetCount(addressArray) == 0) {
460		error = EHOSTUNREACH;
461	}
462	nbns_rq_done(rqp);
463
464	if (error) {
465		if (addressArray)
466			CFRelease(addressArray);
467		addressArray = NULL;
468	}
469	*outAddressArray = addressArray;
470
471	return error;
472}
473
474/*
475 * Resolve a NetBIOS name to an set of address, We always try WINS first if a
476 * server is provide. If no WINS server or we fail to find one with the WINS
477 * server then try broadcast.
478 */
479int nbns_resolvename(struct nb_ctx *ctx, struct smb_prefs *prefs, const char *name,
480					 uint8_t nodeType, CFMutableArrayRef *outAddressArray, uint16_t port,
481					 int allowLocalConn, int tryBothPorts, uint16_t *cancel)
482{
483	int error;
484
485	error = nb_ctx_resolve(ctx, prefs->WINSAddresses);
486	if (!error)
487		error = nbns_resolvename_internal(ctx, prefs, name, nodeType, outAddressArray,
488										  port,  allowLocalConn, tryBothPorts, cancel);
489	/* We tried it with WINS and failed try broadcast now */
490	if (error && (prefs->WINSAddresses != NULL)) {
491		error = nb_ctx_resolve(ctx, NULL);
492		if (error == 0) {
493			error = nbns_resolvename_internal(ctx, prefs, name, nodeType, outAddressArray,
494											  port, allowLocalConn, tryBothPorts, cancel);
495		}
496	}
497	return error;
498}
499
500int nbns_getnodestatus(struct sockaddr *targethost, struct nb_ctx *ctx,
501					   struct smb_prefs *prefs, uint16_t *cancel, char *nbt_server,
502					   char *workgroup, CFMutableArrayRef nbrrArray)
503{
504	struct nbns_rq *rqp;
505	struct nbns_rr rr;
506	struct nb_name nn;
507	struct nbns_nr *nrp;
508	char nrtype;
509	char *cp, *retname = NULL;
510	unsigned char nrcount;
511	int error, i, foundserver = 0, foundgroup = 0;
512
513	if (targethost->sa_family != AF_INET)
514		return EINVAL;
515	error = nbns_rq_create(NBNS_OPCODE_QUERY, prefs, &rqp);
516	if (error)
517		return error;
518	bzero(&nn, sizeof(nn));
519	strlcpy((char *)nn.nn_name, "*", sizeof(nn.nn_name));
520	nn.nn_type = NBT_WKSTA;
521	rqp->nr_nmflags = 0;
522	rqp->nr_qdname = &nn;
523	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
524	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
525	rqp->nr_qdcount = 1;
526	rqp->nr_dest = *(struct sockaddr_in *)(void *)targethost;
527	rqp->nr_dest.sin_port = htons(NBNS_UDP_PORT_137);
528	if (rqp->nr_dest.sin_addr.s_addr == INADDR_ANY)
529		rqp->nr_dest.sin_addr.s_addr = htonl(INADDR_BROADCAST);
530	error = nbns_rq_prepare(rqp);
531	if (error) {
532		nbns_rq_done(rqp);
533		return error;
534	}
535	for (;;) {
536		error = nbns_rq_run(rqp, cancel);
537		if (error)
538			break;
539		if (rqp->nr_rpancount == 0) {
540			error = EHOSTUNREACH;
541			break;
542		}
543		error = nbns_rq_getrr(rqp, &rr);
544		if (error)
545			break;
546		nrcount = (unsigned char)(*(rr.rr_data));
547		rr.rr_data++;
548		for (i = 1, nrp = (struct nbns_nr *)(void *)rr.rr_data;
549		     i <= nrcount; ++i, ++nrp) {
550			uint16_t	nbFlags = betohs(nrp->nr_beflags);
551
552			/*
553			 * They want all the names in the format it came into from the
554			 * network. Copy in the NetBIOS name and then the flags.
555			 */
556			 if (nbrrArray) {
557				CFMutableDataRef addressData = CFDataCreateMutable(NULL, 0);
558				CFDataAppendBytes(addressData, (const UInt8 *)nrp->nr_name, (CFIndex)NB_NAMELEN);
559				CFDataAppendBytes(addressData, (const UInt8 *)&nbFlags, (CFIndex)sizeof(uint16_t));
560				CFArrayAppendValue(nbrrArray, addressData);
561				CFRelease(addressData);
562			}
563
564			nrtype = nrp->nr_name[NB_NAMELEN-1];
565			/* Terminate the string: */
566			nrp->nr_name[NB_NAMELEN-1] = (char)0;
567			/* Strip off trailing spaces */
568			for (cp = &nrp->nr_name[NB_NAMELEN-2];
569			     cp >= nrp->nr_name; --cp) {
570				if (*cp != (char)0x20)
571					break;
572				*cp = (char)0;
573			}
574			if (nbFlags & NBNS_GROUPFLG) {
575				if (!foundgroup ||
576				    (foundgroup != NBT_WKSTA+1 &&
577				     nrtype == NBT_WKSTA)) {
578						if (workgroup)
579							smb_optstrncpy(workgroup, nrp->nr_name, SMB_MAXNetBIOSNAMELEN);
580					foundgroup = nrtype+1;
581				}
582			} else {
583				/* Track at least ONE name, in case
584				   no server name is found */
585				retname = nrp->nr_name;
586			}
587			if (nrtype == NBT_SERVER) {
588				if (nbt_server)
589					smb_optstrncpy(nbt_server, nrp->nr_name, SMB_MAXNetBIOSNAMELEN);
590				foundserver = 1;
591			}
592		}
593		if (!foundserver && nbt_server)
594			smb_optstrncpy(nbt_server, retname, SMB_MAXNetBIOSNAMELEN);
595		ctx->nb_sender = rqp->nr_sender;
596		break;
597	}
598	nbns_rq_done(rqp);
599	return error;
600}
601
602