From 3ae7a3d1f0f868593aa6599b6dbfc351320cebda Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 1 May 2005 11:10:17 +0000 Subject: [PATCH] NMB packets are so similar to DNS packets; we may as well add NMB as a name resolution method. --- src/include/nmb.h | 22 ++++++++++ src/proto/nmb.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/include/nmb.h create mode 100644 src/proto/nmb.c diff --git a/src/include/nmb.h b/src/include/nmb.h new file mode 100644 index 00000000..695f8e08 --- /dev/null +++ b/src/include/nmb.h @@ -0,0 +1,22 @@ +#ifndef NMB_H +#define NMB_H + +#include "dns.h" + +/* + * NetBIOS name query packets are basically the same as DNS packets, + * though the resource record format is different. + * + */ + +#define DNS_TYPE_NB 0x20 +#define DNS_FLAG_BROADCAST ( 0x01 << 4 ) +#define NBNS_UDP_PORT 137 + +struct dns_rr_info_nb { + struct dns_rr_info; + uint16_t nb_flags; + struct in_addr nb_address; +} __attribute__ (( packed )); + +#endif /* NMB_H */ diff --git a/src/proto/nmb.c b/src/proto/nmb.c new file mode 100644 index 00000000..b395dabe --- /dev/null +++ b/src/proto/nmb.c @@ -0,0 +1,103 @@ +#include "resolv.h" +#include "string.h" +#include "dns.h" +#include "nic.h" +#include "nmb.h" + +/* + * Convert a standard NUL-terminated string to an NBNS query name. + * + * Returns a pointer to the character following the constructed NBNS + * query name. + * + */ +static inline char * nbns_make_name ( char *dest, const char *name ) { + char nb_name[16]; + char c; + int i; + + *(dest++) = 32; /* Length is always 32 */ + + /* Name encoding is as follows: pad the name with spaces to + * length 15, and add a NUL. Take this 16-byte string, split + * it into nibbles and add 0x41 to each nibble to form a byte + * of the resulting name string. + */ + memset ( nb_name, ' ', 15 ); + nb_name[15] = '\0'; + memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */ + for ( i = 0 ; i < 16 ; i++ ) { + c = nb_name[i]; + *( ( ( uint16_t * ) dest ) ++ ) = + htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 ); + } + + *(dest++) = 0; /* Terminating 0-length name component */ + return dest; +} + +/* + * Resolve a name using NMB + * + */ +static int nmb_resolv ( struct in_addr *addr, const char *name ) { + struct dns_query query; + struct dns_query_info *query_info; + struct dns_header *reply; + struct dns_rr_info *rr_info; + struct dns_rr_info_nb *rr_info_nb; + struct sockaddr_in nameserver; + + DBG ( "NMB resolving %s\n", name ); + + /* Set up the query data */ + nameserver.sin_addr.s_addr = IP_BROADCAST; + nameserver.sin_port = NBNS_UDP_PORT; + memset ( &query, 0, sizeof ( query ) ); + query.dns.id = htons ( 1 ); + query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | + DNS_FLAG_RD | DNS_FLAG_BROADCAST ); + query.dns.qdcount = htons ( 1 ); + query_info = ( void * ) nbns_make_name ( query.payload, name ); + query_info->qtype = htons ( DNS_TYPE_NB ); + query_info->qclass = htons ( DNS_CLASS_IN ); + + /* Issue query, wait for reply */ + reply = dns_query ( &query, + ( ( ( char * ) query_info ) + + sizeof ( *query_info ) + - ( ( char * ) &query ) ), + &nameserver ); + if ( ! reply ) { + DBG ( "NMB got no response via %@ (port %d)\n", + nameserver.sin_addr.s_addr, + nameserver.sin_port ); + return 0; + } + + /* Search through response for useful answers. */ + rr_info = dns_find_rr ( &query, reply ); + if ( ! rr_info ) { + DBG ( "NMB got invalid response\n" ); + return 0; + } + + /* Check type of response */ + if ( ntohs ( rr_info->type ) != DNS_TYPE_NB ) { + DBG ( "NMB got answer type %hx (wanted %hx)\n", + ntohs ( rr_info->type ), DNS_TYPE_NB ); + return 0; + } + + /* Read response */ + rr_info_nb = ( struct dns_rr_info_nb * ) rr_info; + *addr = rr_info_nb->nb_address; + DBG ( "NMB found address %@\n", addr->s_addr ); + + return 1; +} + +static struct resolver nmb_resolver __resolver = { + .name = "NMB", + .resolv = nmb_resolv, +};