// Emacs style mode select -*- C++ -*-
//------------------------------------------------------------------------
//
// Copyright (C) Andrew Tridgell 1994-1998
// Copyright (c) 2001, 2002 Simon Howard
//
// 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 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
//
//--------------------------------------------------------------------------
//
// SMB Windows Finger code
//
// Uses code ripped from Samba
//
// This file is messy, but it works
//
//------------------------------------------------------------------------

#include "parse.h"
#include "scan.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "byteorder.h"
#include "samba.h"

//#include "includes.h"

#define SELECT_CAST
#define KANJI_CODEPAGE 932
typedef int BOOL;
#define True 1
#define False 0
#define StrnCpy strncpy

/* zero a structure given a pointer to the structure - no zero check */
#define ZERO_STRUCTPN(x) memset((char *)(x), 0, sizeof(*(x)))

#define PTR_DIFF(p1,p2) ((int)(((const char *)(p1)) - (const char *)(p2)))
#define putip(dest,src) memcpy(dest,src,4)

#define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */
#define MIN_DGRAM_SIZE 12

typedef char pstring[1024];
typedef char fstring[128];
enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3};
enum packet_type {NMB_PACKET, DGRAM_PACKET};

/* A netbios name structure. */
struct nmb_name {
  char         name[17];
  char         scope[64];
  unsigned int name_type;
};

/* A resource record. */
struct res_rec {
  struct nmb_name rr_name;
  int rr_type;
  int rr_class;
  int ttl;
  int rdlength;
  char rdata[MAX_DGRAM_SIZE];
};

/* An nmb packet. */
struct nmb_packet
{
  struct {
    int name_trn_id;
    int opcode;
    BOOL response;
    struct {
      BOOL bcast;
      BOOL recursion_available;
      BOOL recursion_desired;
      BOOL trunc;
      BOOL authoritative;
    } nm_flags;
    int rcode;
    int qdcount;
    int ancount;
    int nscount;
    int arcount;
  } header;

  struct {
    struct nmb_name question_name;
    int question_type;
    int question_class;
  } question;

  struct res_rec *answers;
  struct res_rec *nsrecs;
  struct res_rec *additional;
};

#define NMB_PORT 137
#define DGRAM_PORT 138
#define SMB_PORT 139

/* Opcode definitions */
#define NMB_NAME_QUERY_OPCODE       0x0
#define NMB_NAME_REG_OPCODE         0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */
#define NMB_NAME_RELEASE_OPCODE     0x06 /* see rfc1002.txt 4.2.9,10,11 */
#define NMB_WACK_OPCODE             0x07 /* see rfc1002.txt 4.2.16 */
/* Ambiguity in rfc1002 about which of these is correct. */
/* WinNT uses 8 by default but can be made to use 9. */
#define NMB_NAME_REFRESH_OPCODE_8   0x08 /* see rfc1002.txt 4.2.4 */
#define NMB_NAME_REFRESH_OPCODE_9   0x09 /* see rfc1002.txt 4.2.4 */
#define NMB_NAME_MULTIHOMED_REG_OPCODE 0x0F /* Invented by Microsoft. */

/* msg_type field options - from rfc1002. */

#define DGRAM_UNIQUE 0x10
#define DGRAM_GROUP 0x11
#define DGRAM_BROADCAST 0x12
#define DGRAM_ERROR 0x13
#define DGRAM_QUERY_REQUEST 0x14
#define DGRAM_POSITIVE_QUERY_RESPONSE 0x15
#define DGRAM_NEGATIVE_QUERT_RESPONSE 0x16

/* A datagram - this normally contains SMB data in the data[] array. */

struct dgram_packet {
  struct {
    int msg_type;
    struct {
      enum node_type node_type;
      BOOL first;
      BOOL more;
    } flags;
    int dgm_id;
    struct in_addr source_ip;
    int source_port;
    int dgm_length;
    int packet_offset;
  } header;
  struct nmb_name source_name;
  struct nmb_name dest_name;
  int datasize;
  char data[MAX_DGRAM_SIZE];
};

/* Define a structure used to queue packets. This will be a linked
 list of nmb packets. */

struct packet_struct
{
  struct packet_struct *next;
  struct packet_struct *prev;
  BOOL locked;
  struct in_addr ip;
  int port;
  int fd;
  time_t timestamp;
  enum packet_type packet_type;
  union {
    struct nmb_packet nmb;
    struct dgram_packet dgram;
  } packet;
};


#define pstrcpy(d,s) safe_strcpy((d),(s),sizeof(pstring)-1)
#define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1)
#define pstrcat(d,s) safe_strcat((d),(d),sizeof(pstring)-1)
#define fstrcat(d,s) safe_strcat((d),(d),sizeof(fstring)-1)

static int this_machine;
static int first_user;

static pstring global_scope = "";

static int num_good_sends = 0;
static int num_good_receives = 0;
static int global_is_multibyte_codepage = 0;

