/*
 * Heavily modified from mdump by J.P.Knight@lut.ac.uk
 *
 */
/* Includes used for some general things */
#include <stdio.h>
#include <stdlib.h>

#if defined(_WIN32)
#   include <winsock2.h>
#   include <ws2tcpip.h>
#   include <sys\types.h>
#   include <sys\timeb.h>
#   include <time.h>
#   define perror(x) fprintf(stderr,"%s: %d\n",x,::GetLastError());
#else
/* Includes used for most socket operations */
#   include <sys/types.h>
#   include <sys/socket.h>
#   include <netinet/in.h>
#   include <unistd.h>
#   include <netdb.h>
#   include <netinet/tcp.h>
/* Need this for UNIX socket */
#   include <sys/un.h>
/* Includes used for auxiliary calls: memset, etc. */
#   include <strings.h>
#   include <arpa/inet.h>
#   include <sys/time.h>
#   include <time.h>
#   include <string.h>
typedef int SOCKET;
const int INVALID_SOCKET = -1;
const int SOCKET_ERROR = -1;
#endif /* _WIN32 */

#define MAXPDU 4096

typedef unsigned int u_int32;
typedef unsigned int u_int;

#if defined(_WIN32)
typedef int socklen;
#elif defined(__linux__)

#endif /* _WIN32 */

// faster routine to replace inet_ntoa() (from tcpdump)
char *intoa(u_int32 addr)
{
	register char *cp;
	register u_int byte;
	register int n;
	static char buf[sizeof(".xxx.xxx.xxx.xxx")];

	addr = ntohl(addr);
	// NTOHL(addr);
	cp = &buf[sizeof buf];
	*--cp = '\0';

	n = 4;
	do {
		byte = addr & 0xff;
		*--cp = byte % 10 + '0';
		byte /= 10;
		if (byte > 0) {
			*--cp = byte % 10 + '0';
			byte /= 10;
			if (byte > 0)
				*--cp = byte + '0';
		}
		*--cp = '.';
		addr >>= 8;
	} while (--n > 0);

	return cp + 1;
}

char *format_time(const struct timeval *tv)
{
	static char buff[sizeof(".xx:xx:xx.xxxxxx")];
	int min;
	
	u_int32 h = localtime((time_t *)&tv->tv_sec)->tm_hour;
	min = (int)(tv->tv_sec % 86400);
	sprintf(buff,"%02d:%02d:%02d.%06d",h,(min%3600)/60,min%60,tv->tv_usec);
	return buff;
}

void dump(const char *buffer, int size)
{
	int i,j;
	unsigned char c;
	char textver[20];

	for (i=0;i<(size >> 4);i++) {
		for (j=0;j<16;j++) {
			c = buffer[(i << 4)+j];
			printf("%02x ",c);
			textver[j] = ((c<0x20)||(c>0x7e))?'.':c;
		}
		textver[j] = 0;
		printf("\t%s\n",textver);
	}
	for (i=0;i<size%16;i++) {
		c = buffer[size-size%16+i];
		printf("%02x ",c);
		textver[i] = ((c<0x20)||(c>0x7e))?'.':c;
	}
	for (i=size%16;i<16;i++) {
		printf("   ");
		textver[i] = ' ';
	}
	textver[i] = 0;
	printf("\t%s\n",textver);
}

void currenttv(struct timeval &tv)
{
#if defined(_WIN32)
	struct _timeb tb;
	::_ftime(&tb);
	tv.tv_sec = tb.time;
	tv.tv_usec = 1000*tb.millitm;
#else
	::gettimeofday(&tv,NULL);
#endif /* _WIN32 */
}

int main(int argc, char **argv)
{
    SOCKET sock;
	socklen_t fromlen = sizeof(sockaddr_in);
	int length;
    char buff[MAXPDU];
    struct sockaddr_in name;
	struct sockaddr_in src;
    struct ip_mreq imr;
    char *interface = NULL;
	unsigned long int groupaddr;
	unsigned short int groupport;
	struct timeval tv;

#if defined(_WIN32)
	WSADATA wsadata;
	int status;

	if ((status = WSAStartup(MAKEWORD(2,2),&wsadata)) != 0) {
		fprintf(stderr,"%s: WSA startup error - %d\n",argv[0],status);
		exit(1);
	}
#endif /* _WIN32 */
	
    if (argc == 3) {
		groupaddr = inet_addr(argv[1]);
		groupport = (u_short)atoi(argv[2]);
    } else if (argc == 4) {
		groupaddr = inet_addr(argv[1]);
		groupport = (u_short)atoi(argv[2]);
		interface=argv[3];
	} else {
		printf("usage: %s group port [interface]\n", argv[0]);
		exit(1);
	}
		
    if((sock = socket(AF_INET,SOCK_DGRAM,0)) == INVALID_SOCKET) {
        perror("socket");
        exit(1);
    }

	memset((char *)&imr,0,sizeof(imr));
    imr.imr_multiaddr.s_addr = groupaddr;
    if (interface != NULL) {
        imr.imr_interface.s_addr = inet_addr(interface);
    } else {
		imr.imr_interface.s_addr = htonl(INADDR_ANY);
    }

	memset((char *)&name,0,sizeof(name));
    name.sin_family = AF_INET;
    name.sin_addr.s_addr = groupaddr;
    name.sin_port = htons(groupport);
	if (bind(sock,(struct sockaddr *)&name,sizeof(name)) == SOCKET_ERROR) {
		name.sin_addr.s_addr = htonl(INADDR_ANY);
		if (bind(sock,(struct sockaddr *)&name, sizeof(name)) == -1) {
			perror("bind");
			exit(1);
		}
	}

    if (setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,
				   (char *)&imr,sizeof(struct ip_mreq)) == SOCKET_ERROR ) {
		perror("setsockopt - IP_ADD_MEMBERSHIP");
		exit(1);
    }
	for (;;) {
		if ((length = recvfrom(sock,buff,sizeof(buff),0,
							   (struct sockaddr *)&src,&fromlen)) == SOCKET_ERROR) {
			perror("recv");
			exit(1);
		}
		currenttv(tv);
		printf("%s %s.%d %d bytes:\n",format_time(&tv),
			   inet_ntoa(src.sin_addr),ntohs(src.sin_port),
			   length);
		dump(buff,length);
    }
#if defined(_WIN32)
	closesocket(sock);
#else
    close(sock);
#endif /* _WIN32 */
    return(0);
}



