/***********************************************************
        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 <sys/stat.h>	/* for stat, open */
#include <stdio.h> /* for fdopen */

#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for setuid/seteuid/setgid/setegid, unlink */
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h> /* for open */
#else
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h> /* for open, O_ constants defined here on BSD systems */
#endif
#endif


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

/* Insert 1-byte 'tag' and 1-byte 'value' into the packet represented by 'bp' and 'opt'.
   We try to put it in 'opt' if it fits, else overload it into an unused 'bp' header field.
*/
int
insert_byte_opt(bp, opt, tag, value)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
unsigned char value;
{
	int i;
	unsigned char *cp;

	cp = (unsigned char *) bp; /* start of packet */

	/* opt->cur is the offset (from the start of the reply packet) where we should insert the option.
	   opt->len is the length of the reply packet.
	*/

	if (opt->cur == 0)
		/* we've not inserted any options previously, so we begin at the start of the "vendor options" field */
		opt->cur = VEND_OFFSET;
	
	if (opt->cur + 7 > opt->len) {
		/* We won't have enough room in the vendor_options field if opt->cur + num_bytes_to_add > opt->len.
		   We'd like to insert a 3-byte option (tag, len, value) and still have room afterwards.
		   But we need to also save some space after it, to ensure that we could insert a TAG_DHCP_OVL (3 bytes)
		   and a TAG_END (1 byte), in case there are more options to follow.  
		   So we actually check for 7 bytes, not 3.
		*/
		/* We're in the vendor_options field, and don't have enough room left.
		   (We don't need to actually verify that we're in the vendor_options field instead of sname or fname,
		   because we know the vendor_options is much longer than 7 bytes; the test above couldn't have succeeded
		   if we'd been in one of the other fields (which preceed the vendor_options field).
		*/
		/* Add a TAG_DHCP_OVL and TAG_END to the vendor options field, and reset our current insert position to the
		   start of the fname field.  (We will overload the fname field.)
		   XXX Shouldn't we verify first that the fname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use fname field.
		*/
		cp[opt->cur++] = TAG_DHCP_OVL;
		cp[opt->cur++] = 1;
		cp[opt->cur++] = 1; /* file overload */
		cp[opt->cur] = TAG_END;
		opt->cur = FILE_OFFSET;

	} else if ((opt->cur < SNAME_OFFSET) && (opt->cur + 7 > SNAME_OFFSET)) {
		/* We're in the fname field, but don't have enough room left.
		   We want space for our 3-byte option, plus enough space left to add a TAG_END.
		   XXX I'm not sure why we are checking for 7 bytes instead of 4.  (We don't need to have
		   space for a TAG_DHCP_OVL in this case.)
		*/
		cp[opt->cur] = TAG_END; /* We must indicate the end of the options already in the fname field */
		/* Hunt for the TAG_DHCP_OVL in the vendor_options field.
		   We know it must be there, given we're already overlaying the fname field.
		   Change it's value to say that we're overlaying BOTH fname and sname fields.
		   Then reset our current insert position to the start of the sname field.
		   XXX Shouldn't we first verify that the sname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use sname field.
		*/
		for (i=4; (u_long) bp->bp_vend[i] != TAG_DHCP_OVL; i++)
			;
		bp->bp_vend[i+2] = 3;
		opt->cur = SNAME_OFFSET;

	} else if ((opt->cur < VEND_OFFSET) && (opt->cur + 7 > VEND_OFFSET))
		/* We're in the sname field, and don't have enough room left.
		   We want enough room for our 3-byte option, plus enough space to add a TAG_END.
		   XXX I'm not sure why we are checking for 7 bytes instead of 4.
		*/
		return -1;

	/* At this point, we know there is enough room to insert our option at opt->cur */
	cp[opt->cur++]=tag;
	cp[opt->cur++]=1;
	cp[opt->cur++]=value;

	return 0;
}

