jcs
/subtext
/amendments
/509
ipdb: Add IP geolocation database lookup module
jcs made amendment 509 about 1 year ago
--- ipdb.c Thu Jun 15 09:14:37 2023
+++ ipdb.c Thu Jun 15 09:14:37 2023
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ipdb.h"
+#include "logger.h"
+#include "tcp.h" /* for long2ip/ip2long */
+#include "util.h"
+
+struct ipdb_file *
+ipdb_open(char *filename)
+{
+ struct ipdb_file *ipdb;
+ size_t size;
+ short error, fh;
+
+ CtoPstr(filename);
+ error = FSOpen(filename, 0, &fh);
+ PtoCstr(filename);
+ if (error) {
+ logger_printf("[ipdb] Failed opening database %s: %d", filename,
+ err);
+ return NULL;
+ }
+
+ ipdb = xmalloc(sizeof(struct ipdb_file));
+ if (ipdb == NULL) {
+ logger_printf("[ipdb] Failed malloc");
+ goto bail;
+ }
+
+ ipdb->fh = fh;
+ SetFPos(ipdb->fh, fsFromLEOF, 0);
+ GetFPos(ipdb->fh, &ipdb->fsize);
+ SetFPos(ipdb->fh, fsFromStart, 0);
+
+ if (ipdb->fsize == 0) {
+ logger_printf("[ipdb] Empty database file %s", filename);
+ goto bail;
+ }
+
+ size = sizeof(ipdb->starts);
+ error = FSRead(ipdb->fh, &size, &ipdb->starts);
+ if (error) {
+ logger_printf("[ipdb] Error reading network table from %s: %d",
+ filename, error);
+ goto bail;
+ }
+
+ return ipdb;
+
+bail:
+ FSClose(ipdb->fh);
+ if (ipdb != NULL)
+ xfree(&ipdb);
+ return NULL;
+}
+
+void
+ipdb_close(struct ipdb_file **ipdbp)
+{
+ struct ipdb_file *ipdb = (struct ipdb_file *)&ipdbp;
+
+ FSClose(ipdb->fh);
+ xfree(ipdbp);
+}
+
+char *
+ipdb_lookup(struct ipdb_file *ipdb, u_int32_t ip)
+{
+ size_t size;
+ u_int32_t range[3], net_start, net_end, start, end, mid, last_mid, pos;
+ short error;
+ unsigned char ip_o[4], locsize, *tmp;
+ char *loc;
+
+ tmp = (unsigned char *)&ip;
+ ip_o[0] = tmp[0];
+ ip_o[1] = tmp[1];
+ ip_o[2] = tmp[2];
+ ip_o[3] = tmp[3];
+
+ net_start = ipdb->starts[ip_o[0]];
+ if (net_start == 0)
+ return NULL;
+
+ net_end = 0;
+ if (ip_o[0] != 255)
+ net_end = ipdb->starts[ip_o[0] + 1];
+ if (net_end == 0)
+ net_end = ipdb->fsize;
+
+ start = net_start;
+ end = net_end;
+ pos = 0;
+ last_mid = 0;
+
+ for (;;) {
+ mid = start + ((end - start) / 2);
+ /* align */
+ mid -= ((mid - net_start) % (sizeof(u_int32_t) * 3));
+ if (mid == last_mid)
+ break;
+ last_mid = mid;
+
+ SetFPos(ipdb->fh, fsFromStart, mid);
+ size = sizeof(range);
+ error = FSRead(ipdb->fh, &size, &range);
+ if (error) {
+ logger_printf("[ipdb] Error reading from database: %d", error);
+ return NULL;
+ }
+
+ if (ip >= range[0] && ip <= range[1]) {
+ pos = range[2];
+ break;
+ }
+
+ if (ip < range[0])
+ /* too high, clamp to [start, mid] */
+ end = mid;
+ else if (ip > range[1])
+ /* too low, clamp to [mid, end] */
+ start = mid;
+
+ if (mid < net_start || mid > net_end)
+ break;
+ }
+
+ if (!pos)
+ return NULL;
+
+ SetFPos(ipdb->fh, fsFromStart, pos);
+ size = 1;
+ error = FSRead(ipdb->fh, &size, &locsize);
+ if (error) {
+ logger_printf("[ipdb] Error reading from database: %d", error);
+ return NULL;
+ }
+
+ loc = xmalloc(locsize + 1);
+ if (loc == NULL) {
+ logger_printf("[ipdb] Out of memory");
+ return NULL;
+ }
+ size = locsize;
+ error = FSRead(ipdb->fh, &size, loc);
+ if (error) {
+ logger_printf("[ipdb] Error reading from database: %d", error);
+ xfree(&loc);
+ return NULL;
+ }
+ loc[locsize] = '\0';
+
+ return loc;
+}
--- ipdb.h Wed Jun 14 21:02:27 2023
+++ ipdb.h Wed Jun 14 21:02:27 2023
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, 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.
+ */
+
+/* ipdb database format:
+
+ [uint32_t][0] file offset of network "0.0.0.0/8"
+ [uint32_t][1] file offset of network "1.0.0.0/8"
+ ...
+ [uint32_t][255] file offset of network "255.0.0.0/8"
+ [uint8_t] location 0 length
+ [char][length] location 0
+ [uint8_t] location 1 length
+ [char][length] location 1
+ ...
+ [uint32_t] network 0 start (1.0.0.0)
+ [uint32_t] network 0 end (1.0.0.255)
+ [uint32_t] network 0 location pointer
+ [uint32_t] network 1 start (1.0.1.0)
+ [uint32_t] network 1 end (1.0.1.255)
+ [uint32_t] network 1 location pointer
+ ...
+
+ For an ip "5.6.7.8", look up table[5] and seek there, then do a binary
+ search between that location and that of table[6] (or end of file if
+ 255).
+
+ If the table location points to 0, that network is not in the file.
+*/
+
+#ifndef __IPDB_H__
+#define __IPDB_H__
+
+#include "util.h"
+
+struct ipdb_file {
+ short fh;
+ size_t fsize;
+ u_int32_t starts[256];
+};
+
+struct ipdb_file * ipdb_open(char *filename);
+void ipdb_close(struct ipdb_file **ipdbp);
+char * ipdb_lookup(struct ipdb_file *ipdb, u_int32_t ip);
+
+#endif