static void debug_nmb_packet(struct packet_struct *p) {}


static void GetTimeOfDay(struct timeval *tval)
{
	gettimeofday(tval,NULL);
}

static size_t str_charnum(const char *s)
{
  size_t len = 0;
  
  /*
   * sbcs optimization.
   */
  if(!global_is_multibyte_codepage) {
    return strlen(s);
  } else {
    while (*s != '\0') {
	    int skip = 0; //get_character_len(*s);
      s += (skip ? skip : 1);
      len++;
    }
  }
  return len;
}

/*******************************************************************
safe string cat into a string. maxlength does not
include the terminating zero.
********************************************************************/

static char *safe_strcat(char *dest, const char *src, size_t maxlength)
{
    size_t src_len, dest_len;

    if (!dest) {
//        DEBUG(0,("ERROR: NULL dest in safe_strcat\n"));
        return NULL;
    }

    if (!src) {
        return dest;
    }  

    src_len = strlen(src);
    dest_len = strlen(dest);

    if (src_len + dest_len > maxlength) {
//	    DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n",
//		     (int)(src_len + dest_len - maxlength), src));
	    src_len = maxlength - dest_len;
    }
      
    memcpy(&dest[dest_len], src, src_len);
    dest[dest_len + src_len] = 0;
    return dest;
}

static char *safe_strcpy(char *dest,const char *src, size_t maxlength)
{
    size_t len;

    if (!dest) {
//        DEBUG(0,("ERROR: NULL dest in safe_strcpy\n"));
        return NULL;
    }

    if (!src) {
        *dest = 0;
        return dest;
    }  

    len = strlen(src);

    if (len > maxlength) {
//            DEBUG(0,("ERROR: string overflow by %d in safe_strcpy [%.50s]\n",
//                     (int)(len-maxlength), src));
            len = maxlength;
    }
      
    memcpy(dest, src, len);
    dest[len] = 0;
    return dest;
}  

/*******************************************************************
find the difference in milliseconds between two struct timeval
values
********************************************************************/

#define TvalDiff(tvalold,tvalnew) \
  (((tvalnew)->tv_sec - (tvalold)->tv_sec)*1000 +  \
         ((int)(tvalnew)->tv_usec - (int)(tvalold)->tv_usec)/1000)


/* this is like vsnprintf but the 'n' limit does not include
   the terminating null. So if you have a 1024 byte buffer then
   pass 1023 for n */
static int vslprintf(char *str, int n, char *format, va_list ap)
{
	int ret = vsnprintf(str, n, format, ap);
	if (ret > n || ret < 0) {
		str[n] = 0;
		return -1;
	}
	str[ret] = 0;
	return ret;
}

static int slprintf(char *str, int n, char *format, ...)
{
	va_list ap;  
	int ret;

	va_start(ap, format);

	ret = vslprintf(str,n,format,ap);
	va_end(ap);
	return ret;
}

/*******************************************************************
  convert a string to upper case
********************************************************************/
static void strupper(char *s)
{
  while (*s)
  {
#if 0
	  /*
   * For completeness we should put in equivalent code for code pages
   * 949 (Korean hangul) and 950 (Big5 Traditional Chinese) here - but
   * doubt anyone wants Samba to behave differently from Win95 and WinNT
   * here. They both treat full width ascii characters as case senstive
   * filenames (ie. they don't do the work we do here).
   * JRA. 
   */

    if(lp_client_code_page() == KANJI_CODEPAGE)
    {
      /* Win95 treats full width ascii characters as case sensitive. */
      if (is_shift_jis (*s))
      {
        if (is_sj_lower (s[0], s[1]))
          s[1] = sj_toupper2 (s[1]);
        s += 2;
      }
      else if (is_kana (*s))
      {
        s++;
      }
      else
      {
        if (islower(*s))
          *s = toupper(*s);
        s++;
      }
    }
    else
#endif
    {
	    size_t skip = 1; //get_character_len( *s );
      if( skip != 0 )
        s += skip;
      else
      {
        if (islower(*s))
          *s = toupper(*s);
        s++;
      }
    }
  }
}

/****************************************************************************
 Return true if a string could be a pure IP address.
****************************************************************************/

static BOOL is_ipaddress(const char *str)
{
  BOOL pure_address = True;
  int i;
  
  for (i=0; pure_address && str[i]; i++)
    if (!(isdigit((int)str[i]) || str[i] == '.'))
      pure_address = False;

  /* Check that a pure number is not misinterpreted as an IP */
  pure_address = pure_address && (strchr(str, '.') != NULL);

  return pure_address;
}


/*******************************************************************
  put a compressed name pointer record into a packet
  ******************************************************************/
static int put_compressed_name_ptr(unsigned char *buf,int offset,struct res_rec *rec,int ptr_offset)
{  
  int ret=0;
  buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
  buf[offset+1] = (ptr_offset & 0xFF);
  offset += 2;
  ret += 2;
  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 += 10+rec->rdlength;
    
  return(ret);
}

