/*
 * Copyright (c) 2014 Rob Braun <bbraun@synack.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Rob Braun nor the names of his contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <getopt.h>

struct ddppacket {
	uint8_t  dst_mac[6];
	uint8_t  src_mac[6];
	uint16_t mac_len;
	uint8_t  llc_dsap;
	uint8_t  llc_ssap;
	uint8_t  llc_control;
	uint8_t  llc_org[3];
	uint16_t llc_type;
	uint8_t  ddp_unused:2,
	         ddp_hopcount:4;
	uint8_t  ddp_length;
	uint16_t ddp_checksum;
	uint16_t ddp_dst_net;
	uint16_t ddp_src_net;
	uint8_t  ddp_dst_node;
	uint8_t  ddp_src_node;
	uint8_t  ddp_dst_socket;
	uint8_t  ddp_src_socket;
	uint8_t  ddp_type;
	uint8_t  ddp_data[512];
};
typedef struct ddppacket ddppacket_t;

void packet_swizzle(ddppacket_t *pkt) {
	pkt->mac_len = htons(pkt->mac_len);
	pkt->llc_type = htons(pkt->llc_type);
	pkt->ddp_checksum = htons(pkt->ddp_checksum);
	pkt->ddp_dst_net = htons(pkt->ddp_dst_net);
	pkt->ddp_src_net = htons(pkt->ddp_src_net);
}

void usage(const char *progname) {
	fprintf(stderr, "Usage: %s [-s|--socket #] payload\n", progname);
	fprintf(stderr, "\t-s|--socket #\t\tSpecify the destination socket number\n");
	fprintf(stderr, "\t-i|--interface if\t\tInterface name to use\n");
	fprintf(stderr, "\t--hex if\t\tInterpret the payload as a hex string\n");
}

int dehex(const uint8_t *hexstr, uint8_t *out) {
	uint8_t *tmpptr = out;
	uint8_t tmpchar;

	while(*hexstr && *(hexstr+1)) {
		if(*hexstr >= 'A') {
			tmpchar = (*hexstr - 'A' + 0x0A) << 4;
		} else {
			tmpchar = (*hexstr - '0') << 4;
		}

		hexstr++;

		if(*hexstr >= 'A') {
			tmpchar |= (*hexstr - 'A' + 0x0A);
		} else {
			tmpchar |= (*hexstr - '0');
		}
		
		hexstr++;
		*tmpptr++ = tmpchar;
	}

	return tmpptr - out;
}

int main(int argc, char *argv[]) {
	char *dev = NULL;
	pcap_t *handle = NULL;
	char errbuf[PCAP_ERRBUF_SIZE];
	ddppacket_t pkt;
	int datalen = 0;
	uint8_t atlkmacaddr[] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff };
	uint8_t srcmacaddr[] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee };
	uint8_t applesnaporg[] = { 0x08, 0x00, 0x07 };
	int loptind = 0;
	struct option o[] = {
		{"socket", 1, 0, 's'},
		{"interface", 1, 0, 'i'},
		{"hex", 0, 0, 1},
		{ 0, 0, 0, 0}
	};
	char c;
	uint16_t socket = 96;
	char *interface = NULL;
	int i;
	int usehex = 0;

	while((c = getopt_long(argc, argv, "hi:s:", o, &loptind)) != -1) {
		switch(c) {
		case 's':
			socket = strtol(optarg, NULL, 10);
			break;
		case 'i':
			interface = optarg;
			break;
		case 1:
			usehex = 1;
			break;
		case 'h':
		default:
			usage(argv[0]);
			exit(1);
		};
	}

	if(interface) {
		dev = interface;
	} else {
		dev = pcap_lookupdev(errbuf);
	}
	if(!dev) {
		fprintf(stderr, "Error looking up pcap dev: %s\n", errbuf);
		exit(1);
	}

	handle = pcap_open_live(dev, 1, 0, 1000, errbuf);
	if(!handle) {
		fprintf(stderr, "Could not open pcap device %s: %s\n", dev, errbuf);
		exit(1);
	}

	memset(&pkt, 0, sizeof(pkt));

	datalen = 0;
	for(i = optind; i < argc; i++) {
		if(usehex) {
			datalen += dehex((uint8_t*)argv[i], &pkt.ddp_data[datalen]);
		} else {
			int len;
			strcpy((char*)&pkt.ddp_data[datalen+1], argv[i]);
			len = strlen((char*)&pkt.ddp_data[datalen+1]);
			pkt.ddp_data[datalen] = len;
			datalen += len + 1;
		}
	}

	if(datalen < 64) {
		datalen = 64;
	}
	
	memcpy(pkt.dst_mac, atlkmacaddr, 6);
	memcpy(pkt.src_mac, srcmacaddr, 6);
	pkt.mac_len = 21 + datalen;
	pkt.llc_dsap = 0xaa;
	pkt.llc_ssap = 0xaa;
	pkt.llc_control = 0x03;
	memcpy(pkt.llc_org, applesnaporg, 3);
	pkt.llc_type = 0x809b;
	pkt.ddp_hopcount = 0;
	pkt.ddp_length = 13 + datalen;
	pkt.ddp_checksum = 0;
	pkt.ddp_dst_net = 0;
	pkt.ddp_src_net = 0;
	pkt.ddp_dst_node = 255;
	pkt.ddp_src_node = 66;
	pkt.ddp_dst_socket = socket;
	pkt.ddp_src_socket = 95;
	pkt.ddp_type = 19;

	packet_swizzle(&pkt);

	if(pcap_sendpacket(handle, (const u_char*)&pkt, datalen+35) != 0) {
		fprintf(stderr, "Packet send failed\n");
		exit(1);
	}

	pcap_close(handle);
	return 0;
}