/* Insert 1-byte 'tag' and 4-byte 'value' into the packet represented by 'bp' and 'opt'.
   We try to put it in 'opt' if it fits, else overload it into an unused 'bp' header field.
   Note that 'value' is copied in byte-for-byte; if any network-byte ordering conversion needs
   to be done, it must be done before calling us.
*/
int
insert_ulong_opt(bp, opt, tag, value)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
unsigned long value;
{
	int i;
	unsigned char *cp; 
	
	cp = (u_char*) bp; /* start of packet */

	/* opt->cur is the offset (from the start of the reply packet) where we should insert the option.
	   opt->len is the length of the reply packet.
	*/

	if (opt->cur == 0)
		/* we've not inserted any options previously, so we begin at the start of the "vendor options" field */
		opt->cur = VEND_OFFSET;
	
	if (opt->cur + 11 > opt->len) {
		/* We won't have enough room in the vendor_options field if opt->cur + num_bytes_to_add > opt->len.
		   We'd like to insert a 6-byte option (tag, len, value) and still have room afterwards.
		   But we need to also save some space after it, to ensure that we could insert a TAG_DHCP_OVL (3 bytes)
		   and a TAG_END (1 byte), in case there are more options to follow.  
		   So we should actually check for 10 bytes, not 4.
		   XXX I'm not sure why we check for 11 instead of 10.
		*/
		/* We're in the vendor_options field, and don't have enough room left.
		   (We don't need to actually verify that we're in the vendor_options field instead of sname or fname,
		   because we know the vendor_options is much longer than 10 (or 11) bytes; the test above couldn't have succeeded
		   if we'd been in one of the other fields (which preceed the vendor_options field).
		*/
		/* Add a TAG_DHCP_OVL and TAG_END to the vendor options field, and reset our current insert position to the
		   start of the fname field.  (We will overload the fname field.)
		   XXX Shouldn't we verify first that the fname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use fname field.
		*/
		cp[opt->cur++] = TAG_DHCP_OVL;
		cp[opt->cur++] = 1;
		cp[opt->cur++] = 1; /* file overload */
		cp[opt->cur] = TAG_END;
		opt->cur = FILE_OFFSET;

	} else if ((opt->cur + 11 > SNAME_OFFSET) && (opt->cur < SNAME_OFFSET)) {
		/* We're in the fname field, but don't have enough room left.
		   We want space for our 6-byte option, plus enough space left to add a TAG_END.
		   XXX I'm not sure why we are checking for 11 bytes instead of 7.  (We don't need to have
		   space for a TAG_DHCP_OVL in this case.)
		*/
		cp[opt->cur] = TAG_END;
		/* Hunt for the TAG_DHCP_OVL in the vendor_options field.
		   We know it must be there, given we're already overlaying the fname field.
		   Change it's value to say that we're overlaying BOTH fname and sname fields.
		   Then reset our current insert position to the start of the sname field.
		   XXX Shouldn't we first verify that the sname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use sname field.
		*/
		for (i=4; (u_long) bp->bp_vend[i] != TAG_DHCP_OVL; i++)
			;
		bp->bp_vend[i+2] = 3;
		opt->cur = SNAME_OFFSET;
	} else if ((opt->cur < VEND_OFFSET) && (opt->cur + 11 > VEND_OFFSET))
		/* We're in the sname field, and don't have enough room left.
		   We want enough room for our 6-byte option, plus enough space to add a TAG_END.
		   XXX I'm not sure why we are checking for 11 bytes instead of 7.
		*/
		return -1;

	/* At this point, we know there is enough room to insert our option at opt->cur */
	cp[opt->cur++] = tag;
	cp[opt->cur++] = 4;
	bcopy(&value, cp+opt->cur, 4);
	opt->cur += 4;

	return 0;
}


