NMB packets are so similar to DNS packets; we may as well add NMB as a
[people/asdlkf/gpxe.git] / src / proto / nmb.c
1 #include "resolv.h"
2 #include "string.h"
3 #include "dns.h"
4 #include "nic.h"
5 #include "nmb.h"
6
7 /*
8  * Convert a standard NUL-terminated string to an NBNS query name.
9  *
10  * Returns a pointer to the character following the constructed NBNS
11  * query name.
12  *
13  */
14 static inline char * nbns_make_name ( char *dest, const char *name ) {
15         char nb_name[16];
16         char c;
17         int i;
18
19         *(dest++) = 32; /* Length is always 32 */
20
21         /* Name encoding is as follows: pad the name with spaces to
22          * length 15, and add a NUL.  Take this 16-byte string, split
23          * it into nibbles and add 0x41 to each nibble to form a byte
24          * of the resulting name string.
25          */
26         memset ( nb_name, ' ', 15 );
27         nb_name[15] = '\0';
28         memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */
29         for ( i = 0 ; i < 16 ; i++ ) {
30                 c = nb_name[i];
31                 *( ( ( uint16_t * ) dest ) ++ ) = 
32                         htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 );
33         }
34
35         *(dest++) = 0; /* Terminating 0-length name component */
36         return dest;
37 }
38
39 /*
40  * Resolve a name using NMB
41  *
42  */
43 static int nmb_resolv ( struct in_addr *addr, const char *name ) {
44         struct dns_query query;
45         struct dns_query_info *query_info;
46         struct dns_header *reply;
47         struct dns_rr_info *rr_info;
48         struct dns_rr_info_nb *rr_info_nb;
49         struct sockaddr_in nameserver;
50
51         DBG ( "NMB resolving %s\n", name );
52
53         /* Set up the query data */
54         nameserver.sin_addr.s_addr = IP_BROADCAST;
55         nameserver.sin_port = NBNS_UDP_PORT;
56         memset ( &query, 0, sizeof ( query ) );
57         query.dns.id = htons ( 1 );
58         query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
59                                   DNS_FLAG_RD | DNS_FLAG_BROADCAST );
60         query.dns.qdcount = htons ( 1 );
61         query_info = ( void * ) nbns_make_name ( query.payload, name );
62         query_info->qtype = htons ( DNS_TYPE_NB );
63         query_info->qclass = htons ( DNS_CLASS_IN );
64
65         /* Issue query, wait for reply */
66         reply = dns_query ( &query,
67                             ( ( ( char * ) query_info )
68                               + sizeof ( *query_info )
69                               - ( ( char * ) &query ) ),
70                             &nameserver );
71         if ( ! reply ) {
72                 DBG ( "NMB got no response via %@ (port %d)\n",
73                       nameserver.sin_addr.s_addr,
74                       nameserver.sin_port );
75                 return 0;
76         }
77
78         /* Search through response for useful answers. */
79         rr_info = dns_find_rr ( &query, reply );
80         if ( ! rr_info ) {
81                 DBG ( "NMB got invalid response\n" );
82                 return 0;
83         }
84
85         /* Check type of response */
86         if ( ntohs ( rr_info->type ) != DNS_TYPE_NB ) {
87                 DBG ( "NMB got answer type %hx (wanted %hx)\n",
88                       ntohs ( rr_info->type ), DNS_TYPE_NB );
89                 return 0;
90         }
91
92         /* Read response */
93         rr_info_nb = ( struct dns_rr_info_nb * ) rr_info;
94         *addr = rr_info_nb->nb_address;
95         DBG ( "NMB found address %@\n", addr->s_addr );
96
97         return 1;
98 }
99
100 static struct resolver nmb_resolver __resolver = {
101         .name = "NMB",
102         .resolv = nmb_resolv,
103 };