/*
 * Copyright (c) 2002-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: utils.c 12513 2010-03-08 22:41:59Z jomi.gregory $
 *
 * Helper functions.
 */

/* File header. */
#include "utils.h"


/*
 * Swap byte ordering of uint64_t on a big endian
 * machine.
 */
uint64_t
swapll(uint64_t ull)
{
#if defined (BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
	return ((ull & 0xff00000000000000LL) >> 56) |
	    ((ull & 0x00ff000000000000LL) >> 40) |
	    ((ull & 0x0000ff0000000000LL) >> 24) |
	    ((ull & 0x000000ff00000000LL) >> 8) |
	    ((ull & 0x00000000ff000000LL) << 8) |
	    ((ull & 0x0000000000ff0000LL) << 24) |
	    ((ull & 0x000000000000ff00LL) << 40) |
	    ((ull & 0x00000000000000ffLL) << 56);
#else
	return ull;
#endif
}


/*
 * This routine implements a POSIX compliant way to get the seconds
 * offset from UTC. Unfortunately, struct tm's do not need to implement
 * some of the older fields any more, such as tm_gmtoff.
 */
int
gmtoff(void)
{
	time_t now = time(NULL);
	struct tm loc = *localtime(&now);
	struct tm utc = *gmtime(&now);
	int tdiff = 0;

	tdiff = ((loc.tm_hour * 60 + loc.tm_min) * 60 + loc.tm_sec) -
		((utc.tm_hour * 60 + utc.tm_min) * 60 + utc.tm_sec);
	
	if ((loc.tm_yday > utc.tm_yday) || (loc.tm_year > utc.tm_year))
	{
		tdiff += 86400;
	}
	else if ((loc.tm_yday < utc.tm_yday) || (loc.tm_year < utc.tm_year))
	{
		tdiff -= 86400;
	}
	
	return tdiff;
}


INLINE int
get_atm_wire_length(dag_record_t * header)
{
	return ATM_SNAPLEN;
}


INLINE int
get_atm_snap_length(dag_record_t * header)
{
	return ATM_SNAPLEN;
}


INLINE int
get_erf_wire_length(dag_record_t * header)
{
    int wire_length = ntohs(header->wlen) - (gFcsBits >> 3);
    if ( 0 > wire_length) {
        dagutil_panic("Negative wire length, try setting FCS size\n");
    }
    return wire_length;
}


INLINE int
get_ethernet_snap_length(dag_record_t * header)
{
	int wire_length = get_erf_wire_length(header);
	int packet_record_length = ntohs(header->rlen) - dag_record_size - 2;
	
	if (wire_length < packet_record_length)
	{
		return wire_length;
	}
	
	return packet_record_length;
}

/*
 * This function is used by raw packet types as well
 */
INLINE int
get_hdlc_snap_length(dag_record_t * header)
{
	int wire_length = get_erf_wire_length(header);
	int packet_record_length = ntohs(header->rlen) - dag_record_size;
	
	if (wire_length < packet_record_length)
	{
		return wire_length;
	}
	
	return packet_record_length;
}

/*
 * Duplicate of get_hdlc_snap_length
 */ 
INLINE int
get_aal5_snap_length(dag_record_t * header)
{
	int wire_length = get_erf_wire_length(header);
	int packet_record_length = ntohs(header->rlen) - dag_record_size;
	
	if (wire_length < packet_record_length)
	{
		return wire_length;
	}
	
	return packet_record_length;
}

/*
 * Duplicate of get_hdlc_snap_length
 */ 
INLINE int
get_ipv4_snap_length(dag_record_t * header)
{
	int wire_length = get_erf_wire_length(header);
	int packet_record_length = ntohs(header->rlen) - dag_record_size;
	
	if (wire_length < packet_record_length)
	{
		return wire_length;
	}
	
	return packet_record_length;
}

/*
 * This function is used by ERF raw packet types
 */
INLINE int
get_raw_snap_length(dag_record_t * header)
{
	int wire_length = get_erf_wire_length(header);
	int packet_record_length = ntohs(header->rlen) - dag_record_size;
	
	if (wire_length < packet_record_length)
	{
		return wire_length + dag_record_size + 8; // Add 8 bytes for the ERF extension header
	}
	
	return packet_record_length;
}