/* Insert 1-byte 'tag' and 2-byte 'value' into the packet represented by 'bp' and 'opt'.
   We try to put it in 'opt' if it fits, else overload it into an unused 'bp' header field.
   Note that 'value' is copied in byte-for-byte; if any network-byte ordering conversion needs
   to be done, it must be done before calling us.
*/
int
insert_ushort_opt(bp, opt, tag, value)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
unsigned short value;
{
	int i;
	unsigned char *cp;

	cp = (u_char*) bp; /* start of packet */

	/* opt->cur is the offset (from the start of the reply packet) where we should insert the option.
	   opt->len is the length of the reply packet.
	*/

	if (opt->cur == 0)
		/* we've not inserted any options previously, so we begin at the start of the "vendor options" field */
		opt->cur = VEND_OFFSET;
	
	if (opt->cur + 9 > opt->len) {
		/* We won't have enough room in the vendor_options field if opt->cur + num_bytes_to_add > opt->len.
		   We'd like to insert a 4-byte option (tag, len, value) and still have room afterwards.
		   But we need to also save some space after it, to ensure that we could insert a TAG_DHCP_OVL (3 bytes)
		   and a TAG_END (1 byte), in case there are more options to follow.  
		   So we should actually check for 8 bytes, not 4.
		   XXX I'm not sure why we check for 9 instead of 8.
		*/
		/* We're in the vendor_options field, and don't have enough room left.
		   (We don't need to actually verify that we're in the vendor_options field instead of sname or fname,
		   because we know the vendor_options is much longer than 8 (or 9) bytes; the test above couldn't have succeeded
		   if we'd been in one of the other fields (which preceed the vendor_options field).
		*/
		/* Add a TAG_DHCP_OVL and TAG_END to the vendor options field, and reset our current insert position to the
		   start of the fname field.  (We will overload the fname field.)
		   XXX Shouldn't we verify first that the fname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use fname field.
		*/
		cp[opt->cur++] = TAG_DHCP_OVL;
		cp[opt->cur++] = 1;
		cp[opt->cur++] = 1; /* file overload */
		cp[opt->cur] = TAG_END;
		opt->cur = FILE_OFFSET;

	} else if ((opt->cur + 9 > SNAME_OFFSET) && (opt->cur < SNAME_OFFSET)) {
		/* We're in the fname field, but don't have enough room left.
		   We want space for our 4-byte option, plus enough space left to add a TAG_END.
		   XXX I'm not sure why we are checking for 9 bytes instead of 5.  (We don't need to have
		   space for a TAG_DHCP_OVL in this case.)
		*/
		cp[opt->cur] = TAG_END;
		/* Hunt for the TAG_DHCP_OVL in the vendor_options field.
		   We know it must be there, given we're already overlaying the fname field.
		   Change it's value to say that we're overlaying BOTH fname and sname fields.
		   Then reset our current insert position to the start of the sname field.
		   XXX Shouldn't we first verify that the sname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use sname field.
		*/
		for (i=4; (u_long) bp->bp_vend[i] != TAG_DHCP_OVL; i++)
			;
		bp->bp_vend[i+2] = 3;
		opt->cur = SNAME_OFFSET;

	} else if ((opt->cur < VEND_OFFSET) && (opt->cur + 9 > VEND_OFFSET))
		/* We're in the sname field, and don't have enough room left.
		   We want enough room for our 6-byte option, plus enough space to add a TAG_END.
		   XXX I'm not sure why we are checking for 9 bytes instead of 4.
		*/
		return -1;

	/* At this point, we know there is enough room to insert our option at opt->cur */
	cp[opt->cur++] = tag;
	cp[opt->cur++] = 2;
	bcopy(&value, cp + opt->cur, 2);
	opt->cur += 2;

	return 0;
}


