#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>

#define BUFLEN 512
#define NPACK 10

void diep(char *s)
{
	perror(s);
	exit(1);
}

void Help(const char *file)
{
	printf( "Use: %s -m macaddress -p port\n", file );
	exit(0);
}

int ReadNibble( int nibble )
{
	if( !isxdigit( nibble ) )
		return -1;
	if( isdigit( nibble ) )
		return nibble - '0';
	if( isupper( nibble ) )
		return nibble - 'A' + 10;
	return nibble - 'a' + 10;
}

int DecodeMacAddress( const char *in, char *out )
{
	int counter = 0;
	int value = 0;
	while( *in )
	{
		int nib = ReadNibble( *in++ );
		if( -1 == nib )
		{
			out[ counter++ ] = value;
			value = 0;
			if( counter == 6 )
				return 0;
			continue;
		}
		value <<= 4;
		value += nib;
	}
	if( 5 == counter )
	{
		out[ 5 ] = value; 
		return 1;
	}
	return 0;
}

int main( int argc, char **argv )
{
	struct sockaddr_in si_me, si_other;
	int s, i, slen=sizeof(si_other);
	char buf[BUFLEN];
	int port = 9;
	char mac[ 6 ] = { 0, };

	for( i=1; i<argc; i++ )
	{
		if( argv[i][0] != '-' )
			continue;
		switch( argv[i][1] )
		{
			case 'm':
				if( !DecodeMacAddress( argv[ i + 1 ], mac ) )
				{
					printf( "Wrong macaddress %s\n", argv[ i + 1 ] );
					Help(argv[0]);
				}
				i++;
				break;
			case 'p':
				port = atoi( argv[ i + 1 ] );
				if( 0 == port )
					Help(argv[0]);
				i++;
				break;
			default:
			case 'h':
			case '?':
				Help(argv[0]);
				break;
		}
	}

	if( 0 == port )
		Help(argv[0] );

	printf( "Waiting for magic packet for %02x:%02x:%02x:%02x:%02x:%02x on port %d\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], port );

	if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
		diep("socket");
	
	memset( &si_me, 0, sizeof(si_me));
	si_me.sin_family = AF_INET;
	si_me.sin_port = htons(port);
	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
	if( bind( s, &si_me, sizeof(si_me) ) == -1 )
		diep("bind");

	while( 1 )
	{
		int counter=0;
		int rcvlen = recvfrom(s, buf, BUFLEN, 0, &si_other, &slen);
		if( rcvlen ==-1)
		{
			sleep(1);
			continue;
		}
		for( i=0; i<rcvlen; i++ )
		{
			int j;
			if( buf[ i ] != 0xFF )
			{
				counter=0;
				break;
			}

			if( ++counter < 6 )
				continue;
				
			i++;
			if( rcvlen - i < 16 * 6 )
				continue;
			for( j=0; j<16; j++ )
			{
				if( memcmp( &buf[i], mac, 6 ) )
					break;
				i+=6;
			}
			if( j == 16 )
			{
				close(s);
				exit(0);
			}
		}
	}
}
