summaryrefslogtreecommitdiffstats
path: root/uidmap.c
diff options
context:
space:
mode:
authorSimon Rettberg2018-11-09 15:36:21 +0100
committerSimon Rettberg2018-11-09 15:36:21 +0100
commit33db97b72473124697170394efbc993870276c55 (patch)
treeb1f27c1634c07652808af243f245da2809a6e4a3 /uidmap.c
parentMove string functions to lstring.[hc] (diff)
downloadldadp-33db97b72473124697170394efbc993870276c55.tar.gz
ldadp-33db97b72473124697170394efbc993870276c55.tar.xz
ldadp-33db97b72473124697170394efbc993870276c55.zip
Started work on proxy-side uid generation/tracking
Diffstat (limited to 'uidmap.c')
-rw-r--r--uidmap.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/uidmap.c b/uidmap.c
new file mode 100644
index 0000000..8315be2
--- /dev/null
+++ b/uidmap.c
@@ -0,0 +1,262 @@
+#include "hashmap.h"
+#include "lstring.h"
+#include "types.h"
+#include "helper.h"
+#include <stdint.h>
+#include <inttypes.h>
+#include <math.h>
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static struct hashmap _numToName, _nameToNum;
+static BOOL _initDone = FALSE;
+static const char *_saveFileName = NULL;
+
+static const uint16_t MAGIC = 1234;
+static const int64_t MAX_AGE = 86400 * 2; // 2 days
+#define MAX_NAME_LEN 1024
+
+HASHMAP_FUNCS_CREATE(name, struct string, struct user);
+HASHMAP_FUNCS_CREATE(number, uint32_t, struct user);
+
+struct user
+{
+ uint32_t number;
+ const struct string *name;
+ int64_t lastUse;
+};
+
+static void insertTime(const struct string *name, const uint32_t number, const int64_t lastUse)
+{
+ uint8_t *buffer = malloc(sizeof(struct user) + sizeof(struct string) + name->l);
+ struct user *u = (struct user*)buffer;
+ struct string *s = (struct string*)(buffer + sizeof(struct user));
+ char *str = (char*)(buffer + sizeof(struct user) + sizeof(struct string));
+ memcpy(str, name->s, name->l);
+ s->s = str;
+ s->l = name->l;
+ u->name = s;
+ u->number = number;
+ u->lastUse = lastUse;
+ if (name_hashmap_put(&_nameToNum, u->name, u) != u) {
+ plog(DEBUG_WARNING, "[uidmap] Collision when inserting %.*s by name (%"PRIu32")", (int)s->l, s->s, number);
+ }
+ if (number_hashmap_put(&_numToName, &u->number, u) != u) {
+ plog(DEBUG_WARNING, "[uidmap] Collision when inserting %.*s by number (%"PRIu32")", (int)s->l, s->s, number);
+ }
+ // TODO: Leak
+}
+
+static void insert(const struct string *name, const uint32_t number)
+{
+ insertTime(name, number, time(NULL));
+}
+
+static int uint32_compare(const void *a, const void *b)
+{
+ return (*(const uint32_t*)a) != (*(const uint32_t*)b);
+}
+
+static int string_compare(const void *a, const void *b)
+{
+ return !equals((const struct string*)a, (const struct string*)b);
+}
+
+static size_t uint32_hash(const void *key)
+{
+ return *(const uint32_t*)key;
+}
+
+static size_t string_hash(const void *key)
+{
+ const struct string *key_str = (const struct string*)key;
+ size_t hash = 0;
+
+ for (size_t pos = 0; pos < key_str->l; ++pos) {
+ hash += key_str->s[pos];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+uint32_t uidmap_getNumberForName(const struct string *name)
+{
+ // Try string matching
+ char buf[name->l];
+ struct string lower = { .s = buf, .l = name->l };
+ for (size_t i = 0; i < name->l; ++i) {
+ buf[i] = tolower(name->s[i]);
+ }
+ // Get
+ struct user *u = name_hashmap_get(&_nameToNum, &lower);
+ if (u != NULL) {
+ u->lastUse = time(NULL);
+ return u->number;
+ }
+ // Not known yet - try numeric
+ uint32_t asNumber = parseUInt32(name);
+ if (asNumber >= 2000) {
+ u = number_hashmap_get(&_numToName, &asNumber);
+ if (u == NULL) {
+ // No collision, use name as number
+ insert(&lower, asNumber);
+ return asNumber;
+ }
+ plog(DEBUG_WARNING, "%.*s is numeric (%u) but collides", (int)name->l, name->s, asNumber);
+ }
+ // Default case - generate number
+ asNumber = (uint32_t)string_hash(&lower);
+ while (asNumber < 2000) {
+ asNumber = rand() + 2000;
+ }
+ for (;;) {
+ //plog(DEBUG_WARNING, "Using number %u for %.*s", asNumber, (int)name->l, name->s);
+ u = number_hashmap_get(&_numToName, &asNumber);
+ if (u == NULL)
+ break;
+ do {
+ asNumber = rand() + 2000;
+ } while (asNumber < 2000);
+ }
+ insert(&lower, asNumber);
+ return asNumber;
+}
+
+const struct string *uidmap_getNameForNumber(const uint32_t number)
+{
+ struct user *u = number_hashmap_get(&_numToName, &number);
+ if (u == NULL)
+ return NULL;
+ u->lastUse = time(NULL);
+ return u->name;
+}
+
+void uidmap_init(const char *fileName)
+{
+ if (_initDone)
+ return;
+ _initDone = TRUE;
+ hashmap_init(&_numToName, uint32_hash, uint32_compare, 0);
+ hashmap_init(&_nameToNum, string_hash, string_compare, 0);
+ if (fileName == NULL)
+ return;
+ // We have a filename, remember it and try to load DB
+ _saveFileName = strdup(fileName);
+ FILE *fh = fopen(_saveFileName, "rb");
+ if (fh == NULL)
+ return;
+ fseek(fh, 0, SEEK_END);
+ const int fsize = ftell(fh);
+ fseek(fh, 0, SEEK_SET);
+ if (fsize > 0) {
+ const size_t entries = (size_t)fsize / 25;
+ hashmap_destroy(&_numToName);
+ hashmap_destroy(&_nameToNum);
+ hashmap_init(&_numToName, uint32_hash, uint32_compare, entries);
+ hashmap_init(&_nameToNum, string_hash, string_compare, entries);
+ }
+ // Not endian safe, do not copy file around...
+ uint16_t len = 0;
+ int64_t lastUse;
+ uint32_t uidNumber;
+ char nbuffer[MAX_NAME_LEN];
+ struct string name;
+ name.s = nbuffer;
+ if (fread(&len, sizeof(len), 1, fh) != 1 || len != MAGIC) {
+ plog(DEBUG_WARNING, "uid map file missing header magic (%"PRIu16")", len);
+ fclose(fh);
+ return;
+ }
+ int loaded = 0;
+ const int64_t now = time(NULL);
+ for (;;) {
+ if (fread(&len, sizeof(len), 1, fh) != 1)
+ break;
+ if (len > MAX_NAME_LEN) {
+ plog(DEBUG_WARNING, "DB has entry of size %"PRIu16", aborting import");
+ break;
+ }
+ if (fread(&lastUse, sizeof(lastUse), 1, fh) != 1 || fread(&uidNumber, sizeof(uidNumber), 1, fh) != 1) {
+ plog(DEBUG_WARNING, "DB has truncated entry (no lastUse/uidNumber)");
+ break;
+ }
+ if (fread(nbuffer, len, 1, fh) != 1) {
+ plog(DEBUG_WARNING, "DB has truncated entry (no name)");
+ break;
+ }
+ // Check age
+ if (now - lastUse > MAX_AGE)
+ continue;
+ if (lastUse > now)
+ lastUse = now;
+ // Add
+ name.l = len;
+ insertTime(&name, uidNumber, lastUse);
+ loaded++;
+ }
+ fclose(fh);
+ plog(DEBUG_INFO, "Loaded %d entries from %s", loaded, _saveFileName);
+}
+
+void uidmap_cleanupSave()
+{
+ if (!_initDone)
+ return;
+ FILE *fh = NULL;
+ if (_saveFileName != NULL) {
+ fh = fopen(_saveFileName, "wb");
+ if (fh != NULL) {
+ if (fwrite(&MAGIC, sizeof(MAGIC), 1, fh) != 1) {
+ plog(DEBUG_WARNING, "Cannot write magic to %s", _saveFileName);
+ fclose(fh);
+ fh = NULL;
+ }
+ }
+ }
+ const int64_t deadline = time(NULL) - MAX_AGE;
+ struct hashmap_iter *it = hashmap_iter(&_nameToNum);
+ struct user *u = NULL;
+ int saved = 0, removed = 0;
+ uint16_t len;
+ while (it != NULL) {
+ u = name_hashmap_iter_get_data(it);
+ if (u->lastUse < deadline) {
+ it = hashmap_iter_remove(&_nameToNum, it);
+ if (number_hashmap_remove(&_numToName, &u->number) != u) {
+ plog(DEBUG_WARNING, "Could not remove user from numToName that was in nameToNum");
+ }
+ free(u);
+ removed++;
+ continue;
+ }
+ // Save if file
+ if (fh != NULL && u->name->l < MAX_NAME_LEN) {
+ len = (uint16_t)u->name->l;
+ if (fwrite(&len, sizeof(len), 1, fh) != 1
+ || fwrite(&u->lastUse, sizeof(u->lastUse), 1, fh) != 1
+ || fwrite(&u->number, sizeof(u->number), 1, fh) != 1
+ || fwrite(u->name->s, len, 1, fh) != 1
+ ) {
+ plog(DEBUG_WARNING, "Could not write record to %s", _saveFileName);
+ fclose(fh);
+ fh = NULL;
+ } else {
+ saved++;
+ }
+ }
+ it = hashmap_iter_next(&_nameToNum, it);
+ }
+ if (fh != NULL) {
+ fsync(fileno(fh));
+ fclose(fh);
+ }
+ if (saved > 0 || removed > 0) {
+ plog(DEBUG_VERBOSE, "Purged %d old entries, saved %d entries", removed, saved);
+ }
+}
+