187866Ssheldonh/*
287866Ssheldonh * Copyright (c) 2000, Boris Popov
387866Ssheldonh * All rights reserved.
487866Ssheldonh *
587866Ssheldonh * Redistribution and use in source and binary forms, with or without
687866Ssheldonh * modification, are permitted provided that the following conditions
787866Ssheldonh * are met:
887866Ssheldonh * 1. Redistributions of source code must retain the above copyright
987866Ssheldonh *    notice, this list of conditions and the following disclaimer.
1087866Ssheldonh * 2. Redistributions in binary form must reproduce the above copyright
1187866Ssheldonh *    notice, this list of conditions and the following disclaimer in the
1287866Ssheldonh *    documentation and/or other materials provided with the distribution.
1387866Ssheldonh * 3. All advertising materials mentioning features or use of this software
1487866Ssheldonh *    must display the following acknowledgement:
1587866Ssheldonh *    This product includes software developed by Boris Popov.
1687866Ssheldonh * 4. Neither the name of the author nor the names of any co-contributors
1787866Ssheldonh *    may be used to endorse or promote products derived from this software
1887866Ssheldonh *    without specific prior written permission.
1987866Ssheldonh *
2087866Ssheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2187866Ssheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2287866Ssheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2387866Ssheldonh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2487866Ssheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2587866Ssheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2687866Ssheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2787866Ssheldonh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2887866Ssheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2987866Ssheldonh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3087866Ssheldonh * SUCH DAMAGE.
3187866Ssheldonh *
3287866Ssheldonh * $Id: nbns_rq.c,v 1.5 2001/02/17 03:07:24 bp Exp $
33150802Sbp * $FreeBSD$
3487866Ssheldonh */
3587866Ssheldonh#include <sys/param.h>
3687866Ssheldonh#include <sys/socket.h>
3787866Ssheldonh#include <sys/time.h>
3887866Ssheldonh
3987866Ssheldonh#include <ctype.h>
4087866Ssheldonh#include <netdb.h>
4187866Ssheldonh#include <err.h>
4287866Ssheldonh#include <errno.h>
4387866Ssheldonh#include <stdlib.h>
4487866Ssheldonh#include <string.h>
4587866Ssheldonh#include <stdio.h>
4687866Ssheldonh#include <unistd.h>
4787866Ssheldonh
4887866Ssheldonh#define NB_NEEDRESOLVER
4987866Ssheldonh#include <netsmb/netbios.h>
5087866Ssheldonh#include <netsmb/smb_lib.h>
5187866Ssheldonh#include <netsmb/nb_lib.h>
5287866Ssheldonh
5387866Ssheldonh
5487866Ssheldonhstatic int  nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp);
5587866Ssheldonhstatic void nbns_rq_done(struct nbns_rq *rqp);
5687866Ssheldonhstatic int  nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
5787866Ssheldonhstatic int  nbns_rq_prepare(struct nbns_rq *rqp);
5887866Ssheldonhstatic int  nbns_rq(struct nbns_rq *rqp);
5987866Ssheldonh
6087866Ssheldonhstatic struct nb_ifdesc *nb_iflist;
6187866Ssheldonh
6287866Ssheldonhint
6387866Ssheldonhnbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
6487866Ssheldonh{
6587866Ssheldonh	struct nbns_rq *rqp;
6687866Ssheldonh	struct nb_name nn;
6787866Ssheldonh	struct nbns_rr rr;
6887866Ssheldonh	struct sockaddr_in *dest;
6987866Ssheldonh	int error, rdrcount, len;
7087866Ssheldonh
7187866Ssheldonh	if (strlen(name) > NB_NAMELEN)
7287866Ssheldonh		return NBERROR(NBERR_NAMETOOLONG);
7387866Ssheldonh	error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
7487866Ssheldonh	if (error)
7587866Ssheldonh		return error;
7687866Ssheldonh	bzero(&nn, sizeof(nn));
7787866Ssheldonh	strcpy(nn.nn_name, name);
7887866Ssheldonh	nn.nn_scope = ctx->nb_scope;
7987866Ssheldonh	nn.nn_type = NBT_SERVER;
8087866Ssheldonh	rqp->nr_nmflags = NBNS_NMFLAG_RD;
8187866Ssheldonh	rqp->nr_qdname = &nn;
8287866Ssheldonh	rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
8387866Ssheldonh	rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
8487866Ssheldonh	rqp->nr_qdcount = 1;
8587866Ssheldonh	dest = &rqp->nr_dest;
8687866Ssheldonh	*dest = ctx->nb_ns;
8787866Ssheldonh	dest->sin_family = AF_INET;
8887866Ssheldonh	dest->sin_len = sizeof(*dest);
8987866Ssheldonh	if (dest->sin_port == 0)
90150802Sbp		dest->sin_port = htons(ctx->nb_nmbtcpport);
9187866Ssheldonh	if (dest->sin_addr.s_addr == INADDR_ANY)
9287866Ssheldonh		dest->sin_addr.s_addr = htonl(INADDR_BROADCAST);
9387866Ssheldonh	if (dest->sin_addr.s_addr == INADDR_BROADCAST)
9487866Ssheldonh		rqp->nr_flags |= NBRQF_BROADCAST;
9587866Ssheldonh	error = nbns_rq_prepare(rqp);
9687866Ssheldonh	if (error) {
9787866Ssheldonh		nbns_rq_done(rqp);
9887866Ssheldonh		return error;
9987866Ssheldonh	}
10087866Ssheldonh	rdrcount = NBNS_MAXREDIRECTS;
10187866Ssheldonh	for (;;) {
10287866Ssheldonh		error = nbns_rq(rqp);
10387866Ssheldonh		if (error)
10487866Ssheldonh			break;
10587866Ssheldonh		if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
10687866Ssheldonh			if (rdrcount-- == 0) {
10787866Ssheldonh				error = NBERROR(NBERR_TOOMANYREDIRECTS);
10887866Ssheldonh				break;
10987866Ssheldonh			}
11087866Ssheldonh			error = nbns_rq_getrr(rqp, &rr);
11187866Ssheldonh			if (error)
11287866Ssheldonh				break;
11387866Ssheldonh			error = nbns_rq_getrr(rqp, &rr);
11487866Ssheldonh			if (error)
11587866Ssheldonh				break;
11687866Ssheldonh			bcopy(rr.rr_data, &dest->sin_addr, 4);
11787866Ssheldonh			rqp->nr_flags &= ~NBRQF_BROADCAST;
11887866Ssheldonh			continue;
11987866Ssheldonh		}
12087866Ssheldonh		if (rqp->nr_rpancount == 0) {
12187866Ssheldonh			error = NBERROR(NBERR_HOSTNOTFOUND);
12287866Ssheldonh			break;
12387866Ssheldonh		}
12487866Ssheldonh		error = nbns_rq_getrr(rqp, &rr);
12587866Ssheldonh		if (error)
12687866Ssheldonh			break;
12787866Ssheldonh		len = sizeof(struct sockaddr_in);
12887866Ssheldonh		dest = malloc(len);
12987866Ssheldonh		if (dest == NULL)
13087866Ssheldonh			return ENOMEM;
13187866Ssheldonh		bzero(dest, len);
13287866Ssheldonh		dest->sin_len = len;
13387866Ssheldonh		dest->sin_family = AF_INET;
13487866Ssheldonh		bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
135150802Sbp		dest->sin_port = htons(ctx->nb_smbtcpport);
13687866Ssheldonh		*adpp = (struct sockaddr*)dest;
13787866Ssheldonh		ctx->nb_lastns = rqp->nr_sender;
13887866Ssheldonh		break;
13987866Ssheldonh	}
14087866Ssheldonh	nbns_rq_done(rqp);
14187866Ssheldonh	return error;
14287866Ssheldonh}
14387866Ssheldonh
14487866Ssheldonhint
14587866Ssheldonhnbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
14687866Ssheldonh{
14787866Ssheldonh	struct nbns_rq *rqp;
14887866Ssheldonh	static u_int16_t trnid;
14987866Ssheldonh	int error;
15087866Ssheldonh
15187866Ssheldonh	rqp = malloc(sizeof(*rqp));
15287866Ssheldonh	if (rqp == NULL)
15387866Ssheldonh		return ENOMEM;
15487866Ssheldonh	bzero(rqp, sizeof(*rqp));
15587866Ssheldonh	error = mb_init(&rqp->nr_rq, NBDG_MAXSIZE);
15687866Ssheldonh	if (error) {
15787866Ssheldonh		free(rqp);
15887866Ssheldonh		return error;
15987866Ssheldonh	}
16087866Ssheldonh	rqp->nr_opcode = opcode;
16187866Ssheldonh	rqp->nr_nbd = ctx;
16287866Ssheldonh	rqp->nr_trnid = trnid++;
16387866Ssheldonh	*rqpp = rqp;
16487866Ssheldonh	return 0;
16587866Ssheldonh}
16687866Ssheldonh
16787866Ssheldonhvoid
16887866Ssheldonhnbns_rq_done(struct nbns_rq *rqp)
16987866Ssheldonh{
17087866Ssheldonh	if (rqp == NULL)
17187866Ssheldonh		return;
17287866Ssheldonh	if (rqp->nr_fd >= 0)
17387866Ssheldonh		close(rqp->nr_fd);
17487866Ssheldonh	mb_done(&rqp->nr_rq);
17587866Ssheldonh	mb_done(&rqp->nr_rp);
17687866Ssheldonh	free(rqp);
17787866Ssheldonh}
17887866Ssheldonh
17987866Ssheldonh/*
18087866Ssheldonh * Extract resource record from the packet. Assume that there is only
18187866Ssheldonh * one mbuf.
18287866Ssheldonh */
18387866Ssheldonhint
18487866Ssheldonhnbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
18587866Ssheldonh{
18687866Ssheldonh	struct mbdata *mbp = &rqp->nr_rp;
18787866Ssheldonh	u_char *cp;
18887866Ssheldonh	int error, len;
18987866Ssheldonh
19087866Ssheldonh	bzero(rrp, sizeof(*rrp));
19187866Ssheldonh	cp = mbp->mb_pos;
19287866Ssheldonh	len = nb_encname_len(cp);
19387866Ssheldonh	if (len < 1)
19487866Ssheldonh		return NBERROR(NBERR_INVALIDRESPONSE);
19587866Ssheldonh	rrp->rr_name = cp;
19687866Ssheldonh	error = mb_get_mem(mbp, NULL, len);
19787866Ssheldonh	if (error)
19887866Ssheldonh		return error;
19987866Ssheldonh	mb_get_uint16be(mbp, &rrp->rr_type);
20087866Ssheldonh	mb_get_uint16be(mbp, &rrp->rr_class);
20187866Ssheldonh	mb_get_uint32be(mbp, &rrp->rr_ttl);
20287866Ssheldonh	mb_get_uint16be(mbp, &rrp->rr_rdlength);
20387866Ssheldonh	rrp->rr_data = mbp->mb_pos;
20487866Ssheldonh	error = mb_get_mem(mbp, NULL, rrp->rr_rdlength);
20587866Ssheldonh	return error;
20687866Ssheldonh}
20787866Ssheldonh
20887866Ssheldonhint
20987866Ssheldonhnbns_rq_prepare(struct nbns_rq *rqp)
21087866Ssheldonh{
21187866Ssheldonh	struct nb_ctx *ctx = rqp->nr_nbd;
21287866Ssheldonh	struct mbdata *mbp = &rqp->nr_rq;
21387866Ssheldonh	u_int8_t nmflags;
21487866Ssheldonh	u_char *cp;
21587866Ssheldonh	int len, error;
21687866Ssheldonh
21787866Ssheldonh	error = mb_init(&rqp->nr_rp, NBDG_MAXSIZE);
21887866Ssheldonh	if (error)
21987866Ssheldonh		return error;
22087866Ssheldonh	if (rqp->nr_dest.sin_addr.s_addr == INADDR_BROADCAST) {
22187866Ssheldonh		rqp->nr_nmflags |= NBNS_NMFLAG_BCAST;
22287866Ssheldonh		if (nb_iflist == NULL) {
22387866Ssheldonh			error = nb_enum_if(&nb_iflist, 100);
22487866Ssheldonh			if (error)
22587866Ssheldonh				return error;
22687866Ssheldonh		}
22787866Ssheldonh	} else
22887866Ssheldonh		rqp->nr_nmflags &= ~NBNS_NMFLAG_BCAST;
22987866Ssheldonh	mb_put_uint16be(mbp, rqp->nr_trnid);
23087866Ssheldonh	nmflags = ((rqp->nr_opcode & 0x1F) << 3) | ((rqp->nr_nmflags & 0x70) >> 4);
23187866Ssheldonh	mb_put_uint8(mbp, nmflags);
23287866Ssheldonh	mb_put_uint8(mbp, (rqp->nr_nmflags & 0x0f) << 4 /* rcode */);
23387866Ssheldonh	mb_put_uint16be(mbp, rqp->nr_qdcount);
23487866Ssheldonh	mb_put_uint16be(mbp, rqp->nr_ancount);
23587866Ssheldonh	mb_put_uint16be(mbp, rqp->nr_nscount);
23687866Ssheldonh	mb_put_uint16be(mbp, rqp->nr_arcount);
23787866Ssheldonh	if (rqp->nr_qdcount) {
23887866Ssheldonh		if (rqp->nr_qdcount > 1)
23987866Ssheldonh			return EINVAL;
24087866Ssheldonh		len = nb_name_len(rqp->nr_qdname);
24187866Ssheldonh		error = mb_fit(mbp, len, (char**)&cp);
24287866Ssheldonh		if (error)
24387866Ssheldonh			return error;
24487866Ssheldonh		nb_name_encode(rqp->nr_qdname, cp);
24587866Ssheldonh		mb_put_uint16be(mbp, rqp->nr_qdtype);
24687866Ssheldonh		mb_put_uint16be(mbp, rqp->nr_qdclass);
24787866Ssheldonh	}
24887866Ssheldonh	m_lineup(mbp->mb_top, &mbp->mb_top);
24987866Ssheldonh	if (ctx->nb_timo == 0)
25087866Ssheldonh		ctx->nb_timo = 1;	/* by default 1 second */
25187866Ssheldonh	return 0;
25287866Ssheldonh}
25387866Ssheldonh
25487866Ssheldonhstatic int
25587866Ssheldonhnbns_rq_recv(struct nbns_rq *rqp)
25687866Ssheldonh{
25787866Ssheldonh	struct mbdata *mbp = &rqp->nr_rp;
25887866Ssheldonh	void *rpdata = mtod(mbp->mb_top, void *);
25987866Ssheldonh	fd_set rd, wr, ex;
26087866Ssheldonh	struct timeval tv;
26187866Ssheldonh	struct sockaddr_in sender;
26287866Ssheldonh	int s = rqp->nr_fd;
26387866Ssheldonh	int n, len;
26487866Ssheldonh
26587866Ssheldonh	FD_ZERO(&rd);
26687866Ssheldonh	FD_ZERO(&wr);
26787866Ssheldonh	FD_ZERO(&ex);
26887866Ssheldonh	FD_SET(s, &rd);
26987866Ssheldonh
27087866Ssheldonh	tv.tv_sec = rqp->nr_nbd->nb_timo;
27187866Ssheldonh	tv.tv_usec = 0;
27287866Ssheldonh
27387866Ssheldonh	n = select(s + 1, &rd, &wr, &ex, &tv);
27487866Ssheldonh	if (n == -1)
27587866Ssheldonh		return -1;
27687866Ssheldonh	if (n == 0)
27787866Ssheldonh		return ETIMEDOUT;
27887866Ssheldonh	if (FD_ISSET(s, &rd) == 0)
27987866Ssheldonh		return ETIMEDOUT;
28087866Ssheldonh	len = sizeof(sender);
28187866Ssheldonh	n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
28287866Ssheldonh	    (struct sockaddr*)&sender, &len);
28387866Ssheldonh	if (n < 0)
28487866Ssheldonh		return errno;
28587866Ssheldonh	mbp->mb_top->m_len = mbp->mb_count = n;
28687866Ssheldonh	rqp->nr_sender = sender;
28787866Ssheldonh	return 0;
28887866Ssheldonh}
28987866Ssheldonh
29087866Ssheldonhstatic int
29187866Ssheldonhnbns_rq_opensocket(struct nbns_rq *rqp)
29287866Ssheldonh{
29387866Ssheldonh	struct sockaddr_in locaddr;
29487866Ssheldonh	int opt, s;
29587866Ssheldonh
29687866Ssheldonh	s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
29787866Ssheldonh	if (s < 0)
29887866Ssheldonh		return errno;
29987866Ssheldonh	if (rqp->nr_flags & NBRQF_BROADCAST) {
30087866Ssheldonh		opt = 1;
30187866Ssheldonh		if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0)
30287866Ssheldonh			return errno;
30387866Ssheldonh		if (rqp->nr_if == NULL)
30487866Ssheldonh			return NBERROR(NBERR_NOBCASTIFS);
30587866Ssheldonh		bzero(&locaddr, sizeof(locaddr));
30687866Ssheldonh		locaddr.sin_family = AF_INET;
30787866Ssheldonh		locaddr.sin_len = sizeof(locaddr);
30887866Ssheldonh		locaddr.sin_addr = rqp->nr_if->id_addr;
30987866Ssheldonh		rqp->nr_dest.sin_addr.s_addr = rqp->nr_if->id_addr.s_addr | ~rqp->nr_if->id_mask.s_addr;
31087866Ssheldonh		if (bind(s, (struct sockaddr*)&locaddr, sizeof(locaddr)) < 0)
31187866Ssheldonh			return errno;
31287866Ssheldonh	}
31387866Ssheldonh	return 0;
31487866Ssheldonh}
31587866Ssheldonh
31687866Ssheldonhstatic int
31787866Ssheldonhnbns_rq_send(struct nbns_rq *rqp)
31887866Ssheldonh{
31987866Ssheldonh	struct mbdata *mbp = &rqp->nr_rq;
32087866Ssheldonh	int s = rqp->nr_fd;
32187866Ssheldonh
32287866Ssheldonh	if (sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
32387866Ssheldonh	      (struct sockaddr*)&rqp->nr_dest, sizeof(rqp->nr_dest)) < 0)
32487866Ssheldonh		return errno;
32587866Ssheldonh	return 0;
32687866Ssheldonh}
32787866Ssheldonh
32887866Ssheldonhint
32987866Ssheldonhnbns_rq(struct nbns_rq *rqp)
33087866Ssheldonh{
33187866Ssheldonh	struct mbdata *mbp = &rqp->nr_rq;
33287866Ssheldonh	u_int16_t rpid;
33387866Ssheldonh	u_int8_t nmflags;
33487866Ssheldonh	int error, retrycount;
33587866Ssheldonh
33687866Ssheldonh	rqp->nr_if = nb_iflist;
33787866Ssheldonhagain:
33887866Ssheldonh	error = nbns_rq_opensocket(rqp);
33987866Ssheldonh	if (error)
34087866Ssheldonh		return error;
34187866Ssheldonh	retrycount = 3;	/* XXX - configurable */
34287866Ssheldonh	for (;;) {
34387866Ssheldonh		error = nbns_rq_send(rqp);
34487866Ssheldonh		if (error)
34587866Ssheldonh			return error;
34687866Ssheldonh		error = nbns_rq_recv(rqp);
34787866Ssheldonh		if (error) {
34887866Ssheldonh			if (error != ETIMEDOUT || retrycount == 0) {
34987866Ssheldonh				if ((rqp->nr_nmflags & NBNS_NMFLAG_BCAST) &&
35087866Ssheldonh				    rqp->nr_if != NULL &&
35187866Ssheldonh				    rqp->nr_if->id_next != NULL) {
35287866Ssheldonh					rqp->nr_if = rqp->nr_if->id_next;
35387866Ssheldonh					close(rqp->nr_fd);
35487866Ssheldonh					goto again;
35587866Ssheldonh				} else
35687866Ssheldonh					return error;
35787866Ssheldonh			}
35887866Ssheldonh			retrycount--;
35987866Ssheldonh			continue;
36087866Ssheldonh		}
36187866Ssheldonh		mbp = &rqp->nr_rp;
36287866Ssheldonh		if (mbp->mb_count < 12)
36387866Ssheldonh			return NBERROR(NBERR_INVALIDRESPONSE);
36487866Ssheldonh		mb_get_uint16be(mbp, &rpid);
36587866Ssheldonh		if (rpid != rqp->nr_trnid)
36687866Ssheldonh			return NBERROR(NBERR_INVALIDRESPONSE);
36787866Ssheldonh		break;
36887866Ssheldonh	}
36987866Ssheldonh	mb_get_uint8(mbp, &nmflags);
37087866Ssheldonh	rqp->nr_rpnmflags = (nmflags & 7) << 4;
37187866Ssheldonh	mb_get_uint8(mbp, &nmflags);
37287866Ssheldonh	rqp->nr_rpnmflags |= (nmflags & 0xf0) >> 4;
37387866Ssheldonh	rqp->nr_rprcode = nmflags & 0xf;
37487866Ssheldonh	if (rqp->nr_rprcode)
37587866Ssheldonh		return NBERROR(rqp->nr_rprcode);
37687866Ssheldonh	mb_get_uint16be(mbp, &rpid);	/* QDCOUNT */
37787866Ssheldonh	mb_get_uint16be(mbp, &rqp->nr_rpancount);
37887866Ssheldonh	mb_get_uint16be(mbp, &rqp->nr_rpnscount);
37987866Ssheldonh	mb_get_uint16be(mbp, &rqp->nr_rparcount);
38087866Ssheldonh	return 0;
38187866Ssheldonh}
382