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