/** * @file * NetBIOS name service responder * Modifications by Ray Abram to responce to Net BIOS NAme requests when Incomming name = * * - based on code from "https://github.com/esp8266/Arduino/commit/1f7989b31d26d7df9776a08f36d685eae7ac8f99" */ /** * @defgroup netbiosns NETBIOS responder * @ingroup apps * * This is an example implementation of a NetBIOS name server. * It responds to name queries for a configurable name. * Name resolving is not supported. * * Note that the device doesn't broadcast it's own name so can't * detect duplicate names! */ /* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * */ #include "lwip/apps/netbiosns.h" #if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */ #include "lwip/def.h" #include "lwip/udp.h" #include "lwip/netif.h" #include /** default port number for "NetBIOS Name service */ #define NETBIOS_PORT 137 /** size of a NetBIOS name */ #define NETBIOS_NAME_LEN 16 /** The Time-To-Live for NetBIOS name responds (in seconds) * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */ #define NETBIOS_NAME_TTL 300000u /** NetBIOS header flags */ #define NETB_HFLAG_RESPONSE 0x8000U #define NETB_HFLAG_OPCODE 0x7800U #define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U #define NETB_HFLAG_AUTHORATIVE 0x0400U #define NETB_HFLAG_TRUNCATED 0x0200U #define NETB_HFLAG_RECURS_DESIRED 0x0100U #define NETB_HFLAG_RECURS_AVAILABLE 0x0080U #define NETB_HFLAG_BROADCAST 0x0010U #define NETB_HFLAG_REPLYCODE 0x0008U #define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U /** NetBIOS name flags */ #define NETB_NFLAG_UNIQUE 0x8000U #define NETB_NFLAG_NODETYPE 0x6000U #define NETB_NFLAG_NODETYPE_HNODE 0x6000U #define NETB_NFLAG_NODETYPE_MNODE 0x4000U #define NETB_NFLAG_NODETYPE_PNODE 0x2000U #define NETB_NFLAG_NODETYPE_BNODE 0x0000U #define NETB_NFLAG_NAME_IN_CONFLICT 0x0800U // 1=Yes,0=no #define NETB_NFLAG_NAME_IS_ACTIVE 0x0400U // 1=Yes,0=no #define NETB_NFLAG_NAME_IS_PNAME 0x0200U // 1=Yes,0=no Name is Permanent Node Name /** NetBIOS message header */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif PACK_STRUCT_BEGIN struct netbios_hdr { PACK_STRUCT_FIELD(u16_t trans_id); PACK_STRUCT_FIELD(u16_t flags); PACK_STRUCT_FIELD(u16_t questions); PACK_STRUCT_FIELD(u16_t answerRRs); PACK_STRUCT_FIELD(u16_t authorityRRs); PACK_STRUCT_FIELD(u16_t additionalRRs); }PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/epstruct.h" #endif /** NetBIOS message name part */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif PACK_STRUCT_BEGIN struct netbios_name_hdr { PACK_STRUCT_FLD_8(u8_t nametype); PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]); PACK_STRUCT_FIELD(u16_t type); PACK_STRUCT_FIELD(u16_t cls); PACK_STRUCT_FIELD(u32_t ttl); PACK_STRUCT_FIELD(u16_t datalen); PACK_STRUCT_FIELD(u16_t flags); PACK_STRUCT_FLD_S(ip4_addr_p_t addr); }PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/epstruct.h" #endif /** NetBIOS message */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" #endif PACK_STRUCT_BEGIN struct netbios_resp { struct netbios_hdr resp_hdr; struct netbios_name_hdr resp_name; }PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/epstruct.h" #endif // The NBNS Structure NBNS Structure Response Responds to a Name Query PACK_STRUCT_BEGIN struct netbios_answer { struct netbios_hdr answer_hdr; PACK_STRUCT_FIELD(u8_t NameSize); // the length of the next string PACK_STRUCT_FLD_8(u8_t QueryName[(NETBIOS_NAME_LEN*2)+1]); // WARNING!!! this item may be of a different length PACK_STRUCT_FIELD(u16_t PacketType); PACK_STRUCT_FIELD(u16_t PacketClass); PACK_STRUCT_FIELD(u32_t TimeToLive); PACK_STRUCT_FIELD(u16_t DataLength); PACK_STRUCT_FLD_8(u8_t NumberOfNames); // number of names PACK_STRUCT_FLD_8(u8_t AnswerName[NETBIOS_NAME_LEN]); // node name PACK_STRUCT_FIELD(u16_t AnswerNameFlags); // node flags PACK_STRUCT_FLD_8(u8_t NameType); // type name }PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/anstruct.h" #endif #ifdef NETBIOS_LWIP_NAME #define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME #else static char netbiosns_local_name[NETBIOS_NAME_LEN]; #define NETBIOS_LOCAL_NAME netbiosns_local_name #endif struct udp_pcb *netbiosns_pcb; /** Decode a NetBIOS name (from packet to string) */ static int netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len) { char *pname; char cname; char cnbname; int idx = 0; LWIP_UNUSED_ARG(name_dec_len); /* Start decoding netbios name. */ pname = name_enc; for (;;) { /* Every two characters of the first level-encoded name turn into one character in the decoded name. */ cname = *pname; if (cname == '\0') break; /* no more characters */ if (cname == '.') break; /* scope ID follows */ if (cname < 'A' || cname > 'Z') { /* Not legal. */ return -1; } cname -= 'A'; cnbname = cname << 4; pname++; cname = *pname; if (cname == '\0' || cname == '.') { /* No more characters in the name - but we're in * the middle of a pair. Not legal. */ return -1; } if (cname < 'A' || cname > 'Z') { /* Not legal. */ return -1; } cname -= 'A'; cnbname |= cname; pname++; /* Do we have room to store the character? */ if (idx < NETBIOS_NAME_LEN) { /* Yes - store the character. */ name_dec[idx++] = (cnbname != ' ' ? cnbname : '\0'); } } return 0; } #if 0 /** Encode a NetBIOS name (from string to packet) - currently unused because we don't ask for names. */ static int netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len) { char *pname; char cname; unsigned char ucname; int idx = 0; /* Start encoding netbios name. */ pname = name_enc; for (;;) { /* Every two characters of the first level-encoded name * turn into one character in the decoded name. */ cname = toupper(*pname); // ensure incoming name is in upper case as per NetBIOS name spec if (cname == '\0') break; /* no more characters */ if (cname == '.') break; /* scope ID follows */ if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) { /* Not legal. */ return -1; } /* Do we have room to store the character? */ if (idx >= name_dec_len) { return -1; } /* Yes - store the character. */ ucname = cname; name_dec[idx++] = ('A'+((ucname>>4) & 0x0F)); name_dec[idx++] = ('A'+( ucname & 0x0F)); pname++; } /* Fill with "space" coding */ for (;idx < name_dec_len - 1;) { name_dec[idx++] = 'C'; name_dec[idx++] = 'A'; } /* Terminate string */ name_dec[idx] = '\0'; return 0; } #endif /** NetBIOS Name service recv callback */ static void netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { LWIP_UNUSED_ARG(arg); /* if packet is valid */ if (p != NULL) { char netbios_name[NETBIOS_NAME_LEN + 1]; struct netbios_hdr *netbios_hdr = (struct netbios_hdr *) p->payload; struct netbios_name_hdr *netbios_name_hdr = (struct netbios_name_hdr *) (netbios_hdr + 1); /* we only answer if we got a default interface */ if (netif_default != NULL) { /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */ /* if the packet is a NetBIOS name query question */ if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) && ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) && (netbios_hdr->questions == PP_NTOHS(1))) { /* decode the NetBIOS name */ netbiosns_name_decode((char *) (netbios_name_hdr->encname), netbios_name, sizeof(netbios_name)); /* if the packet is for us */ if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) { struct pbuf *q; struct netbios_resp *resp; q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM); if (q != NULL) { resp = (struct netbios_resp *) q->payload; /* prepare NetBIOS header response */ resp->resp_hdr.trans_id = netbios_hdr->trans_id; resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE | NETB_HFLAG_RECURS_DESIRED); resp->resp_hdr.questions = 0; resp->resp_hdr.answerRRs = PP_HTONS(1); resp->resp_hdr.authorityRRs = 0; resp->resp_hdr.additionalRRs = 0; /* prepare NetBIOS header datas */ MEMCPY(resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname)); resp->resp_name.nametype = netbios_name_hdr->nametype; resp->resp_name.type = netbios_name_hdr->type; resp->resp_name.cls = netbios_name_hdr->cls; resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL); resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags) + sizeof(resp->resp_name.addr)); resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE); ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default)); /* send the NetBIOS response */ udp_sendto(upcb, q, addr, port); /* free the "reference" pbuf */ pbuf_free(q); } } else if (0 == lwip_strnicmp(netbios_name, "*", sizeof(NETBIOS_LOCAL_NAME))) { struct pbuf *q; struct netbios_answer *resp; q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_answer), PBUF_RAM); if (q != NULL) { // general query - ask for our IP address resp = (struct netbios_answer *) q->payload; // buffer, to which a response is compiled resp->answer_hdr.trans_id = netbios_hdr->trans_id; // copy the query to the response ID resp->answer_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE); // 0x8400 acknowledgment of termination resp->answer_hdr.questions = PP_HTONS(0); resp->answer_hdr.answerRRs = PP_HTONS(1); // serial number of the answer resp->answer_hdr.authorityRRs = PP_HTONS(0); resp->answer_hdr.additionalRRs = PP_HTONS(0); resp->NameSize = netbios_name_hdr->nametype; // we will copy the length of the station name memcpy(resp->QueryName, netbios_name_hdr->encname, (NETBIOS_NAME_LEN*2)+1); // we will copy the queried name resp->PacketType = PP_HTONS(0x21); // NBSTAT resp->PacketClass = PP_HTONS(1); // Internet name resp->TimeToLive = PP_HTONL(0); resp->DataLength = PP_HTONS(4 + NETBIOS_NAME_LEN); resp->NumberOfNames = 1; // Number of names memset(resp->AnswerName, 0x20, NETBIOS_NAME_LEN); MEMCPY(resp->AnswerName, NETBIOS_LOCAL_NAME, strlen(NETBIOS_LOCAL_NAME)); resp->AnswerNameFlags = PP_HTONS(NETB_NFLAG_NAME_IS_ACTIVE); // b-node, unique, active resp->NameType = 0; // Workstation/Redirector udp_sendto(upcb, q, addr, port); pbuf_free(q); } } } /* free the pbuf */ pbuf_free(p); } } } /** * @ingroup netbiosns * Init netbios responder */ void netbiosns_init(void) { #ifdef NETBIOS_LWIP_NAME LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN); #endif netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); if (netbiosns_pcb != NULL) { /* we have to be allowed to send broadcast packets! */ ip_set_option(netbiosns_pcb, SOF_BROADCAST); udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT); udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb); } } #ifndef NETBIOS_LWIP_NAME /** * @ingroup netbiosns * Set netbios name. ATTENTION: the hostname must be less than 15 characters! * the NetBIOS name spec says the name MUST be upper case, so incomming name is forced into uppercase :-) */ void netbiosns_set_name(const char *hostname) { size_t copy_len = strlen(hostname); LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN); if (copy_len >= NETBIOS_NAME_LEN) { copy_len = NETBIOS_NAME_LEN - 1; } MEMCPY(netbiosns_local_name, hostname, copy_len + 1); // make name into upper case int i; for ( i = 0; i < copy_len; i++ ) { netbiosns_local_name[i] = toupper(netbiosns_local_name[i]); } } #endif /** * @ingroup netbiosns * Stop netbios responder */ void netbiosns_stop(void) { if (netbiosns_pcb != NULL) { udp_remove(netbiosns_pcb); netbiosns_pcb = NULL; } } #endif /* LWIP_IPV4 && LWIP_UDP */