/* Insert 1-byte 'tag' and 'len' in_addrs into the packet represented by 'bp' and 'opt'.
   We try to put it in 'opt' if it fits, else overload it into an unused 'bp' header field.
   Note that 'value' is copied in byte-for-byte; if any network-byte ordering conversion needs
   to be done, it must be done before calling us.
*/
int
insert_addrs_opt(bp, opt, tag, value, len)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
struct in_addr *value;
int len;
{
	int i;
	unsigned char *cp;
	int bytes;

	cp = (u_char*) bp; /* start of packet */

	/* opt->cur is the offset (from the start of the reply packet) where we should insert the option.
	   opt->len is the length of the reply packet.
	*/

	if (opt->cur == 0)
		/* we've not inserted any options previously, so we begin at the start of the "vendor options" field */
		opt->cur = VEND_OFFSET;

	bytes = len * sizeof(struct in_addr); /* how many bytes of in_addrs */

	if ((opt->cur + 6 + bytes) > opt->len) {
		/* We won't have enough room in the vendor_options field if opt->cur + num_bytes_to_add > opt->len.
		   We'd like to insert an option of length '2 + bytes' (tag, len, 'bytes' in_addrs) and still have room afterwards.
		   But we need to also save some space after it, to ensure that we could insert a TAG_DHCP_OVL (3 bytes)
		   and a TAG_END (1 byte), in case there are more options to follow.  
		   So we should actually check for 6+bytes, not 2+bytes.
		*/
		/* We're in the vendor_options field, and don't have enough room left.
		   (We don't actually verify that we're in the vendor_options field instead of sname or fname,
		   because we assume the vendor_options is much longer than 6+bytes bytes; the test above couldn't have succeeded
		   if we'd been in one of the other fields (which preceed the vendor_options field).
		   XXX This assumption is bad; as a sufficiently large number of in_addrs in the option invalidates it.
		*/
		/* Add a TAG_DHCP_OVL and TAG_END to the vendor options field, and reset our current insert position to the
		   start of the fname field.  (We will overload the fname field.)
		   XXX Shouldn't we verify first that the fname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use fname field.
		*/
		cp[opt->cur++] = TAG_DHCP_OVL;
		cp[opt->cur++] = 1;
		cp[opt->cur++] = 1; /* file overload */
		cp[opt->cur] = TAG_END;
		opt->cur = FILE_OFFSET;

	} else if (((opt->cur + 6 + bytes) >SNAME_OFFSET) && (opt->cur < SNAME_OFFSET)) {
		/* We're in the fname field, but don't have enough room left.
		   We want space for our 2+bytes option, plus enough space left to add a TAG_END.
		   XXX I'm not sure why we are checking for 6+bytes instead of 3+bytes.  (We don't need to have
		   space for a TAG_DHCP_OVL in this case.)
		*/
		cp[opt->cur] = TAG_END;
		/* Hunt for the TAG_DHCP_OVL in the vendor_options field.
		   We know it must be there, given we're already overlaying the fname field.
		   Change it's value to say that we're overlaying BOTH fname and sname fields.
		   Then reset our current insert position to the start of the sname field.
		   XXX Shouldn't we first verify that the sname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use sname field.
		*/
		for (i=4; (u_long) bp->bp_vend[i] != TAG_DHCP_OVL; i++)
			;
		bp->bp_vend[i+2] = 3;
		opt->cur = SNAME_OFFSET;

	} else if ((opt->cur < VEND_OFFSET) && ((opt->cur + 6 + bytes) > VEND_OFFSET))
		/* We're in the sname field, and don't have enough room left.
		   We want enough room for our 2+bytes option, plus enough space to add a TAG_END.
		   XXX I'm not sure why we are checking for 6+bytes instead of 3+bytes.
		*/
		return -1;

	/* At this point, we know there is enough room to insert our option at opt->cur */
	cp[opt->cur++] = tag;
	cp[opt->cur++] = bytes;
	bcopy(value, cp + opt->cur, bytes);
	opt->cur += bytes;

	return 0;
}


