/* wol.c	Wake On Lan
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define BUFMAX	1024
#define MACLEN	6

static	int	hextobin(unsigned char);
static	unsigned	char	*httpunescape(unsigned char *);

static unsigned char *httpunescape(unsigned char *sis)
{
/*
	Removes HTTP escapes typically found in an HTTP 
	query string. These are the use of '+' for ' '
	and the use of %xx notation for special characters
	where 'xx' is the hexadecimal equivalent of the
	bits of the character.

	Returns	either
	
	(1) The address of the original string which
		is modified in place.
	(2) NULL meaning that there is some sort of
		error. The string is not modified.

*/
	unsigned char *siptr;		/* input string pointer */
	unsigned char *soptr;		/* output string pointer */
	unsigned char *sos;		/* start of output string */
/*
	Allocate enough space for a copy of the string
*/

	sos = (unsigned char *)calloc(strlen(sis)+1,sizeof (unsigned char));
	if(sos == NULL)	return NULL;
	soptr = sos;
	siptr = sis;
/*
	Loop walks the input string
*/
	while(*siptr)
	{
		if(*siptr == '%')	/* start of hex escape */
		{
			int	c=0,i;
			for(i=0;i<2;i++)
			{
				int h;
				siptr++;
				if(*siptr == '\0')			break;
				if((h=hextobin(*siptr)) == -1)		break;
				c = c*16 + h;	
			}
			if(i != 2)	/* error during hex conversion */
			{
				free(sos);
				return NULL;
			}
			*soptr++ = (unsigned char)c;
		} else if(*siptr == '+')	*soptr++ = ' ';
		else				*soptr++ = *siptr;
		siptr++;
	}
/*
	Input string walk completed, terminate generated
	string and copy onto old string. Finally free
	dynamic space used for building of new string.
*/
	*soptr = '\0';
	strcpy(sis,sos);
	free(sos);
	return sis;
}
		
static	int	hextobin(unsigned char c)
{
/*
	Function to convert hex character to binary.
	Returns value or -1 if there is any error.
*/
	if(isdigit(c))		return	c-'0';
	else if(isxdigit(c))	return  tolower(c)-'a'+10;
	else			return -1;
}





void host2addr(char *name, struct in_addr *addrp, short *familyp) {
    struct hostent *hp;
    if (hp=gethostbyname(name)) {
	bcopy(hp->h_addr,(char *)addrp,hp->h_length);
	if (familyp) *familyp = hp->h_addrtype;
    } else if ((addrp->s_addr=inet_addr(name)) != -1) {
	if (familyp) *familyp = AF_INET;
    } else {
	printf("<P>Unknown host : %s\n",name);
	exit(1);
    }
}

int hex(char c) {
    if ('0' <= c && c <= '9') return c - '0';
    if ('a' <= c && c <= 'f') return c - 'a' + 10;
    if ('A' <= c && c <= 'F') return c - 'A' + 10;
    return -1;
}

int hex2(char *p) {
    int i;
    unsigned char c;
    i = hex(*p++);
    if (i < 0) return i;
    c = (i << 4);
    i = hex(*p);
    if (i < 0) return i;
    return c | i;
}

int main(void)
{
int sd;
int optval;
char unsigned buf[BUFMAX];
int len, from_len;
struct sockaddr_in sin, from;
unsigned char mac[MACLEN];
unsigned char *p;
int i, j;
int     vcnt = 0;       /* count of number of name/value pairs */
unsigned char   *vstr;          /* points to location of received string */
unsigned char   *vptr;          /* used for walking received string */
unsigned char   *eptr;          /* location of next '=' in received string */
unsigned char   *aptr;          /* location of next '&' in received string */
unsigned char macadd[100],ipadd[100];
int mac_or_ip=0;

printf("%s%c%c\n","Content-Type:text/html;charset=iso-8859-1",13,10);
printf("<TITLE>Wake on Lan utility</TITLE>\n");
printf("<H3>Wake on Lan utility</H3>\n");

vstr = getenv("QUERY_STRING");
if(vstr == NULL){
	printf("<p>Input parameters error");
	exit(1);
}
vptr = vstr;
while(*vptr)	if(*vptr++ == '&')	vcnt++;
vcnt++;
vptr = vstr;
for(i=0;i<vcnt;i++){
	eptr = strchr(vptr,'=');
	aptr = strchr(vptr,'&');
	if(eptr == NULL)	break;
	*eptr = '\0';
	if(vptr == NULL)        break;
	if(strcmp(vptr,"mac")==0){
		mac_or_ip=1;
	}else if (strcmp(vptr,"ip")==0){
		mac_or_ip=2;
	}
	if(aptr){
		*aptr = '\0';
		vptr = aptr+1;
	}
	if(eptr+1 == NULL)	break;
	if(mac_or_ip==1){
		strcpy(macadd,eptr+1);
		httpunescape(macadd);
	}else if (mac_or_ip==2){
		strcpy(ipadd,eptr+1);
		httpunescape(ipadd);
	}
}

if(macadd == NULL || ipadd == NULL)
	{
		printf("<p>Input parameters are bad");
		exit(1);
	}

bzero((char *)&sin,sizeof(sin)); /* clear sin struct */
sin.sin_family = AF_INET;
host2addr(ipadd,&sin.sin_addr,&sin.sin_family);	/* host */
sin.sin_port = htons(9);	/* port */
if ((sd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) {
	printf("<P>Cannot get socket");
	exit(1);
    }
    optval = 1;
    if (setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval)) < 0) {
	printf("<P>Cannot set sockopt (%d).\n",errno);
	exit(1);
    }
p=macadd;
j = hex2(p);
    if (j < 0) {
    MACerror:
	printf("<P>Illegal MAC address: %s\n",mac);
	exit(1);
    }
    mac[0] = j;
    p += 2;
    for (i=1; i < MACLEN; i++) {
	if (*p++ != ':') goto MACerror;
	j = hex2(p);
	if (j < 0) goto MACerror;
	mac[i] = j;
	p += 2;
    }
    p = buf;
    for (i=0; i < 6; i++) {	/* 6 bytes of FFhex */
	*p++ = 0xFF;
    }
    for (i=0; i < 16; i++) {	/* MAC addresses repeated 16 times */
	for (j=0; j < MACLEN; j++) {
	    *p++ = mac[j];
	}
    }
    len = p - buf;
#ifdef DEBUG
    for (i=0; i < len; i+=16) {
	for (j=0; j < 16; j++) {
	    if (i+j >= len) break;
	    printf(" %02x",buf[i+j]);
	}
	printf("\n");
    }
#endif
    if (sendto(sd,buf,len,0,
	       (struct sockaddr*)&sin,sizeof(sin)) != len) {
	printf("<P>Sendto failed (%d).\n",errno);
    }
printf("<P>Magic packet sent to IP:%s with MAC:%s",ipadd,macadd);
return 0;
}