/*******************************************************************
  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 doman name system. See RFC1002.
  ******************************************************************/
static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
{
  int ret,m;
  fstring buf1;
  char *p;

  if (strcmp(name->name,"*") == 0) {
    /* special case for wildcard name */
    memset(buf1,'\0',20);
    buf1[0] = '*';
    buf1[15] = name->name_type;
  } else {
    slprintf(buf1, sizeof(buf1) - 1,"%-15.15s%c",name->name,name->name_type);
  }

  buf[offset] = 0x20;

  ret = 34;

  for (m=0;m<16;m++) {
    buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
    buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
  }
  offset += 33;

  buf[offset] = 0;

  if (name->scope[0]) {
    /* XXXX this scope handling needs testing */
    ret += strlen(name->scope) + 1;
    pstrcpy(&buf[offset+1],name->scope);  
  
    p = &buf[offset+1];
    while ((p = strchr(p,'.'))) {
      buf[offset] = PTR_DIFF(p,&buf[offset+1]);
      offset += (buf[offset] + 1);
      p = &buf[offset+1];
    }
    buf[offset] = strlen(&buf[offset+1]);
  }

  return(ret);
}

/*******************************************************************
  put a resource record into a packet
  ******************************************************************/
static int put_res_rec(char *buf,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,offset,&recs[i].rr_name);
    offset += l;
    ret += l;
    RSSVAL(buf,offset,recs[i].rr_type);
    RSSVAL(buf,offset+2,recs[i].rr_class);
    RSIVAL(buf,offset+4,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);
}

/*******************************************************************
  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 an nmb packet
  ******************************************************************/
static void free_nmb_packet(struct nmb_packet *nmb)
{  
  if (nmb->answers) {
    free(nmb->answers);
    nmb->answers = NULL;
  }
  if (nmb->nsrecs) {
    free(nmb->nsrecs);
    nmb->nsrecs = NULL;
  }
  if (nmb->additional) {
    free(nmb->additional);
    nmb->additional = NULL;
  }
}

static int sys_select(int maxfd, fd_set *fds,struct timeval *tval)
{
#ifdef USE_POLL
  struct pollfd pfd[256];
  int i;
  int maxpoll;
  int timeout;
  int pollrtn;

  maxpoll = 0;
  for( i = 0; i < maxfd; i++) {
    if(FD_ISSET(i,fds)) {
      struct pollfd *pfdp = &pfd[maxpoll++];
      pfdp->fd = i;
      pfdp->events = POLLIN;
      pfdp->revents = 0;
    }
  }

  timeout = (tval != NULL) ? (tval->tv_sec * 1000) + (tval->tv_usec/1000) :
                -1;
  errno = 0;
  do {
    pollrtn = poll( &pfd[0], maxpoll, timeout);
  } while (pollrtn<0 && errno == EINTR);

  FD_ZERO(fds);

  for( i = 0; i < maxpoll; i++)
    if( pfd[i].revents & POLLIN )
      FD_SET(pfd[i].fd,fds);

  return pollrtn;
#else /* USE_POLL */

  struct timeval t2;
  int selrtn;

  do {
    if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2));
    errno = 0;
    selrtn = select(maxfd,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL);
  } while (selrtn<0 && errno == EINTR);

  return(selrtn);
}
#endif /* USE_POLL */

/*******************************************************************
  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;
  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;
  
  ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out,
		sizeof(sock_out)) >= 0);

  if (ret)
    num_good_sends++;

  return(ret);
}

/*******************************************************************
  build a dgram packet ready for sending

  XXXX This currently doesn't handle packets too big for one
  datagram. It should split them and use the packet_offset, more and
  first flags to handle the fragmentation. Yuck.
  ******************************************************************/
static int build_dgram(char *buf,struct packet_struct *p)
{
  struct dgram_packet *dgram = &p->packet.dgram;
  unsigned char *ubuf = (unsigned char *)buf;
  int offset=0;

  /* put in the header */
  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,offset,&dgram->source_name);
    offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name);
  }

  memcpy(ubuf+offset,dgram->data,dgram->datasize);
  offset += dgram->datasize;

  /* automatically set the dgm_length */
  dgram->header.dgm_length = offset;
  RSSVAL(ubuf,10,dgram->header.dgm_length); 

  return(offset);
}

/*******************************************************************
  build a nmb packet ready for sending

  XXXX this currently relies on not being passed something that expands
  to a packet too big for the buffer. Eventually this should be
  changed to set the trunc bit so the receiver can request the rest
  via tcp (when that becomes supported)
  ******************************************************************/
