2 * Copyright (c) 2000, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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>
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>
75 struct nb_name
*nr_qdname
;
78 struct in_addr nr_dest
; /* receiver of query */
79 struct sockaddr_in nr_sender
; /* sender of response */
82 uint16_t nr_rpancount
;
83 uint16_t nr_rpnscount
;
84 uint16_t nr_rparcount
;
86 struct nb_ctx
*nr_nbd
;
89 struct nb_ifdesc
*nr_if
;
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
;
117 * Try NetBIOS name lookup.
119 if (strlen(name
) >= NB_NAMELEN
) {
123 ucname
= utf8_str_toupper(name
);
127 /* Note: this returns an NBERROR value. */
128 err
= nbns_resolvename(ucname
, nbc
, &sap
);
131 smb_error(dgettext(TEXT_DOMAIN
,
132 "nbns_resolvename: %s"),
137 /* Note: sap allocated */
140 * Build the addrinfo struct to return.
142 nai
= malloc(sizeof (*nai
));
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
;
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
);
176 nbns_resolvename(const char *name
, struct nb_ctx
*ctx
, struct sockaddr
**adpp
)
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
);
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
;
203 rqp
->nr_maxretry
= 5;
205 error
= nbns_rq_prepare(rqp
);
210 rdrcount
= NBNS_MAXREDIRECTS
;
212 error
= nbns_rq(rqp
);
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
);
224 error
= nbns_rq_getrr(rqp
, &rr
);
227 error
= nbns_rq_getrr(rqp
, &rr
);
230 bcopy(rr
.rr_data
, &rqp
->nr_dest
, 4);
233 if (rqp
->nr_rpancount
== 0) {
234 error
= NBERROR(NBERR_HOSTNOTFOUND
);
237 error
= nbns_rq_getrr(rqp
, &rr
);
240 len
= sizeof (struct sockaddr_in
);
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
;
260 * NB: system, workgroup are both NB_NAMELEN
263 nbns_getnodestatus(struct nb_ctx
*ctx
,
264 struct in_addr
*targethost
, char *system
, char *workgroup
)
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
);
278 bzero(&nn
, sizeof (nn
));
279 strcpy((char *)nn
.nn_name
, "*");
280 nn
.nn_scope
= ctx
->nb_scope
;
281 nn
.nn_type
= NBT_WKSTA
;
283 rqp
->nr_qdname
= &nn
;
284 rqp
->nr_qdtype
= NBNS_QUESTION_TYPE_NBSTAT
;
285 rqp
->nr_qdclass
= NBNS_QUESTION_CLASS_IN
;
287 rqp
->nr_maxretry
= 2;
289 rqp
->nr_dest
= *targethost
;
290 error
= nbns_rq_prepare(rqp
);
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
);
303 if (rqp
->nr_rpancount
== 0) {
304 error
= NBERROR(NBERR_HOSTNOTFOUND
);
307 error
= nbns_rq_getrr(rqp
, &rr
);
311 /* Compiler didn't like cast on lvalue++ */
312 nrcount
= *((unsigned char *)rr
.rr_data
);
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)
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
) {
332 (foundgroup
!= NBT_WKSTA
+1 &&
333 nrtype
== NBT_WKSTA
)) {
334 strlcpy(workgroup
, nrp
->ns_name
,
336 foundgroup
= nrtype
+1;
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
,
354 if (foundserver
== 0 && retname
!= NULL
)
355 strlcpy(system
, retname
, NB_NAMELEN
);
356 ctx
->nb_lastns
= rqp
->nr_sender
;
364 nbns_rq_create(int opcode
, struct nb_ctx
*ctx
, struct nbns_rq
**rqpp
)
367 static uint16_t trnid
;
372 rqp
= malloc(sizeof (*rqp
));
375 bzero(rqp
, sizeof (*rqp
));
376 error
= mb_init_sz(&rqp
->nr_rq
, NBDG_MAXSIZE
);
381 rqp
->nr_opcode
= opcode
;
383 rqp
->nr_trnid
= trnid
++;
389 nbns_rq_done(struct nbns_rq
*rqp
)
395 mb_done(&rqp
->nr_rq
);
396 mb_done(&rqp
->nr_rp
);
402 * Extract resource record from the packet. Assume that there is only
406 nbns_rq_getrr(struct nbns_rq
*rqp
, struct nbns_rr
*rrp
)
408 struct mbdata
*mbp
= &rqp
->nr_rp
;
412 bzero(rrp
, sizeof (*rrp
));
413 cp
= (uchar_t
*)mbp
->mb_pos
;
414 len
= nb_encname_len(cp
);
416 return (NBERROR(NBERR_INVALIDRESPONSE
));
418 error
= md_get_mem(mbp
, NULL
, len
, MB_MSYSTEM
);
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
);
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 */
438 error
= mb_init_sz(&rqp
->nr_rp
, NBDG_MAXSIZE
);
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)
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
);
463 error
= m_lineup(mbp
->mb_top
, &mbp
->mb_top
);
466 if (ctx
->nb_timo
== 0)
467 ctx
->nb_timo
= 1; /* by default 1 second */
472 nbns_rq_recv(struct nbns_rq
*rqp
)
474 struct mbdata
*mbp
= &rqp
->nr_rp
;
475 void *rpdata
= mtod(mbp
->mb_top
, void *);
478 struct sockaddr_in sender
;
488 tv
.tv_sec
= rqp
->nr_nbd
->nb_timo
;
491 n
= select(s
+ 1, &rd
, &wr
, &ex
, &tv
);
496 if (FD_ISSET(s
, &rd
) == 0)
498 len
= sizeof (sender
);
499 n
= recvfrom(s
, rpdata
, mbp
->mb_top
->m_maxlen
, 0,
500 (struct sockaddr
*)&sender
, &len
);
503 mbp
->mb_top
->m_len
= mbp
->mb_count
= n
;
504 rqp
->nr_sender
= sender
;
509 nbns_rq_opensocket(struct nbns_rq
*rqp
)
511 struct sockaddr_in locaddr
;
513 struct nb_ctx
*ctx
= rqp
->nr_nbd
;
515 s
= rqp
->nr_fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
518 if (ctx
->nb_flags
& NBCF_BC_ENABLE
) {
519 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &opt
,
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)
532 nbns_rq_send(struct nbns_rq
*rqp
, in_addr_t ina
)
534 struct sockaddr_in dest
;
535 struct mbdata
*mbp
= &rqp
->nr_rq
;
537 uint16_t ofr
, ofr_save
; /* opcode, nmflags, rcode */
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
;
551 datap
= mtod(mbp
->mb_top
, uint16_t *);
552 ofr
= ((rqp
->nr_opcode
& 0x1F) << 11) |
553 ((nmflags
& 0x7F) << 4); /* rcode=0 */
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. */
574 nbns_rq(struct nbns_rq
*rqp
)
576 struct nb_ctx
*ctx
= rqp
->nr_nbd
;
577 struct mbdata
*mbp
= &rqp
->nr_rq
;
579 int error
, tries
, maxretry
;
581 error
= nbns_rq_opensocket(rqp
);
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
);
595 smb_error(dgettext(TEXT_DOMAIN
,
596 "nbns error %d sending to %s"),
597 0, error
, inet_ntoa(rqp
->nr_dest
));
603 error
= nbns_rq_send(rqp
, ctx
->nb_wins1
);
605 smb_error(dgettext(TEXT_DOMAIN
,
606 "nbns error %d sending to wins1"),
611 if (ctx
->nb_wins2
&& (tries
> 0)) {
612 error
= nbns_rq_send(rqp
, ctx
->nb_wins2
);
614 smb_error(dgettext(TEXT_DOMAIN
,
615 "nbns error %d sending to wins2"),
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
);
629 smb_error(dgettext(TEXT_DOMAIN
,
630 "nbns error %d sending broadcast"),
636 * Wait for responses from ANY of the above.
639 error
= nbns_rq_recv(rqp
);
640 if (error
== ETIMEDOUT
)
643 smb_error(dgettext(TEXT_DOMAIN
,
644 "nbns recv error %d"),
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
));
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;
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
);