dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libsmbfs / smb / nbns_rq.c
blobb40af91a5034a00376080e3a8676232d3af1a5c5
1 /*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
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.
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.
32 * $Id: nbns_rq.c,v 1.9 2005/02/24 02:04:38 lindak Exp $
36 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <ctype.h>
43 #include <netdb.h>
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <libintl.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
55 #define NB_NEEDRESOLVER
56 #include <netsmb/netbios.h>
57 #include <netsmb/smb_lib.h>
58 #include <netsmb/nb_lib.h>
59 #include <netsmb/mchain.h>
61 #include "charsets.h"
62 #include "private.h"
65 * nbns request
67 struct nbns_rq {
68 int nr_opcode;
69 int nr_nmflags;
70 int nr_rcode;
71 int nr_qdcount;
72 int nr_ancount;
73 int nr_nscount;
74 int nr_arcount;
75 struct nb_name *nr_qdname;
76 uint16_t nr_qdtype;
77 uint16_t nr_qdclass;
78 struct in_addr nr_dest; /* receiver of query */
79 struct sockaddr_in nr_sender; /* sender of response */
80 int nr_rpnmflags;
81 int nr_rprcode;
82 uint16_t nr_rpancount;
83 uint16_t nr_rpnscount;
84 uint16_t nr_rparcount;
85 uint16_t nr_trnid;
86 struct nb_ctx *nr_nbd;
87 struct mbdata nr_rq;
88 struct mbdata nr_rp;
89 struct nb_ifdesc *nr_if;
90 int nr_flags;
91 int nr_fd;
92 int nr_maxretry;
94 typedef struct nbns_rq nbns_rq_t;
96 static int nbns_rq_create(int opcode, struct nb_ctx *ctx,
97 struct nbns_rq **rqpp);
98 static void nbns_rq_done(struct nbns_rq *rqp);
99 static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
100 static int nbns_rq_prepare(struct nbns_rq *rqp);
101 static int nbns_rq(struct nbns_rq *rqp);
104 * Call NetBIOS name lookup and return a result in the
105 * same form as getaddrinfo(3) returns. Return code is
106 * zero or one of the EAI_xxx codes like getaddrinfo.
109 nbns_getaddrinfo(const char *name, struct nb_ctx *nbc, struct addrinfo **res)
111 struct addrinfo *nai = NULL;
112 struct sockaddr *sap = NULL;
113 char *ucname = NULL;
114 int err;
117 * Try NetBIOS name lookup.
119 if (strlen(name) >= NB_NAMELEN) {
120 err = EAI_OVERFLOW;
121 goto out;
123 ucname = utf8_str_toupper(name);
124 if (ucname == NULL)
125 goto nomem;
127 /* Note: this returns an NBERROR value. */
128 err = nbns_resolvename(ucname, nbc, &sap);
129 if (err) {
130 if (smb_verbose)
131 smb_error(dgettext(TEXT_DOMAIN,
132 "nbns_resolvename: %s"),
133 err, name);
134 err = EAI_NODATA;
135 goto out;
137 /* Note: sap allocated */
140 * Build the addrinfo struct to return.
142 nai = malloc(sizeof (*nai));
143 if (nai == NULL)
144 goto nomem;
145 bzero(nai, sizeof (*nai));
147 nai->ai_flags = AI_CANONNAME;
148 nai->ai_family = sap->sa_family;
149 nai->ai_socktype = SOCK_STREAM;
150 nai->ai_canonname = ucname;
151 ucname = NULL;
154 * The type of this is really sockaddr_in,
155 * but is returned in the generic form.
156 * See nbns_resolvename.
158 nai->ai_addrlen = sizeof (struct sockaddr_in);
159 nai->ai_addr = sap;
161 *res = nai;
162 return (0);
164 nomem:
165 err = EAI_MEMORY;
166 out:
167 free(nai);
168 free(sap);
169 free(ucname);
170 *res = NULL;
172 return (err);
176 nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
178 struct nbns_rq *rqp;
179 struct nb_name nn;
180 struct nbns_rr rr;
181 struct sockaddr_in *dest;
182 int error, rdrcount, len;
184 if (strlen(name) >= NB_NAMELEN)
185 return (NBERROR(NBERR_NAMETOOLONG));
186 error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
187 if (error)
188 return (error);
190 * Pad the name with blanks, but
191 * leave the "type" byte NULL.
192 * nb_name_encode adds the type.
194 bzero(&nn, sizeof (nn));
195 snprintf(nn.nn_name, NB_NAMELEN, "%-15.15s", name);
196 nn.nn_type = NBT_SERVER;
197 nn.nn_scope = ctx->nb_scope;
198 rqp->nr_nmflags = NBNS_NMFLAG_RD;
199 rqp->nr_qdname = &nn;
200 rqp->nr_qdtype = NBNS_QUESTION_TYPE_NB;
201 rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
202 rqp->nr_qdcount = 1;
203 rqp->nr_maxretry = 5;
205 error = nbns_rq_prepare(rqp);
206 if (error) {
207 nbns_rq_done(rqp);
208 return (error);
210 rdrcount = NBNS_MAXREDIRECTS;
211 for (;;) {
212 error = nbns_rq(rqp);
213 if (error)
214 break;
215 if ((rqp->nr_rpnmflags & NBNS_NMFLAG_AA) == 0) {
217 * Not an authoritative answer. Query again
218 * using the NS address in the 2nd record.
220 if (rdrcount-- == 0) {
221 error = NBERROR(NBERR_TOOMANYREDIRECTS);
222 break;
224 error = nbns_rq_getrr(rqp, &rr);
225 if (error)
226 break;
227 error = nbns_rq_getrr(rqp, &rr);
228 if (error)
229 break;
230 bcopy(rr.rr_data, &rqp->nr_dest, 4);
231 continue;
233 if (rqp->nr_rpancount == 0) {
234 error = NBERROR(NBERR_HOSTNOTFOUND);
235 break;
237 error = nbns_rq_getrr(rqp, &rr);
238 if (error)
239 break;
240 len = sizeof (struct sockaddr_in);
241 dest = malloc(len);
242 if (dest == NULL)
243 return (ENOMEM);
244 bzero(dest, len);
246 * Solaris sockaddr_in doesn't a sin_len field.
247 * dest->sin_len = len;
249 dest->sin_family = AF_NETBIOS; /* nb_lib.h */
250 bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
251 *adpp = (struct sockaddr *)dest;
252 ctx->nb_lastns = rqp->nr_sender;
253 break;
255 nbns_rq_done(rqp);
256 return (error);
260 * NB: system, workgroup are both NB_NAMELEN
263 nbns_getnodestatus(struct nb_ctx *ctx,
264 struct in_addr *targethost, char *system, char *workgroup)
266 struct nbns_rq *rqp;
267 struct nbns_rr rr;
268 struct nb_name nn;
269 struct nbns_nr *nrp;
270 char nrtype;
271 char *cp, *retname = NULL;
272 unsigned char nrcount;
273 int error, i, foundserver = 0, foundgroup = 0;
275 error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
276 if (error)
277 return (error);
278 bzero(&nn, sizeof (nn));
279 strcpy((char *)nn.nn_name, "*");
280 nn.nn_scope = ctx->nb_scope;
281 nn.nn_type = NBT_WKSTA;
282 rqp->nr_nmflags = 0;
283 rqp->nr_qdname = &nn;
284 rqp->nr_qdtype = NBNS_QUESTION_TYPE_NBSTAT;
285 rqp->nr_qdclass = NBNS_QUESTION_CLASS_IN;
286 rqp->nr_qdcount = 1;
287 rqp->nr_maxretry = 2;
289 rqp->nr_dest = *targethost;
290 error = nbns_rq_prepare(rqp);
291 if (error) {
292 nbns_rq_done(rqp);
293 return (error);
297 * Darwin had a loop here, allowing redirect, etc.
298 * but we only handle point-to-point for node status.
300 error = nbns_rq(rqp);
301 if (error)
302 goto out;
303 if (rqp->nr_rpancount == 0) {
304 error = NBERROR(NBERR_HOSTNOTFOUND);
305 goto out;
307 error = nbns_rq_getrr(rqp, &rr);
308 if (error)
309 goto out;
311 /* Compiler didn't like cast on lvalue++ */
312 nrcount = *((unsigned char *)rr.rr_data);
313 rr.rr_data++;
314 /* LINTED */
315 for (i = 1, nrp = (struct nbns_nr *)rr.rr_data;
316 i <= nrcount; ++i, ++nrp) {
317 nrtype = nrp->ns_name[NB_NAMELEN-1];
318 /* Terminate the string: */
319 nrp->ns_name[NB_NAMELEN-1] = '\0';
320 /* Strip off trailing spaces */
321 for (cp = &nrp->ns_name[NB_NAMELEN-2];
322 cp >= nrp->ns_name; --cp) {
323 if (*cp != (char)0x20)
324 break;
325 *cp = '\0';
327 nrp->ns_flags = ntohs(nrp->ns_flags);
328 DPRINT(" %s[%02x] Flags 0x%x",
329 nrp->ns_name, nrtype, nrp->ns_flags);
330 if (nrp->ns_flags & NBNS_GROUPFLG) {
331 if (!foundgroup ||
332 (foundgroup != NBT_WKSTA+1 &&
333 nrtype == NBT_WKSTA)) {
334 strlcpy(workgroup, nrp->ns_name,
335 NB_NAMELEN);
336 foundgroup = nrtype+1;
338 } else {
340 * Track at least ONE name, in case
341 * no server name is found
343 retname = nrp->ns_name;
346 * Keep the first NBT_SERVER name.
348 if (nrtype == NBT_SERVER && foundserver == 0) {
349 strlcpy(system, nrp->ns_name,
350 NB_NAMELEN);
351 foundserver = 1;
354 if (foundserver == 0 && retname != NULL)
355 strlcpy(system, retname, NB_NAMELEN);
356 ctx->nb_lastns = rqp->nr_sender;
358 out:
359 nbns_rq_done(rqp);
360 return (error);
364 nbns_rq_create(int opcode, struct nb_ctx *ctx, struct nbns_rq **rqpp)
366 struct nbns_rq *rqp;
367 static uint16_t trnid;
368 int error;
370 if (trnid == 0)
371 trnid = getpid();
372 rqp = malloc(sizeof (*rqp));
373 if (rqp == NULL)
374 return (ENOMEM);
375 bzero(rqp, sizeof (*rqp));
376 error = mb_init_sz(&rqp->nr_rq, NBDG_MAXSIZE);
377 if (error) {
378 free(rqp);
379 return (error);
381 rqp->nr_opcode = opcode;
382 rqp->nr_nbd = ctx;
383 rqp->nr_trnid = trnid++;
384 *rqpp = rqp;
385 return (0);
388 void
389 nbns_rq_done(struct nbns_rq *rqp)
391 if (rqp == NULL)
392 return;
393 if (rqp->nr_fd >= 0)
394 close(rqp->nr_fd);
395 mb_done(&rqp->nr_rq);
396 mb_done(&rqp->nr_rp);
397 free(rqp->nr_if);
398 free(rqp);
402 * Extract resource record from the packet. Assume that there is only
403 * one mbuf.
406 nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp)
408 struct mbdata *mbp = &rqp->nr_rp;
409 uchar_t *cp;
410 int error, len;
412 bzero(rrp, sizeof (*rrp));
413 cp = (uchar_t *)mbp->mb_pos;
414 len = nb_encname_len(cp);
415 if (len < 1)
416 return (NBERROR(NBERR_INVALIDRESPONSE));
417 rrp->rr_name = cp;
418 error = md_get_mem(mbp, NULL, len, MB_MSYSTEM);
419 if (error)
420 return (error);
421 md_get_uint16be(mbp, &rrp->rr_type);
422 md_get_uint16be(mbp, &rrp->rr_class);
423 md_get_uint32be(mbp, &rrp->rr_ttl);
424 md_get_uint16be(mbp, &rrp->rr_rdlength);
425 rrp->rr_data = (uchar_t *)mbp->mb_pos;
426 error = md_get_mem(mbp, NULL, rrp->rr_rdlength, MB_MSYSTEM);
427 return (error);
431 nbns_rq_prepare(struct nbns_rq *rqp)
433 struct nb_ctx *ctx = rqp->nr_nbd;
434 struct mbdata *mbp = &rqp->nr_rq;
435 uint16_t ofr; /* opcode, flags, rcode */
436 int error;
438 error = mb_init_sz(&rqp->nr_rp, NBDG_MAXSIZE);
439 if (error)
440 return (error);
443 * When looked into the ethereal trace, 'nmblookup' command sets this
444 * flag. We will also set.
446 mb_put_uint16be(mbp, rqp->nr_trnid);
447 ofr = ((rqp->nr_opcode & 0x1F) << 11) |
448 ((rqp->nr_nmflags & 0x7F) << 4); /* rcode=0 */
449 mb_put_uint16be(mbp, ofr);
450 mb_put_uint16be(mbp, rqp->nr_qdcount);
451 mb_put_uint16be(mbp, rqp->nr_ancount);
452 mb_put_uint16be(mbp, rqp->nr_nscount);
453 error = mb_put_uint16be(mbp, rqp->nr_arcount);
454 if (rqp->nr_qdcount) {
455 if (rqp->nr_qdcount > 1)
456 return (EINVAL);
457 (void) nb_name_encode(mbp, rqp->nr_qdname);
458 mb_put_uint16be(mbp, rqp->nr_qdtype);
459 error = mb_put_uint16be(mbp, rqp->nr_qdclass);
461 if (error)
462 return (error);
463 error = m_lineup(mbp->mb_top, &mbp->mb_top);
464 if (error)
465 return (error);
466 if (ctx->nb_timo == 0)
467 ctx->nb_timo = 1; /* by default 1 second */
468 return (0);
471 static int
472 nbns_rq_recv(struct nbns_rq *rqp)
474 struct mbdata *mbp = &rqp->nr_rp;
475 void *rpdata = mtod(mbp->mb_top, void *);
476 fd_set rd, wr, ex;
477 struct timeval tv;
478 struct sockaddr_in sender;
479 int s = rqp->nr_fd;
480 int n;
481 socklen_t len;
483 FD_ZERO(&rd);
484 FD_ZERO(&wr);
485 FD_ZERO(&ex);
486 FD_SET(s, &rd);
488 tv.tv_sec = rqp->nr_nbd->nb_timo;
489 tv.tv_usec = 0;
491 n = select(s + 1, &rd, &wr, &ex, &tv);
492 if (n == -1)
493 return (-1);
494 if (n == 0)
495 return (ETIMEDOUT);
496 if (FD_ISSET(s, &rd) == 0)
497 return (ETIMEDOUT);
498 len = sizeof (sender);
499 n = recvfrom(s, rpdata, mbp->mb_top->m_maxlen, 0,
500 (struct sockaddr *)&sender, &len);
501 if (n < 0)
502 return (errno);
503 mbp->mb_top->m_len = mbp->mb_count = n;
504 rqp->nr_sender = sender;
505 return (0);
508 static int
509 nbns_rq_opensocket(struct nbns_rq *rqp)
511 struct sockaddr_in locaddr;
512 int opt = 1, s;
513 struct nb_ctx *ctx = rqp->nr_nbd;
515 s = rqp->nr_fd = socket(AF_INET, SOCK_DGRAM, 0);
516 if (s < 0)
517 return (errno);
518 if (ctx->nb_flags & NBCF_BC_ENABLE) {
519 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &opt,
520 sizeof (opt)) < 0)
521 return (errno);
523 bzero(&locaddr, sizeof (locaddr));
524 locaddr.sin_family = AF_INET;
525 /* locaddr.sin_len = sizeof (locaddr); */
526 if (bind(s, (struct sockaddr *)&locaddr, sizeof (locaddr)) < 0)
527 return (errno);
528 return (0);
531 static int
532 nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
534 struct sockaddr_in dest;
535 struct mbdata *mbp = &rqp->nr_rq;
536 int s = rqp->nr_fd;
537 uint16_t ofr, ofr_save; /* opcode, nmflags, rcode */
538 uint16_t *datap;
539 uint8_t nmflags;
540 int rc;
542 bzero(&dest, sizeof (dest));
543 dest.sin_family = AF_INET;
544 dest.sin_port = htons(IPPORT_NETBIOS_NS);
545 dest.sin_addr.s_addr = ina;
547 if (ina == INADDR_BROADCAST) {
548 /* Turn on the broadcast bit. */
549 nmflags = rqp->nr_nmflags | NBNS_NMFLAG_BCAST;
550 /*LINTED*/
551 datap = mtod(mbp->mb_top, uint16_t *);
552 ofr = ((rqp->nr_opcode & 0x1F) << 11) |
553 ((nmflags & 0x7F) << 4); /* rcode=0 */
554 ofr_save = datap[1];
555 datap[1] = htons(ofr);
558 rc = sendto(s, mtod(mbp->mb_top, char *), mbp->mb_count, 0,
559 (struct sockaddr *)&dest, sizeof (dest));
561 if (ina == INADDR_BROADCAST) {
562 /* Turn the broadcast bit back off. */
563 datap[1] = ofr_save;
567 if (rc < 0)
568 return (errno);
570 return (0);
574 nbns_rq(struct nbns_rq *rqp)
576 struct nb_ctx *ctx = rqp->nr_nbd;
577 struct mbdata *mbp = &rqp->nr_rq;
578 uint16_t ofr, rpid;
579 int error, tries, maxretry;
581 error = nbns_rq_opensocket(rqp);
582 if (error)
583 return (error);
585 maxretry = rqp->nr_maxretry;
586 for (tries = 0; tries < maxretry; tries++) {
589 * Minor hack: If nr_dest is set, send there only.
590 * Used by _getnodestatus, _resolvname redirects.
592 if (rqp->nr_dest.s_addr) {
593 error = nbns_rq_send(rqp, rqp->nr_dest.s_addr);
594 if (error) {
595 smb_error(dgettext(TEXT_DOMAIN,
596 "nbns error %d sending to %s"),
597 0, error, inet_ntoa(rqp->nr_dest));
599 goto do_recv;
602 if (ctx->nb_wins1) {
603 error = nbns_rq_send(rqp, ctx->nb_wins1);
604 if (error) {
605 smb_error(dgettext(TEXT_DOMAIN,
606 "nbns error %d sending to wins1"),
607 0, error);
611 if (ctx->nb_wins2 && (tries > 0)) {
612 error = nbns_rq_send(rqp, ctx->nb_wins2);
613 if (error) {
614 smb_error(dgettext(TEXT_DOMAIN,
615 "nbns error %d sending to wins2"),
616 0, error);
621 * If broadcast is enabled, start broadcasting
622 * only after wins servers fail to respond, or
623 * immediately if no WINS servers configured.
625 if ((ctx->nb_flags & NBCF_BC_ENABLE) &&
626 ((tries > 1) || (ctx->nb_wins1 == 0))) {
627 error = nbns_rq_send(rqp, INADDR_BROADCAST);
628 if (error) {
629 smb_error(dgettext(TEXT_DOMAIN,
630 "nbns error %d sending broadcast"),
631 0, error);
636 * Wait for responses from ANY of the above.
638 do_recv:
639 error = nbns_rq_recv(rqp);
640 if (error == ETIMEDOUT)
641 continue;
642 if (error) {
643 smb_error(dgettext(TEXT_DOMAIN,
644 "nbns recv error %d"),
645 0, error);
646 return (error);
649 mbp = &rqp->nr_rp;
650 if (mbp->mb_count < 12)
651 return (NBERROR(NBERR_INVALIDRESPONSE));
652 md_get_uint16be(mbp, &rpid);
653 if (rpid != rqp->nr_trnid)
654 return (NBERROR(NBERR_INVALIDRESPONSE));
655 break;
657 if (tries == maxretry)
658 return (NBERROR(NBERR_HOSTNOTFOUND));
660 md_get_uint16be(mbp, &ofr);
661 rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
662 rqp->nr_rprcode = ofr & 0xf;
663 if (rqp->nr_rprcode)
664 return (NBERROR(rqp->nr_rprcode));
665 md_get_uint16be(mbp, &rpid); /* QDCOUNT */
666 md_get_uint16be(mbp, &rqp->nr_rpancount);
667 md_get_uint16be(mbp, &rqp->nr_rpnscount);
668 md_get_uint16be(mbp, &rqp->nr_rparcount);
669 return (0);