xref: /freebsd/contrib/smbfs/lib/smb/nbns_rq.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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$
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 
54 static int  nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp);
55 static void nbns_rq_done(struct nbns_rq *rqp);
56 static int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
57 static int  nbns_rq_prepare(struct nbns_rq *rqp);
58 static int  nbns_rq(struct nbns_rq *rqp);
59 
60 static struct nb_ifdesc *nb_iflist;
61 
62 int
63 nbns_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 
144 int
145 nbns_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 
167 void
168 nbns_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  */
183 int
184 nbns_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 
208 int
209 nbns_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 
254 static int
255 nbns_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 
290 static int
291 nbns_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 
316 static int
317 nbns_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 
328 int
329 nbns_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;
337 again:
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