Download
jcs
/subtext
/ipdb.c
(View History)
jcs ipdb: Add IP geolocation database lookup module | Latest amendment: 509 on 2023-06-15 |
1 | /* |
2 | * Copyright (c) 2023 joshua stein <jcs@jcs.org> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <stdio.h> |
18 | #include <stdlib.h> |
19 | #include <string.h> |
20 | #include "ipdb.h" |
21 | #include "logger.h" |
22 | #include "tcp.h" /* for long2ip/ip2long */ |
23 | #include "util.h" |
24 | |
25 | struct ipdb_file * |
26 | ipdb_open(char *filename) |
27 | { |
28 | struct ipdb_file *ipdb; |
29 | size_t size; |
30 | short error, fh; |
31 | |
32 | CtoPstr(filename); |
33 | error = FSOpen(filename, 0, &fh); |
34 | PtoCstr(filename); |
35 | if (error) { |
36 | logger_printf("[ipdb] Failed opening database %s: %d", filename, |
37 | err); |
38 | return NULL; |
39 | } |
40 | |
41 | ipdb = xmalloc(sizeof(struct ipdb_file)); |
42 | if (ipdb == NULL) { |
43 | logger_printf("[ipdb] Failed malloc"); |
44 | goto bail; |
45 | } |
46 | |
47 | ipdb->fh = fh; |
48 | SetFPos(ipdb->fh, fsFromLEOF, 0); |
49 | GetFPos(ipdb->fh, &ipdb->fsize); |
50 | SetFPos(ipdb->fh, fsFromStart, 0); |
51 | |
52 | if (ipdb->fsize == 0) { |
53 | logger_printf("[ipdb] Empty database file %s", filename); |
54 | goto bail; |
55 | } |
56 | |
57 | size = sizeof(ipdb->starts); |
58 | error = FSRead(ipdb->fh, &size, &ipdb->starts); |
59 | if (error) { |
60 | logger_printf("[ipdb] Error reading network table from %s: %d", |
61 | filename, error); |
62 | goto bail; |
63 | } |
64 | |
65 | return ipdb; |
66 | |
67 | bail: |
68 | FSClose(ipdb->fh); |
69 | if (ipdb != NULL) |
70 | xfree(&ipdb); |
71 | return NULL; |
72 | } |
73 | |
74 | void |
75 | ipdb_close(struct ipdb_file **ipdbp) |
76 | { |
77 | struct ipdb_file *ipdb = (struct ipdb_file *)&ipdbp; |
78 | |
79 | FSClose(ipdb->fh); |
80 | xfree(ipdbp); |
81 | } |
82 | |
83 | char * |
84 | ipdb_lookup(struct ipdb_file *ipdb, u_int32_t ip) |
85 | { |
86 | size_t size; |
87 | u_int32_t range[3], net_start, net_end, start, end, mid, last_mid, pos; |
88 | short error; |
89 | unsigned char ip_o[4], locsize, *tmp; |
90 | char *loc; |
91 | |
92 | tmp = (unsigned char *)&ip; |
93 | ip_o[0] = tmp[0]; |
94 | ip_o[1] = tmp[1]; |
95 | ip_o[2] = tmp[2]; |
96 | ip_o[3] = tmp[3]; |
97 | |
98 | net_start = ipdb->starts[ip_o[0]]; |
99 | if (net_start == 0) |
100 | return NULL; |
101 | |
102 | net_end = 0; |
103 | if (ip_o[0] != 255) |
104 | net_end = ipdb->starts[ip_o[0] + 1]; |
105 | if (net_end == 0) |
106 | net_end = ipdb->fsize; |
107 | |
108 | start = net_start; |
109 | end = net_end; |
110 | pos = 0; |
111 | last_mid = 0; |
112 | |
113 | for (;;) { |
114 | mid = start + ((end - start) / 2); |
115 | /* align */ |
116 | mid -= ((mid - net_start) % (sizeof(u_int32_t) * 3)); |
117 | if (mid == last_mid) |
118 | break; |
119 | last_mid = mid; |
120 | |
121 | SetFPos(ipdb->fh, fsFromStart, mid); |
122 | size = sizeof(range); |
123 | error = FSRead(ipdb->fh, &size, &range); |
124 | if (error) { |
125 | logger_printf("[ipdb] Error reading from database: %d", error); |
126 | return NULL; |
127 | } |
128 | |
129 | if (ip >= range[0] && ip <= range[1]) { |
130 | pos = range[2]; |
131 | break; |
132 | } |
133 | |
134 | if (ip < range[0]) |
135 | /* too high, clamp to [start, mid] */ |
136 | end = mid; |
137 | else if (ip > range[1]) |
138 | /* too low, clamp to [mid, end] */ |
139 | start = mid; |
140 | |
141 | if (mid < net_start || mid > net_end) |
142 | break; |
143 | } |
144 | |
145 | if (!pos) |
146 | return NULL; |
147 | |
148 | SetFPos(ipdb->fh, fsFromStart, pos); |
149 | size = 1; |
150 | error = FSRead(ipdb->fh, &size, &locsize); |
151 | if (error) { |
152 | logger_printf("[ipdb] Error reading from database: %d", error); |
153 | return NULL; |
154 | } |
155 | |
156 | loc = xmalloc(locsize + 1); |
157 | if (loc == NULL) { |
158 | logger_printf("[ipdb] Out of memory"); |
159 | return NULL; |
160 | } |
161 | size = locsize; |
162 | error = FSRead(ipdb->fh, &size, loc); |
163 | if (error) { |
164 | logger_printf("[ipdb] Error reading from database: %d", error); |
165 | xfree(&loc); |
166 | return NULL; |
167 | } |
168 | loc[locsize] = '\0'; |
169 | |
170 | return loc; |
171 | } |