/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   NBT netbios routines and daemon
   Copyright (C) Andrew Tridgell 1994-1995
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"

extern pstring debugf;
extern int DEBUGLEVEL;

extern pstring scope;

static int browse_interval = BROWSE_INTERVAL;

BOOL browse = True;
BOOL dns_serve = False;

extern struct in_addr lastip;
extern int lastport;
extern struct in_addr myip;
extern struct in_addr bcast_ip;
extern struct in_addr Netmask;
extern pstring myhostname;
static pstring host_file;
static pstring myname="";
static int num_good_sends=0;
static int num_good_receives=0;
static pstring lookup="";
static int Client138=-1;

enum name_sources {LMHOSTS, REGISTER, SELF, DNS};

/* this is the structure used for the local netbios name table */
typedef struct
{
  time_t start_time;
  int ttl;
  struct in_addr ip;
  struct in_addr master_ip;
  BOOL found_master;
  BOOL valid;
  BOOL isgroup;
  BOOL unicast;
  char name[100];
  int type;
  int count;  
  enum name_sources source;
} name_struct;


static int num_names=0;
static name_struct *names = NULL;

extern int Client;

#define NAMEVALID(i) names[i].valid
#define ISGROUP(i) (names[i].isgroup)

void process(void);

/* are we running as a daemon ? */
static BOOL is_daemon = False;

/* machine comment */
static pstring comment="";

/* die after this number of 10ths of seconds if no activity and not a daemon */
static int idle_timeout = NMBD_INETD_TIMEOUT*1000;

extern pstring user_socket_options;

static void add_group_name(char *name);
static void add_host_name(char *name,int type,struct in_addr *ip);
static void dump_names(void);


static BOOL got_bcast = False;
static BOOL got_myip = False;
static BOOL got_nmask = False;


/****************************************************************************
catch a sighup
****************************************************************************/
static int sig_hup()
{
  BlockSignals(True);

  DEBUG(0,("Got SIGHUP - not implemented\n"));
  dump_names();
  if (!is_daemon)
    exit(1);

  BlockSignals(False);
#ifndef DONT_REINSTALL_SIG
  signal(SIGHUP,SIGNAL_CAST sig_hup);
#endif
  return(0);
}

/****************************************************************************
catch a sigpipe
****************************************************************************/
static int sig_pipe()
{
  BlockSignals(True);

  DEBUG(0,("Got SIGPIPE\n"));
  if (!is_daemon)
    exit(1);
  BlockSignals(False);
  return(0);
}

/****************************************************************************
possibly continue after a fault
****************************************************************************/
static void fault_continue(void)
{
  static int errcount=0;

  errcount++;

  if (is_daemon && errcount<100)
    process();

  exit(1);
}

/****************************************************************************
  true if two netbios names are equal
****************************************************************************/
static BOOL name_equal(char *s1,char *s2,int type1,int type2)
{
  char n1[20],n2[20];

  if (type1 != type2) return(False);

  StrnCpy(n1,s1,15);
  StrnCpy(n2,s2,15);

  trim_string(n1,NULL," ");
  trim_string(n2,NULL," ");

  return(strequal(n1,n2));
}

/****************************************************************************
add a netbios name
****************************************************************************/
static int add_name(void)
{
  int i;

  for (i=0;i<num_names;i++)
    if (!names[i].valid)
      break;

  if (i==num_names) {
    name_struct *n;
    if (num_names == 0)    
      n = (name_struct *)malloc(sizeof(name_struct));
    else
      n = (name_struct *)realloc(names,sizeof(name_struct)*(num_names+1));
    if (!n) {
      DEBUG(0,("Can't malloc more names space!\n"));
      return(-1);
    }
    i = num_names;
    num_names++;
    names = n;
  }

  bzero(&names[i],sizeof(names[i]));

  return(i);
}

/****************************************************************************
find a name
****************************************************************************/
static int find_name(char *s,int type,BOOL groups)
{
  int i;
  time_t t = time(NULL);

  for (i=0;i<num_names;i++)
    if (names[i].valid && (groups || !ISGROUP(i)))
      {
	if ((names[i].ttl > 0) && (t > (names[i].start_time + names[i].ttl)))
	  names[i].valid = False;
	else
	  {
	    if (name_equal(s,names[i].name,type,names[i].type)) {
	      return(i);
	    }
	  }
      }
  return -1;
}


/****************************************************************************
check names, and change any 0 IPs to myip
****************************************************************************/
static void check_names(void)
{
  int i;
  int group_count=0;

  /* add the magic __SAMBA__ name */
  add_host_name("__SAMBA__",0x20,&myip);
  add_host_name("__SAMBA__",0x0,&myip);

  for (i=0;i<num_names;i++)
    if (names[i].valid) {
      if (ISGROUP(i)) group_count++;
    }

  if (group_count == 0)
    add_group_name(WORKGROUP);


  for (i=0;i<num_names;i++)
    if (names[i].valid && strequal((char *)inet_ntoa(names[i].ip),"0.0.0.0"))
      names[i].ip = (ISGROUP(i)?bcast_ip:myip);
}


/****************************************************************************
dump a copy of the name table
****************************************************************************/
static void dump_names(void)
{
  int i;
  DEBUG(3,("Dump of local name table\n"));
  for (i=0;i<num_names;i++)
    if (names[i].valid) {
      DEBUG(3,("%s %s %d %s",
	       names[i].name,inet_ntoa(names[i].ip),
	       names[i].ttl,BOOLSTR(names[i].isgroup)));
      if (names[i].found_master) 
	DEBUG(3,(" %s",inet_ntoa(names[i].master_ip)));
      DEBUG(3,("\n"));
    }
}


/****************************************************************************
load a netbios hosts file
****************************************************************************/
static void load_hosts_file(char *fname)
{
  int i;
  FILE *f = fopen(fname,"r");
  pstring line;
  if (!f) 
    {
      DEBUG(2,("Not using non-existant lmhosts file %s\n",fname));
      return;
    }

  while (!feof(f))
    {
      if (!fgets_slash(line,sizeof(pstring),f)) continue;
      
      if (*line == '#') continue;

      {
	string ip="",name="",flags="",extra="";
	unsigned long a;
	char *ptr;
	int count = 0;
	ptr = line;
	if (next_token(&ptr,ip,NULL)) ++count;
	if (next_token(&ptr,name,NULL)) ++count;
	if (next_token(&ptr,flags,NULL)) ++count;
	if (next_token(&ptr,extra,NULL)) ++count;

	if (count <= 0) continue;

	if (count > 0 && count < 2)
	  {
	    DEBUG(0,("Ill formed hosts line [%s]\n",line));	    
	    continue;
	  }

	i = add_name();
	if (i < 0) 
	  {
	    fclose(f);
	    return;
	  }

	a = interpret_addr(ip);
	putip((char *)&names[i].ip,(char *)&a);

	names[i].valid = True;
	names[i].source = LMHOSTS;

	StrnCpy(names[i].name,name,15);
	if (strchr(flags,'G') || strchr(flags,'S'))
	  names[i].isgroup = True;
	if (strchr(flags,'M') && !ISGROUP(i))
	  strcpy(myname,name);
	if (strchr(flags,'U'))
	  names[i].unicast = True;
	if (names[i].isgroup) 
	  names[i].type = 0xF0; /* hopefully invalid */
	else
	  names[i].type = 0x20;
      }      
    }

  fclose(f);
}


