/*
 * 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.
 */

/*
 * ouidb.c: Reads in an oui database and stores it in memory
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

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

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

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

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

#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <regex.h>

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.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>

#define OUI_RE "^([0-9a-zA-Z]{6}) (.*)[\n\r \t]+$"

struct oui
{
	uint32_t id;
	char *name;
	struct oui *next;
};
typedef struct oui oui_t;

static oui_t *ouihead = NULL;

/* convert a bdaddr to a hex addr */
uint32_t ba2int(bdaddr_t *ba)
{
	uint32_t oui;

	oui = ba->b[5];
	oui <<= 8;
	oui |= ba->b[4];
	oui <<= 8;
	oui |= ba->b[3];

	return oui;
}


/* initalise the gdbm file and read the oui list */
#define OUIDB_SZ 128
int ouidb_init (const char *filename)
{
	FILE *f;
	char buf[OUIDB_SZ], *s;
	regex_t preg;
	regmatch_t pmatch[4];
	int i;
	oui_t *ouitail, *ouinew;

	ouihead = ouitail = NULL;

	i = regcomp(&preg, OUI_RE, REG_EXTENDED);
	if (i != 0) {
		fprintf(stderr, "Unable to compile re: \"%s\"\n", OUI_RE);
		return 1;
	}

	fprintf(stdout, "Opening the OUI database\n");
	f = fopen(filename, "r");
	if (NULL == f) {
		fprintf(stdout, "Error opening the OUI database: %s\n",
		  strerror(errno));
		return 1;
	}
	fprintf(stdout, "Reading the OUI database\n");

	while (NULL != fgets(buf, OUIDB_SZ, f)) {
		if(regexec(&preg, buf, 4, pmatch, 0))
			continue;

		buf[pmatch[1].rm_eo] = 0;
		buf[pmatch[2].rm_eo] = 0;

		ouinew = (oui_t*)malloc(sizeof(oui_t));
		if (!ouinew) {
			fprintf(stderr, "%s:malloc(): %s\n",
			  __FUNCTION__, strerror(errno));
			return 1;
		}
		memset(ouinew, 0, sizeof(oui_t));
		ouinew->id = strtol(buf+pmatch[1].rm_so, (char**)NULL, 16);

		i = pmatch[2].rm_eo-pmatch[2].rm_so;
		s = (char*)malloc(sizeof(char) * (i+1));
		if (!s) {
			fprintf(stderr, "%s:malloc(): %s\n",
			  __FUNCTION__, strerror(errno));
			return 1;
		}
		strncpy(s, buf+pmatch[2].rm_so, i);
		s[i] = 0;
		ouinew->name = s;

		if (ouitail)
			ouitail->next = ouinew;
		else
			ouihead = ouinew;
		ouitail = ouinew;
	}

	fprintf(stdout, "Finished reading the OUI database\n");
	fclose(f);
	regfree(&preg);
	return 0;
}

/* query a device */
char *ouidb_query(bdaddr_t *ba)
{
	oui_t *curr;
	uint32_t id = ba2int(ba);

	for(curr = ouihead; curr; curr = curr->next) {
		if (curr->id == id)
			return curr->name;
	}

	return NULL;
}


/* close down and cleanup */
int ouidb_close (void)
{
	oui_t *curr, *next;

	for(curr = ouihead; curr; curr = next) {
		next = curr->next;
		free(curr->name);
		free(curr);
	}
	ouihead = NULL;

	return 0;
}

