bat-hosts.c 5.72 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
/* Copyright (C) 2007-2019  B.A.T.M.A.N. contributors:
3
 *
4
 * Andreas Langer <an.langer@gmx.de>, Marek Lindner <mareklindner@neomailbox.ch>
5
 *
6
 * License-Filename: LICENSES/preferred/GPL-2.0
7 8 9 10 11
 */



#include <stdint.h>
12
#include <stdio.h>
13
#include <limits.h>
14
#include <stdlib.h>
15
#include <errno.h>
16
#include <string.h>
17 18
#include <stddef.h>
#include <netinet/ether.h>
19 20 21

#include "bat-hosts.h"
#include "hash.h"
22
#include "functions.h"
23 24


25
static struct hashtable_t *host_hash = NULL;
26
const char *bat_hosts_path[3] = {"/etc/bat-hosts", "~/bat-hosts", "bat-hosts"};
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52


static int compare_mac(void *data1, void *data2)
{
	return (memcmp(data1, data2, sizeof(struct ether_addr)) == 0 ? 1 : 0);
}

static int choose_mac(void *data, int32_t size)
{
	unsigned char *key= data;
	uint32_t hash = 0, m_size = sizeof(struct ether_addr);
	size_t i;

	for (i = 0; i < m_size; i++) {
		hash += key[i];
		hash += (hash << 10);
		hash ^= (hash >> 6);
	}

	hash += (hash << 3);
	hash ^= (hash >> 11);
	hash += (hash << 15);

	return (hash % size);
}

53
static void parse_hosts_file(struct hashtable_t **hash, const char path[], int read_opt)
54 55
{
	FILE *fd;
56
	char *line_ptr = NULL;
57 58
	char name[HOST_NAME_MAX_LEN], mac_str[18];
	struct ether_addr *mac_addr;
59 60
	struct bat_host *bat_host;
	struct hashtable_t *swaphash;
61
	size_t len = 0;
62

63
	name[0] = mac_str[0] = '\0';
64

65
	fd = fopen(path, "r");
66
	if (!fd)
67 68
		return;

69 70 71 72 73
	while (getline(&line_ptr, &len, fd) != -1) {
		/* ignore empty lines and comments */
		if ((line_ptr[0] == '\n') || (line_ptr[0] == '#'))
			continue;

74
		if (sscanf(line_ptr, "%17[^ \t]%49s\n", mac_str, name) != 2) {
75 76
			if (read_opt & USE_BAT_HOSTS)
				fprintf(stderr, "Warning - unrecognized bat-host definition: %s", line_ptr);
77 78
			continue;
		}
79

80 81
		mac_addr = ether_aton(mac_str);
		if (!mac_addr) {
82 83
			if (read_opt & USE_BAT_HOSTS)
				fprintf(stderr, "Warning - invalid mac address in '%s' detected: %s\n", path, mac_str);
84 85 86 87 88 89 90
			continue;
		}

		bat_host = bat_hosts_find_by_mac((char *)mac_addr);

		/* mac entry already exists - we found a new name for it */
		if (bat_host) {
91 92 93 94
			/* if the mac addresses and the names are the same we can safely ignore the entry */
			if (strcmp(bat_host->name, name) == 0)
				continue;

95 96
			if (read_opt & USE_BAT_HOSTS)
				fprintf(stderr, "Warning - mac already known (changing name from '%s' to '%s'): %s\n",
97
					bat_host->name, name, mac_str);
98 99
			strncpy(bat_host->name, name, HOST_NAME_MAX_LEN);
			bat_host->name[HOST_NAME_MAX_LEN - 1] = '\0';
100 101 102 103 104 105 106
			continue;
		}

		bat_host = bat_hosts_find_by_name(name);

		/* name entry already exists - we found a new mac address for it */
		if (bat_host) {
107 108
			if (read_opt & USE_BAT_HOSTS)
				fprintf(stderr, "Warning - name already known (changing mac from '%s' to '%s'): %s\n",
109
					ether_ntoa(&bat_host->mac_addr), mac_str, name);
110
			hash_remove(*hash, bat_host);
111
			free(bat_host);
112 113 114 115 116
		}

		bat_host = malloc(sizeof(struct bat_host));

		if (!bat_host) {
117
			if (read_opt & USE_BAT_HOSTS)
118
				perror("Error - could not allocate memory");
119
			goto out;
120 121
		}

122
		memcpy(&bat_host->mac_addr, mac_addr, sizeof(struct ether_addr));
123 124
		strncpy(bat_host->name, name, HOST_NAME_MAX_LEN);
		bat_host->name[HOST_NAME_MAX_LEN - 1] = '\0';
125

126
		hash_add(*hash, bat_host);
127

128 129
		if ((*hash)->elements * 4 > (*hash)->size) {
			swaphash = hash_resize((*hash), (*hash)->size * 2);
130

131
			if (swaphash)
132
				*hash = swaphash;
133 134
			else if (read_opt & USE_BAT_HOSTS)
				fprintf(stderr, "Warning - couldn't resize bat hosts hash table\n");
135 136 137
		}
	}

138 139 140
out:
	if (fd)
		fclose(fd);
141 142
	if (line_ptr)
		free(line_ptr);
143 144 145
	return;
}

