#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>

int hexchar2int(char ch) {
  if(ch >= '0' && ch <= '9')
    return ch - '0';
  else if(ch >= 'A' && ch <= 'F')
    return ch - 'A' + 10;
  else if(ch >= 'a' && ch <= 'f')
    return ch - 'a' + 10;
  return 0;
}

int main(int argc, const char *argv[]) {
  int sock;
  int on = 1;
  struct sockaddr_in dest;
  unsigned char magic[102];
  
  /* check arguments */
  if(argc != 2) {
    printf("usage: wakeonlan <MAC address>\n");
    exit(1);
  }
  
  /* create magic packet */
  memset(&magic[0], 0xFF, 6);
  
  {
    int n;
    const char *mac = argv[1];
    
    for(n = 0;n < 17;n ++) {
      int i1 = n / 3;
      int i2 = n % 3;
      
      if(mac[n] == 0 || (i2 == 2 && mac[n] != ':')) {
	printf("invalid MAC address.\n");
	exit(1);
      }
      if(i2 == 2)
	continue;
      
      magic[6 + i1] <<= 4;
      magic[6 + i1] |= hexchar2int(mac[n]);
    }
    for(n = 0;n < 15;n ++)
      memcpy(&magic[12 + 6 * n], &magic[6], 6);
  }
  
  /* set IP destination */
  memset(&dest, 0x00, sizeof(dest));
  dest.sin_family = AF_INET;
  dest.sin_addr.s_addr = inet_addr("255.255.255.255");
  dest.sin_port = htons(65533);
  
  /* prepare socket */
  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if(sock < 0) {
    perror("socket failed"); 
    exit(1);
  }
  
  /* set broadcast */
  if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
    perror("setsockopt SOL_SOCKET, SO_BROADCAST failed");
    exit(1);
  }
  
  /* send */
  if(sendto(sock, magic, sizeof(magic), 0, (struct sockaddr *)&dest, sizeof(dest)) != sizeof(magic)) {
    perror("sendto failed");
    exit(1);
  }
  
  /* complete */
  close(sock);
  return 0;
}