static int build_nmb(char *buf,struct packet_struct *p)
{
  struct nmb_packet *nmb = &p->packet.nmb;
  unsigned char *ubuf = (unsigned char *)buf;
  int offset=0;

  /* put in the header */
  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 */
    offset += put_nmb_name((char *)ubuf,offset,&nmb->question.question_name);
    RSSVAL(ubuf,offset,nmb->question.question_type);
    RSSVAL(ubuf,offset+2,nmb->question.question_class);
    offset += 4;
  }

  if (nmb->header.ancount)
    offset += put_res_rec((char *)ubuf,offset,nmb->answers,
			  nmb->header.ancount);

  if (nmb->header.nscount)
    offset += put_res_rec((char *)ubuf,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)) {

    offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12);

  } else if (nmb->header.arcount) {
    offset += put_res_rec((char *)ubuf,offset,nmb->additional,
			  nmb->header.arcount);  
  }
  return(offset);
}

/*******************************************************************
trim the specified elements off the front and back of a string
********************************************************************/

static BOOL trim_string(char *s,const char *front,const char *back)
{
  BOOL ret = False;
  size_t front_len = (front && *front) ? strlen(front) : 0;
  size_t back_len = (back && *back) ? strlen(back) : 0;
  size_t s_len;

  while (front_len && strncmp(s, front, front_len) == 0)
  {
    char *p = s;
    ret = True;
    while (1)
    {
      if (!(*p = p[front_len]))
        break;
      p++;
    }
  }

  /*
   * We split out the multibyte code page
   * case here for speed purposes. Under a
   * multibyte code page we need to walk the
   * string forwards only and multiple times.
   * Thanks to John Blair for finding this
   * one. JRA.
   */

  if(back_len)
  {
    if(!global_is_multibyte_codepage)
    {
      s_len = strlen(s);
      while ((s_len >= back_len) && 
             (strncmp(s + s_len - back_len, back, back_len)==0))  
      {
        ret = True;
        s[s_len - back_len] = '\0';
        s_len = strlen(s);
      }
    }
    else
    {

      /*
       * Multibyte code page case.
       * Keep going through the string, trying
       * to match the 'back' string with the end
       * of the string. If we get a match, truncate
       * 'back' off the end of the string and
       * go through the string again from the
       * start. Keep doing this until we have
       * gone through the string with no match
       * at the string end.
       */

      size_t mb_back_len = str_charnum(back);
      size_t mb_s_len = str_charnum(s);

      while(mb_s_len >= mb_back_len)
      {
        size_t charcount = 0;
        char *mbp = s;

        /*
         * sbcs optimization.
         */
        if(!global_is_multibyte_codepage) {
          while(charcount < (mb_s_len - mb_back_len)) {
            mbp += 1;
            charcount++;
          }
        } else {
          while(charcount < (mb_s_len - mb_back_len)) {
		  size_t skip = 0; //skip_multibyte_char(*mbp);
            mbp += (skip ? skip : 1);
            charcount++;
          }
        }

        /*
         * mbp now points at mb_back_len multibyte
         * characters from the end of s.
         */

        if(strcmp(mbp, back) == 0)
        {
          ret = True;
          *mbp = '\0';
          mb_s_len = str_charnum(s);
          mbp = s;
        }
        else
          break;
      } /* end while mb_s_len... */
    } /* end else .. */
  } /* end if back_len .. */

  return(ret);
}


/*******************************************************************
  free up any resources associated with a packet
  ******************************************************************/
static 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);
  free(packet);
}


/*******************************************************************
  send a packet_struct
  ******************************************************************/
static BOOL send_packet(struct packet_struct *p)
{
  char buf[1024];
  int len=0;

  memset(buf,'\0',sizeof(buf));

  switch (p->packet_type) 
    {
    case NMB_PACKET:
      len = build_nmb(buf,p);
      debug_nmb_packet(p);
      break;

    case DGRAM_PACKET:
      len = build_dgram(buf,p);
      break;
    }

  if (!len) return(False);

  return(send_udp(p->fd,buf,len,p->ip,p->port));
}

/*******************************************************************
  build a nmb name
 *******************************************************************/
static void make_nmb_name( struct nmb_name *n, const char *name, int type )
{
	memset( (char *)n, '\0', sizeof(struct nmb_name) );
	StrnCpy( n->name, name, 15 );
	strupper( n->name );
	n->name_type = (unsigned int)type & 0xFF;
	StrnCpy( n->scope, global_scope, 63 );
	strupper( n->scope );
}

static struct in_addr lastip;
static int lastport = 0;

/****************************************************************************
 Read from a socket.
****************************************************************************/

static ssize_t read_udp_socket(int fd,char *buf,size_t len)
{
  ssize_t ret;
  struct sockaddr_in sock;
  int socklen;
  
  socklen = sizeof(sock);
  memset((char *)&sock,'\0',socklen);
  memset((char *)&lastip,'\0',sizeof(lastip));
  ret = (ssize_t)recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen);
  if (ret <= 0) {
//    DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno)));
    return(0);
  }

  lastip = sock.sin_addr;
  lastport = ntohs(sock.sin_port);

//  DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %d\n",
//             inet_ntoa(lastip), lastport, ret));

  return(ret);
}

