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