/**************************************************************************** ** File: netbios_ns.c ** ** Author: Mike Borella ** ** Dump netbios name service data ** ** $Id: netbios_ns.c,v 1.8 2002/01/03 00:04:01 mborella Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU Library General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ** *****************************************************************************/ #include "global.h" #include "netbios_ns.h" #include "ns_labels.h" #define HOLDER_SIZE 256 extern struct arg_t *my_args; /* * R flag map */ strmap_t netbios_ns_r_map [] = { { NETBIOS_NS_R_QUERY, "query" }, { NETBIOS_NS_R_RESPONSE, "response" }, { 0, "" } }; /* * Opcode map */ strmap_t netbios_ns_opcode_map [] = { { NETBIOS_NS_OPCODE_QUERY, "query" }, { NETBIOS_NS_OPCODE_REGISTRATION, "registration" }, { NETBIOS_NS_OPCODE_RELEASE, "release" }, { NETBIOS_NS_OPCODE_WACK, "WACK" }, { NETBIOS_NS_OPCODE_REFRESH, "refresh" }, { 0, "" } }; /* * Rcode map */ strmap_t netbios_ns_rcode_map [] = { { NETBIOS_NS_RCODE_NOERROR, "no error" }, { 0, "" } }; /* * Query type map */ strmap_t netbios_ns_querytype_map [] = { { NETBIOS_NS_QUERYTYPE_IPADDR, "A - IP address" }, { NETBIOS_NS_QUERYTYPE_NAMESERVER, "NS - name server" }, { NETBIOS_NS_QUERYTYPE_NULL, "NULL" }, { NETBIOS_NS_QUERYTYPE_GENERAL, "NB - general" }, { NETBIOS_NS_QUERYTYPE_NODESTATUS, "NBSTAT - node status" }, { 0, "" } }; /* * Query class map */ strmap_t netbios_ns_queryclass_map [] = { { NETBIOS_NS_QUERYCLASS_IP, "Internet" }, { 0, "" } }; /*---------------------------------------------------------------------------- ** ** translate_netbios_ns_format() ** ** Translate the NETBIOS name format to something that makes sense to humans ** ** We do the reverse of this: ** 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |a b c d|w x y z| ORIGINAL BYTE +-+-+-+-+-+-+-+-+ | | +--------+ +--------+ | | SPLIT THE NIBBLES v v 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |0 0 0 0 a b c d| |0 0 0 0 w x y z| +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ | | + + ADD 'A' | | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |0 1 0 0 0 0 0 1| |0 1 0 0 0 0 0 1| +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ **---------------------------------------------------------------------------- */ void translate_netbios_name(char *s) { char output[32]; int output_ptr = 0; int input_ptr = 0; int input_length = strlen(s); char *ptr; while(input_ptr < input_length) { char high_char, low_char; /* get next two chars */ high_char = s[input_ptr]; input_ptr ++; low_char = s[input_ptr]; input_ptr ++; /* subtract A from each */ high_char = high_char - 'A'; low_char = low_char - 'A'; /* shift the high char left by 4 */ high_char = high_char << 4; /* combine them into the output */ output[output_ptr] = high_char | low_char; output_ptr++; } /* null terminate, eliminate spaces, and copy back to the input string */ output[output_ptr] = '\0'; strcat(output, " "); ptr = strchr(output, ' '); *ptr = '\0'; strcpy(s, output); } /*---------------------------------------------------------------------------- ** ** dump_netbios_ns_questions() ** ** Parse NETBIOS name service questions and display them ** **---------------------------------------------------------------------------- */ void dump_netbios_ns_questions(packet_t *pkt, u_int16_t num) { char holder[HOLDER_SIZE]; u_int16_t query_type, query_class; while(num > 0) { /* * Parse the name */ parse_ns_labels(pkt, holder); /* * The name is stored in wacky NETBIOS format, with each * character spread across 2 bytes. Let's fix that. * */ /* * Parse the query type and class */ if (get_packet_bytes((u_int8_t *) &query_type, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &query_class, pkt, 2) == 0) return; /* * Conversions */ query_type = ntohs(query_type); query_class = ntohs(query_class); /* * The name may be stored in wacky NETBIOS format, with each * character spread across 2 bytes. Let's fix that. */ if (query_type == NETBIOS_NS_QUERYTYPE_GENERAL) translate_netbios_name(holder); /* * Dump the info */ if (my_args->m) { display_minimal_string(holder); display_minimal_string(" "); } else { display_string("Query", holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", query_type, map2str(netbios_ns_querytype_map, query_type)); display_string(" Type", holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", query_class, map2str(netbios_ns_queryclass_map, query_class)); display_string(" Query class", holder); } num --; } } /*---------------------------------------------------------------------------- ** ** dump_netbios_ns_answers() ** ** Parse NETBIOS NS answers and display them ** **---------------------------------------------------------------------------- */ void dump_netbios_ns_answers(packet_t *pkt, u_int8_t num, char *answer_type) { char holder[HOLDER_SIZE]; u_int16_t query_type, query_class; u_int32_t ttl; u_int16_t rdl; u_int8_t resource_data[64]; u_int16_t nb_flags; while(num > 0) { /* * Parse the name */ parse_ns_labels(pkt, holder); /* * Parse the query type and class */ if (get_packet_bytes((u_int8_t *) &query_type, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &query_class, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &ttl, pkt, 4) == 0) return; if (get_packet_bytes((u_int8_t *) &rdl, pkt, 2) == 0) return; /* * Conversions */ query_type = ntohs(query_type); query_class = ntohs(query_class); ttl = ntohl(ttl); rdl = ntohs(rdl); /* * The name may be stored in wacky NETBIOS format, with each * character spread across 2 bytes. Let's fix that. */ if (query_type == NETBIOS_NS_QUERYTYPE_GENERAL) translate_netbios_name(holder); /* * Get the resource data */ switch (query_type) { case NETBIOS_NS_QUERYTYPE_NAMESERVER: parse_ns_labels(pkt, resource_data); break; case NETBIOS_NS_QUERYTYPE_IPADDR: case NETBIOS_NS_QUERYTYPE_GENERAL: default: if (get_packet_bytes((u_int8_t *) &nb_flags, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &resource_data, pkt, 4) == 0) return; break; } /* * Dump the info */ if (my_args->m) { if (query_type == NETBIOS_NS_QUERYTYPE_GENERAL && query_class == NETBIOS_NS_QUERYCLASS_IP) { display_minimal_ipv4((u_int8_t *) resource_data); display_minimal_string(" "); } } else { display_string(answer_type, holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", query_type, map2str(netbios_ns_querytype_map, query_type)); display_string(" Query type", holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", query_class, map2str(netbios_ns_queryclass_map, query_class)); display_string(" Query class", holder); display(" TTL", (u_int8_t *) &ttl, 4, DISP_DEC); display(" Resource data length", (u_int8_t *) &rdl, 2, DISP_DEC); switch(query_type) { case NETBIOS_NS_QUERYTYPE_IPADDR: case NETBIOS_NS_QUERYTYPE_GENERAL: display(" NB flags", (u_int8_t *) &nb_flags, 2, DISP_HEX); display_ipv4(" Resource data", (u_int8_t *) resource_data); break; case NETBIOS_NS_QUERYTYPE_NAMESERVER: display_string(" Resource data", resource_data); break; default: display(" Resource data", (u_int8_t *) resource_data, rdl, DISP_HEX); } } num --; } } /*---------------------------------------------------------------------------- ** ** dump_netbios_ns() ** ** Parse netbios name service header and dump fields ** **---------------------------------------------------------------------------- */ void dump_netbios_ns(packet_t *pkt) { netbios_ns_t nb_ns; u_int8_t holder[64]; u_int8_t r, opcode, flags, rcode; u_int8_t flag_aa, flag_tc, flag_rd, flag_ra, flag_zeros, flag_b; /* Set the layer */ set_layer(LAYER_APPLICATION); /* * Mark the beginning of the NS portion so that labels can be stored * properly */ set_packet_mark(pkt); /* * Reset the NS label module */ reset_nslabels(); /* * Read the header */ if (get_packet_bytes((u_int8_t *) &nb_ns, pkt, sizeof(netbios_ns_t)) == 0) return; /* * Conversions */ nb_ns.transaction_id = ntohs(nb_ns.transaction_id); r = nb_ns.r; opcode = nb_ns.opcode; flags = (nb_ns.flags_high << 4) || nb_ns.flags_low; flag_aa = (flags & 0x40) >> 7; flag_tc = (flags & 0x20) >> 6; flag_rd = (flags & 0x10) >> 5; flag_ra = (flags & 0x08) >> 4; flag_zeros = (flags & 0x06) >> 1; flag_b = (flags & 0x01); rcode = nb_ns.rcode; nb_ns.questions = ntohs(nb_ns.questions); nb_ns.answers = ntohs(nb_ns.answers); nb_ns.auth_answers = ntohs(nb_ns.auth_answers); nb_ns.add_answers = ntohs(nb_ns.add_answers); /* * Dump header */ if (my_args->m) { display_minimal_string("| NETBIOS NS "); display_minimal_string(map2str(netbios_ns_r_map, r)); display_minimal_string(" "); } else { /* announcement */ display_header_banner("NETBIOS Name Service"); /* fields */ display("Transaction ID", (u_int8_t *) &nb_ns.transaction_id, 2, DISP_DEC); snprintf(holder, HOLDER_SIZE, "%d (%s)", r, map2str(netbios_ns_r_map, r)); display_string("R", holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", opcode, map2str(netbios_ns_opcode_map, opcode)); display_string("Opcode", holder); snprintf(holder, HOLDER_SIZE, "%d (%s)", rcode, map2str(netbios_ns_rcode_map, rcode)); display_string("Rcode", holder); /* number of questions and answers */ display("Questions", (u_int8_t *) &nb_ns.questions, 2, DISP_DEC); display("Answers", (u_int8_t *) &nb_ns.answers, 2, DISP_DEC); display("Auth answers", (u_int8_t *) &nb_ns.auth_answers, 2, DISP_DEC); display("Add answers", (u_int8_t *) &nb_ns.add_answers, 2, DISP_DEC); } /* * Parse the question and answers */ dump_netbios_ns_questions(pkt, nb_ns.questions); dump_netbios_ns_answers(pkt, nb_ns.answers, "Answers"); dump_netbios_ns_answers(pkt, nb_ns.auth_answers, "Auth answers"); dump_netbios_ns_answers(pkt, nb_ns.add_answers, "Addtl answers"); /* dump the hex buffer */ hexbuffer_flush(); }