146
void bat_hosts_init(int read_opt)
147
{
148
	unsigned int i, j, parse;
149 150
	char confdir[CONF_DIR_LEN];
	char *homedir;
151
	size_t locations = sizeof(bat_hosts_path) / sizeof(char *);
152 153 154 155 156 157 158 159
	char *normalized;

	/***
	 * realpath could allocate the memory for us but some embedded libc
	 * implementations seem to expect a buffer as second argument
	 */
	normalized = malloc(locations * PATH_MAX);
	if (!normalized) {
160 161
		if (read_opt & USE_BAT_HOSTS)
			printf("Warning - could not get memory for bat-hosts file parsing\n");
162 163
		return;
	}
164

165
	memset(normalized, 0, locations * PATH_MAX);
166 167
	host_hash = hash_new(64, compare_mac, choose_mac);

168
	if (!host_hash) {
169 170
		if (read_opt & USE_BAT_HOSTS)
			printf("Warning - could not create bat hosts hash table\n");
171
		goto out;
172
	}
173

174 175
	homedir = getenv("HOME");

176
	for (i = 0; i < locations; i++) {
177
		strcpy(confdir, "");
178

179 180
		if (strlen(bat_hosts_path[i]) >= 2
		    && bat_hosts_path[i][0] == '~' && bat_hosts_path[i][1] == '/') {
181 182 183
			if (!homedir)
				continue;

184
			snprintf(confdir, CONF_DIR_LEN, "%s%s", homedir, &bat_hosts_path[i][1]);
185 186 187 188
		} else {
			strncpy(confdir, bat_hosts_path[i], CONF_DIR_LEN);
			confdir[CONF_DIR_LEN - 1] = '\0';
		}
189

190
		if (!realpath(confdir, normalized + (i * PATH_MAX)))
191 192 193 194 195
			continue;

		/* check for duplicates: don't parse the same file twice */
		parse = 1;
		for (j = 0; j < i; j++) {
196
			if (strncmp(normalized + (i * PATH_MAX), normalized + (j * PATH_MAX), CONF_DIR_LEN) == 0) {
197 198 199 200 201
				parse = 0;
				break;
			}
		}

202
		if (parse)
203
			parse_hosts_file(&host_hash, normalized + (i * PATH_MAX), read_opt);
204 205
	}

206
out:
207
	free(normalized);
208 209 210 211 212 213 214
}

struct bat_host *bat_hosts_find_by_name(char *name)
{
	struct hash_it_t *hashit = NULL;
	struct bat_host *bat_host = NULL, *tmp_bat_host;

215 216 217
	if (!host_hash)
		return NULL;

218 219 220
	while (NULL != (hashit = hash_iterate(host_hash, hashit))) {
		tmp_bat_host = (struct bat_host *)hashit->bucket->data;

221
		if (strncmp(tmp_bat_host->name, name, HOST_NAME_MAX_LEN - 1) == 0)
222 223 224 225 226 227 228 229
			bat_host = tmp_bat_host;
	}

	return bat_host;
}

struct bat_host *bat_hosts_find_by_mac(char *mac)
{
230 231 232
	if (!host_hash)
		return NULL;

233 234 235
	return (struct bat_host *)hash_find(host_hash, mac);
}

236 237 238 239 240
static void bat_host_free(void *data)
{
	free(data);
}

241 242 243
void bat_hosts_free(void)
{
	if (host_hash)
244
		hash_delete(host_hash, bat_host_free);
245
}