/*******************************************************************
  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;
    (*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 offset,int length, struct nmb_name *name)
{
  int m,n=0;
  unsigned char *ubuf = (unsigned char *)inbuf;
  int ret = 0;
  BOOL got_pointer=False;
  int loop_count=0;

  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];

  if (!m)
    return(0);
  if ((m & 0xC0) || 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) || (n > sizeof(name->name)-1))
      return(0);
    name->name[n++] = (c1<<4) | c2;
    m -= 2;
  }
  name->name[n] = 0;

  if (n==16) {
    /* 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);
}

/*******************************************************************
  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 = (struct res_rec *)malloc(sizeof(**recs)*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) {
      free(*recs);
      *recs = NULL;
      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) {
      free(*recs);
      *recs = NULL;
      return(False);
    }
    memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
    (*offset) += (*recs)[i].rdlength;    
  }
  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);
}

/*******************************************************************
  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)
{
  int 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);

  return(True);
}


/*******************************************************************
  read a packet from a socket and parse it, returning a packet ready
  to be used or put on the queue. This assumes a UDP socket
  ******************************************************************/
static struct packet_struct *read_packet(int fd,enum packet_type packet_type)
{
  extern int lastport;
  struct packet_struct *packet;
  char buf[MAX_DGRAM_SIZE];
  int length;
  BOOL ok=False;
  
  length = read_udp_socket(fd,buf,sizeof(buf));
  if (length < MIN_DGRAM_SIZE) return(NULL);

  packet = (struct packet_struct *)malloc(sizeof(*packet));
  if (!packet) return(NULL);

  packet->next = NULL;
  packet->prev = NULL;
  packet->ip = lastip;
  packet->port = lastport;
  packet->fd = fd;
  packet->locked = False;
  packet->timestamp = time(NULL);
  packet->packet_type = packet_type;
  switch (packet_type) 
    {
    case NMB_PACKET:
      ok = parse_nmb(buf,length,&packet->packet.nmb);
      break;

    case DGRAM_PACKET:
      ok = parse_dgram(buf,length,&packet->packet.dgram);
      break;
    }
  if (!ok) {
//    DEBUG(10,("read_packet: discarding packet id = %d\n", 
//                 packet->packet.nmb.header.name_trn_id));
    free_packet(packet);
    return(NULL);
  }

  num_good_receives++;

//  DEBUG(5,("Received a packet of len %d from (%s) port %d\n",
//	    length, inet_ntoa(packet->ip), packet->port ) );

  return(packet);
}
       
/****************************************************************************
  receive a packet with timeout on a open UDP filedescriptor
  The timeout is in milliseconds
  ***************************************************************************/
static struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
{
  fd_set fds;
  struct timeval timeout;

  FD_ZERO(&fds);
  FD_SET(fd,&fds);
  timeout.tv_sec = t/1000;
  timeout.tv_usec = 1000*(t%1000);

  sys_select(fd+1,&fds,&timeout);

  if (FD_ISSET(fd,&fds)) 
    return(read_packet(fd,type));

  return(NULL);
}

/****************************************************************************
 Interpret a node status response.
****************************************************************************/

static void _interpret_node_status(char *p, char *master,char *rname)
{
  int numnames = CVAL(p,0);
//  DEBUG(1,("received %d names\n",numnames));

  if (rname) *rname = 0;
  if (master) *master = 0;

  p += 1;
  while (numnames--) {
    char qname[17];
    int type;
    fstring flags;
    int i;
    *flags = 0;
    StrnCpy(qname,p,15);
    type = CVAL(p,15);
    p += 16;

    fstrcat(flags, (p[0] & 0x80) ? "<GROUP> " : "        ");
    if ((p[0] & 0x60) == 0x00) fstrcat(flags,"B ");
    if ((p[0] & 0x60) == 0x20) fstrcat(flags,"P ");
    if ((p[0] & 0x60) == 0x40) fstrcat(flags,"M ");
    if ((p[0] & 0x60) == 0x60) fstrcat(flags,"H ");
    if (p[0] & 0x10) fstrcat(flags,"<DEREGISTERING> ");
    if (p[0] & 0x08) fstrcat(flags,"<CONFLICT> ");
    if (p[0] & 0x04) fstrcat(flags,"<ACTIVE> ");
    if (p[0] & 0x02) fstrcat(flags,"<PERMANENT> ");

    if (master && !*master && type == 0x1d) {
      StrnCpy(master,qname,15);
      trim_string(master,NULL," ");
    }

    if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) {
      StrnCpy(rname,qname,15);
      trim_string(rname,NULL," ");
    }
      
    for (i = strlen( qname) ; --i >= 0 ; ) {
      if (!isprint((int)qname[i])) qname[i] = '.';
    }

    if (type == 0x03) {

	    // names with dollars in are also not user names - ignore

	    for (i=0; i<strlen(qname); ++i) {
		    if (qname[i] == '$') {
			    first_user = 1; 
			    break;
		    }
	    }

	    if (first_user) {

		    // skip the first user: this is the machine name

		    first_user = 0;

	    } else {
		    pstring username;

		    strncpy(username, qname, sizeof(username) - 1);
		    username[sizeof(username)-1] = '\0';
	    
		    // clear up username
		    
		    {
			    char *p;
			    
			    for(p=username; *p && !isspace(*p); p++)
				    *p = tolower(*p);
			    
			    // cut off ending spaces
			    
			    *p = '\0';
		    }
		    
		    // pass data back to the scanner
		    
		    scan_add_user(this_machine, username);
	    }
    }

//    DEBUG(1,("\t%-15s <%02x> - %s\n",qname,type,flags));
    p+=2;
  }

