| 1 | /* |
|---|
| 2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: |
|---|
| 3 | * |
|---|
| 4 | * Andreas Langer <a.langer@q-dsl.de>, Marek Lindner <lindner_marek@yahoo.de> |
|---|
| 5 | * |
|---|
| 6 | * This program is free software; you can redistribute it and/or |
|---|
| 7 | * modify it under the terms of version 2 of the GNU General Public |
|---|
| 8 | * License as published by the Free Software Foundation. |
|---|
| 9 | * |
|---|
| 10 | * This program is distributed in the hope that it will be useful, but |
|---|
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 13 | * General Public License for more details. |
|---|
| 14 | * |
|---|
| 15 | * You should have received a copy of the GNU General Public License |
|---|
| 16 | * along with this program; if not, write to the Free Software |
|---|
| 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|---|
| 18 | * 02110-1301, USA |
|---|
| 19 | * |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | |
|---|
| 24 | #define _GNU_SOURCE |
|---|
| 25 | #include <stdio.h> |
|---|
| 26 | #include <stdint.h> |
|---|
| 27 | #include <limits.h> |
|---|
| 28 | #include <stdlib.h> |
|---|
| 29 | #include <errno.h> |
|---|
| 30 | #include <string.h> |
|---|
| 31 | |
|---|
| 32 | #include "main.h" |
|---|
| 33 | #include "bat-hosts.h" |
|---|
| 34 | #include "hash.h" |
|---|
| 35 | |
|---|
| 36 | |
|---|
| 37 | static struct hashtable_t *host_hash = NULL; |
|---|
| 38 | const char *bat_hosts_path[3] = {"/etc/bat-hosts", "~/bat-hosts", "bat-hosts"}; |
|---|
| 39 | |
|---|
| 40 | |
|---|
| 41 | static int compare_mac(void *data1, void *data2) |
|---|
| 42 | { |
|---|
| 43 | return (memcmp(data1, data2, sizeof(struct ether_addr)) == 0 ? 1 : 0); |
|---|
| 44 | } |
|---|
| 45 | |
|---|
| 46 | static int choose_mac(void *data, int32_t size) |
|---|
| 47 | { |
|---|
| 48 | unsigned char *key= data; |
|---|
| 49 | uint32_t hash = 0, m_size = sizeof(struct ether_addr); |
|---|
| 50 | size_t i; |
|---|
| 51 | |
|---|
| 52 | for (i = 0; i < m_size; i++) { |
|---|
| 53 | hash += key[i]; |
|---|
| 54 | hash += (hash << 10); |
|---|
| 55 | hash ^= (hash >> 6); |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | hash += (hash << 3); |
|---|
| 59 | hash ^= (hash >> 11); |
|---|
| 60 | hash += (hash << 15); |
|---|
| 61 | |
|---|
| 62 | return (hash % size); |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | static void parse_hosts_file(struct hashtable_t **hash, const char path[]) |
|---|
| 66 | { |
|---|
| 67 | FILE *fd; |
|---|
| 68 | char *line_ptr = NULL; |
|---|
| 69 | char name[HOST_NAME_MAX_LEN], mac_str[18]; |
|---|
| 70 | struct ether_addr *mac_addr; |
|---|
| 71 | struct bat_host *bat_host; |
|---|
| 72 | struct hashtable_t *swaphash; |
|---|
| 73 | size_t len = 0; |
|---|
| 74 | |
|---|
| 75 | name[0] = mac_str[0] = '\0'; |
|---|
| 76 | |
|---|
| 77 | fd = fopen(path, "r"); |
|---|
| 78 | if (!fd) |
|---|
| 79 | return; |
|---|
| 80 | |
|---|
| 81 | while (getline(&line_ptr, &len, fd) != -1) { |
|---|
| 82 | /* ignore empty lines and comments */ |
|---|
| 83 | if ((line_ptr[0] == '\n') || (line_ptr[0] == '#')) |
|---|
| 84 | continue; |
|---|
| 85 | |
|---|
| 86 | if (sscanf(line_ptr, "%17[^ \t]%49s\n", mac_str, name) != 2) { |
|---|
| 87 | fprintf(stderr, "Warning - unrecognized bat-host definition: %s", line_ptr); |
|---|
| 88 | continue; |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | mac_addr = ether_aton(mac_str); |
|---|
| 92 | if (!mac_addr) { |
|---|
| 93 | fprintf(stderr, "Warning - invalid mac address in '%s' detected: %s\n", path, mac_str); |
|---|
| 94 | continue; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | bat_host = bat_hosts_find_by_mac((char *)mac_addr); |
|---|
| 98 | |
|---|
| 99 | /* mac entry already exists - we found a new name for it */ |
|---|
| 100 | if (bat_host) { |
|---|
| 101 | fprintf(stderr, "Warning - mac already known (changing name from '%s' to '%s'): %s\n", |
|---|
| 102 | bat_host->name, name, mac_str); |
|---|
| 103 | strncpy(bat_host->name, name, HOST_NAME_MAX_LEN - 1); |
|---|
| 104 | continue; |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | bat_host = bat_hosts_find_by_name(name); |
|---|
| 108 | |
|---|
| 109 | /* name entry already exists - we found a new mac address for it */ |
|---|
| 110 | if (bat_host) { |
|---|
| 111 | fprintf(stderr, "Warning - name already known (changing mac from '%s' to '%s'): %s\n", |
|---|
| 112 | ether_ntoa(&bat_host->mac_addr), mac_str, name); |
|---|
| 113 | hash_remove(*hash, bat_host); |
|---|
| 114 | free(bat_host); |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | bat_host = malloc(sizeof(struct bat_host)); |
|---|
| 118 | |
|---|
| 119 | if (!bat_host) { |
|---|
| 120 | fprintf(stderr, "Error - could not allocate memory: %s\n", strerror(errno)); |
|---|
| 121 | goto out; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | memcpy(&bat_host->mac_addr, mac_addr, sizeof(struct ether_addr)); |
|---|
| 125 | strncpy(bat_host->name, name, HOST_NAME_MAX_LEN - 1); |
|---|
| 126 | |
|---|
| 127 | hash_add(*hash, bat_host); |
|---|
| 128 | |
|---|
| 129 | if ((*hash)->elements * 4 > (*hash)->size) { |
|---|
| 130 | swaphash = hash_resize((*hash), (*hash)->size * 2); |
|---|
| 131 | |
|---|
| 132 | if (swaphash == NULL) |
|---|
| 133 | fprintf(stderr, "Warning - couldn't resize bat hosts hash table\n"); |
|---|
| 134 | else |
|---|
| 135 | *hash = swaphash; |
|---|
| 136 | } |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | out: |
|---|
| 140 | if (fd) |
|---|
| 141 | fclose(fd); |
|---|
| 142 | if (line_ptr) |
|---|
| 143 | free(line_ptr); |
|---|
| 144 | return; |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | void bat_hosts_init(void) |
|---|
| 148 | { |
|---|
| 149 | unsigned int i, j, parse; |
|---|
| 150 | char confdir[CONF_DIR_LEN]; |
|---|
| 151 | char *homedir; |
|---|
| 152 | size_t locations = sizeof(bat_hosts_path) / sizeof(char *); |
|---|
| 153 | char *normalized; |
|---|
| 154 | |
|---|
| 155 | /*** |
|---|
| 156 | * realpath could allocate the memory for us but some embedded libc |
|---|
| 157 | * implementations seem to expect a buffer as second argument |
|---|
| 158 | */ |
|---|
| 159 | normalized = malloc(locations * PATH_MAX); |
|---|
| 160 | if (!normalized) { |
|---|
| 161 | printf("Warning - could not get memory for bat-hosts file parsing\n"); |
|---|
| 162 | return; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | memset(normalized, 0, locations * PATH_MAX); |
|---|
| 166 | host_hash = hash_new(64, compare_mac, choose_mac); |
|---|
| 167 | |
|---|
| 168 | if (!host_hash) { |
|---|
| 169 | printf("Warning - could not create bat hosts hash table\n"); |
|---|
| 170 | return; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | homedir = getenv("HOME"); |
|---|
| 174 | |
|---|
| 175 | for (i = 0; i < locations; i++) { |
|---|
| 176 | strcpy(confdir, ""); |
|---|
| 177 | |
|---|
| 178 | if (strlen(bat_hosts_path[i]) >= 2 |
|---|
| 179 | && bat_hosts_path[i][0] == '~' && bat_hosts_path[i][1] == '/') { |
|---|
| 180 | strncpy(confdir, homedir, CONF_DIR_LEN); |
|---|
| 181 | confdir[CONF_DIR_LEN - 1] = '\0'; |
|---|
| 182 | strncat(confdir, &bat_hosts_path[i][1], CONF_DIR_LEN - strlen(confdir)); |
|---|
| 183 | } else { |
|---|
| 184 | strncpy(confdir, bat_hosts_path[i], CONF_DIR_LEN); |
|---|
| 185 | confdir[CONF_DIR_LEN - 1] = '\0'; |
|---|
| 186 | } |
|---|
| 187 | |
|---|
| 188 | if (!realpath(confdir, normalized + (i * PATH_MAX))) |
|---|
| 189 | continue; |
|---|
| 190 | |
|---|
| 191 | /* check for duplicates: don't parse the same file twice */ |
|---|
| 192 | parse = 1; |
|---|
| 193 | for (j = 0; j < i; j++) { |
|---|
| 194 | if (strncmp(normalized + (i * PATH_MAX), normalized + (j * PATH_MAX), CONF_DIR_LEN) == 0) { |
|---|
| 195 | parse = 0; |
|---|
| 196 | break; |
|---|
| 197 | } |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | if (parse) |
|---|
| 201 | parse_hosts_file(&host_hash, normalized + (i * PATH_MAX)); |
|---|
| 202 | } |
|---|
| 203 | |
|---|
| 204 | free(normalized); |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | struct bat_host *bat_hosts_find_by_name(char *name) |
|---|
| 208 | { |
|---|
| 209 | struct hash_it_t *hashit = NULL; |
|---|
| 210 | struct bat_host *bat_host = NULL, *tmp_bat_host; |
|---|
| 211 | |
|---|
| 212 | if (!host_hash) |
|---|
| 213 | return NULL; |
|---|
| 214 | |
|---|
| 215 | while (NULL != (hashit = hash_iterate(host_hash, hashit))) { |
|---|
| 216 | tmp_bat_host = (struct bat_host *)hashit->bucket->data; |
|---|
| 217 | |
|---|
| 218 | if (strncmp(tmp_bat_host->name, name, HOST_NAME_MAX_LEN - 1) == 0) |
|---|
| 219 | bat_host = tmp_bat_host; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | return bat_host; |
|---|
| 223 | } |
|---|
| 224 | |
|---|
| 225 | struct bat_host *bat_hosts_find_by_mac(char *mac) |
|---|
| 226 | { |
|---|
| 227 | if (!host_hash) |
|---|
| 228 | return NULL; |
|---|
| 229 | |
|---|
| 230 | return (struct bat_host *)hash_find(host_hash, mac); |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | static void bat_host_free(void *data) |
|---|
| 234 | { |
|---|
| 235 | free(data); |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | void bat_hosts_free(void) |
|---|
| 239 | { |
|---|
| 240 | if (host_hash) |
|---|
| 241 | hash_delete(host_hash, bat_host_free); |
|---|
| 242 | } |
|---|