/*
 * btscanner - Displays the output of Bluetooth scans
 * Copyright (C) 2003 Pentest Limited
 * 
 * Written 2003 by Tim Hurman <timh at pentest.co.uk>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE
 * FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
 * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE
 * IS DISCLAIMED.
 */

/*
 * btscanner.c: keep a bluetooth card scanning the network to find devices
 * that are discoverable. Heavily based on hcitool.c
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <getopt.h>
#include <ncurses.h>

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#ifdef HAVE_SYSLOG_H
#include <sys/syslog.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#include <main.h>
#include <cfg.h>
#include <screen.h>
#include <misc.h>
#include <scan.h>
#include <log.h>
#include <ll.h>
#include <oui.h>

/* globals */
hcicfg_t *hcihead = NULL;
char bts_run = 1;
char bts_run_scan = 1;
int bts_reset=1;

/* signal handlers */
/* handle a signal */
static RETSIGTYPE handlesig (int sig)
{
	sig=sig;
	bts_run = 0;
	bts_run_scan = 0;
}

/* just interrupt */
static RETSIGTYPE dointerrupt(int sig)
{
	/* do nothing, just interrupt */
	sig=sig;
}


/* usage */
void usage (char *pname) {
	fprintf(stderr, "Usage: %s [options]\n", pname);
	fprintf(stderr, "options\n\t--help\tDisplay help\n");
	fprintf(stderr, "\t--cfg=<file>\tUse <file> as the config file\n");
	fprintf(stderr, "\t--no-reset\tDo not reset the Bluetooth adapter before scanning\n");
}


/* command line options */
static struct option main_options[] = {
	{"help", 0,0, 'h'},
	{"cfg", 1,0, 'c'},
	{"no-reset", 0, &bts_reset, 0},
	{0, 0, 0, 0}
};


/* free the hci device list */
void hci_devices_free(hcicfg_t *h)
{
	if (h->next) hci_devices_free(h->next);
	free(h);
}


/* append to the hci list */
void  hci_devices_append(hcicfg_t *h, hcicfg_t *n)
{
	if (!hcihead) {
		hcihead = n;
		return;
	}
	if (h->next)
		hci_devices_append(h->next, n);
	else
		h->next = n;
	return;
}