/****************************************************************************
add a netbios group name
****************************************************************************/
static void add_group_name(char *name)
{
  int i = add_name();
  if (i < 0) 
    return;

  bzero((char *)&names[i].ip,sizeof(names[i].ip));

  strcpy(names[i].name,name);
  names[i].isgroup = True;
  names[i].valid = True;
  names[i].type = 0xF0;
  names[i].source = SELF;
}

/****************************************************************************
add a host name
****************************************************************************/
static void add_host_name(char *name,int type,struct in_addr *ip)
{
  int i;

  if (find_name(name,type,True) >= 0) return;

  i = add_name();
  if (i < 0) 
    return;

  names[i].ip = *ip;
  strcpy(names[i].name,name);
  names[i].valid = True;
  names[i].start_time = time(NULL);
  names[i].ttl = 0;
  names[i].type = type;
  names[i].source = SELF;
}

/****************************************************************************
word out the length of a nmb message
****************************************************************************/
static int nmb_len(char *buf)
{
int i;
int ret = 12;
char *p = buf;
int qdcount = RSVAL(buf,4);
int ancount = RSVAL(buf,6);
int nscount = RSVAL(buf,8);
int arcount = RSVAL(buf,10);

/* check for insane qdcount values? */
if (qdcount > 100 || qdcount < 0)
  {
    DEBUG(6,("Invalid qdcount? qdcount=%d\n",qdcount));
    return(0);
  }

for (i=0;i<qdcount;i++)
  {
    p = buf + ret;
    ret += name_len(p) + 4;
  }

for (i=0;i<(ancount + nscount + arcount);i++)
  {
    int rdlength;
    p = buf + ret;
    ret += name_len(p) + 8;
    p = buf + ret;
    rdlength = RSVAL(p,0);
    ret += rdlength + 2;
  }

return(ret);
}

/****************************************************************************
receive a name message. timeout is in milliseconds
****************************************************************************/
static int receive_nmb(char *buffer,int timeout)
{
  int ret = read_max_udp(Client,buffer,sizeof(pstring),timeout);

  if (ret < 0)
    {
      DEBUG(0,("No bytes from client\n"));
      if (!is_daemon)
	{
	  close_sockets();
	  exit(0);
	}
    }
  
  if (ret <= 1)
    return 0;

  log_in(buffer,ret);

  DEBUG(3,("received packet from (%s) nmb_len=%d len=%d\n",
	inet_ntoa(lastip),nmb_len(buffer),ret));

  num_good_receives++;

  return(ret);
}

