nbns_rq.c revision 335781
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.5 2001/02/17 03:07:24 bp Exp $
33 * $FreeBSD: stable/10/contrib/smbfs/lib/smb/nbns_rq.c 335781 2018-06-28 21:23:05Z brooks $
34 */
35#include <sys/param.h>
36#include <sys/socket.h>
37#include <sys/time.h>
38
39#include <ctype.h>
40#include <netdb.h>
41#include <err.h>
42#include <errno.h>
43#include <stdlib.h>
44#include <string.h>
45#include <stdio.h>
46#include <unistd.h>
47
48#define NB_NEEDRESOLVER
49#include <netsmb/netbios.h>
50#include <netsmb/smb_lib.h>
51#include <netsmb/nb_lib.h>
52
53
54static int  nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp);
55static void nbns_rq_done(struct nbns_rq *rqp);
56static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
57static int  nbns_rq_prepare(struct nbns_rq *rqp);
58static int  nbns_rq(struct nbns_rq *rqp);
59
60static struct nb_ifdesc *nb_iflist;
61
62int
63nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
64{
65	struct nbns_rq *rqp;
66	struct nb_name nn;
67	struct nbns_rr rr;
68	struct sockaddr_in *dest;
69	int error, rdrcount, len;
70
71	if (strlen(name) > NB_NAMELEN)
72		return NBERROR(NBERR_NAMETOOLONG);
73	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
74	if (error)
75		return error;
76	bzero(&nn, sizeof(nn));
77	strlcpy(nn.nn_name, name, sizeof(nn.nn_name));
78	nn.nn_scope = ctx->nb_scope;
79	nn.nn_type = NBT_SERVER;
80	rqp->nr_nmflags = NBNS_NMFLAG_RD;
81	rqp->nr_qdname = &nn;
82	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
83	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
84	rqp->nr_qdcount = 1;
85	dest = &rqp->nr_dest;
86	*dest = ctx->nb_ns;
87	dest->sin_family = AF_INET;
88	dest->sin_len = sizeof(*dest);
89	if (dest->sin_port == 0)
90		dest->sin_port = htons(ctx->nb_nmbtcpport);
91	if (dest->sin_addr.s_addr == INADDR_ANY)
92		dest->sin_addr.s_addr = htonl(INADDR_BROADCAST);
93	if (dest->sin_addr.s_addr == INADDR_BROADCAST)
94		rqp->nr_flags |= NBRQF_BROADCAST;
95	error = nbns_rq_prepare(rqp);
96	if (error) {
97		nbns_rq_done(rqp);
98		return error;
99	}
100	rdrcount = NBNS_MAXREDIRECTS;
101	for (;;) {
102		error = nbns_rq(rqp);
103		if (error)
104			break;
105		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
106			if (rdrcount-- == 0) {
107				error = NBERROR(NBERR_TOOMANYREDIRECTS);
108				break;
109			}
110			error = nbns_rq_getrr(rqp, &rr);
111			if (error)
112				break;
113			error = nbns_rq_getrr(rqp, &rr);
114			if (error)
115				break;
116			bcopy(rr.rr_data, &dest->sin_addr, 4);
117			rqp->nr_flags &= ~NBRQF_BROADCAST;
118			continue;
119		}
120		if (rqp->nr_rpancount == 0) {
121			error = NBERROR(NBERR_HOSTNOTFOUND);
122			break;
123		}
124		error = nbns_rq_getrr(rqp, &rr);
125		if (error)
126			break;
127		len = sizeof(struct sockaddr_in);
128		dest = malloc(len);
129		if (dest == NULL)
130			return ENOMEM;
131		bzero(dest, len);
132		dest->sin_len = len;
133		dest->sin_family = AF_INET;
134		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
135		dest->sin_port = htons(ctx->nb_smbtcpport);
136		*adpp = (struct sockaddr*)dest;
137		ctx->nb_lastns = rqp->nr_sender;
138		break;
139	}
140	nbns_rq_done(rqp);
141	return error;
142}
143
144int
145nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
146{
147	struct nbns_rq *rqp;
148	static u_int16_t trnid;
149	int error;
150
151	rqp = malloc(sizeof(*rqp));
152	if (rqp == NULL)
153		return ENOMEM;
154	bzero(rqp, sizeof(*rqp));
155	error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE);
156	if (error) {
157		free(rqp);
158		return error;
159	}
160	rqp->nr_opcode = opcode;
161	rqp->nr_nbd = ctx;
162	rqp->nr_trnid = trnid++;
163	*rqpp = rqp;
164	return 0;
165}
166
167void
168nbns_rq_done(struct nbns_rq *rqp)
169{
170	if (rqp == NULL)
171		return;
172	if (rqp->nr_fd >= 0)
173		close(rqp->nr_fd);
174	mb_done(&rqp->nr_rq);
175	mb_done(&rqp->nr_rp);
176	free(rqp);
177}
178
179/*
180 * Extract resource record from the packet. Assume that there is only
181 * one mbuf.
182 */
183int
184nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
185{
186	struct mbdata *mbp = &rqp->nr_rp;
187	u_char *cp;
188	int error, len;
189
190	bzero(rrp, sizeof(*rrp));
191	cp = mbp->mb_pos;
192	len = nb_encname_len(cp);
193	if (len < 1)
194		return NBERROR(NBERR_INVALIDRESPONSE);
195	rrp->rr_name = cp;
196	error = mb_get_mem(mbp, NULL, len);
197	if (error)
198		return error;
199	mb_get_uint16be(mbp, &rrp->rr_type);
200	mb_get_uint16be(mbp, &rrp->rr_class);
201	mb_get_uint32be(mbp, &rrp->rr_ttl);
202	mb_get_uint16be(mbp, &rrp->rr_rdlength);
203	rrp->rr_data = mbp->mb_pos;
204	error = mb_get_mem(mbp, NULL, rrp->rr_rdlength);
205	return error;
206}
207
208int
209nbns_rq_prepare(struct nbns_rq *rqp)
210{
211	struct nb_ctx *ctx = rqp->nr_nbd;
212	struct mbdata *mbp = &rqp->nr_rq;
213	u_int8_t nmflags;
214	u_char *cp;
215	int len, error;
216
217	error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE);
218	if (error)
219		return error;
220	if (rqp->nr_dest.sin_addr.s_addr == INADDR_BROADCAST) {
221		rqp->nr_nmflags |= NBNS_NMFLAG_BCAST;
222		if (nb_iflist == NULL) {
223			error = nb_enum_if(&nb_iflist, 100);
224			if (error)
225				return error;
226		}
227	} else
228		rqp->nr_nmflags &= ~NBNS_NMFLAG_BCAST;
229	mb_put_uint16be(mbp, rqp->nr_trnid);
230	nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4);
231	mb_put_uint8(mbp, nmflags);
232	mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */);
233	mb_put_uint16be(mbp, rqp->nr_qdcount);
234	mb_put_uint16be(mbp, rqp->nr_ancount);
235	mb_put_uint16be(mbp, rqp->nr_nscount);
236	mb_put_uint16be(mbp, rqp->nr_arcount);
237	if (rqp->nr_qdcount) {
238		if (rqp->nr_qdcount > 1)
239			return EINVAL;
240		len = nb_name_len(rqp->nr_qdname);
241		error = mb_fit(mbp, len, (char**)&cp);
242		if (error)
243			return error;
244		nb_name_encode(rqp->nr_qdname, cp);
245		mb_put_uint16be(mbp, rqp->nr_qdtype);
246		mb_put_uint16be(mbp, rqp->nr_qdclass);
247	}
248	m_lineup(mbp->mb_top, &mbp->mb_top);
249	if (ctx->nb_timo == 0)
250		ctx->nb_timo = 1;	/* by default 1 second */
251	return 0;
252}
253
254static int
255nbns_rq_recv(struct nbns_rq *rqp)
256{
257	struct mbdata *mbp = &rqp->nr_rp;
258	void *rpdata = mtod(mbp->mb_top, void *);
259	fd_set rd, wr, ex;
260	struct timeval tv;
261	struct sockaddr_in sender;
262	int s = rqp->nr_fd;
263	int n, len;
264
265	FD_ZERO(&rd);
266	FD_ZERO(&wr);
267	FD_ZERO(&ex);
268	FD_SET(s, &rd);
269
270	tv.tv_sec = rqp->nr_nbd->nb_timo;
271	tv.tv_usec = 0;
272
273	n = select(s + 1, &rd, &wr, &ex, &tv);
274	if (n == -1)
275		return -1;
276	if (n == 0)
277		return ETIMEDOUT;
278	if (FD_ISSET(s, &rd) == 0)
279		return ETIMEDOUT;
280	len = sizeof(sender);
281	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
282	    (struct sockaddr*)&sender, &len);
283	if (n < 0)
284		return errno;
285	mbp->mb_top->m_len = mbp->mb_count = n;
286	rqp->nr_sender = sender;
287	return 0;
288}
289
290static int
291nbns_rq_opensocket(struct nbns_rq *rqp)
292{
293	struct sockaddr_in locaddr;
294	int opt, s;
295
296	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
297	if (s < 0)
298		return errno;
299	if (rqp->nr_flags & NBRQF_BROADCAST) {
300		opt = 1;
301		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0)
302			return errno;
303		if (rqp->nr_if == NULL)
304			return NBERROR(NBERR_NOBCASTIFS);
305		bzero(&locaddr, sizeof(locaddr));
306		locaddr.sin_family = AF_INET;
307		locaddr.sin_len = sizeof(locaddr);
308		locaddr.sin_addr = rqp->nr_if->id_addr;
309		rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr;
310		if (bind(s, (struct sockaddr*)&locaddr, sizeof(locaddr)) < 0)
311			return errno;
312	}
313	return 0;
314}
315
316static int
317nbns_rq_send(struct nbns_rq *rqp)
318{
319	struct mbdata *mbp = &rqp->nr_rq;
320	int s = rqp->nr_fd;
321
322	if (sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
323	      (struct sockaddr*)&rqp->nr_dest, sizeof(rqp->nr_dest)) < 0)
324		return errno;
325	return 0;
326}
327
328int
329nbns_rq(struct nbns_rq *rqp)
330{
331	struct mbdata *mbp = &rqp->nr_rq;
332	u_int16_t rpid;
333	u_int8_t nmflags;
334	int error, retrycount;
335
336	rqp->nr_if = nb_iflist;
337again:
338	error = nbns_rq_opensocket(rqp);
339	if (error)
340		return error;
341	retrycount = 3;	/* XXX - configurable */
342	for (;;) {
343		error = nbns_rq_send(rqp);
344		if (error)
345			return error;
346		error = nbns_rq_recv(rqp);
347		if (error) {
348			if (error != ETIMEDOUT || retrycount == 0) {
349				if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) &&
350				    rqp->nr_if != NULL &&
351				    rqp->nr_if->id_next != NULL) {
352					rqp->nr_if = rqp->nr_if->id_next;
353					close(rqp->nr_fd);
354					goto again;
355				} else
356					return error;
357			}
358			retrycount--;
359			continue;
360		}
361		mbp = &rqp->nr_rp;
362		if (mbp->mb_count < 12)
363			return NBERROR(NBERR_INVALIDRESPONSE);
364		mb_get_uint16be(mbp, &rpid);
365		if (rpid != rqp->nr_trnid)
366			return NBERROR(NBERR_INVALIDRESPONSE);
367		break;
368	}
369	mb_get_uint8(mbp, &nmflags);
370	rqp->nr_rpnmflags = (nmflags & 7) << 4;
371	mb_get_uint8(mbp, &nmflags);
372	rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4;
373	rqp->nr_rprcode = nmflags & 0xf;
374	if (rqp->nr_rprcode)
375		return NBERROR(rqp->nr_rprcode);
376	mb_get_uint16be(mbp, &rpid);	/* QDCOUNT */
377	mb_get_uint16be(mbp, &rqp->nr_rpancount);
378	mb_get_uint16be(mbp, &rqp->nr_rpnscount);
379	mb_get_uint16be(mbp, &rqp->nr_rparcount);
380	return 0;
381}
382