summaryrefslogtreecommitdiffstats
path: root/source3/libsmb/nmblib.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/libsmb/nmblib.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/libsmb/nmblib.c')
-rw-r--r--source3/libsmb/nmblib.c1466
1 files changed, 1466 insertions, 0 deletions
diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c
new file mode 100644
index 0000000..c90e92e
--- /dev/null
+++ b/source3/libsmb/nmblib.c
@@ -0,0 +1,1466 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007
+
+ 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 3 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libsmb/nmblib.h"
+#include "lib/util/string_wrappers.h"
+
+static const struct opcode_names {
+ const char *nmb_opcode_name;
+ int opcode;
+} nmb_header_opcode_names[] = {
+ {"Query", 0 },
+ {"Registration", 5 },
+ {"Release", 6 },
+ {"WACK", 7 },
+ {"Refresh", 8 },
+ {"Refresh(altcode)", 9 },
+ {"Multi-homed Registration", 15 },
+ {0, -1 }
+};
+
+/****************************************************************************
+ Lookup a nmb opcode name.
+****************************************************************************/
+
+static const char *lookup_opcode_name( int opcode )
+{
+ const struct opcode_names *op_namep;
+ int i;
+
+ for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) {
+ op_namep = &nmb_header_opcode_names[i];
+ if(opcode == op_namep->opcode)
+ return op_namep->nmb_opcode_name;
+ }
+ return "<unknown opcode>";
+}
+
+/****************************************************************************
+ Print out a res_rec structure.
+****************************************************************************/
+
+static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
+{
+ int i, j;
+
+ DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+ hdr,
+ nmb_namestr(&res->rr_name),
+ res->rr_type,
+ res->rr_class,
+ res->ttl ) );
+
+ if (res->rdlength == 0) {
+ return;
+ }
+
+ for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) {
+ DEBUGADD(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ unsigned char x = res->rdata[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%c", x));
+ }
+
+ DEBUGADD(4, (" hex "));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j]));
+ }
+
+ DEBUGADD(4, ("\n"));
+ }
+}
+
+/****************************************************************************
+ Process a nmb packet.
+****************************************************************************/
+
+void debug_nmb_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "nmb packet from %s(%d) header: id=%d "
+ "opcode=%s(%d) response=%s\n",
+ inet_ntoa(p->ip), p->port,
+ nmb->header.name_trn_id,
+ lookup_opcode_name(nmb->header.opcode),
+ nmb->header.opcode,
+ BOOLSTR(nmb->header.response) );
+ dbgtext( " header: flags: bcast=%s rec_avail=%s "
+ "rec_des=%s trunc=%s auth=%s\n",
+ BOOLSTR(nmb->header.nm_flags.bcast),
+ BOOLSTR(nmb->header.nm_flags.recursion_available),
+ BOOLSTR(nmb->header.nm_flags.recursion_desired),
+ BOOLSTR(nmb->header.nm_flags.trunc),
+ BOOLSTR(nmb->header.nm_flags.authoritative) );
+ dbgtext( " header: rcode=%d qdcount=%d ancount=%d "
+ "nscount=%d arcount=%d\n",
+ nmb->header.rcode,
+ nmb->header.qdcount,
+ nmb->header.ancount,
+ nmb->header.nscount,
+ nmb->header.arcount );
+ }
+
+ if (nmb->header.qdcount) {
+ DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n",
+ nmb_namestr(&nmb->question.question_name),
+ nmb->question.question_type,
+ nmb->question.question_class) );
+ }
+
+ if (nmb->answers && nmb->header.ancount) {
+ debug_nmb_res_rec(nmb->answers,"answers");
+ }
+ if (nmb->nsrecs && nmb->header.nscount) {
+ debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+ }
+ if (nmb->additional && nmb->header.arcount) {
+ debug_nmb_res_rec(nmb->additional,"additional");
+ }
+}
+
+/*******************************************************************
+ Handle "compressed" name pointers.
+******************************************************************/
+
+static bool handle_name_ptrs(unsigned char *ubuf,int *offset,int length,
+ bool *got_pointer,int *ret)
+{
+ int loop_count=0;
+
+ while ((ubuf[*offset] & 0xC0) == 0xC0) {
+ if (!*got_pointer)
+ (*ret) += 2;
+ (*got_pointer)=True;
+ if (*offset > length - 2) {
+ return False;
+ }
+ (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+ if (loop_count++ == 10 ||
+ (*offset) < 0 || (*offset)>(length-2)) {
+ return False;
+ }
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a nmb name from "compressed" format to something readable
+ return the space taken by the name, or 0 if the name is invalid
+******************************************************************/
+
+static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name)
+{
+ size_t m,n=0;
+ unsigned char *ubuf = (unsigned char *)inbuf;
+ int ret = 0;
+ bool got_pointer=False;
+ size_t loop_count=0;
+ int offset = ofs;
+
+ if (length - offset < 2)
+ return(0);
+
+ /* handle initial name pointers */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+
+ /* m must be 32 to exactly fill in the 16 bytes of the netbios name */
+ if (m != 32) {
+ return 0;
+ }
+ /* Cannot go past length. */
+ if (offset+m+2 > length) {
+ return 0;
+ }
+
+ memset((char *)name,'\0',sizeof(*name));
+
+ /* the "compressed" part */
+ if (!got_pointer)
+ ret += m + 2;
+ offset++;
+ while (m > 0) {
+ unsigned char c1,c2;
+ c1 = ubuf[offset++]-'A';
+ c2 = ubuf[offset++]-'A';
+ if ((c1 & 0xF0) || (c2 & 0xF0)) {
+ return(0);
+ }
+ if (n >= sizeof(name->name)) {
+ return 0;
+ }
+ name->name[n++] = (c1<<4) | c2;
+ m -= 2;
+ }
+ /*
+ * RFC1002: For a valid NetBIOS name, exiting from the above,
+ * n *must* be MAX_NETBIOSNAME_LEN (16).
+ */
+ if (n != MAX_NETBIOSNAME_LEN) {
+ return 0;
+ }
+
+ /* parse out the name type, its always
+ * in the 16th byte of the name */
+ name->name_type = ((unsigned char)name->name[15]) & 0xff;
+
+ /* remove trailing spaces */
+ name->name[15] = 0;
+ n = 14;
+ while (n && name->name[n]==' ')
+ name->name[n--] = 0;
+
+ /* now the domain parts (if any) */
+ n = 0;
+ while (ubuf[offset]) {
+ /* we can have pointers within the domain part as well */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+ /*
+ * Don't allow null domain parts.
+ */
+ if (!m)
+ return(0);
+ if (!got_pointer)
+ ret += m+1;
+ if (n)
+ name->scope[n++] = '.';
+ if (m+2+offset>length || n+m+1>sizeof(name->scope))
+ return(0);
+ offset++;
+ while (m--)
+ name->scope[n++] = (char)ubuf[offset++];
+
+ /*
+ * Watch for malicious loops.
+ */
+ if (loop_count++ == 10)
+ return 0;
+ }
+ name->scope[n++] = 0;
+
+ return(ret);
+}
+
+/****************************************************************************
+ Put a netbios name, padding(s) and a name type into a 16 character buffer.
+ name is already in DOS charset.
+ [15 bytes name + padding][1 byte name type].
+****************************************************************************/
+
+void put_name(char *dest, const char *name, int pad, unsigned int name_type)
+{
+ size_t len = strlen(name);
+
+ memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ?
+ len : MAX_NETBIOSNAME_LEN - 1);
+ if (len < MAX_NETBIOSNAME_LEN - 1) {
+ memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
+ }
+ dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
+}
+
+/*******************************************************************
+ Put a compressed nmb name into a buffer. Return the length of the
+ compressed name.
+
+ Compressed names are really weird. The "compression" doubles the
+ size. The idea is that it also means that compressed names conform
+ to the domain name system. See RFC1002.
+
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_nmb_name(char *buf, size_t buflen, int offset,struct nmb_name *name)
+{
+ int ret,m;
+ nstring buf1;
+ char *p;
+
+ if (strcmp(name->name,"*") == 0) {
+ /* special case for wildcard name */
+ put_name(buf1, "*", '\0', name->name_type);
+ } else {
+ put_name(buf1, name->name, ' ', name->name_type);
+ }
+
+ if (buf) {
+ if (offset >= buflen) {
+ return 0;
+ }
+ buf[offset] = 0x20;
+ }
+
+ ret = 34;
+
+ for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
+ if (buf) {
+ if (offset+2+2*m >= buflen) {
+ return 0;
+ }
+ buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+ buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+ }
+ }
+ offset += 33;
+
+ if (buf) {
+ if (offset >= buflen) {
+ return 0;
+ }
+ buf[offset] = 0;
+ }
+
+ if (name->scope[0]) {
+ /* XXXX this scope handling needs testing */
+ size_t scopenamelen = strlen(name->scope) + 1;
+ ret += scopenamelen;
+ if (buf) {
+ if (offset+1+scopenamelen >= buflen) {
+ return 0;
+ }
+ strlcpy(&buf[offset+1],name->scope,
+ buflen - (offset+1));
+
+ p = &buf[offset+1];
+ while ((p = strchr_m(p,'.'))) {
+ buf[offset] = PTR_DIFF(p,&buf[offset+1]);
+ offset += (buf[offset] + 1);
+ if (offset+1 >= buflen) {
+ return 0;
+ }
+ p = &buf[offset+1];
+ }
+ buf[offset] = strlen(&buf[offset+1]);
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Useful for debugging messages.
+******************************************************************/
+
+char *nmb_namestr(const struct nmb_name *n)
+{
+ fstring name;
+ char *result;
+
+ pull_ascii_fstring(name, n->name);
+ if (!n->scope[0])
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>", name,
+ n->name_type);
+ else
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>.%s", name,
+ n->name_type, n->scope);
+
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*******************************************************************
+ Allocate and parse some resource records.
+******************************************************************/
+
+static bool parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+
+ *recs = SMB_MALLOC_ARRAY(struct res_rec, count);
+ if (!*recs)
+ return(False);
+
+ memset((char *)*recs,'\0',sizeof(**recs)*count);
+
+ for (i=0;i<count;i++) {
+ int l = parse_nmb_name(inbuf,*offset,length,
+ &(*recs)[i].rr_name);
+ (*offset) += l;
+ if (!l || (*offset)+10 > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+ (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+ (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+ (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+ (*offset) += 10;
+ if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) ||
+ (*offset)+(*recs)[i].rdlength > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+ (*offset) += (*recs)[i].rdlength;
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Put a resource record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_res_rec(char *buf, size_t buflen, int offset,struct res_rec *recs,int count)
+{
+ int ret=0;
+ int i;
+
+ for (i=0;i<count;i++) {
+ int l = put_nmb_name(buf,buflen,offset,&recs[i].rr_name);
+ offset += l;
+ ret += l;
+ if (buf) {
+ RSSVAL(buf,offset,recs[i].rr_type);
+ RSSVAL(buf,offset+2,recs[i].rr_class);
+ RSIVAL(buf,offset+4,(unsigned int)recs[i].ttl);
+ RSSVAL(buf,offset+8,recs[i].rdlength);
+ memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+ }
+ offset += 10+recs[i].rdlength;
+ ret += 10+recs[i].rdlength;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Put a compressed name pointer record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_compressed_name_ptr(unsigned char *buf,
+ int offset,
+ struct res_rec *rec,
+ int ptr_offset)
+{
+ int ret=offset;
+ if (buf) {
+ buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+ buf[offset+1] = (ptr_offset & 0xFF);
+ }
+ offset += 2;
+ if (buf) {
+ RSSVAL(buf,offset,rec->rr_type);
+ RSSVAL(buf,offset+2,rec->rr_class);
+ RSIVAL(buf,offset+4,rec->ttl);
+ RSSVAL(buf,offset+8,rec->rdlength);
+ memcpy(buf+offset+10,rec->rdata,rec->rdlength);
+ }
+ offset += 10+rec->rdlength;
+ ret = (offset - ret);
+
+ return ret;
+}
+
+/*******************************************************************
+ Parse a dgram packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+
+ This is documented in section 4.4.1 of RFC1002.
+******************************************************************/
+
+static bool parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+ size_t offset;
+ int flags;
+
+ memset((char *)dgram,'\0',sizeof(*dgram));
+
+ if (length < 14)
+ return(False);
+
+ dgram->header.msg_type = CVAL(inbuf,0);
+ flags = CVAL(inbuf,1);
+ dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+ if (flags & 1)
+ dgram->header.flags.more = True;
+ if (flags & 2)
+ dgram->header.flags.first = True;
+ dgram->header.dgm_id = RSVAL(inbuf,2);
+ putip((char *)&dgram->header.source_ip,inbuf+4);
+ dgram->header.source_port = RSVAL(inbuf,8);
+ dgram->header.dgm_length = RSVAL(inbuf,10);
+ dgram->header.packet_offset = RSVAL(inbuf,12);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->source_name);
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->dest_name);
+ }
+
+ if (offset >= length || (length-offset > sizeof(dgram->data)))
+ return(False);
+
+ dgram->datasize = length-offset;
+ memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+ /* Paranioa. Ensure the last 2 bytes in the dgram buffer are
+ zero. This should be true anyway, just enforce it for
+ paranioa sake. JRA. */
+ SMB_ASSERT(dgram->datasize <= (sizeof(dgram->data)-2));
+ memset(&dgram->data[sizeof(dgram->data)-2], '\0', 2);
+
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+******************************************************************/
+
+static bool parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+ int nm_flags,offset;
+
+ memset((char *)nmb,'\0',sizeof(*nmb));
+
+ if (length < 12)
+ return(False);
+
+ /* parse the header */
+ nmb->header.name_trn_id = RSVAL(inbuf,0);
+
+ DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id));
+
+ nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF;
+ nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False;
+ nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
+ nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+ nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False;
+ nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False;
+ nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False;
+ nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False;
+ nmb->header.rcode = CVAL(inbuf,3) & 0xF;
+ nmb->header.qdcount = RSVAL(inbuf,4);
+ nmb->header.ancount = RSVAL(inbuf,6);
+ nmb->header.nscount = RSVAL(inbuf,8);
+ nmb->header.arcount = RSVAL(inbuf,10);
+
+ if (nmb->header.qdcount) {
+ offset = parse_nmb_name(inbuf,12,length,
+ &nmb->question.question_name);
+ if (!offset)
+ return(False);
+
+ if (length - (12+offset) < 4)
+ return(False);
+ nmb->question.question_type = RSVAL(inbuf,12+offset);
+ nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+ offset += 12+4;
+ } else {
+ offset = 12;
+ }
+
+ /* and any resource records */
+ if (nmb->header.ancount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+ nmb->header.ancount))
+ return(False);
+
+ if (nmb->header.nscount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+ nmb->header.nscount))
+ return(False);
+
+ if (nmb->header.arcount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,
+ &nmb->additional, nmb->header.arcount))
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ 'Copy constructor' for an nmb packet.
+******************************************************************/
+
+static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb;
+ struct nmb_packet *copy_nmb;
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) {
+ DEBUG(0,("copy_nmb_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+ pkt_copy->recv_fd = -1;
+ pkt_copy->send_fd = -1;
+
+ /* Ensure this copy has no resource records. */
+ nmb = &packet->packet.nmb;
+ copy_nmb = &pkt_copy->packet.nmb;
+
+ copy_nmb->answers = NULL;
+ copy_nmb->nsrecs = NULL;
+ copy_nmb->additional = NULL;
+
+ /* Now copy any resource records. */
+
+ if (nmb->answers) {
+ if((copy_nmb->answers = SMB_MALLOC_ARRAY(
+ struct res_rec,nmb->header.ancount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->answers, (char *)nmb->answers,
+ nmb->header.ancount * sizeof(struct res_rec));
+ }
+ if (nmb->nsrecs) {
+ if((copy_nmb->nsrecs = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.nscount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs,
+ nmb->header.nscount * sizeof(struct res_rec));
+ }
+ if (nmb->additional) {
+ if((copy_nmb->additional = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.arcount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->additional, (char *)nmb->additional,
+ nmb->header.arcount * sizeof(struct res_rec));
+ }
+
+ return pkt_copy;
+
+ free_and_exit:
+
+ SAFE_FREE(copy_nmb->answers);
+ SAFE_FREE(copy_nmb->nsrecs);
+ SAFE_FREE(copy_nmb->additional);
+ SAFE_FREE(pkt_copy);
+
+ DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n"));
+ return NULL;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a dgram packet.
+******************************************************************/
+
+static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
+{
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) {
+ DEBUG(0,("copy_dgram_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+ pkt_copy->recv_fd = -1;
+ pkt_copy->send_fd = -1;
+
+ /* There are no additional pointers in a dgram packet,
+ we are finished. */
+ return pkt_copy;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a generic packet.
+******************************************************************/
+
+struct packet_struct *copy_packet(struct packet_struct *packet)
+{
+ if(packet->packet_type == NMB_PACKET)
+ return copy_nmb_packet(packet);
+ else if (packet->packet_type == DGRAM_PACKET)
+ return copy_dgram_packet(packet);
+ return NULL;
+}
+
+/*******************************************************************
+ Free up any resources associated with an nmb packet.
+******************************************************************/
+
+static void free_nmb_packet(struct nmb_packet *nmb)
+{
+ SAFE_FREE(nmb->answers);
+ SAFE_FREE(nmb->nsrecs);
+ SAFE_FREE(nmb->additional);
+}
+
+/*******************************************************************
+ Free up any resources associated with a dgram packet.
+******************************************************************/
+
+static void free_dgram_packet(struct dgram_packet *nmb)
+{
+ /* We have nothing to do for a dgram packet. */
+}
+
+/*******************************************************************
+ Free up any resources associated with a packet.
+******************************************************************/
+
+void free_packet(struct packet_struct *packet)
+{
+ if (packet->locked)
+ return;
+ if (packet->packet_type == NMB_PACKET)
+ free_nmb_packet(&packet->packet.nmb);
+ else if (packet->packet_type == DGRAM_PACKET)
+ free_dgram_packet(&packet->packet.dgram);
+ ZERO_STRUCTPN(packet);
+ SAFE_FREE(packet);
+}
+
+int packet_trn_id(struct packet_struct *p)
+{
+ int result;
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ result = p->packet.nmb.header.name_trn_id;
+ break;
+ case DGRAM_PACKET:
+ result = p->packet.dgram.header.dgm_id;
+ break;
+ default:
+ result = -1;
+ }
+ return result;
+}
+
+/*******************************************************************
+ Parse a packet buffer into a packet structure.
+******************************************************************/
+
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port)
+{
+ struct packet_struct *p;
+ bool ok=False;
+
+ p = SMB_MALLOC_P(struct packet_struct);
+ if (!p)
+ return(NULL);
+
+ ZERO_STRUCTP(p); /* initialize for possible padding */
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->ip = ip;
+ p->port = port;
+ p->locked = False;
+ p->timestamp = time(NULL);
+ p->packet_type = packet_type;
+
+ switch (packet_type) {
+ case NMB_PACKET:
+ ok = parse_nmb(buf,length,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ ok = parse_dgram(buf,length,&p->packet.dgram);
+ break;
+ }
+
+ if (!ok) {
+ free_packet(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+static struct packet_struct *copy_packet_talloc(
+ TALLOC_CTX *mem_ctx, const struct packet_struct *src)
+{
+ struct packet_struct *pkt;
+
+ pkt = talloc_memdup(mem_ctx, src, sizeof(struct packet_struct));
+ if (pkt == NULL) {
+ return NULL;
+ }
+ pkt->locked = false;
+ pkt->recv_fd = -1;
+ pkt->send_fd = -1;
+
+ if (src->packet_type == NMB_PACKET) {
+ const struct nmb_packet *nsrc = &src->packet.nmb;
+ struct nmb_packet *ndst = &pkt->packet.nmb;
+
+ if (nsrc->answers != NULL) {
+ ndst->answers = talloc_memdup(
+ pkt, nsrc->answers,
+ sizeof(struct res_rec) * nsrc->header.ancount);
+ if (ndst->answers == NULL) {
+ goto fail;
+ }
+ }
+ if (nsrc->nsrecs != NULL) {
+ ndst->nsrecs = talloc_memdup(
+ pkt, nsrc->nsrecs,
+ sizeof(struct res_rec) * nsrc->header.nscount);
+ if (ndst->nsrecs == NULL) {
+ goto fail;
+ }
+ }
+ if (nsrc->additional != NULL) {
+ ndst->additional = talloc_memdup(
+ pkt, nsrc->additional,
+ sizeof(struct res_rec) * nsrc->header.arcount);
+ if (ndst->additional == NULL) {
+ goto fail;
+ }
+ }
+ }
+
+ return pkt;
+
+ /*
+ * DGRAM packets have no substructures
+ */
+
+fail:
+ TALLOC_FREE(pkt);
+ return NULL;
+}
+
+struct packet_struct *parse_packet_talloc(TALLOC_CTX *mem_ctx,
+ char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port)
+{
+ struct packet_struct *pkt, *result;
+
+ pkt = parse_packet(buf, length, packet_type, ip, port);
+ if (pkt == NULL) {
+ return NULL;
+ }
+ result = copy_packet_talloc(mem_ctx, pkt);
+ free_packet(pkt);
+ return result;
+}
+
+/*******************************************************************
+ Send a udp packet on a already open socket.
+******************************************************************/
+
+static bool send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+ bool ret = False;
+ int i;
+ struct sockaddr_in sock_out;
+
+ /* set the address and port */
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)&ip);
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = AF_INET;
+
+ DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n",
+ len, inet_ntoa(ip), port ) );
+
+ /*
+ * Patch to fix asynch error notifications from Linux kernel.
+ */
+
+ for (i = 0; i < 5; i++) {
+ ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out,
+ sizeof(sock_out)) >= 0);
+ if (ret || errno != ECONNREFUSED)
+ break;
+ }
+
+ if (!ret)
+ DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+ inet_ntoa(ip),port,strerror(errno)));
+
+ return(ret);
+}
+
+/*******************************************************************
+ Build a dgram packet ready for sending.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_dgram(char *buf, size_t len, struct dgram_packet *dgram)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ if (buf) {
+ ubuf[0] = dgram->header.msg_type;
+ ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+ if (dgram->header.flags.more)
+ ubuf[1] |= 1;
+ if (dgram->header.flags.first)
+ ubuf[1] |= 2;
+ RSSVAL(ubuf,2,dgram->header.dgm_id);
+ putip(ubuf+4,(char *)&dgram->header.source_ip);
+ RSSVAL(ubuf,8,dgram->header.source_port);
+ RSSVAL(ubuf,12,dgram->header.packet_offset);
+ }
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += put_nmb_name((char *)ubuf,len,offset,&dgram->source_name);
+ offset += put_nmb_name((char *)ubuf,len,offset,&dgram->dest_name);
+ }
+
+ if (buf) {
+ memcpy(ubuf+offset,dgram->data,dgram->datasize);
+ }
+ offset += dgram->datasize;
+
+ /* automatically set the dgm_length
+ * NOTE: RFC1002 says the dgm_length does *not*
+ * include the fourteen-byte header. crh
+ */
+ dgram->header.dgm_length = (offset - 14);
+ if (buf) {
+ RSSVAL(ubuf,10,dgram->header.dgm_length);
+ }
+
+ return offset;
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ (void)strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, lp_netbios_scope(), 64, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Compare two nmb names
+******************************************************************/
+
+bool nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2)
+{
+ return ((n1->name_type == n2->name_type) &&
+ strequal(n1->name ,n2->name ) &&
+ strequal(n1->scope,n2->scope));
+}
+
+/*******************************************************************
+ Build a nmb packet ready for sending.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_nmb(char *buf, size_t len, struct nmb_packet *nmb)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ if (len && len < 12) {
+ return 0;
+ }
+
+ /* put in the header */
+ if (buf) {
+ RSSVAL(ubuf,offset,nmb->header.name_trn_id);
+ ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.response)
+ ubuf[offset+2] |= (1<<7);
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response)
+ ubuf[offset+2] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ ubuf[offset+2] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ ubuf[offset+2] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response)
+ ubuf[offset+3] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ ubuf[offset+3] |= 0x10;
+ ubuf[offset+3] |= (nmb->header.rcode & 0xF);
+
+ RSSVAL(ubuf,offset+4,nmb->header.qdcount);
+ RSSVAL(ubuf,offset+6,nmb->header.ancount);
+ RSSVAL(ubuf,offset+8,nmb->header.nscount);
+ RSSVAL(ubuf,offset+10,nmb->header.arcount);
+ }
+
+ offset += 12;
+ if (nmb->header.qdcount) {
+ /* XXXX this doesn't handle a qdcount of > 1 */
+ if (len) {
+ /* Length check. */
+ int extra = put_nmb_name(NULL,0,offset,
+ &nmb->question.question_name);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_nmb_name((char *)ubuf,len,offset,
+ &nmb->question.question_name);
+ if (buf) {
+ RSSVAL(ubuf,offset,nmb->question.question_type);
+ RSSVAL(ubuf,offset+2,nmb->question.question_class);
+ }
+ offset += 4;
+ }
+
+ if (nmb->header.ancount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->answers,
+ nmb->header.ancount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->answers,
+ nmb->header.ancount);
+ }
+
+ if (nmb->header.nscount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ }
+
+ /*
+ * The spec says we must put compressed name pointers
+ * in the following outgoing packets :
+ * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
+ * NAME_RELEASE_REQUEST.
+ */
+
+ if((nmb->header.response == False) &&
+ ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
+ (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
+ (nmb->header.arcount == 1)) {
+
+ if (len) {
+ /* Length check. */
+ int extra = put_compressed_name_ptr(NULL,offset,
+ nmb->additional,12);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_compressed_name_ptr(ubuf,offset,
+ nmb->additional,12);
+ } else if (nmb->header.arcount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->additional,
+ nmb->header.arcount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->additional,
+ nmb->header.arcount);
+ }
+ return offset;
+}
+
+/*******************************************************************
+ Linearise a packet.
+******************************************************************/
+
+int build_packet(char *buf, size_t buflen, struct packet_struct *p)
+{
+ int len = 0;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ len = build_nmb(buf,buflen,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ len = build_dgram(buf,buflen,&p->packet.dgram);
+ break;
+ }
+
+ return len;
+}
+
+/*******************************************************************
+ Send a packet_struct.
+******************************************************************/
+
+bool send_packet(struct packet_struct *p)
+{
+ char buf[1024];
+ int len=0;
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, sizeof(buf), p);
+
+ if (!len)
+ return(False);
+
+ return(send_udp(p->send_fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+ Receive a UDP/138 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified mailslot name
+ The timeout is in milliseconds.
+***************************************************************************/
+
+/****************************************************************************
+ See if a datagram has the right mailslot name.
+***************************************************************************/
+
+bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *buf;
+
+ buf = &dgram->data[0];
+ buf -= 4;
+
+ buf = smb_buf(buf);
+
+ if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the number of bits that match between two len character buffers
+***************************************************************************/
+
+int matching_len_bits(const unsigned char *p1, const unsigned char *p2, size_t len)
+{
+ size_t i, j;
+ int ret = 0;
+ for (i=0; i<len; i++) {
+ if (p1[i] != p2[i])
+ break;
+ ret += 8;
+ }
+
+ if (i==len)
+ return ret;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+static unsigned char sort_ip[4];
+
+/****************************************************************************
+ Compare two query reply records.
+***************************************************************************/
+
+static int name_query_comp(unsigned char *p1, unsigned char *p2)
+{
+ return matching_len_bits(p2+2, sort_ip, 4) -
+ matching_len_bits(p1+2, sort_ip, 4);
+}
+
+/****************************************************************************
+ Sort a set of 6 byte name query response records so that the IPs that
+ have the most leading bits in common with the specified address come first.
+***************************************************************************/
+
+void sort_query_replies(char *data, int n, struct in_addr ip)
+{
+ if (n <= 1)
+ return;
+
+ putip(sort_ip, (char *)&ip);
+
+ /* TODO:
+ this can't use TYPESAFE_QSORT() as the types are wrong.
+ It should be fixed to use a real type instead of char*
+ */
+ qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+/****************************************************************************
+ Interpret the weird netbios "name" into a unix fstring. Return the name type.
+ Returns -1 on error.
+****************************************************************************/
+
+static int name_interpret(unsigned char *buf, size_t buf_len,
+ unsigned char *in, fstring name)
+{
+ unsigned char *end_ptr = buf + buf_len;
+ int ret;
+ unsigned int len;
+ fstring out_string;
+ unsigned char *out = (unsigned char *)out_string;
+
+ *out=0;
+
+ if (in >= end_ptr) {
+ return -1;
+ }
+ len = (*in++) / 2;
+
+ if (len<1) {
+ return -1;
+ }
+
+ while (len--) {
+ if (&in[1] >= end_ptr) {
+ return -1;
+ }
+ if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((in[0]-'A')<<4) + (in[1]-'A');
+ in += 2;
+ out++;
+ if (PTR_DIFF(out,out_string) >= sizeof(fstring)) {
+ return -1;
+ }
+ }
+ ret = out[-1];
+ out[-1] = 0;
+
+ pull_ascii_fstring(name, out_string);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Mangle a name into netbios format.
+ Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum.
+****************************************************************************/
+
+char *name_mangle(TALLOC_CTX *mem_ctx, const char *In, char name_type)
+{
+ int i;
+ int len;
+ nstring buf;
+ char *result;
+ char *p;
+
+ result = talloc_array(mem_ctx, char, 33 + strlen(lp_netbios_scope()) + 2);
+ if (result == NULL) {
+ return NULL;
+ }
+ p = result;
+
+ /* Safely copy the input string, In, into buf[]. */
+ if (strcmp(In,"*") == 0)
+ put_name(buf, "*", '\0', 0x00);
+ else {
+ /* We use an fstring here as mb dos names can expend x3 when
+ going to utf8. */
+ fstring buf_unix;
+ nstring buf_dos;
+
+ pull_ascii_fstring(buf_unix, In);
+ if (!strupper_m(buf_unix)) {
+ return NULL;
+ }
+
+ push_ascii_nstring(buf_dos, buf_unix);
+ put_name(buf, buf_dos, ' ', name_type);
+ }
+
+ /* Place the length of the first field into the output buffer. */
+ p[0] = 32;
+ p++;
+
+ /* Now convert the name to the rfc1001/1002 format. */
+ for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) {
+ p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A';
+ p[(i*2)+1] = (buf[i] & 0x000F) + 'A';
+ }
+ p += 32;
+ p[0] = '\0';
+
+ /* Add the scope string. */
+ for( i = 0, len = 0; *(lp_netbios_scope()) != '\0'; i++, len++ ) {
+ switch( (lp_netbios_scope())[i] ) {
+ case '\0':
+ p[0] = len;
+ if( len > 0 )
+ p[len+1] = 0;
+ return result;
+ case '.':
+ p[0] = len;
+ p += (len + 1);
+ len = -1;
+ break;
+ default:
+ p[len+1] = (lp_netbios_scope())[i];
+ break;
+ }
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Find a pointer to a netbios name.
+****************************************************************************/
+
+static unsigned char *name_ptr(unsigned char *buf, size_t buf_len, unsigned int ofs)
+{
+ unsigned char c = 0;
+
+ if (ofs > buf_len || buf_len < 1) {
+ return NULL;
+ }
+
+ c = *(unsigned char *)(buf+ofs);
+ if ((c & 0xC0) == 0xC0) {
+ uint16_t l = 0;
+
+ if (ofs > buf_len - 1) {
+ return NULL;
+ }
+ l = RSVAL(buf, ofs) & 0x3FFF;
+ if (l > buf_len) {
+ return NULL;
+ }
+ DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
+ return(buf + l);
+ } else {
+ return(buf+ofs);
+ }
+}
+
+/****************************************************************************
+ Extract a netbios name from a buf (into a unix string) return name type.
+ Returns -1 on error.
+****************************************************************************/
+
+int name_extract(unsigned char *buf, size_t buf_len, unsigned int ofs, fstring name)
+{
+ unsigned char *p = name_ptr(buf,buf_len,ofs);
+
+ name[0] = '\0';
+ if (p == NULL) {
+ return -1;
+ }
+ return(name_interpret(buf,buf_len,p,name));
+}
+
+/****************************************************************************
+ Return the total storage length of a mangled name.
+ Returns -1 on error.
+****************************************************************************/
+
+int name_len(unsigned char *s1, size_t buf_len)
+{
+ /* NOTE: this argument _must_ be unsigned */
+ unsigned char *s = (unsigned char *)s1;
+ int len = 0;
+
+ if (buf_len < 1) {
+ return -1;
+ }
+ /* If the two high bits of the byte are set, return 2. */
+ if (0xC0 == (*s & 0xC0)) {
+ if (buf_len < 2) {
+ return -1;
+ }
+ return(2);
+ }
+
+ /* Add up the length bytes. */
+ for (len = 1; (*s); s += (*s) + 1) {
+ len += *s + 1;
+ if (len > buf_len) {
+ return -1;
+ }
+ }
+
+ return(len);
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a client smb message.
+********************************************************************/
+
+int cli_set_message(char *buf,int num_words,int num_bytes,bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}