void
get_dlt(dag_record_t * header, int * linktype, int * snapshot)
{
	int ltype, sshot;
	
	switch (header->type & 0x7f)
	{
	case ERF_TYPE_ATM:
	case ERF_TYPE_MC_ATM:
		if (gOutputDataLinkType == DLT_NULL)
			ltype = DLT_ATM_RFC1483;
		else if ( (gOutputDataLinkType == DLT_ATM_RFC1483) ||
			  (gOutputDataLinkType == DLT_SUNATM) )
			ltype = gOutputDataLinkType;
		else
			dagutil_panic("Illegal output DLT '%d' for ERF type '%s'.\n", gOutputDataLinkType, dagerf_type_to_string(header->type, 0));
		sshot = ATM_SNAPLEN;
		break;
			
	case ERF_TYPE_AAL5:
	case ERF_TYPE_MC_AAL5:
	case ERF_TYPE_AAL2:
	case ERF_TYPE_MC_AAL2:
		if (gOutputDataLinkType == DLT_NULL)
			ltype = DLT_ATM_RFC1483;
		else if ( (gOutputDataLinkType == DLT_ATM_RFC1483) ||
			  (gOutputDataLinkType == DLT_SUNATM) )
			ltype = gOutputDataLinkType;
		else
			dagutil_panic("Illegal output DLT '%d' for ERF type '%s'.\n", gOutputDataLinkType, dagerf_type_to_string(header->type, 0));

		sshot = DEFAULT_SNAPLEN;
		break;
			
	case ERF_TYPE_ETH:
	case ERF_TYPE_COLOR_ETH:
	case ERF_TYPE_DSM_COLOR_ETH:
		if (gOutputDataLinkType == DLT_NULL)
			ltype = DLT_EN10MB;
#if defined(DLT_DOCSIS)
		else if ( (gOutputDataLinkType == DLT_EN10MB) || (gOutputDataLinkType == DLT_DOCSIS) )
#else
		else if ( (gOutputDataLinkType == DLT_EN10MB) )
#endif
			ltype = gOutputDataLinkType;
		else
			dagutil_panic("Illegal output DLT '%d' for ERF type '%s'.\n", gOutputDataLinkType, dagerf_type_to_string(header->type, 0));

		sshot = DEFAULT_SNAPLEN;
		break;
			
	case ERF_TYPE_MC_HDLC:
	case ERF_TYPE_COLOR_MC_HDLC_POS:
	case ERF_TYPE_HDLC_POS:
	case ERF_TYPE_COLOR_HDLC_POS:
	case ERF_TYPE_DSM_COLOR_HDLC_POS:
		if (gOutputDataLinkType == DLT_NULL)
#if defined(__NetBSD__)
			ltype = DLT_HDLC;
#else
			ltype = DLT_CHDLC;
#endif
		else if ( (gOutputDataLinkType == DLT_PPP_SERIAL)
#if defined(__NetBSD__)
			  || (gOutputDataLinkType == DLT_HDLC)
#else
			  || (gOutputDataLinkType == DLT_CHDLC)
#endif
			  || (gOutputDataLinkType == DLT_FRELAY)
#if defined(DLT_MTP2)
			  || (gOutputDataLinkType == DLT_MTP2)
#endif
			  )
			ltype = gOutputDataLinkType;
		else
			dagutil_panic("Illegal output DLT '%d' for ERF type '%s'.\n", gOutputDataLinkType, dagerf_type_to_string(header->type, 0));

		sshot = DEFAULT_SNAPLEN;
		break;
			
	case ERF_TYPE_MC_RAW:
	case ERF_TYPE_MC_RAW_CHANNEL:
		ltype = DLT_RAW;
		sshot = DEFAULT_SNAPLEN;
		break;

#if defined(DLT_ERF)
	case ERF_TYPE_RAW_LINK:
		ltype = DLT_ERF;
		sshot = DEFAULT_SNAPLEN;
		break;

#endif
	case ERF_TYPE_IPV4:	
	case ERF_TYPE_IPV6:
		if (gOutputDataLinkType == DLT_NULL) 
			ltype = DLT_RAW;
		else if ( (gOutputDataLinkType == DLT_RAW) 
#if defined(DLT_IPV4)
			|| (gOutputDataLinkType == DLT_IPV4)
#endif
#if defined(DLT_IPV6)
			|| (gOutputDataLinkType == DLT_IPV6) 
#endif
			)
			ltype = gOutputDataLinkType;
		else
			dagutil_panic("Illegal output DLT '%d' for ERF type '%s'.\n", gOutputDataLinkType, dagerf_type_to_string(header->type, 0));
	
		sshot = DEFAULT_SNAPLEN;
		break;

	default:
		dagutil_panic("Unhandled ERF Type %d: %s line %d\n", header->type, __FILE__, __LINE__);
	}

	dagutil_verbose("Using DLT_%d\n", ltype);

	if (linktype)
		*linktype = ltype;

	if (snapshot)
		*snapshot = sshot;

}


int change_output_file_extn(char *file_name, char *old_extn, char* new_extn)
{
    size_t len = 0, new_len = 0;
    char *new_name = NULL;
    char *ext_start = NULL;
    int ret_val = 0;
    
    if ( NULL == file_name) 
    {
        return 0;
    }
    ext_start = strstr(file_name, old_extn); /* case sensitive */
    if ( NULL == ext_start)
    {
        /* ext not present. Don't do anything */
        return 0;
    }
    len = ext_start - file_name;
    
    new_len = len + strlen(new_extn) + 1; /* for extn  */
    new_name = calloc(sizeof(char), new_len);
    strncpy(new_name, file_name, len);
    strncat(new_name, new_extn, strlen(new_extn));
    if ( rename(file_name , new_name) != 0 )
    {
       ret_val = -1;
    }
    if ( new_name )
    {
        free( new_name);
    }   
    return ret_val;
}