/* get the max num of devices */
int get_max_devices(int s)
{
	struct hci_dev_list_req *dl = NULL;
	struct hci_dev_req *dr = NULL;
	struct hci_dev_info di;
	int i, ret, up;
	hcicfg_t *hci;

	ret = 1;
	dl = (struct hci_dev_list_req*)malloc
	  (HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
	if(!dl) {
		applog(LOG_ERR,
		  "%s::malloc(): %s", __FUNCTION__, strerror(errno));
		goto get_max_devices_leave;
	}
	memset(dl, 0, HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
	dl->dev_num = HCI_MAX_DEV;
	dr = dl->dev_req;

	/* get the device list */
	if( ioctl(s, HCIGETDEVLIST, (void*)dl) ) {
		applog(LOG_ERR,
		  "%s::ioctl(): %s", __FUNCTION__, strerror(errno));
		goto get_max_devices_leave;
	}

	ret = 0;
	/* enumerate the devices */
	for(up=i=0; i< dl->dev_num; i++, dr++) {
		di.dev_id = dr->dev_id;

		/* get the device info */
		if( ioctl(s, HCIGETDEVINFO, (void*)&di) )
			continue;

		/* make a new hci device */
		hci = (hcicfg_t*)malloc(sizeof(hcicfg_t));
		if (!hci) {
			ret = 1;
			applog(LOG_ERR,
			  "%s::malloc(): %s", __FUNCTION__, strerror(errno));
			break;
		}
		memset(hci, 0, sizeof(hcicfg_t));
		hci->id = di.dev_id;
		bacpy(&(hci->bdaddr), &(di.bdaddr));

		/* is the device enabled? */
		if (hci_test_bit(HCI_UP, &dr->dev_opt)) {
			hci->enabled = HCI_ENABLED;
			up++;
		} else {
			hci->enabled = HCI_DISABLED;
		}

		/* save */
		hci_devices_append(hcihead, hci);
	}

	if (0 == up) {
		applog(LOG_ERR,
		  "%s(): no valid HCI devices were found", __FUNCTION__);
		ret = 1;
	}

	get_max_devices_leave:
	if(dl) free(dl);
	return ret;
}


#define MAX_DEVICES 128
/* main */
int main (int argc, char **argv)
{
	int opt, ctl;
	struct sigaction act;
	sigset_t sset;
	char *cfg_filename = NULL;

/*
	printf("gdb ./btscanner %d\n", getpid());
	sleep(5);
*/

	/* get the args */
	opterr = 0;
	while ((opt=getopt_long(argc, argv, "+hc:r", main_options, NULL)) != -1)
		switch(opt) {
		case 'c':
			cfg_filename = optarg;
			break;
		case 'h':
			opterr++;
			break;
		}

	if (opterr) {
		usage(argv[0]);
		exit(0);
	}

	/* intialise the config options */
	if(cfg_parsefile(cfg_filename)) {
		fprintf(stderr, "Config file validation error\n");
		exit(1);
	}

	/* startup the logging */
	if(log_init()) {
		fprintf(stderr, "Logging failed to initialise\n");
		exit(1);
	}

	/* initialise the signals */
	memset (&sset, 0, sizeof(sset));
	sigfillset(&sset);
	sigdelset(&sset, SIGKILL);
	sigdelset(&sset, SIGSTOP);
	sigdelset(&sset, SIGTERM);
	sigdelset(&sset, SIGINT);
	sigdelset(&sset, SIGSEGV);
	sigdelset(&sset, SIGWINCH); /* need for wresize */
	if (-1 == sigprocmask(SIG_SETMASK, &sset, NULL)) {
		applog(LOG_ERR,
		  "%s::sigprocmask(): %s", __FUNCTION__, strerror(errno));
		exit(0);
	}
	if (0 != pthread_sigmask(SIG_SETMASK, &sset, NULL)) {
		applog(LOG_ERR,
		  "%s::sigprocmask(): %s", __FUNCTION__, strerror(errno));
		exit(0);
	}

	/* initialise the signal handler */
	memset(&act, 0, sizeof(act));
	act.sa_handler = handlesig;
	sigfillset(&(act.sa_mask));
	sigdelset(&(act.sa_mask), SIGKILL);
	sigdelset(&(act.sa_mask), SIGSTOP);
	if (-1 == sigaction(SIGTERM, &act, NULL)) {
		applog(LOG_ERR,
		  "%s::sigaction(): %s", __FUNCTION__, strerror(errno));
		exit(0);
	}
	if (-1 == sigaction(SIGINT, &act, NULL)) {
		applog(LOG_ERR,
		  "%s::sigaction(): %s", __FUNCTION__, strerror(errno));
		exit(0);
	}
	act.sa_handler = dointerrupt;
	if (-1 == sigaction(SIGUSR1, &act, NULL)) {
		applog(LOG_ERR,
		  "%s::sigaction(): %s", __FUNCTION__, strerror(errno));
		exit(0);
	}

	/* initialise the oui db */
	if (ouidb_init(cfg_oui_filename()))
		exit(0);

	/* initialise the linked list functions */
	ll_init();

	/* firstly open a raw socket */
	if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
		applog(LOG_ERR,
		  "%s::socket(): %s", __FUNCTION__, strerror(errno));
		exit(0);
    }

	/* get a list of available hci devices */
	hcihead = NULL;
	if(get_max_devices(ctl)) {
		fprintf(stderr, "No Bluetooth devices available\n");
		goto btscanner_leave;
	}

	/* start the screen runner */
	screen_on();

	/* run the main screen */
	screen_run();

	/* goodbye */
	screen_off();

	btscanner_leave:
	if (hcihead) hci_devices_free(hcihead);
	if (ctl != -1) {
		shutdown(ctl, 2);
		close(ctl);
	}
	/* tidy up */
	ouidb_close();
	ll_free();
	log_close();
	cfg_cleanup();
	exit(0);
}