int
insert_end_opt(bp, opt)
struct bootp *bp;
struct opts *opt;
{
	unsigned char *cp;

	cp = (u_char*) bp;
	cp[opt->cur++] = TAG_END;
	return 0;
}


/* Insert 1-byte 'tag' and 'len'-bytes string 'value' into the packet represented by 'bp' and 'opt'.
   We try to put it in 'opt' if it fits, else overload it into an unused 'bp' header field.
*/
int
insert_string_opt(bp, opt, tag, value, len)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
char *value;
int len; /* the length of the string itself */
{
	int i;
	unsigned char *cp;
	int bytes;

	cp = (u_char*) bp; /* start of packet */

	/* opt->cur is the offset (from the start of the reply packet) where we should insert the option.
	   opt->len is the length of the reply packet.
	*/

	if (opt->cur == 0)
		/* we've not inserted any options previously, so we begin at the start of the "vendor options" field */
		opt->cur = VEND_OFFSET;

	bytes = len;	

	if ((opt->cur + 6 + bytes) > opt->len) {
		/* We won't have enough room in the vendor_options field if opt->cur + num_bytes_to_add > opt->len.
		   We'd like to insert an option of length '2 + bytes' (tag, len, 'bytes' string) and still have room afterwards.
		   But we need to also save some space after it, to ensure that we could insert a TAG_DHCP_OVL (3 bytes)
		   and a TAG_END (1 byte), in case there are more options to follow.  
		   So we should actually check for 6+bytes, not 2+bytes.
		*/
		/* We're in the vendor_options field, and don't have enough room left.
		   (We don't actually verify that we're in the vendor_options field instead of sname or fname,
		   because we assume the vendor_options is much longer than 6+bytes bytes; the test above couldn't have succeeded
		   if we'd been in one of the other fields (which preceed the vendor_options field).
		   XXX This assumption is bad; as a sufficiently large long string value in the option invalidates it.
		*/
		/* Add a TAG_DHCP_OVL and TAG_END to the vendor options field, and reset our current insert position to the
		   start of the fname field.  (We will overload the fname field.)
		   XXX Shouldn't we verify first that the fname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use fname field.
		*/
		cp[opt->cur++] = TAG_DHCP_OVL;
		cp[opt->cur++] = 1;
		cp[opt->cur++] = 1; /* file overload */
		cp[opt->cur] =TAG_END;
		opt->cur = FILE_OFFSET;

	} else if (((opt->cur + 6 + bytes) > SNAME_OFFSET) && (opt->cur < SNAME_OFFSET)) {
		/* We're in the fname field, but don't have enough room left.
		   We want space for our 2+bytes option, plus enough space left to add a TAG_END.
		   XXX I'm not sure why we are checking for 6+bytes instead of 3+bytes.  (We don't need to have
		   space for a TAG_DHCP_OVL in this case.)
		*/
		cp[opt->cur] = TAG_END;
		/* Hunt for the TAG_DHCP_OVL in the vendor_options field.
		   We know it must be there, given we're already overlaying the fname field.
		   Change it's value to say that we're overlaying BOTH fname and sname fields.
		   Then reset our current insert position to the start of the sname field.
		   XXX Shouldn't we first verify that the sname field is not already in-use?  Looks to me like
		   this is a bug -- we are going to trash any in-use sname field.
		*/
		for (i=4; (u_long) bp->bp_vend[i] != TAG_DHCP_OVL; i++)
			;
		bp->bp_vend[i+2] = 3;
		opt->cur = SNAME_OFFSET;

	} else if ((opt->cur < VEND_OFFSET) && ((opt->cur + 6 + bytes) > VEND_OFFSET))
		/* We're in the sname field, and don't have enough room left.
		   We want enough room for our 2+bytes option, plus enough space to add a TAG_END.
		   XXX I'm not sure why we are checking for 6+bytes instead of 3+bytes.
		*/
		return -1;

	/* At this point, we know there is enough room to insert our option at opt->cur */
	cp[opt->cur++] = tag;
	cp[opt->cur++] = bytes;
	bcopy(value, cp + opt->cur, bytes);
	opt->cur += bytes;

	return 0;
}