/****************************************************************************
send a name message
****************************************************************************/
static BOOL send_nmb(char *buf, int len, struct in_addr *ip,int port)
{
  BOOL ret;
  struct sockaddr_in sock_out;

  /* set the address and port */
  bzero((char *)&sock_out,sizeof(sock_out));
  putip((char *)&sock_out.sin_addr,(char *)ip);
  sock_out.sin_port = htons( port );
  sock_out.sin_family = AF_INET;
  
  /* log the packet */
  log_out(buf,len);

  if (DEBUGLEVEL > 0)
    DEBUG(3,("sending a packet of len %d to (%s) on port 137 of type DGRAM\n",
	  len,inet_ntoa(*ip)));
	
  /* send it */
  ret = (sendto(Client,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0);

  if (!ret)
    DEBUG(0,("Send packet failed. ERRNO=%d\n",errno));

  if (ret)
    num_good_sends++;

  return(ret);
}

/*******************************************************************
check if an IP is on my net
********************************************************************/
static BOOL is_mynet(struct in_addr ip)
{
  unsigned int net1,net2,nmask,subnet1,subnet2;

  nmask   = *(unsigned int *)&Netmask;
  net1    = (*(unsigned int *)&myip);
  subnet1 = net1 & nmask;
  net2    = (*(unsigned int *)&ip);
  subnet2 = net2 & nmask;
	    
  return((net1 != net2) && (subnet1 == subnet2));
}

/****************************************************************************
interpret a node status response
****************************************************************************/
static void interpret_node_status(char *inbuf,char *master)
{
  int level = master?3:0;
  char *p = inbuf + 12 + name_len(inbuf+12) + 10;
  int numnames = CVAL(p,0);
  DEBUG(level,("received %d names\n",numnames));
  p += 1;
  while (numnames--)
    {
      char qname[17];
      int type;
      fstring flags="";
      StrnCpy(qname,p,15);
      type = CVAL(p,15);
      p += 16;
      if (p[0] & 0x80) strcat(flags,"<GROUP> ");
      if (p[0] & 0x60 == 0) strcat(flags,"B ");
      if (p[0] & 0x60 == 1) strcat(flags,"P ");
      if (p[0] & 0x60 == 2) strcat(flags,"M ");
      if (p[0] & 0x60 == 3) strcat(flags,"_ ");
      if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> ");
      if (p[0] & 0x08) strcat(flags,"<CONFLICT> ");
      if (p[0] & 0x04) strcat(flags,"<ACTIVE> ");
      if (p[0] & 0x02) strcat(flags,"<PERMANENT> ");

      if (master && type == 0x1d) {
	StrnCpy(master,qname,15);
      }
      
      DEBUG(level,("\t%s (type=0x%x)\t%s\n",qname,type,flags));
      p+=2;
    }
  DEBUG(level,("num_good_sends=%d num_good_receives=%d\n",
	       IVAL(p,20),IVAL(p,24)));
}


/****************************************************************************
show a nmb message
****************************************************************************/
static void show_nmb(char *inbuf)
{
  int i,l;
  int name_trn_id = RSVAL(inbuf,0);
  int opcode = (CVAL(inbuf,2) >> 3) & 0xF;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  int rcode = CVAL(inbuf,3) & 0xF;
  int qdcount = RSVAL(inbuf,4);
  int ancount = RSVAL(inbuf,6);
  int nscount = RSVAL(inbuf,8);
  int arcount = RSVAL(inbuf,10);
  char name[100];

  if (DEBUGLEVEL < 3) return;

  DEBUG(3,("\nPACKET INTERPRETATION\n"));

  if (opcode == 5 && ((nm_flags & ~1) == 0x10) && rcode == 0)
    DEBUG(3,("NAME REGISTRATION REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast"));

  if (opcode == 5 && ((nm_flags & ~1) == 0x00) && rcode == 0)
    DEBUG(3,("NAME OVERWRITE REQUEST AND DEMAND (%s)\n",nm_flags&1?"Broadcast":"Unicast"));
  
  if (opcode == 9 && ((nm_flags & ~1) == 0x00) && rcode == 0)
    DEBUG(3,("NAME REFRESH REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast"));

  if (opcode == 8)
    DEBUG(3,("NAME REFRESH (%s)\n",nm_flags&1?"Broadcast":"Unicast"));
  
  if (opcode == 5 && nm_flags == 0x58 && rcode == 0)
    DEBUG(3,("POSITIVE NAME REGISTRATION RESPONSE\n"));
  
  if (opcode == 5 && nm_flags == 0x58 && rcode != 0 && rcode != 7)
    DEBUG(3,("NEGATIVE NAME REGISTRATION RESPONSE\n"));
  
  if (opcode == 5 && nm_flags == 0x50 && rcode == 0)
    DEBUG(3,("END-NODE CHALLENGE REGISTRATION RESPONSE\n"));
  
  if (opcode == 5 && nm_flags == 0x58 && rcode != 0 && rcode == 7)
    DEBUG(3,("NAME CONFLICT DEMAND\n"));
  
  if (opcode == 6 && (nm_flags&~1) == 0x00 && rcode == 0)
    DEBUG(3,("NAME RELEASE REQUEST & DEMAND (%s)\n",nm_flags&1?"Broadcast":"Unicast"));
  
  if (opcode == 6 && (nm_flags&~1) == 0x40 && rcode == 0)
    DEBUG(3,("POSITIVE NAME RELEASE RESPONSE\n"));
  
  if (opcode == 6 && (nm_flags&~1) == 0x40 && rcode != 0)
    DEBUG(3,("NEGATIVE NAME RELEASE RESPONSE\n"));
  
  if (opcode == 0 && (nm_flags&~1) == 0x10 && rcode == 0)
    DEBUG(3,("NAME QUERY REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast"));
  
  if (opcode == 0 && (nm_flags&~0x28) == 0x50 && rcode == 0)
    DEBUG(3,("POSITIVE NAME QUERY RESPONSE\n"));
  
  if (opcode == 0 && (nm_flags&~0x08) == 0x50 && rcode != 0)
    DEBUG(3,("NEGATIVE NAME QUERY RESPONSE\n"));
  
  if (opcode == 0 && nm_flags == 0x10 && rcode == 0)
    DEBUG(3,("REDIRECT NAME QUERY RESPONSE\n"));

  if (opcode == 7 && nm_flags == 0x80 && rcode == 0)
    DEBUG(3,("WAIT FOR ACKNOWLEDGEMENT RESPONSE\n"));
  
  if (opcode == 0 && (nm_flags&~1) == 0x00 && rcode == 0)
    DEBUG(3,("NODE STATUS REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast"));

  if (opcode == 0 && nm_flags == 0x40 && rcode == 0)
    {
      DEBUG(3,("NODE STATUS RESPONSE\n"));
      interpret_node_status(inbuf,NULL);
    }
  
  
  DEBUG(3,("name_trn_id=0x%x\nopcode=0x%x\nnm_flags=0x%x\nrcode=0x%x\n",
	name_trn_id,opcode,nm_flags,rcode));
  DEBUG(3,("qdcount=%d\nancount=%d\nnscount=%d\narcount=%d\n",
	qdcount,ancount,nscount,arcount));

  l = 12;
  for (i=0;i<qdcount;i++)
    {
      int type,class;
      DEBUG(3,("QUESTION %d\n",i));
      name_extract(inbuf,l,name);
      l += name_len(inbuf+l);
      type = RSVAL(inbuf+l,0);
      class = RSVAL(inbuf+l,2);
      l += 4;
      DEBUG(3,("\t%s\n\ttype=0x%x\n\tclass=0x%x\n",name,type,class));
    }

  for (i=0;i<(ancount + nscount + arcount);i++)
    {
      int type,class,ttl,rdlength;
      DEBUG(3,("RESOURCE %d\n",i));
      name_extract(inbuf,l,name);
      l += name_len(inbuf + l);
      type = RSVAL(inbuf+l,0);
      class = RSVAL(inbuf+l,2);
      ttl = RIVAL(inbuf+l,4);
      rdlength = RSVAL(inbuf+l,8);
      l += 10 + rdlength;
      DEBUG(3,("\t%s\n\ttype=0x%x\n\tclass=0x%x\n",name,type,class));
      DEBUG(3,("\tttl=%d\n\trdlength=%d\n",ttl,rdlength));
    }

  DEBUG(3,("\n"));
  
}


/****************************************************************************
do a netbios name query to find someones IP
****************************************************************************/
static BOOL name_query(char *name,char name_type,BOOL bcast,
		       struct in_addr to_ip,struct in_addr *ip,void (*fn)())
{
  BOOL found=False;
  pstring inbuf,outbuf;
  static uint16 name_trn_id = 0x6242;
  char *p;
  int retries = 3;
  int retry_time = bcast?250:5000;
  struct timeval tval;

  bzero(inbuf,sizeof(inbuf));
  bzero(outbuf,sizeof(outbuf));

  name_trn_id += getpid() % 100;
  name_trn_id = (name_trn_id % 0x7FFF);

  RSSVAL(outbuf,0,name_trn_id);
  CVAL(outbuf,2) = 0x1;
  CVAL(outbuf,3) = bcast?(1<<4):0;
  RSSVAL(outbuf,4,1);
  RSSVAL(outbuf,6,0);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  name_mangle(name,p,name_type);
  p += name_len(p);
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  p += 4;

  DEBUG(2,("Sending name query for %s\n",name));

  show_nmb(outbuf);

  GetTimeOfDay(&tval);

  if (!send_nmb(outbuf,nmb_len(outbuf), &to_ip, 137))
    return False;
  retries--;

  while (1)
    {
      struct timeval tval2;
      GetTimeOfDay(&tval2);
      if (TvalDiff(&tval,&tval2) > retry_time) {
	if (!retries) break;
	if (!found && !send_nmb(outbuf,nmb_len(outbuf), &to_ip, 137))
	  return False;
	GetTimeOfDay(&tval);
	retries--;
      }

      if (receive_nmb(inbuf,90))
	{     
	  int rec_name_trn_id = RSVAL(inbuf,0);
	  int opcode = (CVAL(inbuf,2) >> 3) & 0xF;
	  int rcode = CVAL(inbuf,3) & 0xF;
	  int response = (CVAL(inbuf,2)>>7);
	  int qdcount = RSVAL(inbuf,4);
	  int ancount = RSVAL(inbuf,6);
	  show_nmb(inbuf);

	  /* is it a positive response to our request? */
	  if ((rec_name_trn_id == name_trn_id) && response) {
	    if (opcode == 0 && qdcount==0 && ancount==1 && rcode == 0) {
	      putip((char *)ip,inbuf + 12 + name_len(inbuf+12) + 12);
	      DEBUG(2,("Got a positive name query response from %s",
		       inet_ntoa(lastip)));
	      DEBUG(2,(" (%s)\n",inet_ntoa(*ip)));
	      found=True; retries=0;
	      continue;
	    } else {
	      /* some other sort of response, yuck */
	      DEBUG(2,("Unknown response\n"));
	      continue;
	    }
	  } else {
	    if (fn) fn(inbuf);
	  }
	}
    }

  return(found);
}

/****************************************************************************
do a netbios name status to a host
****************************************************************************/
static BOOL name_status(char *name,int type,struct in_addr to_ip,void (*fn)(),char *master)
{
  pstring inbuf,outbuf;
  static uint16 name_trn_id = 0x4262;
  char *p;
  int retries = 3;
  int retry_time = 5000;
  struct timeval tval;

  bzero(inbuf,sizeof(inbuf));
  bzero(outbuf,sizeof(outbuf));

  if (!fn)
    DEBUG(1,("Querying status of name %s\n",name));

  name_trn_id += getpid() % 100;
  name_trn_id = (name_trn_id % 10000);

  RSSVAL(outbuf,0,name_trn_id);
  CVAL(outbuf,2) = 0;
  CVAL(outbuf,3) = 0x0;
  RSSVAL(outbuf,4,1);
  RSSVAL(outbuf,6,0);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  name_mangle(name,p,type);
  p += name_len(p);
  RSSVAL(p,0,0x21);
  RSSVAL(p,2,0x1);
  p += 4;

  DEBUG(fn?3:2,("Sending name status query for %s\n",name));

  show_nmb(outbuf);

  GetTimeOfDay(&tval);

  if (!send_nmb(outbuf,nmb_len(outbuf), &to_ip, 137))
    return False;
  
  while (1)
    {
      struct timeval tval2;
      GetTimeOfDay(&tval2);
      if (TvalDiff(&tval,&tval2) > retry_time) {
	if (!retries) break;
	if (!send_nmb(outbuf,nmb_len(outbuf), &to_ip, 137))
	  return False;
	GetTimeOfDay(&tval);
	retries--;	
      }
      
      if (receive_nmb(inbuf,90))
	{
	  int rec_name_trn_id = RSVAL(inbuf,0);
	  int rcode = CVAL(inbuf,3) & 0xF;
	  int response = (CVAL(inbuf,2)>>7);
	  int qdcount = RSVAL(inbuf,4);
	  int ancount = RSVAL(inbuf,6);

	  show_nmb(inbuf);
	  
	  /* is it a positive response to our request? */
	  if (response && (rec_name_trn_id == name_trn_id)) {
	    if (rcode==0 && ancount==1 && qdcount==0) {
	      DEBUG(fn?3:0,("Got a positive node status response from %s\n",
			    inet_ntoa(lastip)));
	      interpret_node_status(inbuf,master);
	      return(True);
	    }
	    return(False);
	  }
	  if (fn) fn(inbuf);
	}
    }

  DEBUG(0,("No response (this is not unusual)\n"));

  return(False);
}


/****************************************************************************
reply to a reg request
****************************************************************************/
static void reply_name_reg(char *inbuf)
{
  pstring outbuf;
  int rec_name_trn_id = RSVAL(inbuf,0);
  char qname[100]="";
  int ttl;
  char *p = inbuf;
  struct in_addr ip;
  int n=0;
  int name_type;
  unsigned char nb_flags;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  int bcast = (nm_flags&1);
  struct in_addr tmpip;
  
  bzero(outbuf,sizeof(outbuf));

  name_type = name_extract(inbuf,12,qname);

  p += 12;
  p += name_len(p);
  p += 4;
  p += name_len(p);
  p += 4;
  ttl = RIVAL(p,0);
  nb_flags = CVAL(p,6);
  p += 8;
  putip((char *)&ip,p);

  DEBUG(2,("Name registration request for %s (%s) nb_flags=0x%x ntype=%d\n",
	qname,inet_ntoa(ip),nb_flags,name_type));

  if ((nb_flags&0x80) == 0 && (name_type != 0x1d)) {
    if (ttl==0) ttl = NMBD_MAX_TTL;
    ttl = MIN(ttl,NMBD_MAX_TTL);

    n = find_name(qname,name_type,True);

    if (n>=0 && names[n].source != REGISTER && names[n].source != DNS)
      return;

    if (n<0)
      n = add_name();
    if (n<0) return;

    bzero(&names[n],sizeof(names[n]));

    StrnCpy(names[n].name,qname,15);
    names[n].type = name_type;
    names[n].unicast = !dns_serve || is_mynet(ip);
    names[n].ip = ip;
    names[n].valid = True;
    names[n].ttl = ttl;
    names[n].source = REGISTER;
    names[n].start_time = time(NULL);
    DEBUG(2,("Added %s with ttl %d as (%s) (uni=%s)\n",
	     qname,names[n].ttl,inet_ntoa(ip),BOOLSTR(names[n].unicast)));
  }

  if (bcast) return;

  /* Send a POSITIVE NAME REGISTRATION RESPONSE */
  RSSVAL(outbuf,0,rec_name_trn_id);
  CVAL(outbuf,2) = (1<<7) | (5<<3) | 5;
  CVAL(outbuf,3) = (1<<7);
  RSSVAL(outbuf,4,0);
  RSSVAL(outbuf,6,1);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);
  p += name_len(p);
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  /* we want them to refresh in case we die */
  if (!ttl) ttl = 15*60;
  ttl = MIN(ttl,15*60);
  RSIVAL(p,4,ttl);

  RSSVAL(p,8,6);
  RSSVAL(p,10,nb_flags);  
  p += 12;
  putip(p,&ip);  
  p += 4;

  DEBUG(2,("Sending a positive registration response\n"));
  if (DEBUGLEVEL > 2)
    show_nmb(outbuf);

  tmpip = lastip;
  num_good_sends++;
  send_nmb(outbuf,PTR_DIFF(p,outbuf),&tmpip,lastport>0?lastport:137);

  return;
}

