/***********************************************************
        Copyright 1996 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include "defs.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/route.h>
#include <netdb.h>
#include <errno.h>


#include "defaults.h"
#include "bootp.h"
#include "bootpd.h"
#include "dhcpd.h"
#include "dhcp.h"
#include "perm.h"
#include "hash.h"
#include "utils.h"
#include "my_syslog.h"


/* Given a struct lastbind *p, and a buffer 'buf' of length 'len', construct a flat lastbind array in 'buf'.
   Caller is required to provide a large enough buffer.
   Returns buffer length actually used.
   XXX No check is made to ensure that buf is large enough.  To be safe, allocate a buffer of LASTBIND_FLAT_MAXLEN bytes.
   XXX Apparently is required to always succeed; there is no error return value.
*/
int bind2data(p,buf,len)
struct lastbind *p;
unsigned char *buf;
int len;
{
		int datalen;
		datalen=0;

		/* XXX we assume p->cliid->length is small enough to store in an unsigned char.  This should
		   always be true since we constructed the former that way, but this fails if the data (e.g.
		   retrieved from disk) has been corrupted. 
		*/
		buf[datalen++]=p->cliid->length; 
		bcopy(p->cliid->data,&buf[datalen], p->cliid->length);
		datalen+=p->cliid->length;

		/* XXX we assume p->cliidhw->length is small enough to store in an unsigned char.  This should
		   always be true since we constructed the former that way, but this fails if the data (e.g.
		   retrieved from disk) has been corrupted. 
		*/
		buf[datalen++]=p->cliidhw->length;
		bcopy(p->cliidhw->data,&buf[datalen], p->cliidhw->length);
		datalen+=p->cliidhw->length;

		bcopy(&(p->last_ip.s_addr),&buf[datalen],4); /* XXX hardcoded sizeof(unsigned long) */
		datalen+=4;

		bcopy(&p->replys,&buf[datalen],4); /* XXX hardcoded sizeof(unsigned long) */
		datalen+=4;

		bcopy(&p->last_touch,&buf[datalen],4); /* XXX hardcoded sizeof(unsigned long) */
		datalen+=4;

		bcopy(&p->expire,&buf[datalen],4); /* XXX hardcoded sizeof(unsigned long) */
		datalen+=4;

		buf[datalen++]=p->code;

		if(p->vend){
			/* XXX we assume p->vend->length is small enough to store in an unsigned char.  This should
		   	always be true since we constructed the former that way, but this fails if the data (e.g.
		   	retrieved from disk) has been corrupted. 
			*/
			buf[datalen++]=p->vend->length;
			bcopy(p->vend->data,&buf[datalen], p->vend->length);
			datalen+=p->vend->length;
		} else
			buf[datalen++]=0;

		if(p->user){
			/* XXX we assume p->user->length is small enough to store in an unsigned char.  This should
		   	always be true since we constructed the former that way, but this fails if the data (e.g.
		   	retrieved from disk) has been corrupted. 
			*/
			buf[datalen++]=p->user->length;
			bcopy(p->user->data,&buf[datalen], p->user->length);
			datalen+=p->user->length;
		} else
			buf[datalen++]=0;

		return datalen;
}
	

/* Given a flat lastbind array 'data' of length 'len' bytes, construct and return a new struct lastbind *.
   Its linkcount starts at 0.
   Return NULL on error.
   Note Well: Frees 'data' on success. 
*/
struct lastbind *data2bind(data,len)
hash_datum *data;
int len;
{
 struct lastbind *p;
 /* char *cp; */
 unsigned char *cp;

 if(!data)
	return NULL;
 cp=data;

 /* Walk data to check that the lengths of its components are legal.
	This first time, we just verify that the embedded 'length' fields
	don't indicate there's more data than 'len' (so we won't try to walk
	off the end of 'data'.
	Any failure here indicates that the data we've been handed is corrupt.
 */

 /* the struct shared_bindata *cliid */
 len-=1+*cp;
 if(len<0) 
	return NULL;
 cp+=1+*cp;

 /* the struct shared_bindata *cliidhw */
 len-=1+*cp;
 if(len<0) 
	return NULL;
 cp+=1+*cp;

 /* the struct in_addr last_ip; unsigned long replys, last_touch, expire; unsigned char code */
 /* XXX hardcoded length? */
 len-=17;
 if(len<0)
	return NULL;
 cp+=17;

 /* the struct shared_bindata *vend */
 len-=1+*cp;
 if(len<0) 
	return NULL;
 cp+=1+*cp;

 /* the struct shared_bindata *user */
 len-=1+*cp;
 if(len<0) 
	return NULL;

 /* note clhashcode is not part of data, it is computed anew */
 /* note linkcount is not part of data, it is computed anew */

 /* we've finished walking the data to check that the lengths of its components would not force
	us to go beyond the end of the the buffer.
 */

 /* walk it again, this time constructing the struct lastbind */

 cp=data;
 p=(struct lastbind*)smalloc(sizeof(struct lastbind));
 /* bzero(p,sizeof(struct lastbind)); */ /* now is done by smalloc */

 p->cliid=(struct shared_bindata*)smalloc(sizeof(struct shared_bindata)+*cp-1);
 p->cliid->linkcount=1;
 p->cliid->length=*cp; /* XXX  p->cliid->length is safely < UINT_MAX as long as sizeof(char)==1 */
 bcopy(++cp,p->cliid->data,p->cliid->length);
 cp+=p->cliid->length;

 /* clhashcode is not part of data, we intead compute it */
 p->clhashcode=hash_HashFunction(p->cliid->data,p->cliid->length);

 p->cliidhw=(struct shared_bindata*)smalloc(sizeof(struct shared_bindata)+*cp-1);
 p->cliidhw->linkcount=1;
 p->cliidhw->length=*cp; /* XXX p->cliidhw->length is safely < UINT_MAX as long as sizeof(char)==1 */
 bcopy(++cp,p->cliidhw->data,p->cliidhw->length);
 cp+=p->cliidhw->length;

 bcopy(cp,&(p->last_ip.s_addr),4); /* XXX hardcoded sizeof(struct inaddr) */
 cp+=4;

 bcopy(cp,&p->replys,4); /* XXX hardcoded sizeof(unsigned long) */
 cp+=4;

 bcopy(cp,&p->last_touch,4); /* XXX hardcoded sizeof(unsigned long) */
 cp+=4;

 bcopy(cp,&p->expire,4); /* XXX hardcoded sizeof(unsigned long) */
 cp+=4;

 p->code=*cp++;

 if(*cp){ /* length of vend field is non-zero */
     p->vend=(struct shared_bindata*)smalloc(sizeof(struct shared_bindata)+*cp-1);
     p->vend->length=*cp; /* XXX p->vend->length is safely < UINT_MAX as long as sizeof(char)==1 */
     bcopy(++cp,p->vend->data,p->vend->length);
     p->vend->linkcount=1;
     cp+=p->vend->length;
 } else
     cp++;

 if(*cp){ /* length of user field is non-zero */
     p->user=(struct shared_bindata*)smalloc(sizeof(struct shared_bindata)+*cp-1);
     p->user->length=*cp; /* XXX p->user->length is safely < UINT_MAX as long as sizeof(char)==1 */
     bcopy(++cp,p->user->data,p->user->length);
     p->user->linkcount=1;
     cp+=p->user->length;
 }
 free(data);

 return p;
}