/* Just a wrapper for insert_string_opt() */
int
insert_str_opt(bp, opt, tag, value)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
char *value;
{
	return insert_string_opt(bp, opt, tag, value, strlen(value));
}


/* Just a wrapper for insert_string_opt().  We can re-use that for generic opts, since it makes
   no assumption about the contents of 'value'.
 */
int
insert_generic_opt(bp, opt, tag, data, data_len)
struct bootp *bp;
struct opts *opt;
unsigned char tag;
byte *data; /* the data portion of the generic option - does not include that tag or len bytes */
int data_len;
{
	return insert_string_opt(bp, opt, tag, data, data_len);
}


/* smalloc()  --  safe malloc()
 *
 * Always returns a valid pointer (if it returns at all).  The allocated
 * memory is initialized to all zeros.  If malloc() returns an error, a
 * message is printed using the report() function and the program aborts
 * with a status of 1.
 */

void *
smalloc(nbytes)
	unsigned nbytes;
{
	void *retvalue;

	retvalue = malloc(nbytes);
	if (!retvalue) {
		report(LOG_ERR, "malloc() failure -- exiting");
		exit(1);
	}
	bzero((char *) retvalue, nbytes);
	return retvalue;
}


/* change_eid() -- change effective uid or gid
 *
 * Change effective uid or gid to id specified.
 * Return 0 on success, non-zero on error.
 * Only supported if HAVE_SETEUID available, to ensure we can change back later.
 */
int change_eid(uid_or_gid, uid, gid)
int uid_or_gid;		/* 1 == uid, 2==gid */
uid_t uid;			/* if uid_or_gid==1, this is uid to change to; else this is ignored */
gid_t gid;			/* if uid_or_gid==2, this is gid to change to; else this is ignored */
{
	int rc;
	long tmp_id; /* for error reporting */
	static char tmp_str[256];	/* for error reporting */
	char * funcname_ptr; /* for error reporting, will point to one of the following: */
	static char * funcname_seteuid="seteuid";
	static char * funcname_setegid="setegid";

	if ((uid_or_gid != 1) && (uid_or_gid != 2)) {
		report(LOG_ERR, "change_id(): uid_or_gid flag has invalid value: %d", uid_or_gid);
		return -1;
	}

#ifdef HAVE_SETEUID
	/* sete*id() changes only the e*id, leaving real *id (and saved set *id, if it exists) unchanged.
	   Since this server must be started by root, the real *id remains 0, so we'll always be able to
	   change back and forth. */
	if (uid_or_gid==1) {
		funcname_ptr = funcname_seteuid;
		tmp_id = uid;
		rc=seteuid(uid);
	} else {
		funcname_ptr = funcname_setegid;
		tmp_id = gid;
		rc=setegid(gid);
	}
#else
		report(LOG_ERR, "change_id(): HAVE_SETEUID not defined, huh?");
		return -1;
#endif /* HAVE_SETEUID */

	if (rc) {
		sprintf(tmp_str, "%s(%d) failed: %s", funcname_ptr, tmp_id, get_errmsg());
		report(LOG_ERR, tmp_str);
		return -1;
	}

	return 0;
}

/* open_for_writing() - open a regular file for writing.
 * If the file already exists, it will be removed first.
 * On error we return NULL and log an error message.
 * Based closely on write_open() from BIND 8.1.2.
 */