/****************************************************************************
reply to a name release
****************************************************************************/
static void reply_name_release(char *inbuf)
{
  pstring outbuf;
  int rec_name_trn_id = RSVAL(inbuf,0);
  char qname[100]="";
  char *p = inbuf;
  struct in_addr ip;
  int n=0;
  int name_type;
  unsigned char nb_flags;
  struct in_addr tmpip;
  BOOL release_ok=False;
  int reason=5;


  bzero(outbuf,sizeof(outbuf));

  name_type = name_extract(inbuf,12,qname);

  p += 12;
  p += name_len(p);
  p += 4;
  p += name_len(p);
  p += 4;
  nb_flags = CVAL(p,6);
  p += 8;
  putip((char *)&ip,p);

  DEBUG(2,("Name release request for %s (%s) nb_flags=0x%x ntype=%d\n",
	qname,inet_ntoa(ip),nb_flags,name_type));

  n = find_name(qname,name_type,True);
  if (n>=0 && names[n].source == REGISTER &&
      !memcmp((char *)&ip,(char *)&names[n].ip,sizeof(ip))) {
    release_ok = True;
    names[n].valid = False;
  }

  /* Send a POSITIVE NAME RELEASE RESPONSE */
  RSSVAL(outbuf,0,rec_name_trn_id);
  CVAL(outbuf,2) = (1<<7) | (6<<3) | 4;
  CVAL(outbuf,3) = release_ok?0:reason;
  RSSVAL(outbuf,4,0);
  RSSVAL(outbuf,6,1);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);
  p += name_len(p);
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  RSIVAL(p,4,0); /* ttl */
  RSSVAL(p,8,6);
  RSSVAL(p,10,nb_flags);
  p += 12;
  putip(p,&ip);  
  p += 4;

  DEBUG(2,("Sending a %s name release response\n",release_ok?"positive":"negative"));
  if (DEBUGLEVEL > 2)
    show_nmb(outbuf);

  tmpip = lastip;
  num_good_sends++;
  send_nmb(outbuf,PTR_DIFF(p,outbuf),&tmpip,lastport>0?lastport:137);

  return;
}