//  DEBUG(1,("num_good_sends=%d num_good_receives=%d\n",
//	       IVAL(p,20),IVAL(p,24)));
}

/****************************************************************************
 Internal function handling a netbios name status query on a host.
**************************************************************************/

static BOOL internal_name_status(int fd,char *name,int name_type,BOOL recurse,
		 struct in_addr to_ip,char *master,char *rname, BOOL verbose,
         void (*fn_interpret_node_status)(char *, char *,char *),
		 void (*fn)(struct packet_struct *))
{
  BOOL found=False;
  int retries = 2;
  int retry_time = 1000;
  struct timeval tval;
  struct packet_struct p;
  struct packet_struct *p2;
  struct nmb_packet *nmb = &p.packet.nmb;
  static int name_trn_id = 0;

  memset((char *)&p,'\0',sizeof(p));

  if (!name_trn_id) name_trn_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + 
    ((unsigned)getpid()%(unsigned)100);
  name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF;

  nmb->header.name_trn_id = name_trn_id;
  nmb->header.opcode = 0;
  nmb->header.response = False;
  nmb->header.nm_flags.bcast = False;
  nmb->header.nm_flags.recursion_available = False;
  nmb->header.nm_flags.recursion_desired = False;
  nmb->header.nm_flags.trunc = False;
  nmb->header.nm_flags.authoritative = False;
  nmb->header.rcode = 0;
  nmb->header.qdcount = 1;
  nmb->header.ancount = 0;
  nmb->header.nscount = 0;
  nmb->header.arcount = 0;

  make_nmb_name(&nmb->question.question_name,name,name_type);

  nmb->question.question_type = 0x21;
  nmb->question.question_class = 0x1;

  p.ip = to_ip;
  p.port = NMB_PORT;
  p.fd = fd;
  p.timestamp = time(NULL);
  p.packet_type = NMB_PACKET;

  GetTimeOfDay(&tval);

  if (!send_packet(&p)) 
    return(False);

  retries--;

  while (1) {
    struct timeval tval2;
    GetTimeOfDay(&tval2);
    if (TvalDiff(&tval,&tval2) > retry_time) {
      if (!retries)
        break;
      if (!found && !send_packet(&p))
        return False;
      GetTimeOfDay(&tval);
      retries--;
    }

    if ((p2=receive_packet(fd,NMB_PACKET,90))) {     
      struct nmb_packet *nmb2 = &p2->packet.nmb;
      debug_nmb_packet(p2);

      if (nmb->header.name_trn_id != nmb2->header.name_trn_id ||
             !nmb2->header.response) {
        /* its not for us - maybe deal with it later */
        if (fn) 
          fn(p2);
        else
          free_packet(p2);
        continue;
      }
	  
	  if (nmb2->header.opcode != 0 ||
	      nmb2->header.nm_flags.bcast ||
	      nmb2->header.rcode ||
	      !nmb2->header.ancount ||
	      nmb2->answers->rr_type != 0x21) {
	    /* XXXX what do we do with this? could be a redirect, but
	       we'll discard it for the moment */
	    free_packet(p2);
	    continue;
	  }

      if(fn_interpret_node_status)
	    (*fn_interpret_node_status)(&nmb2->answers->rdata[0],master,rname);
	  free_packet(p2);
	  return(True);
	}
  }

//  if(verbose)
//    DEBUG(0,("No status response (this is not unusual)\n"));

  return(False);
}

/****************************************************************************
 Do a netbios name status query on a host.
 The "master" parameter is a hack used for finding workgroups.
**************************************************************************/

static BOOL name_status(int fd,char *name,int name_type,BOOL recurse,
			struct in_addr to_ip,char *master,char *rname,
			void (*fn)(struct packet_struct *))
{
  return internal_name_status(fd,name,name_type,recurse,
		 to_ip,master,rname,True,
         _interpret_node_status, fn);
}

/****************************************************************************
a wrapper for gethostbyname() that tries with all lower and all upper case 
if the initial name fails
****************************************************************************/
static struct hostent *Get_Hostbyname(const char *name)
{
  char *name2 = strdup(name);
  struct hostent *ret;