FILE *
open_for_writing(filename) 
char * filename;
{
	int fd;
	FILE *stream;
	struct stat stat_buffer;

	if (stat(filename, &stat_buffer) < 0) {
		if (errno != ENOENT) {
			report(LOG_ERR, "open_for_writing(): stat of %s failed: %s", filename, get_errmsg());
			return NULL;
		} 
	} else { /* filename already exists */
		if (!(stat_buffer.st_mode & S_IFREG)) {
			/* since we just want to open a regular file for writing, its existance as a special
			   file probably means something is wrong, so let's TRY to avoid removing it. */
			report(LOG_ERR, "open_for_writing(): %s exists but isn't a regular file", filename);
			return(NULL);
		}
	}

	/* At this point, filename either does not exist, or already exists as a regular file.
	   Either way, it's toast.  Note that it is possible for it's status to have changed since
	   we checked earlier; e.g. it may have changed from non-existance to existance as a special file.
	   So it's still possible we'll wipe out a special file, but we've made an effort to avoid it. */
	if (unlink(filename) < 0) {
		if (errno != ENOENT) {
			report(LOG_ERR, "open_for_writing(): unlink of %s failed: %s", filename, get_errmsg());
			return NULL;
		}
	}

	/* Use open() to get the atomic behavior provided by O_CREAT|O_EXCL.  If it succeeds,
	   convert the file descriptor into a file stream, which is what we're supposed to return. */
	fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (fd < 0) {
		report(LOG_ERR, "open_for_writing(): open of %s failed: %s", filename, get_errmsg());
		return NULL;
	}
	stream = fdopen(fd, "w");
	if (stream == NULL) {
		report(LOG_ERR, "open_for_writing(): fdopen failed: %s", get_errmsg());
		(void) close(fd);
		return NULL;
	}
	return(stream);
}


/*************************/
/* bitvector package     */

/* How many bytes are needed for a Bitvector large enough to represent 'num_bits' bits? */
int
size_of_bitvector_for_n_bits(int num_bits)
{
	return BITVECTOR__BYTES_PER_VECTOR_OF_BITSIZE(num_bits);
}

/* Return a Bitvector large enough to represent 'num_bits' bits.
   We create a ptr to some number of bytes, 
   but later will use it as an array of unsigned int32.
*/
Bitvector
create_bitvector(int num_bits)
{
	Bitvector vector = NULL;
	vector = (Bitvector) smalloc(size_of_bitvector_for_n_bits(num_bits));
	return vector;
}

void
destroy_bitvector(Bitvector vector)
{
	if (vector != NULL) {
		free(vector);
	}
	return;
}

void
init_bitvector(Bitvector vector, int num_bits)
{
	/* It's up to you to specify the same 'num_bits' as you did when you created the vector. */
	bzero(vector, size_of_bitvector_for_n_bits(num_bits));
	return;
}

/* Set bit 'i' within vector */
void set_bitvector(Bitvector vector, int i)
{
	vector[i >> BITVECTOR__SHIFT] |= (1<<(i & BITVECTOR__MASK));
	return;
}

/* Clear bit 'i' within vector */
void
clear_bitvector(Bitvector vector, int i)
{
	vector[i >> BITVECTOR__SHIFT] &= ~(1 << (i & BITVECTOR__MASK));
	return;
}

/* Get bit 'i' within vector.
   Returns a true value (not necessarily 1) if the bit was set, 0 otherwise.
*/
int
get_bitvector(Bitvector vector, int i)
{
	return vector[i >> BITVECTOR__SHIFT] & (1 << (i & BITVECTOR__MASK));
}

/* bitvector package     */
/*************************/

/* 
	Sleep for n_usecs microseconds.
	(BSD systems provide usleep(), but it may not be avail everywhere.)
	Note this may return sooner; e.g. if interrupted by a caught signal.
*/
void sleep_us(unsigned int n_usecs)
{
	struct timeval timeout;

	/* break it into secs and microseconds */
	timeout.tv_sec = n_usecs / 1000000;
	timeout.tv_usec = n_usecs % 1000000;

	select(0, NULL, NULL, NULL, &timeout);
	/* may return before timeout, e.g. due to a caught signal */

	return;
}