/****************************************************************************
reply to a name status query
****************************************************************************/
static void reply_name_status(char *inbuf)
{
  pstring outbuf;
  fstring qname;
  int rec_name_trn_id = RSVAL(inbuf,0);
  char *p = inbuf;
  struct in_addr tmpip;
  int i;
  int count=0;
  int name_type;
  BOOL wildcard = False;

  bzero(outbuf,sizeof(outbuf));

  name_type = name_extract(inbuf,12,qname);
  wildcard = (qname[0]=='*');

  DEBUG(2,("(%s) status query on name (%s)\n",inet_ntoa(lastip), qname));

  if (!wildcard) {
    i = find_name(qname,name_type,False);

    if (i < 0)
      return;
    if (names[i].source != SELF && names[i].source != LMHOSTS)
      return;
  }

  /* Send a POSITIVE NAME STATUS RESPONSE */
  RSSVAL(outbuf,0,rec_name_trn_id);
  CVAL(outbuf,2) = (1<<7) | (1<<2);
  CVAL(outbuf,3) = 0;
  RSSVAL(outbuf,4,0);
  RSSVAL(outbuf,6,1);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);
  p += name_len(p);
  RSSVAL(p,0,0x21);
  RSSVAL(p,2,0x1);
  RSIVAL(p,4,0);
  RSSVAL(p,8,6);
  p += 10;

  for (i=0;i<num_names;i++)
    if (names[i].valid) count++;
  count = MIN(count,(512 - (46 + 1 + PTR_DIFF(p,outbuf)))/18);

  SCVAL(p,0,count);
  p++;

  for (i=0;i<num_names && count>0;i++)
    if (names[i].valid)
      {
	bzero(p,18);
	strcpy(p,names[i].name);
	strupper(p);
	p[15] = names[i].type;
	p += 16;
	p[0] = 0x4; /* active */
	if (strequal(names[i].name,myname)) p[0] |= 0x2; /* permanent */
	if (ISGROUP(i)) p[0] |= 0x80; /* group */
	p += 2;
	count--;
      }

  bzero(p,46);
  putip(p,(char *)&myip);
  SIVAL(p,20,num_good_sends);
  SIVAL(p,24,num_good_receives);
  
  p += 46;

  tmpip = lastip;
  num_good_sends++;
  send_nmb(outbuf,PTR_DIFF(p,outbuf),&tmpip,lastport>0?lastport:137);

  return;
}


/****************************************************************************
reply to a name query
****************************************************************************/
static void reply_name_query(char *inbuf)
{
  pstring outbuf;
  int rec_name_trn_id = RSVAL(inbuf,0);
  char qname[100]="";
  char *p = inbuf;
  unsigned char nb_flags = 0;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  struct in_addr tmpip;
  struct in_addr retip;
  int i=-1;
  BOOL broadcast = ((nm_flags&1) != 0);
  int name_type;
  int qtype;
  BOOL wildcard=False;

  bzero(outbuf,sizeof(outbuf));

  name_type = name_extract(inbuf,12,qname);

  wildcard = (qname[0] == '*');

  qtype = RSVAL(inbuf+12+name_len(inbuf+12),0);

  if (qtype == 0x21) {
    reply_name_status(inbuf);
    return;
  }

  if (qtype != 0x20) 
    return;

  DEBUG(2,("(%s) querying name (%s)(%d) (%s)",
	   inet_ntoa(lastip), qname, name_type,
	   broadcast?"BROADCAST":"UNICAST"));

  if (!wildcard) {
    i = find_name(qname,name_type,False);

    if (i < 0)
      i = find_name(qname,name_type,True);
    
    if (i >= 0)
      {
	if (broadcast && (names[i].unicast || names[i].source == REGISTER)) {
	  DEBUG(2,(" - broadcast. No reply\n"));
	  return;
	}
  
	if (ISGROUP(i)) {
	  DEBUG(2,(" - group name. No reply\n"));
	  return;
	}

	retip = names[i].ip;
	DEBUG(2,(" sending positive reply\n"));
      }
    else
      {
	if ((name_type!=0 && name_type!=0x3 && name_type!=0x20) ||
	    (broadcast && !dns_serve))
	  {
	    DEBUG(2,("\n"));
	    return;
	  }
	else
	  /* try a DNS query to get the IP */
	  {
	    struct hostent *hp;
	    pstring hname;
	    
	    StrnCpy(hname,qname,15);
	    trim_string(hname," "," ");
	    trim_string(hname,".",".");
	    p = strchr(hname,' ');
	    if (p) *p = 0;
	    
	    if ((hp = Get_Hostbyname(hname)) == 0) 
	      {
		DEBUG(2,(": unknown name sending no reply\n"));
		return;
	      }
	    
	    putip((char *)&retip,(char *)hp->h_addr);
	    
	    if (broadcast && is_mynet(retip)) {
	      DEBUG(2,(" on same subnet (%s), no reply\n",inet_ntoa(retip)));
	      return;
	    }
	    i = find_name(qname,name_type,True);
	    if (i < 0) {
	      if ((i=add_name())>=0) {
		StrnCpy(names[i].name,qname,15);
		names[i].type = name_type;
		names[i].unicast = is_mynet(retip);
		names[i].ip = retip;
		names[i].valid = True;
		names[i].ttl = 120; /* give it two minutes */
		names[i].start_time = time(NULL);
		names[i].source = DNS;		
	      }
	    }
	  }
	DEBUG(2,(" sending positive reply (%s)\n",inet_ntoa(retip)));
      }
  } else {
    retip = myip;
  }
  
  /* Send a POSITIVE NAME QUERY RESPONSE */
  RSSVAL(outbuf,0,rec_name_trn_id);
  CVAL(outbuf,2) = (1<<7) | 0x5;
  CVAL(outbuf,3) = 0;
  RSSVAL(outbuf,4,0);
  RSSVAL(outbuf,6,1);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,0);  
  p = outbuf+12;
  strcpy(p,inbuf+12);
  p += name_len(p);
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  RSIVAL(p,4,0); /* my ttl ? */
  RSSVAL(p,8,6);
  CVAL(p,10) = nb_flags;
  CVAL(p,11) = 0;
  p += 12;
  putip(p,(char *)&retip);
  p += 4;

  show_nmb(outbuf);

  tmpip = lastip;
  num_good_sends++;
  send_nmb(outbuf,nmb_len(outbuf),&tmpip,lastport>0?lastport:137);

  return;
}