  if (!name2)
    {
//      DEBUG(0,("Memory allocation error in Get_Hostbyname! panic\n"));
      exit(0);
    }

   
  /* 
   * This next test is redundent and causes some systems (with
   * broken isalnum() calls) problems.
   * JRA.
   */

#if 0
  if (!isalnum(*name2))
    {
      free(name2);
      return(NULL);
    }
#endif /* 0 */

  ret = gethostbyname(name2);
  if (ret != NULL)
    {
      free(name2);
      return(ret);
    }

#if 0
  /* try with all lowercase */
  strlower(name2);
  ret = gethostbyname(name2);
  if (ret != NULL)
    {
      free(name2);
      return(ret);
    }

  /* try with all uppercase */
  strupper(name2);
  ret = gethostbyname(name2);
  if (ret != NULL)
    {
      free(name2);
      return(ret);
    }
#endif
  
  /* nothing works :-( */
  free(name2);
  return(NULL);
}

/****************************************************************************
open a socket of the specified type, port and address for incoming data
****************************************************************************/

#define MAXHOSTNAMELEN 254

static int open_socket_in(int type, int port, int dlevel,uint32 socket_addr, BOOL rebind)
{
  struct hostent *hp;
  struct sockaddr_in sock;
  pstring host_name;
  int res;

  /* get my host name */
  if (gethostname(host_name, MAXHOSTNAMELEN) == -1) 
    {
//	    DEBUG(0,("gethostname failed\n"));
	    return -1;
    } 

  /* get host info */
  if ((hp = Get_Hostbyname(host_name)) == 0) 
    {
//      DEBUG(0,( "Get_Hostbyname: Unknown host %s\n",host_name));
      return -1;
    }
  
  memset((char *)&sock,'\0',sizeof(sock));
  memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);

#ifdef HAVE_SOCK_SIN_LEN
  sock.sin_len = sizeof(sock);
#endif
  sock.sin_port = htons( port );
  sock.sin_family = hp->h_addrtype;
  sock.sin_addr.s_addr = socket_addr;
  res = socket(hp->h_addrtype, type, 0);
  if (res == -1) 
    {
//	    DEBUG(0,("socket failed\n"));
	    return -1;
    }

  {
    int val=1;
	if(rebind)
		val=1;
	else
		val=0;
	if(setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1) {
//		DEBUG(dlevel,("setsockopt: SO_REUSEADDR=%d on port %d failed with error = %s\n",
//			val, port, strerror(errno) ));
	}
	
#ifdef SO_REUSEPORT
	if(setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1) {
//		DEBUG(dlevel,("setsockopt: SO_REUSEPORT=%d on port %d failed with error = %s\n",
//			val, port, strerror(errno) ));
	}
#endif /* SO_REUSEPORT */
  }

  /* now we've got a socket - we need to bind it */
  if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) 
    { 
      if (port) {
	      if (port == SMB_PORT || port == NMB_PORT) {
//	  DEBUG(dlevel,("bind failed on port %d socket_addr=%s (%s)\n",
//			port,inet_ntoa(sock.sin_addr),strerror(errno)));
	      }
	close(res); 

	if (dlevel > 0 && port < 1000)
	  port = 7999;

	if (port >= 1000 && port < 9000)
	  return(open_socket_in(type,port+1,dlevel,socket_addr,rebind));
      }

      return(-1); 
    }
//  DEBUG(3,("bind succeeded on port %d\n",port));

  return res;
}

/****************************************************************************
interpret an internet address or name into an IP address in 4 byte form
****************************************************************************/

static uint32 interpret_addr(char *str)
{
  struct hostent *hp;
  uint32 res;

  if (strcmp(str,"0.0.0.0") == 0) return(0);
  if (strcmp(str,"255.255.255.255") == 0) return(0xFFFFFFFF);

  /* if it's in the form of an IP address then get the lib to interpret it */
  if (is_ipaddress(str)) {
    res = inet_addr(str);
  } else {
    /* otherwise assume it's a network name of some sort and use 
       Get_Hostbyname */
    if ((hp = Get_Hostbyname(str)) == 0) {
//      DEBUG(3,("Get_Hostbyname: Unknown host. %s\n",str));
      return 0;
    }
    if(hp->h_addr == NULL) {
//      DEBUG(3,("Get_Hostbyname: host address is invalid for host %s\n",str));
      return 0;
    }
    putip((char *)&res,(char *)hp->h_addr);
  }

  if (res == (uint32)-1) return(0);

  return(res);
}

/*******************************************************************
  a convenient addition to interpret_addr()
  ******************************************************************/
static struct in_addr *interpret_addr2(char *str)
{
  static struct in_addr ret;
  uint32 a = interpret_addr(str);
  ret.s_addr = a;
  return(&ret);
}



//===========================================================================
//
// nmblookup.c
//
//===========================================================================

//extern int DEBUGLEVEL;

extern struct in_addr ipzero;

//static BOOL use_bcast = True;
//static BOOL got_bcast = False;
//static struct in_addr bcast_addr;
//static BOOL recursion_desired = False;
//static BOOL translate_addresses = False;
static int ServerFD= -1;
static int RootPort = 0;
//static BOOL find_status=False;

