/*

Utility functions used by bgpcrack.c

elparis@cisco.com

$Id: bgpcrack-util.c,v 1.5 2003/05/19 17:04:28 peloy Exp $

*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <netinet/tcp.h>

#include "bgpcrack.h"

/*
 * Copy arg vector into a new buffer, concatenating arguments with spaces.
 */
char *
copy_argv(register char **argv)
{
	register char **p;
	register u_int len = 0;
	char *buf;
	char *src, *dst;

	p = argv;
	if (*p == 0)
		return 0;

	while (*p)
		len += strlen(*p++) + 1;

	buf = (char *)malloc(len);
	if (buf == NULL)
		error(1, "copy_argv: malloc");

	p = argv;
	dst = buf;
	while ((src = *p++) != NULL) {
		while ((*dst++ = *src++) != '\0')
			;
		dst[-1] = ' ';
	}
	dst[-1] = '\0';

	return buf;
}

void dump(const unsigned char *data, unsigned len)
{
	unsigned i, j;

	for (i = 0; i <= len/16; i++) {
		printf("%08x  ", i*16);

		for (j = 0; j < 16; j++) {
			if (i*16 + j < len)
				printf("%02x", data[i*16 + j]);
			else
				printf("  ");

			if (j & 1)
				printf(" ");
		}

		for (j = 0; j < 16; j++)
			if (i*16 + j < len)
				printf("%c", isprint(data[i*16 + j]) ? data[i*16 + j] : '.');

		printf("\n");
	}

	printf("\n");
}

/* VARARGS */
void
error(int eval, const char *fmt, ...)
{
	va_list ap;

	(void)fprintf(stderr, "%s: ", program_name);
	va_start(ap, fmt);
	(void)vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (*fmt) {
		fmt += strlen(fmt);
		if (fmt[-1] != '\n')
			(void)fputc('\n', stderr);
	}
	exit(eval);
}

void debug(const char *fmt, ...)
{
#ifdef DEBUG
	va_list ap;

	va_start(ap, fmt);
	(void)vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (*fmt) {
		fmt += strlen(fmt);
		if (fmt[-1] != '\n')
			(void)fputc('\n', stderr);
	}
#endif
}

void print_md5_digest(u_char *md5_digest)
{
	int i;

	for (i = 0; i < MD5_DIGEST_LENGTH; i++)
		printf("%02x", md5_digest[i]);
}

/*
 * This function calculates the MD5 digest of a TCP segment.
 * RFC2385 describes how the digest is calculated. Step 4 in section 2.0
 * of the RFC is done only if key != NULL.
 */
MD5_CTX *tcp_sig(struct iphdr *ip, char *key)
{
	struct {
		in_addr_t saddr;
		in_addr_t daddr;
		u_int16_t protocol;
		u_int16_t len;
	} pseudo_header;
	struct tcphdr *tcp;
	static MD5_CTX c;
	int size_of_tcp_data;

	MD5_Init(&c);

	/*
	 * Calculate MD5 sum of pseudo header (step 1 in section 2.0 of
	 * RFC2385)
	 */
	pseudo_header.saddr = ip->saddr;
	pseudo_header.daddr = ip->daddr;
	pseudo_header.protocol = htons(ip->protocol);
	/*
	 * Remember: the length field in the pseudo-header is the length
	 * of the TCP segment, that is, size of the TCP header (which may
	 * or may not include TCP options) + the size of the TCP payload.
	 * This easy to calculate: total IP packet size - size of IP header.
	 */
	pseudo_header.len = htons(ntohs(ip->tot_len) - ip->ihl*4);
	MD5_Update(&c, &pseudo_header, sizeof(pseudo_header) );

	/*
	 * Calculate MD5 sum of TCP header (_excluding_ options!) (step 2 in
	 * section 2.0 of RFC2385).
	 */
	tcp = (struct tcphdr *) ( (char *) ip + ip->ihl*4);
	/* Warning: remember we're altering the TCP packet with this! */
	tcp->check = 0;
	MD5_Update(&c, tcp, sizeof(struct tcphdr) );

	/* Calculate MD5 sum of TCP data (step 3 in section 2.0 of RFC2385) */
	size_of_tcp_data = ntohs(ip->tot_len) - ip->ihl*4 - tcp->doff*4;
	MD5_Update(&c, (char *) tcp + tcp->doff*4, size_of_tcp_data);

	if (key)
		MD5_Update(&c, key, strlen(key) );

	return &c;
}

inline u_short in_cksum(void *data, int len)
{
	int counter = len;
	u_short *w = data;
	int sum = 0;
	u_short tmp = 0;

	while (counter > 1) {
		sum += *w++;
		counter -= 2;
	}

	if (counter == 1) {
		/* Length is odd */
		*(u_char *) &tmp = *(u_char *) w;
		sum += tmp;
	}

	/* We want a 1's complement sum, so we must add whatever was
	   carried out from the low 16 bits to the low 16 bits */
	sum = (sum >> 16) + (u_short) sum;

	/* Add the carry from the previous sum */
	sum += sum >> 16;

	/* Now we take the 1's complement of the 1's complement sum */
	tmp = ~sum;

	return tmp;
}