/****************************************************************************
  construct a reply to the incoming packet
****************************************************************************/
void construct_reply(char *inbuf)
{
  int opcode = CVAL(inbuf,2) >> 3;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  int rcode = CVAL(inbuf,3) & 0xF;
  int bcast = (nm_flags&1);
  int response = (CVAL(inbuf,2)>>7);
  int qdcount = RSVAL(inbuf,4);
  int arcount = RSVAL(inbuf,10);

  /* if this is a response then ignore it */
  if (response || rcode) return;
  
  if (!bcast && opcode == 0x5 && qdcount==1 && arcount==1)
    {
      reply_name_reg(inbuf);
      return;
    }

  if (opcode == 0 && qdcount==1)
    {
      reply_name_query(inbuf);
      return;
    }

  if (!bcast && opcode == 6 && qdcount==1 && arcount==1)
    {
      reply_name_release(inbuf);
      return;
    }

  if (!bcast && ((opcode == 8) || (opcode == 9)) && qdcount==1 && arcount==1)
    {
      reply_name_reg(inbuf);
      return;
    }
}


/****************************************************************************
construct a host announcement unicast
****************************************************************************/
static BOOL send_udp_138(char *buf,int len,
			 char *srcname,char *dstname,
			 int src_type,int dest_type,
			 struct in_addr dest_ip)
{
  pstring outbuf;
  char *p,*p2;
  static int id=0;
  char tmp[4];
  struct sockaddr_in sock_out;

  bzero(outbuf,sizeof(outbuf));
  RSSVAL(outbuf,0,0x1102); /* what is this? */
  RSSVAL(outbuf,2,++id); 
  putip(outbuf+4,(void *)&myip);
  RSSVAL(outbuf,8,138);
  RSSVAL(outbuf,12,0);
  p = outbuf + 14;
  p += name_mangle(srcname,p,src_type);
  p += name_mangle(dstname,p,dest_type);

  /* now setup the smb part */
  p -= 4;
  memcpy(tmp,p,4);
  set_message(p,17,17 + len,True);
  memcpy(p,tmp,4);

  CVAL(p,smb_com) = SMBtrans;
  SSVAL(p,smb_vwv1,len);
  SSVAL(p,smb_vwv11,len);
  SSVAL(p,smb_vwv12,86);
  SSVAL(p,smb_vwv13,3);
  SSVAL(p,smb_vwv14,1);
  SSVAL(p,smb_vwv15,1);
  SSVAL(p,smb_vwv16,2);
  p2 = smb_buf(p);
  strcpy(p2,"\\MAILSLOT\\BROWSE");
  p2 = skip_string(p2,1);

  memcpy(p2,buf,len);
  p2 += len;

  len = PTR_DIFF(p2,outbuf);
  RSSVAL(outbuf,10,len);


  /* set the address and port */
  bzero((char *)&sock_out,sizeof(sock_out));
  putip((char *)&sock_out.sin_addr,(char *)&dest_ip);
  sock_out.sin_port = htons(138);
  sock_out.sin_family = AF_INET;
  
  /* log the packet */
  log_out(outbuf,len);
  
  /* send it */
  if (sendto(Client138,outbuf,len,0,
	     (struct sockaddr *)&sock_out,sizeof(sock_out)) < 0) {
    DEBUG(3,("Sendto failed errno=%d (%s)\n",errno,strerror(errno)));
    return(False);
  } else {
    num_good_sends++;
  }
  return(True);
}

/****************************************************************************
construct a host announcement unicast
****************************************************************************/
static void announce_host(int i,char *my_name,char *Comment)
{
  static int announce_interval = 1;
  char *group = names[i].name;
  struct in_addr dest_ip = names[i].ip;
  pstring outbuf;
  char *p;

  names[i].count++;

  if ((names[i].count % announce_interval) != 0) return;

  if (announce_interval < 2) announce_interval++;

  DEBUG(2,("Sending host announcement to %s for group %s\n",
	   inet_ntoa(dest_ip),group));	   

  if (!*Comment) Comment = "NoComment";
  if (!*my_name) my_name = "NoName";
  if (!*group) group = "NoGroup";

  if (strlen(Comment) > 47) Comment[47] = 0;  

  bzero(outbuf,sizeof(outbuf));
  p = outbuf;
  CVAL(p,0) = 1; /* host announce */
  SSVAL(p,1,0x6006); /* update count?? */
  CVAL(p,3) = 0xEA; /* res1 */ 
  SSVAL(p,4,announce_interval);
  p += 6;
  StrnCpy(p,my_name,16);
  strupper(p);
  p += 16;
  CVAL(p,0) = 1; /* major version (was 1) */
  CVAL(p,1) = 0x33; /* minor version (was 51) */
  SIVAL(p,2,0xB03); /* server and w'station + unix + printq + domain member*/
  SSVAL(p,6,0x30B); /* election version */
  SSVAL(p,8,0xAA55); /* browse constant */
  p += 10;
  strcpy(p,Comment);
  p += strlen(p)+1;

  send_udp_138(outbuf,PTR_DIFF(p,outbuf),my_name,group,0,0x1d,dest_ip);
}



/****************************************************************************
send a name registration packet
****************************************************************************/
static void send_registration(char *name,int name_type,struct in_addr dest_ip,struct in_addr ip,BOOL refresh,int ttl)
{
  char *p;
  static int t = 0x176;
  pstring outbuf;
  bzero(outbuf,sizeof(outbuf));
  /* send a registration request */
  RSSVAL(outbuf,0,t++);
  if (refresh)
    CVAL(outbuf,2) = (0<<7) | (9<<3) | 0;
  else
    CVAL(outbuf,2) = (0<<7) | (5<<3) | 1;
    
  CVAL(outbuf,3) = (1<<4);
  RSSVAL(outbuf,4,1);
  RSSVAL(outbuf,6,0);
  RSSVAL(outbuf,8,0);
  RSSVAL(outbuf,10,1);  
  p = outbuf+12;
  name_mangle(name,p,name_type);
  p += name_len(p);
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  p += 4;
  RSSVAL(p,0,12);
  CVAL(p,0) = CVAL(p,0) | 0xC0;
  p += 2;
  RSSVAL(p,0,0x20);
  RSSVAL(p,2,0x1);
  p += 4;  
  RSIVAL(p,0,ttl);

  RSSVAL(p,4,6);
  RSSVAL(p,6,0);  
  p += 8;
  putip(p,&ip);  
  p += 4;

  DEBUG(2,("Sending a name registration request\n"));
  if (DEBUGLEVEL > 2)
    show_nmb(outbuf);

  num_good_sends++;
  send_nmb(outbuf,PTR_DIFF(p,outbuf),&dest_ip,137);
}

/*******************************************************************
find a master browser
********************************************************************/
BOOL find_master(char *name1,struct in_addr ip,struct in_addr *ipout,
		 void (*fn)())
{
  int type = 0x1d;
  fstring name;
  BOOL ret;
  strcpy(name,name1);
  if (strequal(name,"*")) {
    strcpy(name,"\001\002__MSBROWSE__\002");
    type = 1;
  }
  ret = name_query(name,type,True,ip,ipout,fn);
  if (!ret) return(False);
  if (type != 1) return(True);

  name_status(name,type,*ipout,fn,name1);
  return(name1[0] != '*');
}