static char *lp_socket_address() { return "0.0.0.0"; }

/****************************************************************************
  Get the next token from a string, return False if none found
  handles double-quotes. 
Based on a routine by GJC@VILLAGE.COM. 
Extensively modified by Andrew.Tridgell@anu.edu.au
****************************************************************************/

static char *last_ptr=NULL;

static BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
{
  char *s;
  BOOL quoted;
  size_t len=1;

  if (!ptr) ptr = &last_ptr;
  if (!ptr) return(False);

  s = *ptr;

  /* default to simple separators */
  if (!sep) sep = " \t\n\r";

  /* find the first non sep char */
  while(*s && strchr(sep,*s)) s++;

  /* nothing left? */
  if (! *s) return(False);

  /* copy over the token */
  for (quoted = False; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++)
    {
	    if (*s == '\"') {
		    quoted = !quoted;
	    } else {
		    len++;
		    *buff++ = *s;
	    }
    }

  *ptr = (*s) ? s+1 : s;  
  *buff = 0;
  last_ptr = *ptr;

  return(True);
}

/****************************************************************************
 Set user socket options.
****************************************************************************/

enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};

static struct
{
  char *name;
  int level;
  int option;
  int value;
  int opttype;
} socket_options[] = {
  {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
  {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
  {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
#ifdef TCP_NODELAY
  {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
#endif
#ifdef IPTOS_LOWDELAY
  {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
#endif
#ifdef IPTOS_THROUGHPUT
  {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
#endif
#ifdef SO_REUSEPORT
  {"SO_REUSEPORT",      SOL_SOCKET,    SO_REUSEPORT,    0,                 OPT_BOOL},
#endif
#ifdef SO_SNDBUF
  {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
#endif
#ifdef SO_RCVBUF
  {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
#endif
#ifdef SO_SNDLOWAT
  {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
#endif
#ifdef SO_RCVLOWAT
  {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
#endif
#ifdef SO_SNDTIMEO
  {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
#endif
#ifdef SO_RCVTIMEO
  {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
#endif
  {NULL,0,0,0,0}};


static void set_socket_options(int fd, char *options)
{
	fstring tok;

	while (next_token(&options,tok," \t,", sizeof(tok))) {
		int ret=0,i;
		int value = 1;
		char *p;
		BOOL got_value = False;

		if ((p = strchr(tok,'='))) {
			*p = 0;
			value = atoi(p+1);
			got_value = True;
		}

		for (i=0;socket_options[i].name;i++)
			if (!strcasecmp(socket_options[i].name,tok))
				break;

		if (!socket_options[i].name) {
//			DEBUG(0,("Unknown socket option %s\n",tok));
			continue;
		}

		switch (socket_options[i].opttype) {
		case OPT_BOOL:
		case OPT_INT:
			ret = setsockopt(fd,socket_options[i].level,
						socket_options[i].option,(char *)&value,sizeof(int));
			break;

		case OPT_ON:
			if (got_value) {
//				DEBUG(0,("syntax error - %s does not take a value\n",tok));
			}
			
			{
				int on = socket_options[i].value;
				ret = setsockopt(fd,socket_options[i].level,
							socket_options[i].option,(char *)&on,sizeof(int));
			}
			break;	  
		}
      
		if (ret != 0) {
//			DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
		}
	}
}


/****************************************************************************
  open the socket communication
  **************************************************************************/
static BOOL open_sockets(void)
{
  ServerFD = open_socket_in( SOCK_DGRAM,
                             (RootPort ? 137 :0),
                             3,
                             interpret_addr(lp_socket_address()), True );

  if (ServerFD == -1)
    return(False);

  set_socket_options(ServerFD,"SO_BROADCAST");

//  DEBUG(3, ("Socket opened.\n"));
  return True;
}


// main scan process

void samba_finger_machine(int machine)
{
        unsigned int lookup_type = 0x0;
        char *hostname = machines[machine]->hostname;
        static int opened_sockets = 0;
        
        if(!opened_sockets) {
                if (!open_sockets()) {
                        printf("cant open sockets\n");
                }
                opened_sockets = 1;
        }

	this_machine = machine;
	first_user = 1;           // skip first user: this is the machine name
        
        {
                struct in_addr ip;
                int ret;
                
                fstring lookup;
                fstrcpy(lookup,"*");
                ip = *interpret_addr2(hostname);
                //  printf("Looking up status of %s\n",inet_ntoa(ip));
                ret = name_status(ServerFD, lookup, lookup_type,
                                  True, ip, NULL, NULL, NULL);
                
//	  printf("\n");
                
                if(ret) {

			scan_set_status(machine, STATUS_UP);

		} else {
                        
                        // no response from machine - assume down
                        
                        fprintf(stderr,
                                "samba_finger_machine: timeout querying %s\n",
                                hostname);
                        scan_set_status(machine, STATUS_DOWN);
                }
        }
}