/****************************************************************************
process a workgroup announce frame
****************************************************************************/
static void process_workgroup_announce(char *group,struct in_addr ip)
{
  int i;
  for (i=0;i<num_names;i++)
    if (names[i].valid) {
      if (names[i].name[0] == '*') {
	StrnCpy(names[i].name,group,15);
	names[i].ip = names[i].master_ip = ip;
	names[i].found_master = True;
	names[i].count=0;
	announce_host(i,myname,comment);
	return;
      }
      if (names[i].isgroup && name_equal(names[i].name,group,0,0)) {
	int j;
	for (j=i;j<num_names;j++)
	  if (names[j].valid && names[j].isgroup && names[i].found_master && 
	      name_equal(names[i].name,group,0,0)) return;
	names[i].ip = names[i].master_ip = ip;
	names[i].found_master = True;
	names[i].count=0;
	announce_host(i,myname,comment);
	return;
      }
    }
}


/****************************************************************************
process a browse frame
****************************************************************************/
static void process_browse_packet(char *buf,int len)
{
  char *p;
  int command = CVAL(buf,0);
  switch (command) {
  case 0xc: /* workgroup announcement */
    {
      fstring group;
      p = buf + 6;
      StrnCpy(group,p,15);
      DEBUG(2,("Got workgroup announce for %s (%s)\n",
	       group,inet_ntoa(lastip)));
      process_workgroup_announce(group,lastip);
      break;
    }
  }

}

/****************************************************************************
process udp 138 datagrams
****************************************************************************/
static void process_138(void)
{
  pstring inbuf;
  int len;
  while (read_max_udp(Client138,inbuf,sizeof(inbuf),1) > 4) {
    char *p = inbuf;
    len = RSVAL(inbuf,10);
    p += 14;
    p += name_len(p);
    p += name_len(p);
    p -= 4;
    if (CVAL(p,smb_com) != SMBtrans) continue;
    if (!strequal(smb_buf(p),"\\MAILSLOT\\BROWSE")) continue;
    len = SVAL(p,smb_vwv11);
    p = smb_base(p) + SVAL(p,smb_vwv12);
    if (len <= 0) continue;
    process_browse_packet(p,len);
  }
}

/****************************************************************************
a hook for registration of my own names
****************************************************************************/
static void do_registration_hook(void)
{
  static int count = 0;
  static time_t last_t=0;
  time_t t = time(NULL);
  
  if (last_t && (t-last_t)<REGISTRATION_INTERVAL) return;
  last_t = t;
  
  send_registration(myname,0x20,bcast_ip,myip,count>0,300000);
  count++;
}

/****************************************************************************
a hook for browsing handling - called every BROWSE_INTERVAL secs
****************************************************************************/
static void do_browse_hook(void)
{
  static BOOL first = True;
  int i;
  static time_t last_t=0;
  time_t t = time(NULL);
  
  if (last_t && (t-last_t)<browse_interval) return;
  last_t = t;

  for (i=0;i<num_names;i++)
    {
      BOOL old_found_master = names[i].found_master;

      if (!NAMEVALID(i) || !ISGROUP(i)) continue;

      if (names[i].found_master) {
	struct in_addr ip2;
	announce_host(i,myname,comment);

	if (!name_query(names[i].name,0x1d,True,
			names[i].master_ip,
			&ip2,construct_reply)) {
	  DEBUG(2,("%s Master browser at %s failed to respond\n",
		   timestring(),
		   inet_ntoa(names[i].master_ip)));
	  names[i].found_master = False;
	} else {
	  names[i].master_ip = ip2;
	}
      }

      if (!names[i].found_master) {
	struct in_addr ip2;
	names[i].found_master = find_master(names[i].name,names[i].ip,&ip2,
					    construct_reply);

	if (names[i].found_master) {
	  names[i].master_ip = ip2;
	  DEBUG(1,("%s New master browser for %s at %s\n",
		   timestring(),
		   names[i].name,inet_ntoa(names[i].master_ip)));
	  names[i].count = 0;
	  announce_host(i,myname,comment);
	}
      }

      if (!names[i].found_master) {
	int level = (old_found_master||first)?1:2;
	DEBUG(level,("%s Failed to find a master browser for %s using %s\n",
		     timestring(),
		     names[i].name,inet_ntoa(names[i].ip)));
      }
    }
  first = False;
}


/****************************************************************************
  process commands from the client
****************************************************************************/
void process(void)
{
  pstring inbuf;
  static int trans_num = 0;
  int nread;

  bzero(inbuf,sizeof(inbuf));

  while (True)
    {
      if (!browse)
	{
	  if (!(nread = receive_nmb(inbuf,is_daemon?0:idle_timeout)))
	    {
	      if (is_daemon) continue;
	      return;
	    }
	}
      else
	{
	  fd_set fds;
	  int selrtn;
	  struct timeval timeout;

	  do_registration_hook();

	  do_browse_hook();

	  process_138();

	  FD_ZERO(&fds);
	  FD_SET(Client,&fds);

	  timeout.tv_sec = NMBD_SELECT_LOOP;
	  timeout.tv_usec = 0;

	  while (1)
	    {
	      selrtn = select(255,SELECT_CAST &fds,NULL,NULL,&timeout);
	      if (!(selrtn < 0 && errno == EINTR)) break;
	    }

	  if (!FD_ISSET(Client,&fds))
	    continue;

	  nread = read_udp_socket(Client, inbuf, sizeof(pstring));
	  if (nread <= 0)
	    continue;
	  num_good_receives++;
	}

      if (nread < 4)
	continue;

      if (DEBUGLEVEL > 2)
	show_nmb(inbuf);

      DEBUG(2,("%s Transaction %d\n",timestring(),trans_num));

      construct_reply(inbuf);

      trans_num++;
    }
}


/****************************************************************************
  open the socket communication
****************************************************************************/
static BOOL open_sockets(BOOL isdaemon,int port)
{
  struct hostent *hp;
  
  /* get host info */
  if ((hp = Get_Hostbyname(myhostname)) == 0) 
    {
      DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
      return False;
    }

  if (isdaemon)
    Client = open_socket_in(SOCK_DGRAM, port,*lookup?3:0);
  else
    Client = 0;

  Client138 = open_socket_in(SOCK_DGRAM,138,3);

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

  signal(SIGPIPE, SIGNAL_CAST sig_pipe);

  set_socket_options(Client,"SO_BROADCAST");
  set_socket_options(Client138,"SO_BROADCAST");
  set_socket_options(Client,user_socket_options);

  return True;
}


/****************************************************************************
  initialise connect, service and file structs
****************************************************************************/
static BOOL init_structs(void )
{
  if (!get_myname(myhostname,got_myip?NULL:&myip))
    return(False);

  /* Read the broadcast address from the interface */
  {
    struct in_addr ip0,ip1,ip2;

    ip0 = myip;

    if (!(got_bcast && got_nmask))
      {
	get_broadcast(&ip0,&ip1,&ip2);

	if (!got_myip)
	  myip = ip0;
    
	if (!got_bcast)
	  bcast_ip = ip1;
    
	if (!got_nmask)
	  Netmask = ip2;   
      } 

    DEBUG(1,("Using IP %s  ",inet_ntoa(myip)));
    DEBUG(1,("broadcast %s  ",inet_ntoa(bcast_ip)));
    DEBUG(1,("netmask %s\n",inet_ntoa(Netmask)));    

  }

  if (! *myname) {
    char *p;
    strcpy(myname,myhostname);
    p = strchr(myname,'.');
    if (p) *p = 0;
  }

  add_host_name(myname,0x20,&myip);
  add_host_name(myname,0x0,&myip);
  add_host_name(myname,0x3,&myip);

  return True;
}

/****************************************************************************
usage on the program
****************************************************************************/
static void usage(char *pname)
{
  DEBUG(0,("Incorrect program usage - is the command line correct?\n"));

  printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
  printf("Version %s\n",VERSION);
  printf("\t-D                    become a daemon\n");
  printf("\t-P                    passive only. don't respond\n");
  printf("\t-R                    only reply to queries, don't actively send claims\n");
  printf("\t-p port               listen on the specified port\n");
  printf("\t-d debuglevel         set the debuglevel\n");
  printf("\t-l log basename.      Basename for log/debug files\n");
  printf("\t-n netbiosname.       the netbios name to advertise for this host\n");
  printf("\t-B broadcast address  the address to use for broadcasts\n");
  printf("\t-N netmask           the netmask to use for subnet determination\n");
  printf("\t-L name              lookup this netbios name then exit\n");
  printf("\t-S                   serve queries via DNS if not on the same subnet\n");
  printf("\t-H hosts file        load a netbios hosts file\n");
  printf("\t-G group name        add a group name to be part of\n");
  printf("\t-b                   toggles browsing support (defaults to on)\n");
  printf("\t-M group name        searches for a master browser for the given group\n");
  printf("\t-T interval          sets the browse announcement interval in seconds\n");
  printf("\t-C comment           sets the machine comment that appears in browse lists\n");
  printf("\n");
}


/****************************************************************************
  main program
****************************************************************************/
int main(int argc,char *argv[])
{
  int port = 137;
  int opt;
  unsigned int lookup_type = 0;
  extern FILE *dbf;
  extern char *optarg;

  *lookup = *host_file = 0;

  charset_initialise();

  strcpy(debugf,NMBLOGFILE);

#ifdef LMHOSTSFILE
  strcpy(host_file,LMHOSTSFILE);
#endif

  /* this is for people who can't start the program correctly */
  while (argc > 1 && (*argv[1] != '-'))
    {
      argv++;
      argc--;
    }

  fault_setup(fault_continue);

  signal(SIGHUP,SIGNAL_CAST sig_hup);


  while ((opt = getopt (argc, argv, "T:O:M:I:C:bAL:i:B:N:Rn:l:d:Dp:hPSH:G:")) != EOF)
    switch (opt)
      {
      case 'T':
	browse_interval = atoi(optarg);
	browse_interval = MAX(browse_interval,10);
	break;
      case 'O':
	strcpy(user_socket_options,optarg);
	break;
      case 'C':
	strcpy(comment,optarg);
	break;
      case 'G':
	add_group_name(optarg);
	break;
      case 'b':
	browse = !browse;
	break;
      case 'A':
	dns_serve = True;
	break;
      case 'H':
	strcpy(host_file,optarg);
	break;
      case 'I':
	{
	  unsigned long a = interpret_addr(optarg);
	  putip((char *)&myip,(char *)&a);
	  got_myip = True;
	}
	break;
      case 'B':
	{
	  unsigned long a = interpret_addr(optarg);
	  putip((char *)&bcast_ip,(char *)&a);
	  got_bcast = True;
	}
	break;
      case 'N':
	{
	  unsigned long a = interpret_addr(optarg);
	  putip((char *)&Netmask,(char *)&a);
	  got_nmask = True;
	}
	break;
      case 'n':
	strcpy(myname,optarg);
	break;
      case 'P':
	{
	  extern BOOL passive;
	  passive = True;
	}
	break;
      case 'S':
	dns_serve = !dns_serve;
	break;
      case 'l':
	sprintf(debugf,"%s.nmb",optarg);
	break;
      case 'i':
	strcpy(scope,optarg);
	strupper(scope);
	break;
      case 'L':
	strcpy(lookup,optarg);
	break;
      case 'M':
	if (*optarg == '-') {
	  strcpy(lookup,"\01\02__MSBROWSE__\02");
	  lookup_type = 1;
	} else {
	  strcpy(lookup,optarg);
	  lookup_type = 0x1d;
	}
	break;
      case 'D':
	is_daemon = True;
	break;
      case 'd':
	DEBUGLEVEL = atoi(optarg);
	break;
      case 'p':
	port = atoi(optarg);
	break;
      case 'h':
	usage(argv[0]);
	exit(0);
	break;
      default:
	usage(argv[0]);
	exit(1);
      }

  
  if (*lookup)
    DEBUGLEVEL++;
  
  if (DEBUGLEVEL > 10)
    {
      extern FILE *login,*logout;
      pstring fname;
      sprintf(fname,"%s.in",debugf);
      login = fopen(fname,"w"); 
      sprintf(fname,"%s.out",debugf);
      logout = fopen(fname,"w");
    }
  
  if (*lookup)
    {
      if (dbf)
	fclose(dbf);
      dbf = stdout;
    }

  DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION));
  DEBUG(1,("Copyright Andrew Tridgell 1994\n"));

  if (*host_file)
    {
      load_hosts_file(host_file);
      DEBUG(3,("Loaded hosts file\n"));
    }

  init_structs();

  if (*lookup) {
    BOOL bcast = True;
    int retries = 2;
    char *p = strchr(lookup,'#');
    struct in_addr ip;
    if (p) {
      *p = 0;
      sscanf(p+1,"%x",&lookup_type);
      bcast = False;
      retries = 1;
    }
    if (!open_sockets(True,port)) return(1);
    while (retries--)
      if (name_query(lookup,lookup_type,bcast,bcast_ip,&ip,NULL)) {
	printf("%s %s\n",inet_ntoa(ip),lookup);
	name_status(lookup,lookup_type,ip,NULL,NULL);
	return(0);
      } 
    printf("couldn't find name %s\n",lookup);
    return(0);
  }

  if (!*comment)
    strcpy(comment,"Samba %v");
  string_sub(comment,"%v",VERSION);
  string_sub(comment,"%h",myhostname);

  check_names();

  DEBUG(3,("Checked names\n"));
  
  dump_names();

  DEBUG(3,("Dumped names\n"));

  if (!is_daemon && !is_a_socket(0)) {
    DEBUG(0,("standard input is not a socket, assuming -D option\n"));
    is_daemon = True;
  }
  
  if (is_daemon) {
    DEBUG(2,("%s becoming a daemon\n",timestring()));
    become_daemon();
  }

  if (open_sockets(is_daemon,port))
    {
      process();
      close_sockets();
    }

  if (dbf)
    fclose(dbf);
  return(0);
}
