geoip2_c 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/ext/geoip2/libmaxminddb/.gitignore +35 -0
  3. data/ext/geoip2/libmaxminddb/.gitmodules +9 -0
  4. data/ext/geoip2/libmaxminddb/.perltidyrc +11 -0
  5. data/ext/geoip2/libmaxminddb/.travis.yml +48 -0
  6. data/ext/geoip2/libmaxminddb/.uncrustify.cfg +78 -0
  7. data/ext/geoip2/libmaxminddb/AUTHORS +0 -0
  8. data/ext/geoip2/libmaxminddb/Changes.md +238 -0
  9. data/ext/geoip2/libmaxminddb/LICENSE +202 -0
  10. data/ext/geoip2/libmaxminddb/Makefile.am +41 -0
  11. data/ext/geoip2/libmaxminddb/NOTICE +13 -0
  12. data/ext/geoip2/libmaxminddb/README.dev.md +58 -0
  13. data/ext/geoip2/libmaxminddb/README.md +122 -0
  14. data/ext/geoip2/libmaxminddb/appveyor.yml +33 -0
  15. data/ext/geoip2/libmaxminddb/bin/Makefile.am +5 -0
  16. data/ext/geoip2/libmaxminddb/bin/mmdblookup.c +397 -0
  17. data/ext/geoip2/libmaxminddb/bootstrap +21 -0
  18. data/ext/geoip2/libmaxminddb/common.mk +7 -0
  19. data/ext/geoip2/libmaxminddb/configure.ac +132 -0
  20. data/ext/geoip2/libmaxminddb/dev-bin/make-man-pages.pl +76 -0
  21. data/ext/geoip2/libmaxminddb/dev-bin/ppa-release.sh +50 -0
  22. data/ext/geoip2/libmaxminddb/dev-bin/regen-prototypes.pl +136 -0
  23. data/ext/geoip2/libmaxminddb/dev-bin/regen-win32-test-projs.pl +54 -0
  24. data/ext/geoip2/libmaxminddb/dev-bin/release.sh +106 -0
  25. data/ext/geoip2/libmaxminddb/dev-bin/uncrustify-all.sh +21 -0
  26. data/ext/geoip2/libmaxminddb/dev-bin/valgrind-all.pl +46 -0
  27. data/ext/geoip2/libmaxminddb/doc/libmaxminddb.md +889 -0
  28. data/ext/geoip2/libmaxminddb/doc/mmdblookup.md +103 -0
  29. data/ext/geoip2/libmaxminddb/include/maxminddb.h +232 -0
  30. data/ext/geoip2/libmaxminddb/include/maxminddb_config.h.in +14 -0
  31. data/ext/geoip2/libmaxminddb/projects/VS12/README.md +59 -0
  32. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb-release.props +32 -0
  33. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb-x64.props +14 -0
  34. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.props +32 -0
  35. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.sln +150 -0
  36. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.vcxproj +141 -0
  37. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.vcxproj.filters +26 -0
  38. data/ext/geoip2/libmaxminddb/projects/VS12/maxminddb_config.h +14 -0
  39. data/ext/geoip2/libmaxminddb/projects/VS12-tests/bad_pointers.vcxproj +105 -0
  40. data/ext/geoip2/libmaxminddb/projects/VS12-tests/basic_lookup.vcxproj +105 -0
  41. data/ext/geoip2/libmaxminddb/projects/VS12-tests/data_entry_list.vcxproj +105 -0
  42. data/ext/geoip2/libmaxminddb/projects/VS12-tests/data_types.vcxproj +105 -0
  43. data/ext/geoip2/libmaxminddb/projects/VS12-tests/dump.vcxproj +105 -0
  44. data/ext/geoip2/libmaxminddb/projects/VS12-tests/get_value.vcxproj +105 -0
  45. data/ext/geoip2/libmaxminddb/projects/VS12-tests/get_value_pointer_bug.vcxproj +105 -0
  46. data/ext/geoip2/libmaxminddb/projects/VS12-tests/ipv4_start_cache.vcxproj +105 -0
  47. data/ext/geoip2/libmaxminddb/projects/VS12-tests/ipv6_lookup_in_ipv4.vcxproj +105 -0
  48. data/ext/geoip2/libmaxminddb/projects/VS12-tests/libtap.vcxproj +85 -0
  49. data/ext/geoip2/libmaxminddb/projects/VS12-tests/maxminddb_test_helper.vcxproj +107 -0
  50. data/ext/geoip2/libmaxminddb/projects/VS12-tests/metadata.vcxproj +105 -0
  51. data/ext/geoip2/libmaxminddb/projects/VS12-tests/metadata_pointers.vcxproj +105 -0
  52. data/ext/geoip2/libmaxminddb/projects/VS12-tests/no_map_get_value.vcxproj +105 -0
  53. data/ext/geoip2/libmaxminddb/projects/VS12-tests/read_node.vcxproj +105 -0
  54. data/ext/geoip2/libmaxminddb/projects/VS12-tests/shared.vcxproj +104 -0
  55. data/ext/geoip2/libmaxminddb/projects/VS12-tests/threads.vcxproj +103 -0
  56. data/ext/geoip2/libmaxminddb/projects/VS12-tests/version.vcxproj +105 -0
  57. data/ext/geoip2/libmaxminddb/projects/test.vcxproj.template +105 -0
  58. data/ext/geoip2/libmaxminddb/src/Makefile.am +9 -0
  59. data/ext/geoip2/libmaxminddb/src/libmaxminddb.pc.in +11 -0
  60. data/ext/geoip2/libmaxminddb/src/maxminddb-compat-util.h +167 -0
  61. data/ext/geoip2/libmaxminddb/src/maxminddb.c +2171 -0
  62. data/ext/geoip2/libmaxminddb/t/Makefile.am +23 -0
  63. data/ext/geoip2/libmaxminddb/t/bad_databases_t.c +66 -0
  64. data/ext/geoip2/libmaxminddb/t/bad_pointers_t.c +53 -0
  65. data/ext/geoip2/libmaxminddb/t/basic_lookup_t.c +172 -0
  66. data/ext/geoip2/libmaxminddb/t/compile_c++_t.pl +107 -0
  67. data/ext/geoip2/libmaxminddb/t/data_entry_list_t.c +353 -0
  68. data/ext/geoip2/libmaxminddb/t/data_types_t.c +439 -0
  69. data/ext/geoip2/libmaxminddb/t/dump_t.c +103 -0
  70. data/ext/geoip2/libmaxminddb/t/get_value_pointer_bug_t.c +66 -0
  71. data/ext/geoip2/libmaxminddb/t/get_value_t.c +249 -0
  72. data/ext/geoip2/libmaxminddb/t/ipv4_start_cache_t.c +36 -0
  73. data/ext/geoip2/libmaxminddb/t/ipv6_lookup_in_ipv4_t.c +48 -0
  74. data/ext/geoip2/libmaxminddb/t/maxminddb_test_helper.c +255 -0
  75. data/ext/geoip2/libmaxminddb/t/maxminddb_test_helper.h +69 -0
  76. data/ext/geoip2/libmaxminddb/t/metadata_pointers_t.c +32 -0
  77. data/ext/geoip2/libmaxminddb/t/metadata_t.c +226 -0
  78. data/ext/geoip2/libmaxminddb/t/mmdblookup_t.pl +158 -0
  79. data/ext/geoip2/libmaxminddb/t/no_map_get_value_t.c +32 -0
  80. data/ext/geoip2/libmaxminddb/t/read_node_t.c +157 -0
  81. data/ext/geoip2/libmaxminddb/t/threads_t.c +196 -0
  82. data/ext/geoip2/libmaxminddb/t/version_t.c +10 -0
  83. data/geoip2_c.gemspec +1 -1
  84. data/lib/geoip2/version.rb +1 -1
  85. metadata +82 -1
@@ -0,0 +1,2171 @@
1
+ #if HAVE_CONFIG_H
2
+ #include <config.h>
3
+ #endif
4
+ #include "maxminddb.h"
5
+ #include "maxminddb-compat-util.h"
6
+ #include <assert.h>
7
+ #include <errno.h>
8
+ #include <fcntl.h>
9
+ #include <inttypes.h>
10
+ #include <stdint.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+ #include <sys/stat.h>
14
+
15
+ #ifdef _WIN32
16
+ #include <windows.h>
17
+ #include <ws2ipdef.h>
18
+ #else
19
+ #include <arpa/inet.h>
20
+ #include <sys/mman.h>
21
+ #include <unistd.h>
22
+ #endif
23
+
24
+ #define MMDB_DATA_SECTION_SEPARATOR (16)
25
+ #define MAXIMUM_DATA_STRUCTURE_DEPTH (512)
26
+
27
+ #ifdef MMDB_DEBUG
28
+ #define LOCAL
29
+ #define NO_PROTO
30
+ #define DEBUG_FUNC
31
+ #define DEBUG_MSG(msg) fprintf(stderr, msg "\n")
32
+ #define DEBUG_MSGF(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
33
+ #define DEBUG_BINARY(fmt, byte) \
34
+ do { \
35
+ char *binary = byte_to_binary(byte); \
36
+ if (NULL == binary) { \
37
+ fprintf(stderr, "Malloc failed in DEBUG_BINARY\n"); \
38
+ abort(); \
39
+ } \
40
+ fprintf(stderr, fmt "\n", binary); \
41
+ free(binary); \
42
+ } while (0)
43
+ #define DEBUG_NL fprintf(stderr, "\n")
44
+ #else
45
+ #define LOCAL static
46
+ #define NO_PROTO static
47
+ #define DEBUG_MSG(...)
48
+ #define DEBUG_MSGF(...)
49
+ #define DEBUG_BINARY(...)
50
+ #define DEBUG_NL
51
+ #endif
52
+
53
+ #ifdef MMDB_DEBUG
54
+ DEBUG_FUNC char *byte_to_binary(uint8_t byte)
55
+ {
56
+ char *bits = malloc(sizeof(char) * 9);
57
+ if (NULL == bits) {
58
+ return bits;
59
+ }
60
+
61
+ for (uint8_t i = 0; i < 8; i++) {
62
+ bits[i] = byte & (128 >> i) ? '1' : '0';
63
+ }
64
+ bits[8] = '\0';
65
+
66
+ return bits;
67
+ }
68
+
69
+ DEBUG_FUNC char *type_num_to_name(uint8_t num)
70
+ {
71
+ switch (num) {
72
+ case 0:
73
+ return "extended";
74
+ case 1:
75
+ return "pointer";
76
+ case 2:
77
+ return "utf8_string";
78
+ case 3:
79
+ return "double";
80
+ case 4:
81
+ return "bytes";
82
+ case 5:
83
+ return "uint16";
84
+ case 6:
85
+ return "uint32";
86
+ case 7:
87
+ return "map";
88
+ case 8:
89
+ return "int32";
90
+ case 9:
91
+ return "uint64";
92
+ case 10:
93
+ return "uint128";
94
+ case 11:
95
+ return "array";
96
+ case 12:
97
+ return "container";
98
+ case 13:
99
+ return "end_marker";
100
+ case 14:
101
+ return "boolean";
102
+ case 15:
103
+ return "float";
104
+ default:
105
+ return "unknown type";
106
+ }
107
+ }
108
+ #endif
109
+
110
+ /* None of the values we check on the lhs are bigger than uint32_t, so on
111
+ * platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it
112
+ * makes the compiler complain if we do the check anyway. */
113
+ #if SIZE_MAX == UINT32_MAX
114
+ #define MAYBE_CHECK_SIZE_OVERFLOW(lhs, rhs, error) \
115
+ if ((lhs) > (rhs)) { \
116
+ return error; \
117
+ }
118
+ #else
119
+ #define MAYBE_CHECK_SIZE_OVERFLOW(...)
120
+ #endif
121
+
122
+ typedef struct record_info_s {
123
+ uint16_t record_length;
124
+ uint32_t (*left_record_getter)(const uint8_t *);
125
+ uint32_t (*right_record_getter)(const uint8_t *);
126
+ uint8_t right_record_offset;
127
+ } record_info_s;
128
+
129
+ #define METADATA_MARKER "\xab\xcd\xefMaxMind.com"
130
+ /* This is 128kb */
131
+ #define METADATA_BLOCK_MAX_SIZE 131072
132
+
133
+ /* *INDENT-OFF* */
134
+ /* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */
135
+ LOCAL int map_file(MMDB_s *const mmdb);
136
+ LOCAL const uint8_t *find_metadata(const uint8_t *file_content,
137
+ ssize_t file_size, uint32_t *metadata_size);
138
+ LOCAL int read_metadata(MMDB_s *mmdb);
139
+ LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb);
140
+ LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key,
141
+ uint16_t *value);
142
+ LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key,
143
+ uint32_t *value);
144
+ LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key,
145
+ uint64_t *value);
146
+ LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key,
147
+ char const **value);
148
+ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db,
149
+ MMDB_entry_s *metadata_start);
150
+ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db,
151
+ MMDB_entry_s *metadata_start);
152
+ LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses);
153
+ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address,
154
+ sa_family_t address_family,
155
+ MMDB_lookup_result_s *result);
156
+ LOCAL record_info_s record_info_for_database(MMDB_s *mmdb);
157
+ LOCAL int find_ipv4_start_node(MMDB_s *mmdb);
158
+ LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record,
159
+ uint16_t netmask,
160
+ MMDB_lookup_result_s *result);
161
+ LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record);
162
+ LOCAL uint32_t get_left_28_bit_record(const uint8_t *record);
163
+ LOCAL uint32_t get_right_28_bit_record(const uint8_t *record);
164
+ LOCAL uint32_t data_section_offset_for_record(MMDB_s *const mmdb,
165
+ uint64_t record);
166
+ LOCAL int path_length(va_list va_path);
167
+ LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb,
168
+ MMDB_entry_data_s *entry_data);
169
+ LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb,
170
+ MMDB_entry_data_s *entry_data);
171
+ LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data);
172
+ LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset,
173
+ MMDB_entry_data_s *entry_data);
174
+ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset,
175
+ MMDB_entry_data_s *entry_data);
176
+ LOCAL int get_ext_type(int raw_ext_type);
177
+ LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr,
178
+ int ptr_size);
179
+ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
180
+ MMDB_entry_data_list_s *const entry_data_list,
181
+ int depth);
182
+ LOCAL float get_ieee754_float(const uint8_t *restrict p);
183
+ LOCAL double get_ieee754_double(const uint8_t *restrict p);
184
+ LOCAL uint32_t get_uint32(const uint8_t *p);
185
+ LOCAL uint32_t get_uint24(const uint8_t *p);
186
+ LOCAL uint32_t get_uint16(const uint8_t *p);
187
+ LOCAL uint64_t get_uintX(const uint8_t *p, int length);
188
+ LOCAL int32_t get_sintX(const uint8_t *p, int length);
189
+ LOCAL MMDB_entry_data_list_s *new_entry_data_list(void);
190
+ LOCAL void free_mmdb_struct(MMDB_s *const mmdb);
191
+ LOCAL void free_languages_metadata(MMDB_s *mmdb);
192
+ LOCAL void free_descriptions_metadata(MMDB_s *mmdb);
193
+ LOCAL MMDB_entry_data_list_s *dump_entry_data_list(
194
+ FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent,
195
+ int *status);
196
+ LOCAL void print_indentation(FILE *stream, int i);
197
+ LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size);
198
+ /* --prototypes end - don't remove this comment-- */
199
+ /* *INDENT-ON* */
200
+
201
+ #define CHECKED_DECODE_ONE(mmdb, offset, entry_data) \
202
+ do { \
203
+ int status = decode_one(mmdb, offset, entry_data); \
204
+ if (MMDB_SUCCESS != status) { \
205
+ DEBUG_MSGF("CHECKED_DECODE_ONE failed." \
206
+ " status = %d (%s)", status, MMDB_strerror(status)); \
207
+ return status; \
208
+ } \
209
+ } while (0)
210
+
211
+ #define CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data) \
212
+ do { \
213
+ int status = decode_one_follow(mmdb, offset, entry_data); \
214
+ if (MMDB_SUCCESS != status) { \
215
+ DEBUG_MSGF("CHECKED_DECODE_ONE_FOLLOW failed." \
216
+ " status = %d (%s)", status, MMDB_strerror(status)); \
217
+ return status; \
218
+ } \
219
+ } while (0)
220
+
221
+ #define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; }
222
+
223
+ int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb)
224
+ {
225
+ int status = MMDB_SUCCESS;
226
+
227
+ mmdb->file_content = NULL;
228
+ mmdb->data_section = NULL;
229
+ mmdb->metadata.database_type = NULL;
230
+ mmdb->metadata.languages.count = 0;
231
+ mmdb->metadata.description.count = 0;
232
+
233
+ mmdb->filename = mmdb_strdup(filename);
234
+ if (NULL == mmdb->filename) {
235
+ status = MMDB_OUT_OF_MEMORY_ERROR;
236
+ goto cleanup;
237
+ }
238
+
239
+ if ((flags & MMDB_MODE_MASK) == 0) {
240
+ flags |= MMDB_MODE_MMAP;
241
+ }
242
+ mmdb->flags = flags;
243
+
244
+ if (MMDB_SUCCESS != (status = map_file(mmdb)) ) {
245
+ goto cleanup;
246
+ }
247
+
248
+ #ifdef _WIN32
249
+ WSADATA wsa;
250
+ WSAStartup(MAKEWORD(2, 2), &wsa);
251
+ #endif
252
+
253
+ uint32_t metadata_size = 0;
254
+ const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size,
255
+ &metadata_size);
256
+ if (NULL == metadata) {
257
+ status = MMDB_INVALID_METADATA_ERROR;
258
+ goto cleanup;
259
+ }
260
+
261
+ mmdb->metadata_section = metadata;
262
+ mmdb->metadata_section_size = metadata_size;
263
+
264
+ status = read_metadata(mmdb);
265
+ if (MMDB_SUCCESS != status) {
266
+ goto cleanup;
267
+ }
268
+
269
+ if (mmdb->metadata.binary_format_major_version != 2) {
270
+ status = MMDB_UNKNOWN_DATABASE_FORMAT_ERROR;
271
+ goto cleanup;
272
+ }
273
+
274
+ uint32_t search_tree_size = mmdb->metadata.node_count *
275
+ mmdb->full_record_byte_size;
276
+
277
+ mmdb->data_section = mmdb->file_content + search_tree_size
278
+ + MMDB_DATA_SECTION_SEPARATOR;
279
+ if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR >
280
+ (uint32_t)mmdb->file_size) {
281
+ status = MMDB_INVALID_METADATA_ERROR;
282
+ goto cleanup;
283
+ }
284
+ mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size -
285
+ MMDB_DATA_SECTION_SEPARATOR;
286
+
287
+ // Although it is likely not possible to construct a database with valid
288
+ // valid metadata, as parsed above, and a data_section_size less than 3,
289
+ // we do this check as later we assume it is at least three when doing
290
+ // bound checks.
291
+ if (mmdb->data_section_size < 3) {
292
+ status = MMDB_INVALID_DATA_ERROR;
293
+ goto cleanup;
294
+ }
295
+
296
+ mmdb->metadata_section = metadata;
297
+ mmdb->ipv4_start_node.node_value = 0;
298
+ mmdb->ipv4_start_node.netmask = 0;
299
+
300
+ cleanup:
301
+ if (MMDB_SUCCESS != status) {
302
+ int saved_errno = errno;
303
+ free_mmdb_struct(mmdb);
304
+ errno = saved_errno;
305
+ }
306
+ return status;
307
+ }
308
+
309
+ #ifdef _WIN32
310
+
311
+ LOCAL int map_file(MMDB_s *const mmdb)
312
+ {
313
+ DWORD size;
314
+ int status = MMDB_SUCCESS;
315
+ HANDLE mmh = NULL;
316
+ HANDLE fd = CreateFileA(mmdb->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
317
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
318
+ if (fd == INVALID_HANDLE_VALUE) {
319
+ status = MMDB_FILE_OPEN_ERROR;
320
+ goto cleanup;
321
+ }
322
+ size = GetFileSize(fd, NULL);
323
+ if (size == INVALID_FILE_SIZE) {
324
+ status = MMDB_FILE_OPEN_ERROR;
325
+ goto cleanup;
326
+ }
327
+ mmh = CreateFileMappingA(fd, NULL, PAGE_READONLY, 0, size, NULL);
328
+ /* Microsoft documentation for CreateFileMapping indicates this returns
329
+ NULL not INVALID_HANDLE_VALUE on error */
330
+ if (NULL == mmh) {
331
+ status = MMDB_IO_ERROR;
332
+ goto cleanup;
333
+ }
334
+ uint8_t *file_content =
335
+ (uint8_t *)MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0);
336
+ if (file_content == NULL) {
337
+ status = MMDB_IO_ERROR;
338
+ goto cleanup;
339
+ }
340
+
341
+ mmdb->file_size = size;
342
+ mmdb->file_content = file_content;
343
+
344
+ cleanup:;
345
+ int saved_errno = errno;
346
+ if (INVALID_HANDLE_VALUE != fd) {
347
+ CloseHandle(fd);
348
+ }
349
+ if (NULL != mmh) {
350
+ CloseHandle(mmh);
351
+ }
352
+ errno = saved_errno;
353
+
354
+ return status;
355
+ }
356
+
357
+ #else
358
+
359
+ LOCAL int map_file(MMDB_s *const mmdb)
360
+ {
361
+ ssize_t size;
362
+ int status = MMDB_SUCCESS;
363
+
364
+ int fd = open(mmdb->filename, O_RDONLY);
365
+ struct stat s;
366
+ if (fd < 0 || fstat(fd, &s)) {
367
+ status = MMDB_FILE_OPEN_ERROR;
368
+ goto cleanup;
369
+ }
370
+
371
+ size = s.st_size;
372
+ if (size < 0 || size != s.st_size) {
373
+ status = MMDB_OUT_OF_MEMORY_ERROR;
374
+ goto cleanup;
375
+ }
376
+
377
+ uint8_t *file_content =
378
+ (uint8_t *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
379
+ if (MAP_FAILED == file_content) {
380
+ if (ENOMEM == errno) {
381
+ status = MMDB_OUT_OF_MEMORY_ERROR;
382
+ } else {
383
+ status = MMDB_IO_ERROR;
384
+ }
385
+ goto cleanup;
386
+ }
387
+
388
+ mmdb->file_size = size;
389
+ mmdb->file_content = file_content;
390
+
391
+ cleanup:;
392
+ int saved_errno = errno;
393
+ if (fd >= 0) {
394
+ close(fd);
395
+ }
396
+ errno = saved_errno;
397
+
398
+ return status;
399
+ }
400
+
401
+ #endif
402
+
403
+ LOCAL const uint8_t *find_metadata(const uint8_t *file_content,
404
+ ssize_t file_size, uint32_t *metadata_size)
405
+ {
406
+ const ssize_t marker_len = sizeof(METADATA_MARKER) - 1;
407
+ ssize_t max_size = file_size >
408
+ METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE :
409
+ file_size;
410
+
411
+ uint8_t *search_area = (uint8_t *)(file_content + (file_size - max_size));
412
+ uint8_t *start = search_area;
413
+ uint8_t *tmp;
414
+ do {
415
+ tmp = mmdb_memmem(search_area, max_size,
416
+ METADATA_MARKER, marker_len);
417
+
418
+ if (NULL != tmp) {
419
+ max_size -= tmp - search_area;
420
+ search_area = tmp;
421
+
422
+ /* Continue searching just after the marker we just read, in case
423
+ * there are multiple markers in the same file. This would be odd
424
+ * but is certainly not impossible. */
425
+ max_size -= marker_len;
426
+ search_area += marker_len;
427
+ }
428
+ } while (NULL != tmp);
429
+
430
+ if (search_area == start) {
431
+ return NULL;
432
+ }
433
+
434
+ *metadata_size = (uint32_t)max_size;
435
+
436
+ return search_area;
437
+ }
438
+
439
+ LOCAL int read_metadata(MMDB_s *mmdb)
440
+ {
441
+ /* We need to create a fake MMDB_s struct in order to decode values from
442
+ the metadata. The metadata is basically just like the data section, so we
443
+ want to use the same functions we use for the data section to get metadata
444
+ values. */
445
+ MMDB_s metadata_db = make_fake_metadata_db(mmdb);
446
+
447
+ MMDB_entry_s metadata_start = {
448
+ .mmdb = &metadata_db,
449
+ .offset = 0
450
+ };
451
+
452
+ int status =
453
+ value_for_key_as_uint32(&metadata_start, "node_count",
454
+ &mmdb->metadata.node_count);
455
+ if (MMDB_SUCCESS != status) {
456
+ return status;
457
+ }
458
+ if (!mmdb->metadata.node_count) {
459
+ DEBUG_MSG("could not find node_count value in metadata");
460
+ return MMDB_INVALID_METADATA_ERROR;
461
+ }
462
+
463
+ status = value_for_key_as_uint16(&metadata_start, "record_size",
464
+ &mmdb->metadata.record_size);
465
+ if (MMDB_SUCCESS != status) {
466
+ return status;
467
+ }
468
+ if (!mmdb->metadata.record_size) {
469
+ DEBUG_MSG("could not find record_size value in metadata");
470
+ return MMDB_INVALID_METADATA_ERROR;
471
+ }
472
+
473
+ if (mmdb->metadata.record_size != 24 && mmdb->metadata.record_size != 28
474
+ && mmdb->metadata.record_size != 32) {
475
+ DEBUG_MSGF("bad record size in metadata: %i",
476
+ mmdb->metadata.record_size);
477
+ return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR;
478
+ }
479
+
480
+ status = value_for_key_as_uint16(&metadata_start, "ip_version",
481
+ &mmdb->metadata.ip_version);
482
+ if (MMDB_SUCCESS != status) {
483
+ return status;
484
+ }
485
+ if (!mmdb->metadata.ip_version) {
486
+ DEBUG_MSG("could not find ip_version value in metadata");
487
+ return MMDB_INVALID_METADATA_ERROR;
488
+ }
489
+ if (!(mmdb->metadata.ip_version == 4 || mmdb->metadata.ip_version == 6)) {
490
+ DEBUG_MSGF("ip_version value in metadata is not 4 or 6 - it was %i",
491
+ mmdb->metadata.ip_version);
492
+ return MMDB_INVALID_METADATA_ERROR;
493
+ }
494
+
495
+ status = value_for_key_as_string(&metadata_start, "database_type",
496
+ &mmdb->metadata.database_type);
497
+ if (MMDB_SUCCESS != status) {
498
+ DEBUG_MSG("error finding database_type value in metadata");
499
+ return status;
500
+ }
501
+
502
+ status =
503
+ populate_languages_metadata(mmdb, &metadata_db, &metadata_start);
504
+ if (MMDB_SUCCESS != status) {
505
+ DEBUG_MSG("could not populate languages from metadata");
506
+ return status;
507
+ }
508
+
509
+ status = value_for_key_as_uint16(
510
+ &metadata_start, "binary_format_major_version",
511
+ &mmdb->metadata.binary_format_major_version);
512
+ if (MMDB_SUCCESS != status) {
513
+ return status;
514
+ }
515
+ if (!mmdb->metadata.binary_format_major_version) {
516
+ DEBUG_MSG(
517
+ "could not find binary_format_major_version value in metadata");
518
+ return MMDB_INVALID_METADATA_ERROR;
519
+ }
520
+
521
+ status = value_for_key_as_uint16(
522
+ &metadata_start, "binary_format_minor_version",
523
+ &mmdb->metadata.binary_format_minor_version);
524
+ if (MMDB_SUCCESS != status) {
525
+ return status;
526
+ }
527
+
528
+ status = value_for_key_as_uint64(&metadata_start, "build_epoch",
529
+ &mmdb->metadata.build_epoch);
530
+ if (MMDB_SUCCESS != status) {
531
+ return status;
532
+ }
533
+ if (!mmdb->metadata.build_epoch) {
534
+ DEBUG_MSG("could not find build_epoch value in metadata");
535
+ return MMDB_INVALID_METADATA_ERROR;
536
+ }
537
+
538
+ status = populate_description_metadata(mmdb, &metadata_db, &metadata_start);
539
+ if (MMDB_SUCCESS != status) {
540
+ DEBUG_MSG("could not populate description from metadata");
541
+ return status;
542
+ }
543
+
544
+ mmdb->full_record_byte_size = mmdb->metadata.record_size * 2 / 8U;
545
+
546
+ mmdb->depth = mmdb->metadata.ip_version == 4 ? 32 : 128;
547
+
548
+ return MMDB_SUCCESS;
549
+ }
550
+
551
+ LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb)
552
+ {
553
+ MMDB_s fake_metadata_db = {
554
+ .data_section = mmdb->metadata_section,
555
+ .data_section_size = mmdb->metadata_section_size
556
+ };
557
+
558
+ return fake_metadata_db;
559
+ }
560
+
561
+ LOCAL int value_for_key_as_uint16(MMDB_entry_s *start, char *key,
562
+ uint16_t *value)
563
+ {
564
+ MMDB_entry_data_s entry_data;
565
+ const char *path[] = { key, NULL };
566
+ int status = MMDB_aget_value(start, &entry_data, path);
567
+ if (MMDB_SUCCESS != status) {
568
+ return status;
569
+ }
570
+ if (MMDB_DATA_TYPE_UINT16 != entry_data.type) {
571
+ DEBUG_MSGF("expect uint16 for %s but received %s", key,
572
+ type_num_to_name(
573
+ entry_data.type));
574
+ return MMDB_INVALID_METADATA_ERROR;
575
+ }
576
+ *value = entry_data.uint16;
577
+ return MMDB_SUCCESS;
578
+ }
579
+
580
+ LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key,
581
+ uint32_t *value)
582
+ {
583
+ MMDB_entry_data_s entry_data;
584
+ const char *path[] = { key, NULL };
585
+ int status = MMDB_aget_value(start, &entry_data, path);
586
+ if (MMDB_SUCCESS != status) {
587
+ return status;
588
+ }
589
+ if (MMDB_DATA_TYPE_UINT32 != entry_data.type) {
590
+ DEBUG_MSGF("expect uint32 for %s but received %s", key,
591
+ type_num_to_name(
592
+ entry_data.type));
593
+ return MMDB_INVALID_METADATA_ERROR;
594
+ }
595
+ *value = entry_data.uint32;
596
+ return MMDB_SUCCESS;
597
+ }
598
+
599
+ LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key,
600
+ uint64_t *value)
601
+ {
602
+ MMDB_entry_data_s entry_data;
603
+ const char *path[] = { key, NULL };
604
+ int status = MMDB_aget_value(start, &entry_data, path);
605
+ if (MMDB_SUCCESS != status) {
606
+ return status;
607
+ }
608
+ if (MMDB_DATA_TYPE_UINT64 != entry_data.type) {
609
+ DEBUG_MSGF("expect uint64 for %s but received %s", key,
610
+ type_num_to_name(
611
+ entry_data.type));
612
+ return MMDB_INVALID_METADATA_ERROR;
613
+ }
614
+ *value = entry_data.uint64;
615
+ return MMDB_SUCCESS;
616
+ }
617
+
618
+ LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key,
619
+ char const **value)
620
+ {
621
+ MMDB_entry_data_s entry_data;
622
+ const char *path[] = { key, NULL };
623
+ int status = MMDB_aget_value(start, &entry_data, path);
624
+ if (MMDB_SUCCESS != status) {
625
+ return status;
626
+ }
627
+ if (MMDB_DATA_TYPE_UTF8_STRING != entry_data.type) {
628
+ DEBUG_MSGF("expect string for %s but received %s", key,
629
+ type_num_to_name(
630
+ entry_data.type));
631
+ return MMDB_INVALID_METADATA_ERROR;
632
+ }
633
+ *value = mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size);
634
+ if (NULL == *value) {
635
+ return MMDB_OUT_OF_MEMORY_ERROR;
636
+ }
637
+ return MMDB_SUCCESS;
638
+ }
639
+
640
+ LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db,
641
+ MMDB_entry_s *metadata_start)
642
+ {
643
+ MMDB_entry_data_s entry_data;
644
+
645
+ const char *path[] = { "languages", NULL };
646
+ int status = MMDB_aget_value(metadata_start, &entry_data, path);
647
+ if (MMDB_SUCCESS != status) {
648
+ return status;
649
+ }
650
+ if (MMDB_DATA_TYPE_ARRAY != entry_data.type) {
651
+ return MMDB_INVALID_METADATA_ERROR;
652
+ }
653
+
654
+ MMDB_entry_s array_start = {
655
+ .mmdb = metadata_db,
656
+ .offset = entry_data.offset
657
+ };
658
+
659
+ MMDB_entry_data_list_s *member;
660
+ status = MMDB_get_entry_data_list(&array_start, &member);
661
+ if (MMDB_SUCCESS != status) {
662
+ return status;
663
+ }
664
+
665
+ MMDB_entry_data_list_s *first_member = member;
666
+
667
+ uint32_t array_size = member->entry_data.data_size;
668
+ MAYBE_CHECK_SIZE_OVERFLOW(array_size, SIZE_MAX / sizeof(char *),
669
+ MMDB_INVALID_METADATA_ERROR);
670
+
671
+ mmdb->metadata.languages.count = 0;
672
+ mmdb->metadata.languages.names = malloc(array_size * sizeof(char *));
673
+ if (NULL == mmdb->metadata.languages.names) {
674
+ return MMDB_OUT_OF_MEMORY_ERROR;
675
+ }
676
+
677
+ for (uint32_t i = 0; i < array_size; i++) {
678
+ member = member->next;
679
+ if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) {
680
+ return MMDB_INVALID_METADATA_ERROR;
681
+ }
682
+
683
+ mmdb->metadata.languages.names[i] =
684
+ mmdb_strndup((char *)member->entry_data.utf8_string,
685
+ member->entry_data.data_size);
686
+
687
+ if (NULL == mmdb->metadata.languages.names[i]) {
688
+ return MMDB_OUT_OF_MEMORY_ERROR;
689
+ }
690
+ // We assign this as we go so that if we fail a malloc and need to
691
+ // free it, the count is right.
692
+ mmdb->metadata.languages.count = i + 1;
693
+ }
694
+
695
+ MMDB_free_entry_data_list(first_member);
696
+
697
+ return MMDB_SUCCESS;
698
+ }
699
+
700
+ LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db,
701
+ MMDB_entry_s *metadata_start)
702
+ {
703
+ MMDB_entry_data_s entry_data;
704
+
705
+ const char *path[] = { "description", NULL };
706
+ int status = MMDB_aget_value(metadata_start, &entry_data, path);
707
+ if (MMDB_SUCCESS != status) {
708
+ return status;
709
+ }
710
+
711
+ if (MMDB_DATA_TYPE_MAP != entry_data.type) {
712
+ DEBUG_MSGF("Unexpected entry_data type: %d", entry_data.type);
713
+ return MMDB_INVALID_METADATA_ERROR;
714
+ }
715
+
716
+ MMDB_entry_s map_start = {
717
+ .mmdb = metadata_db,
718
+ .offset = entry_data.offset
719
+ };
720
+
721
+ MMDB_entry_data_list_s *member;
722
+ status = MMDB_get_entry_data_list(&map_start, &member);
723
+ if (MMDB_SUCCESS != status) {
724
+ DEBUG_MSGF(
725
+ "MMDB_get_entry_data_list failed while populating description."
726
+ " status = %d (%s)", status, MMDB_strerror(status));
727
+ return status;
728
+ }
729
+
730
+ MMDB_entry_data_list_s *first_member = member;
731
+
732
+ uint32_t map_size = member->entry_data.data_size;
733
+ mmdb->metadata.description.count = 0;
734
+ if (0 == map_size) {
735
+ mmdb->metadata.description.descriptions = NULL;
736
+ goto cleanup;
737
+ }
738
+ MAYBE_CHECK_SIZE_OVERFLOW(map_size, SIZE_MAX / sizeof(MMDB_description_s *),
739
+ MMDB_INVALID_METADATA_ERROR);
740
+
741
+ mmdb->metadata.description.descriptions =
742
+ malloc(map_size * sizeof(MMDB_description_s *));
743
+ if (NULL == mmdb->metadata.description.descriptions) {
744
+ status = MMDB_OUT_OF_MEMORY_ERROR;
745
+ goto cleanup;
746
+ }
747
+
748
+ for (uint32_t i = 0; i < map_size; i++) {
749
+ mmdb->metadata.description.descriptions[i] =
750
+ malloc(sizeof(MMDB_description_s));
751
+ if (NULL == mmdb->metadata.description.descriptions[i]) {
752
+ status = MMDB_OUT_OF_MEMORY_ERROR;
753
+ goto cleanup;
754
+ }
755
+
756
+ mmdb->metadata.description.count = i + 1;
757
+ mmdb->metadata.description.descriptions[i]->language = NULL;
758
+ mmdb->metadata.description.descriptions[i]->description = NULL;
759
+
760
+ member = member->next;
761
+
762
+ if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) {
763
+ status = MMDB_INVALID_METADATA_ERROR;
764
+ goto cleanup;
765
+ }
766
+
767
+ mmdb->metadata.description.descriptions[i]->language =
768
+ mmdb_strndup((char *)member->entry_data.utf8_string,
769
+ member->entry_data.data_size);
770
+
771
+ if (NULL == mmdb->metadata.description.descriptions[i]->language) {
772
+ status = MMDB_OUT_OF_MEMORY_ERROR;
773
+ goto cleanup;
774
+ }
775
+
776
+ member = member->next;
777
+
778
+ if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) {
779
+ status = MMDB_INVALID_METADATA_ERROR;
780
+ goto cleanup;
781
+ }
782
+
783
+ mmdb->metadata.description.descriptions[i]->description =
784
+ mmdb_strndup((char *)member->entry_data.utf8_string,
785
+ member->entry_data.data_size);
786
+
787
+ if (NULL == mmdb->metadata.description.descriptions[i]->description) {
788
+ status = MMDB_OUT_OF_MEMORY_ERROR;
789
+ goto cleanup;
790
+ }
791
+ }
792
+
793
+ cleanup:
794
+ MMDB_free_entry_data_list(first_member);
795
+
796
+ return status;
797
+ }
798
+
799
+ MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb,
800
+ const char *const ipstr,
801
+ int *const gai_error,
802
+ int *const mmdb_error)
803
+ {
804
+ MMDB_lookup_result_s result = {
805
+ .found_entry = false,
806
+ .netmask = 0,
807
+ .entry = {
808
+ .mmdb = mmdb,
809
+ .offset = 0
810
+ }
811
+ };
812
+
813
+ struct addrinfo *addresses = NULL;
814
+ *gai_error = resolve_any_address(ipstr, &addresses);
815
+
816
+ if (!*gai_error) {
817
+ result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, mmdb_error);
818
+ }
819
+
820
+ if (NULL != addresses) {
821
+ freeaddrinfo(addresses);
822
+ }
823
+
824
+ return result;
825
+ }
826
+
827
+ LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses)
828
+ {
829
+ struct addrinfo hints = {
830
+ .ai_family = AF_UNSPEC,
831
+ .ai_flags = AI_NUMERICHOST,
832
+ // We set ai_socktype so that we only get one result back
833
+ .ai_socktype = SOCK_STREAM
834
+ };
835
+
836
+ int gai_status = getaddrinfo(ipstr, NULL, &hints, addresses);
837
+ if (gai_status) {
838
+ return gai_status;
839
+ }
840
+
841
+ return 0;
842
+ }
843
+
844
+ MMDB_lookup_result_s MMDB_lookup_sockaddr(
845
+ MMDB_s *const mmdb,
846
+ const struct sockaddr *const sockaddr,
847
+ int *const mmdb_error)
848
+ {
849
+ MMDB_lookup_result_s result = {
850
+ .found_entry = false,
851
+ .netmask = 0,
852
+ .entry = {
853
+ .mmdb = mmdb,
854
+ .offset = 0
855
+ }
856
+ };
857
+
858
+ uint8_t mapped_address[16], *address;
859
+ if (mmdb->metadata.ip_version == 4) {
860
+ if (sockaddr->sa_family == AF_INET6) {
861
+ *mmdb_error = MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR;
862
+ return result;
863
+ }
864
+ address = (uint8_t *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
865
+ } else {
866
+ if (sockaddr->sa_family == AF_INET6) {
867
+ address =
868
+ (uint8_t *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr.
869
+ s6_addr;
870
+ } else {
871
+ address = mapped_address;
872
+ memset(address, 0, 12);
873
+ memcpy(address + 12,
874
+ &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4);
875
+ }
876
+ }
877
+
878
+ *mmdb_error =
879
+ find_address_in_search_tree(mmdb, address, sockaddr->sa_family,
880
+ &result);
881
+
882
+ return result;
883
+ }
884
+
885
+ LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address,
886
+ sa_family_t address_family,
887
+ MMDB_lookup_result_s *result)
888
+ {
889
+ record_info_s record_info = record_info_for_database(mmdb);
890
+ if (0 == record_info.right_record_offset) {
891
+ return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR;
892
+ }
893
+
894
+ DEBUG_NL;
895
+ DEBUG_MSG("Looking for address in search tree");
896
+
897
+ uint32_t value = 0;
898
+ uint16_t max_depth0 = mmdb->depth - 1;
899
+ uint16_t start_bit = max_depth0;
900
+
901
+ if (mmdb->metadata.ip_version == 6 && address_family == AF_INET) {
902
+ int mmdb_error = find_ipv4_start_node(mmdb);
903
+ if (MMDB_SUCCESS != mmdb_error) {
904
+ return mmdb_error;
905
+ }
906
+ DEBUG_MSGF("IPv4 start node is %u (netmask %u)",
907
+ mmdb->ipv4_start_node.node_value,
908
+ mmdb->ipv4_start_node.netmask);
909
+
910
+ uint8_t type = maybe_populate_result(mmdb,
911
+ mmdb->ipv4_start_node.node_value,
912
+ mmdb->ipv4_start_node.netmask,
913
+ result);
914
+ if (MMDB_RECORD_TYPE_INVALID == type) {
915
+ return MMDB_CORRUPT_SEARCH_TREE_ERROR;
916
+ }
917
+
918
+ /* We have an IPv6 database with no IPv4 data */
919
+ if (MMDB_RECORD_TYPE_SEARCH_NODE != type) {
920
+ return MMDB_SUCCESS;
921
+ }
922
+
923
+ value = mmdb->ipv4_start_node.node_value;
924
+ start_bit -= mmdb->ipv4_start_node.netmask;
925
+ }
926
+
927
+ const uint8_t *search_tree = mmdb->file_content;
928
+ const uint8_t *record_pointer;
929
+ for (int current_bit = start_bit; current_bit >= 0; current_bit--) {
930
+ uint8_t bit_is_true =
931
+ address[(max_depth0 - current_bit) >> 3]
932
+ & (1U << (~(max_depth0 - current_bit) & 7)) ? 1 : 0;
933
+
934
+ DEBUG_MSGF("Looking at bit %i - bit's value is %i", current_bit,
935
+ bit_is_true);
936
+ DEBUG_MSGF(" current node = %u", value);
937
+
938
+ record_pointer = &search_tree[value * record_info.record_length];
939
+ if (record_pointer + record_info.record_length > mmdb->data_section) {
940
+ return MMDB_CORRUPT_SEARCH_TREE_ERROR;
941
+ }
942
+ if (bit_is_true) {
943
+ record_pointer += record_info.right_record_offset;
944
+ value = record_info.right_record_getter(record_pointer);
945
+ } else {
946
+ value = record_info.left_record_getter(record_pointer);
947
+ }
948
+
949
+ uint8_t type = maybe_populate_result(mmdb, value, (uint16_t)current_bit,
950
+ result);
951
+ if (MMDB_RECORD_TYPE_INVALID == type) {
952
+ return MMDB_CORRUPT_SEARCH_TREE_ERROR;
953
+ }
954
+
955
+ if (MMDB_RECORD_TYPE_SEARCH_NODE != type) {
956
+ return MMDB_SUCCESS;
957
+ }
958
+
959
+ DEBUG_MSGF(" proceeding to search tree node %i", value);
960
+ }
961
+
962
+ DEBUG_MSG(
963
+ "Reached the end of the address bits without leaving the search tree");
964
+
965
+ // We should not be able to reach this return. If we do, something very bad happened.
966
+ return MMDB_CORRUPT_SEARCH_TREE_ERROR;
967
+ }
968
+
969
+ LOCAL record_info_s record_info_for_database(MMDB_s *mmdb)
970
+ {
971
+ record_info_s record_info = {
972
+ .record_length = mmdb->full_record_byte_size,
973
+ .right_record_offset = 0
974
+ };
975
+
976
+ if (record_info.record_length == 6) {
977
+ record_info.left_record_getter = &get_uint24;
978
+ record_info.right_record_getter = &get_uint24;
979
+ record_info.right_record_offset = 3;
980
+ } else if (record_info.record_length == 7) {
981
+ record_info.left_record_getter = &get_left_28_bit_record;
982
+ record_info.right_record_getter = &get_right_28_bit_record;
983
+ record_info.right_record_offset = 3;
984
+ } else if (record_info.record_length == 8) {
985
+ record_info.left_record_getter = &get_uint32;
986
+ record_info.right_record_getter = &get_uint32;
987
+ record_info.right_record_offset = 4;
988
+ } else {
989
+ assert(false);
990
+ }
991
+
992
+ return record_info;
993
+ }
994
+
995
+ LOCAL int find_ipv4_start_node(MMDB_s *mmdb)
996
+ {
997
+ /* In a pathological case of a database with a single node search tree,
998
+ * this check will be true even after we've found the IPv4 start node, but
999
+ * that doesn't seem worth trying to fix. */
1000
+ if (mmdb->ipv4_start_node.node_value != 0) {
1001
+ return MMDB_SUCCESS;
1002
+ }
1003
+
1004
+ record_info_s record_info = record_info_for_database(mmdb);
1005
+
1006
+ const uint8_t *search_tree = mmdb->file_content;
1007
+ uint32_t node_value = 0;
1008
+ const uint8_t *record_pointer;
1009
+ uint16_t netmask;
1010
+ for (netmask = 0; netmask < 96; netmask++) {
1011
+ record_pointer = &search_tree[node_value * record_info.record_length];
1012
+ if (record_pointer + record_info.record_length > mmdb->data_section) {
1013
+ return MMDB_CORRUPT_SEARCH_TREE_ERROR;
1014
+ }
1015
+ node_value = record_info.left_record_getter(record_pointer);
1016
+ /* This can happen if there's no IPv4 data _or_ if there is a subnet
1017
+ * with data that contains the entire IPv4 range (like ::/64) */
1018
+ if (node_value >= mmdb->metadata.node_count) {
1019
+ break;
1020
+ }
1021
+ }
1022
+
1023
+ mmdb->ipv4_start_node.node_value = node_value;
1024
+ mmdb->ipv4_start_node.netmask = netmask;
1025
+
1026
+ return MMDB_SUCCESS;
1027
+ }
1028
+
1029
+ LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record,
1030
+ uint16_t netmask,
1031
+ MMDB_lookup_result_s *result)
1032
+ {
1033
+ uint8_t type = record_type(mmdb, record);
1034
+
1035
+ if (MMDB_RECORD_TYPE_SEARCH_NODE == type ||
1036
+ MMDB_RECORD_TYPE_INVALID == type) {
1037
+ return type;
1038
+ }
1039
+
1040
+ result->netmask = mmdb->depth - netmask;
1041
+
1042
+ result->entry.offset = data_section_offset_for_record(mmdb, record);
1043
+
1044
+ // type is either MMDB_RECORD_TYPE_DATA or MMDB_RECORD_TYPE_EMPTY
1045
+ // at this point
1046
+ result->found_entry = MMDB_RECORD_TYPE_DATA == type;
1047
+
1048
+ return type;
1049
+ }
1050
+
1051
+ LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record)
1052
+ {
1053
+ uint32_t node_count = mmdb->metadata.node_count;
1054
+
1055
+ /* Ideally we'd check to make sure that a record never points to a
1056
+ * previously seen value, but that's more complicated. For now, we can
1057
+ * at least check that we don't end up at the top of the tree again. */
1058
+ if (record == 0) {
1059
+ DEBUG_MSG("record has a value of 0");
1060
+ return MMDB_RECORD_TYPE_INVALID;
1061
+ }
1062
+
1063
+ if (record < node_count) {
1064
+ return MMDB_RECORD_TYPE_SEARCH_NODE;
1065
+ }
1066
+
1067
+ if (record == node_count) {
1068
+ return MMDB_RECORD_TYPE_EMPTY;
1069
+ }
1070
+
1071
+ if (record - node_count < mmdb->data_section_size) {
1072
+ return MMDB_RECORD_TYPE_DATA;
1073
+ }
1074
+
1075
+ DEBUG_MSG("record has a value that points outside of the database");
1076
+ return MMDB_RECORD_TYPE_INVALID;
1077
+ }
1078
+
1079
+ LOCAL uint32_t get_left_28_bit_record(const uint8_t *record)
1080
+ {
1081
+ return record[0] * 65536 + record[1] * 256 + record[2] +
1082
+ ((record[3] & 0xf0) << 20);
1083
+ }
1084
+
1085
+ LOCAL uint32_t get_right_28_bit_record(const uint8_t *record)
1086
+ {
1087
+ uint32_t value = get_uint32(record);
1088
+ return value & 0xfffffff;
1089
+ }
1090
+
1091
+ int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number,
1092
+ MMDB_search_node_s *const node)
1093
+ {
1094
+ record_info_s record_info = record_info_for_database(mmdb);
1095
+ if (0 == record_info.right_record_offset) {
1096
+ return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR;
1097
+ }
1098
+
1099
+ if (node_number > mmdb->metadata.node_count) {
1100
+ return MMDB_INVALID_NODE_NUMBER_ERROR;
1101
+ }
1102
+
1103
+ const uint8_t *search_tree = mmdb->file_content;
1104
+ const uint8_t *record_pointer =
1105
+ &search_tree[node_number * record_info.record_length];
1106
+ node->left_record = record_info.left_record_getter(record_pointer);
1107
+ record_pointer += record_info.right_record_offset;
1108
+ node->right_record = record_info.right_record_getter(record_pointer);
1109
+
1110
+ node->left_record_type = record_type(mmdb, node->left_record);
1111
+ node->right_record_type = record_type(mmdb, node->right_record);
1112
+
1113
+ // Note that offset will be invalid if the record type is not
1114
+ // MMDB_RECORD_TYPE_DATA, but that's ok. Any use of the record entry
1115
+ // for other data types is a programming error.
1116
+ node->left_record_entry = (struct MMDB_entry_s) {
1117
+ .mmdb = mmdb,
1118
+ .offset = data_section_offset_for_record(mmdb, node->left_record),
1119
+ };
1120
+ node->right_record_entry = (struct MMDB_entry_s) {
1121
+ .mmdb = mmdb,
1122
+ .offset = data_section_offset_for_record(mmdb, node->right_record),
1123
+ };
1124
+
1125
+ return MMDB_SUCCESS;
1126
+ }
1127
+
1128
+ LOCAL uint32_t data_section_offset_for_record(MMDB_s *const mmdb,
1129
+ uint64_t record)
1130
+ {
1131
+ return (uint32_t)record - mmdb->metadata.node_count -
1132
+ MMDB_DATA_SECTION_SEPARATOR;
1133
+ }
1134
+
1135
+ int MMDB_get_value(MMDB_entry_s *const start,
1136
+ MMDB_entry_data_s *const entry_data,
1137
+ ...)
1138
+ {
1139
+ va_list path;
1140
+ va_start(path, entry_data);
1141
+ int status = MMDB_vget_value(start, entry_data, path);
1142
+ va_end(path);
1143
+ return status;
1144
+ }
1145
+
1146
+ int MMDB_vget_value(MMDB_entry_s *const start,
1147
+ MMDB_entry_data_s *const entry_data,
1148
+ va_list va_path)
1149
+ {
1150
+ int length = path_length(va_path);
1151
+ const char *path_elem;
1152
+ int i = 0;
1153
+
1154
+ MAYBE_CHECK_SIZE_OVERFLOW(length, SIZE_MAX / sizeof(const char *) - 1,
1155
+ MMDB_INVALID_METADATA_ERROR);
1156
+
1157
+ const char **path = malloc((length + 1) * sizeof(const char *));
1158
+ if (NULL == path) {
1159
+ return MMDB_OUT_OF_MEMORY_ERROR;
1160
+ }
1161
+
1162
+ while (NULL != (path_elem = va_arg(va_path, char *))) {
1163
+ path[i] = path_elem;
1164
+ i++;
1165
+ }
1166
+ path[i] = NULL;
1167
+
1168
+ int status = MMDB_aget_value(start, entry_data, path);
1169
+
1170
+ free((char **)path);
1171
+
1172
+ return status;
1173
+ }
1174
+
1175
+ LOCAL int path_length(va_list va_path)
1176
+ {
1177
+ int i = 0;
1178
+ const char *ignore;
1179
+ va_list path_copy;
1180
+ va_copy(path_copy, va_path);
1181
+
1182
+ while (NULL != (ignore = va_arg(path_copy, char *))) {
1183
+ i++;
1184
+ }
1185
+
1186
+ va_end(path_copy);
1187
+
1188
+ return i;
1189
+ }
1190
+
1191
+ int MMDB_aget_value(MMDB_entry_s *const start,
1192
+ MMDB_entry_data_s *const entry_data,
1193
+ const char *const *const path)
1194
+ {
1195
+ MMDB_s *mmdb = start->mmdb;
1196
+ uint32_t offset = start->offset;
1197
+
1198
+ memset(entry_data, 0, sizeof(MMDB_entry_data_s));
1199
+ DEBUG_NL;
1200
+ DEBUG_MSG("looking up value by path");
1201
+
1202
+ CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data);
1203
+
1204
+ DEBUG_NL;
1205
+ DEBUG_MSGF("top level element is a %s", type_num_to_name(entry_data->type));
1206
+
1207
+ /* Can this happen? It'd probably represent a pathological case under
1208
+ * normal use, but there's nothing preventing someone from passing an
1209
+ * invalid MMDB_entry_s struct to this function */
1210
+ if (!entry_data->has_data) {
1211
+ return MMDB_INVALID_LOOKUP_PATH_ERROR;
1212
+ }
1213
+
1214
+ const char *path_elem;
1215
+ int i = 0;
1216
+ while (NULL != (path_elem = path[i++])) {
1217
+ DEBUG_NL;
1218
+ DEBUG_MSGF("path elem = %s", path_elem);
1219
+
1220
+ /* XXX - it'd be good to find a quicker way to skip through these
1221
+ entries that doesn't involve decoding them
1222
+ completely. Basically we need to just use the size from the
1223
+ control byte to advance our pointer rather than calling
1224
+ decode_one(). */
1225
+ if (entry_data->type == MMDB_DATA_TYPE_ARRAY) {
1226
+ int status = lookup_path_in_array(path_elem, mmdb, entry_data);
1227
+ if (MMDB_SUCCESS != status) {
1228
+ memset(entry_data, 0, sizeof(MMDB_entry_data_s));
1229
+ return status;
1230
+ }
1231
+ } else if (entry_data->type == MMDB_DATA_TYPE_MAP) {
1232
+ int status = lookup_path_in_map(path_elem, mmdb, entry_data);
1233
+ if (MMDB_SUCCESS != status) {
1234
+ memset(entry_data, 0, sizeof(MMDB_entry_data_s));
1235
+ return status;
1236
+ }
1237
+ } else {
1238
+ /* Once we make the code traverse maps & arrays without calling
1239
+ * decode_one() we can get rid of this. */
1240
+ memset(entry_data, 0, sizeof(MMDB_entry_data_s));
1241
+ return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR;
1242
+ }
1243
+ }
1244
+
1245
+ return MMDB_SUCCESS;
1246
+ }
1247
+
1248
+ LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb,
1249
+ MMDB_entry_data_s *entry_data)
1250
+ {
1251
+ uint32_t size = entry_data->data_size;
1252
+ char *first_invalid;
1253
+
1254
+ int saved_errno = errno;
1255
+ errno = 0;
1256
+ int array_index = strtol(path_elem, &first_invalid, 10);
1257
+ if (array_index < 0 || ERANGE == errno) {
1258
+ errno = saved_errno;
1259
+ return MMDB_INVALID_LOOKUP_PATH_ERROR;
1260
+ }
1261
+ errno = saved_errno;
1262
+
1263
+ if (*first_invalid || (uint32_t)array_index >= size) {
1264
+ return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR;
1265
+ }
1266
+
1267
+ for (int i = 0; i < array_index; i++) {
1268
+ /* We don't want to follow a pointer here. If the next element is a
1269
+ * pointer we simply skip it and keep going */
1270
+ CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data);
1271
+ int status = skip_map_or_array(mmdb, entry_data);
1272
+ if (MMDB_SUCCESS != status) {
1273
+ return status;
1274
+ }
1275
+ }
1276
+
1277
+ MMDB_entry_data_s value;
1278
+ CHECKED_DECODE_ONE_FOLLOW(mmdb, entry_data->offset_to_next, &value);
1279
+ memcpy(entry_data, &value, sizeof(MMDB_entry_data_s));
1280
+
1281
+ return MMDB_SUCCESS;
1282
+ }
1283
+
1284
+ LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb,
1285
+ MMDB_entry_data_s *entry_data)
1286
+ {
1287
+ uint32_t size = entry_data->data_size;
1288
+ uint32_t offset = entry_data->offset_to_next;
1289
+ size_t path_elem_len = strlen(path_elem);
1290
+
1291
+ while (size-- > 0) {
1292
+ MMDB_entry_data_s key, value;
1293
+ CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, &key);
1294
+
1295
+ uint32_t offset_to_value = key.offset_to_next;
1296
+
1297
+ if (MMDB_DATA_TYPE_UTF8_STRING != key.type) {
1298
+ return MMDB_INVALID_DATA_ERROR;
1299
+ }
1300
+
1301
+ if (key.data_size == path_elem_len &&
1302
+ !memcmp(path_elem, key.utf8_string, path_elem_len)) {
1303
+
1304
+ DEBUG_MSG("found key matching path elem");
1305
+
1306
+ CHECKED_DECODE_ONE_FOLLOW(mmdb, offset_to_value, &value);
1307
+ memcpy(entry_data, &value, sizeof(MMDB_entry_data_s));
1308
+ return MMDB_SUCCESS;
1309
+ } else {
1310
+ /* We don't want to follow a pointer here. If the next element is
1311
+ * a pointer we simply skip it and keep going */
1312
+ CHECKED_DECODE_ONE(mmdb, offset_to_value, &value);
1313
+ int status = skip_map_or_array(mmdb, &value);
1314
+ if (MMDB_SUCCESS != status) {
1315
+ return status;
1316
+ }
1317
+ offset = value.offset_to_next;
1318
+ }
1319
+ }
1320
+
1321
+ memset(entry_data, 0, sizeof(MMDB_entry_data_s));
1322
+ return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR;
1323
+ }
1324
+
1325
+ LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data)
1326
+ {
1327
+ if (entry_data->type == MMDB_DATA_TYPE_MAP) {
1328
+ uint32_t size = entry_data->data_size;
1329
+ while (size-- > 0) {
1330
+ CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // key
1331
+ CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value
1332
+ int status = skip_map_or_array(mmdb, entry_data);
1333
+ if (MMDB_SUCCESS != status) {
1334
+ return status;
1335
+ }
1336
+ }
1337
+ } else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) {
1338
+ uint32_t size = entry_data->data_size;
1339
+ while (size-- > 0) {
1340
+ CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value
1341
+ int status = skip_map_or_array(mmdb, entry_data);
1342
+ if (MMDB_SUCCESS != status) {
1343
+ return status;
1344
+ }
1345
+ }
1346
+ }
1347
+
1348
+ return MMDB_SUCCESS;
1349
+ }
1350
+
1351
+ LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset,
1352
+ MMDB_entry_data_s *entry_data)
1353
+ {
1354
+ CHECKED_DECODE_ONE(mmdb, offset, entry_data);
1355
+ if (entry_data->type == MMDB_DATA_TYPE_POINTER) {
1356
+ uint32_t next = entry_data->offset_to_next;
1357
+ CHECKED_DECODE_ONE(mmdb, entry_data->pointer, entry_data);
1358
+ /* Pointers to pointers are illegal under the spec */
1359
+ if (entry_data->type == MMDB_DATA_TYPE_POINTER) {
1360
+ DEBUG_MSG("pointer points to another pointer");
1361
+ return MMDB_INVALID_DATA_ERROR;
1362
+ }
1363
+
1364
+ /* The pointer could point to any part of the data section but the
1365
+ * next entry for this particular offset may be the one after the
1366
+ * pointer, not the one after whatever the pointer points to. This
1367
+ * depends on whether the pointer points to something that is a simple
1368
+ * value or a compound value. For a compound value, the next one is
1369
+ * the one after the pointer result, not the one after the pointer. */
1370
+ if (entry_data->type != MMDB_DATA_TYPE_MAP
1371
+ && entry_data->type != MMDB_DATA_TYPE_ARRAY) {
1372
+
1373
+ entry_data->offset_to_next = next;
1374
+ }
1375
+ }
1376
+
1377
+ return MMDB_SUCCESS;
1378
+ }
1379
+
1380
+ #if !MMDB_UINT128_IS_BYTE_ARRAY
1381
+ NO_PROTO mmdb_uint128_t get_uint128(const uint8_t *p, int length)
1382
+ {
1383
+ mmdb_uint128_t value = 0;
1384
+ while (length-- > 0) {
1385
+ value <<= 8;
1386
+ value += *p++;
1387
+ }
1388
+ return value;
1389
+ }
1390
+ #endif
1391
+
1392
+ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset,
1393
+ MMDB_entry_data_s *entry_data)
1394
+ {
1395
+ const uint8_t *mem = mmdb->data_section;
1396
+
1397
+ // We subtract rather than add as it possible that offset + 1
1398
+ // could overflow for a corrupt database while an underflow
1399
+ // from data_section_size - 1 should not be possible.
1400
+ if (offset > mmdb->data_section_size - 1) {
1401
+ DEBUG_MSGF("Offset (%d) past data section (%d)", offset,
1402
+ mmdb->data_section_size);
1403
+ return MMDB_INVALID_DATA_ERROR;
1404
+ }
1405
+
1406
+ entry_data->offset = offset;
1407
+ entry_data->has_data = true;
1408
+
1409
+ DEBUG_NL;
1410
+ DEBUG_MSGF("Offset: %i", offset);
1411
+
1412
+ uint8_t ctrl = mem[offset++];
1413
+ DEBUG_BINARY("Control byte: %s", ctrl);
1414
+
1415
+ int type = (ctrl >> 5) & 7;
1416
+ DEBUG_MSGF("Type: %i (%s)", type, type_num_to_name(type));
1417
+
1418
+ if (type == MMDB_DATA_TYPE_EXTENDED) {
1419
+ // Subtracting 1 to avoid possible overflow on offset + 1
1420
+ if (offset > mmdb->data_section_size - 1) {
1421
+ DEBUG_MSGF("Extended type offset (%d) past data section (%d)",
1422
+ offset,
1423
+ mmdb->data_section_size);
1424
+ return MMDB_INVALID_DATA_ERROR;
1425
+ }
1426
+ type = get_ext_type(mem[offset++]);
1427
+ DEBUG_MSGF("Extended type: %i (%s)", type, type_num_to_name(type));
1428
+ }
1429
+
1430
+ entry_data->type = type;
1431
+
1432
+ if (type == MMDB_DATA_TYPE_POINTER) {
1433
+ uint8_t psize = ((ctrl >> 3) & 3) + 1;
1434
+ DEBUG_MSGF("Pointer size: %i", psize);
1435
+
1436
+ // We check that the offset does not extend past the end of the
1437
+ // database and that the subtraction of psize did not underflow.
1438
+ if (offset > mmdb->data_section_size - psize ||
1439
+ mmdb->data_section_size < psize) {
1440
+ DEBUG_MSGF("Pointer offset (%d) past data section (%d)", offset +
1441
+ psize,
1442
+ mmdb->data_section_size);
1443
+ return MMDB_INVALID_DATA_ERROR;
1444
+ }
1445
+ entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize);
1446
+ DEBUG_MSGF("Pointer to: %i", entry_data->pointer);
1447
+
1448
+ entry_data->data_size = psize;
1449
+ entry_data->offset_to_next = offset + psize;
1450
+ return MMDB_SUCCESS;
1451
+ }
1452
+
1453
+ uint32_t size = ctrl & 31;
1454
+ switch (size) {
1455
+ case 29:
1456
+ // We subtract when checking offset to avoid possible overflow
1457
+ if (offset > mmdb->data_section_size - 1) {
1458
+ DEBUG_MSGF("String end (%d, case 29) past data section (%d)",
1459
+ offset,
1460
+ mmdb->data_section_size);
1461
+ return MMDB_INVALID_DATA_ERROR;
1462
+ }
1463
+ size = 29 + mem[offset++];
1464
+ break;
1465
+ case 30:
1466
+ // We subtract when checking offset to avoid possible overflow
1467
+ if (offset > mmdb->data_section_size - 2) {
1468
+ DEBUG_MSGF("String end (%d, case 30) past data section (%d)",
1469
+ offset,
1470
+ mmdb->data_section_size);
1471
+ return MMDB_INVALID_DATA_ERROR;
1472
+ }
1473
+ size = 285 + get_uint16(&mem[offset]);
1474
+ offset += 2;
1475
+ break;
1476
+ case 31:
1477
+ // We subtract when checking offset to avoid possible overflow
1478
+ if (offset > mmdb->data_section_size - 3) {
1479
+ DEBUG_MSGF("String end (%d, case 31) past data section (%d)",
1480
+ offset,
1481
+ mmdb->data_section_size);
1482
+ return MMDB_INVALID_DATA_ERROR;
1483
+ }
1484
+ size = 65821 + get_uint24(&mem[offset]);
1485
+ offset += 3;
1486
+ default:
1487
+ break;
1488
+ }
1489
+
1490
+ DEBUG_MSGF("Size: %i", size);
1491
+
1492
+ if (type == MMDB_DATA_TYPE_MAP || type == MMDB_DATA_TYPE_ARRAY) {
1493
+ entry_data->data_size = size;
1494
+ entry_data->offset_to_next = offset;
1495
+ return MMDB_SUCCESS;
1496
+ }
1497
+
1498
+ if (type == MMDB_DATA_TYPE_BOOLEAN) {
1499
+ entry_data->boolean = size ? true : false;
1500
+ entry_data->data_size = 0;
1501
+ entry_data->offset_to_next = offset;
1502
+ DEBUG_MSGF("boolean value: %s", entry_data->boolean ? "true" : "false");
1503
+ return MMDB_SUCCESS;
1504
+ }
1505
+
1506
+ // Check that the data doesn't extend past the end of the memory
1507
+ // buffer and that the calculation in doing this did not underflow.
1508
+ if (offset > mmdb->data_section_size - size ||
1509
+ mmdb->data_section_size < size) {
1510
+ DEBUG_MSGF("Data end (%d) past data section (%d)", offset + size,
1511
+ mmdb->data_section_size);
1512
+ return MMDB_INVALID_DATA_ERROR;
1513
+ }
1514
+
1515
+ if (type == MMDB_DATA_TYPE_UINT16) {
1516
+ if (size > 2) {
1517
+ DEBUG_MSGF("uint16 of size %d", size);
1518
+ return MMDB_INVALID_DATA_ERROR;
1519
+ }
1520
+ entry_data->uint16 = (uint16_t)get_uintX(&mem[offset], size);
1521
+ DEBUG_MSGF("uint16 value: %u", entry_data->uint16);
1522
+ } else if (type == MMDB_DATA_TYPE_UINT32) {
1523
+ if (size > 4) {
1524
+ DEBUG_MSGF("uint32 of size %d", size);
1525
+ return MMDB_INVALID_DATA_ERROR;
1526
+ }
1527
+ entry_data->uint32 = (uint32_t)get_uintX(&mem[offset], size);
1528
+ DEBUG_MSGF("uint32 value: %u", entry_data->uint32);
1529
+ } else if (type == MMDB_DATA_TYPE_INT32) {
1530
+ if (size > 4) {
1531
+ DEBUG_MSGF("int32 of size %d", size);
1532
+ return MMDB_INVALID_DATA_ERROR;
1533
+ }
1534
+ entry_data->int32 = get_sintX(&mem[offset], size);
1535
+ DEBUG_MSGF("int32 value: %i", entry_data->int32);
1536
+ } else if (type == MMDB_DATA_TYPE_UINT64) {
1537
+ if (size > 8) {
1538
+ DEBUG_MSGF("uint64 of size %d", size);
1539
+ return MMDB_INVALID_DATA_ERROR;
1540
+ }
1541
+ entry_data->uint64 = get_uintX(&mem[offset], size);
1542
+ DEBUG_MSGF("uint64 value: %" PRIu64, entry_data->uint64);
1543
+ } else if (type == MMDB_DATA_TYPE_UINT128) {
1544
+ if (size > 16) {
1545
+ DEBUG_MSGF("uint128 of size %d", size);
1546
+ return MMDB_INVALID_DATA_ERROR;
1547
+ }
1548
+ #if MMDB_UINT128_IS_BYTE_ARRAY
1549
+ memset(entry_data->uint128, 0, 16);
1550
+ if (size > 0) {
1551
+ memcpy(entry_data->uint128 + 16 - size, &mem[offset], size);
1552
+ }
1553
+ #else
1554
+ entry_data->uint128 = get_uint128(&mem[offset], size);
1555
+ #endif
1556
+ } else if (type == MMDB_DATA_TYPE_FLOAT) {
1557
+ if (size != 4) {
1558
+ DEBUG_MSGF("float of size %d", size);
1559
+ return MMDB_INVALID_DATA_ERROR;
1560
+ }
1561
+ size = 4;
1562
+ entry_data->float_value = get_ieee754_float(&mem[offset]);
1563
+ DEBUG_MSGF("float value: %f", entry_data->float_value);
1564
+ } else if (type == MMDB_DATA_TYPE_DOUBLE) {
1565
+ if (size != 8) {
1566
+ DEBUG_MSGF("double of size %d", size);
1567
+ return MMDB_INVALID_DATA_ERROR;
1568
+ }
1569
+ size = 8;
1570
+ entry_data->double_value = get_ieee754_double(&mem[offset]);
1571
+ DEBUG_MSGF("double value: %f", entry_data->double_value);
1572
+ } else if (type == MMDB_DATA_TYPE_UTF8_STRING) {
1573
+ entry_data->utf8_string = size == 0 ? "" : (char *)&mem[offset];
1574
+ entry_data->data_size = size;
1575
+ #ifdef MMDB_DEBUG
1576
+ char *string = mmdb_strndup(entry_data->utf8_string,
1577
+ size > 50 ? 50 : size);
1578
+ if (NULL == string) {
1579
+ abort();
1580
+ }
1581
+ DEBUG_MSGF("string value: %s", string);
1582
+ free(string);
1583
+ #endif
1584
+ } else if (type == MMDB_DATA_TYPE_BYTES) {
1585
+ entry_data->bytes = &mem[offset];
1586
+ entry_data->data_size = size;
1587
+ }
1588
+
1589
+ entry_data->offset_to_next = offset + size;
1590
+
1591
+ return MMDB_SUCCESS;
1592
+ }
1593
+
1594
+ LOCAL int get_ext_type(int raw_ext_type)
1595
+ {
1596
+ return 7 + raw_ext_type;
1597
+ }
1598
+
1599
+ LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr,
1600
+ int ptr_size)
1601
+ {
1602
+ uint32_t new_offset;
1603
+ switch (ptr_size) {
1604
+ case 1:
1605
+ new_offset = ( (ctrl & 7) << 8) + ptr[0];
1606
+ break;
1607
+ case 2:
1608
+ new_offset = 2048 + ( (ctrl & 7) << 16 ) + ( ptr[0] << 8) + ptr[1];
1609
+ break;
1610
+ case 3:
1611
+ new_offset = 2048 + 524288 + ( (ctrl & 7) << 24 ) + get_uint24(ptr);
1612
+ break;
1613
+ case 4:
1614
+ default:
1615
+ new_offset = get_uint32(ptr);
1616
+ break;
1617
+ }
1618
+ return new_offset;
1619
+ }
1620
+
1621
+ int MMDB_get_metadata_as_entry_data_list(
1622
+ MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list)
1623
+ {
1624
+ MMDB_s metadata_db = make_fake_metadata_db(mmdb);
1625
+
1626
+ MMDB_entry_s metadata_start = {
1627
+ .mmdb = &metadata_db,
1628
+ .offset = 0
1629
+ };
1630
+
1631
+ return MMDB_get_entry_data_list(&metadata_start, entry_data_list);
1632
+ }
1633
+
1634
+ int MMDB_get_entry_data_list(
1635
+ MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list)
1636
+ {
1637
+ *entry_data_list = new_entry_data_list();
1638
+ if (NULL == *entry_data_list) {
1639
+ return MMDB_OUT_OF_MEMORY_ERROR;
1640
+ }
1641
+ return get_entry_data_list(start->mmdb, start->offset, *entry_data_list, 0);
1642
+ }
1643
+
1644
+ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
1645
+ MMDB_entry_data_list_s *const entry_data_list,
1646
+ int depth)
1647
+ {
1648
+ if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) {
1649
+ DEBUG_MSG("reached the maximum data structure depth");
1650
+ return MMDB_INVALID_DATA_ERROR;
1651
+ }
1652
+ depth++;
1653
+ CHECKED_DECODE_ONE(mmdb, offset, &entry_data_list->entry_data);
1654
+
1655
+ switch (entry_data_list->entry_data.type) {
1656
+ case MMDB_DATA_TYPE_POINTER:
1657
+ {
1658
+ uint32_t next_offset = entry_data_list->entry_data.offset_to_next;
1659
+ uint32_t last_offset;
1660
+ CHECKED_DECODE_ONE(mmdb, last_offset =
1661
+ entry_data_list->entry_data.pointer,
1662
+ &entry_data_list->entry_data);
1663
+
1664
+ /* Pointers to pointers are illegal under the spec */
1665
+ if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_POINTER) {
1666
+ DEBUG_MSG("pointer points to another pointer");
1667
+ return MMDB_INVALID_DATA_ERROR;
1668
+ }
1669
+
1670
+ if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_ARRAY
1671
+ || entry_data_list->entry_data.type == MMDB_DATA_TYPE_MAP) {
1672
+
1673
+ int status =
1674
+ get_entry_data_list(mmdb, last_offset, entry_data_list,
1675
+ depth);
1676
+ if (MMDB_SUCCESS != status) {
1677
+ DEBUG_MSG("get_entry_data_list on pointer failed.");
1678
+ return status;
1679
+ }
1680
+ }
1681
+ entry_data_list->entry_data.offset_to_next = next_offset;
1682
+ }
1683
+ break;
1684
+ case MMDB_DATA_TYPE_ARRAY:
1685
+ {
1686
+ uint32_t array_size = entry_data_list->entry_data.data_size;
1687
+ uint32_t array_offset = entry_data_list->entry_data.offset_to_next;
1688
+ MMDB_entry_data_list_s *previous = entry_data_list;
1689
+ while (array_size-- > 0) {
1690
+ MMDB_entry_data_list_s *entry_data_list_to = previous->next =
1691
+ new_entry_data_list();
1692
+ if (NULL == entry_data_list_to) {
1693
+ return MMDB_OUT_OF_MEMORY_ERROR;
1694
+ }
1695
+
1696
+ int status =
1697
+ get_entry_data_list(mmdb, array_offset, entry_data_list_to,
1698
+ depth);
1699
+ if (MMDB_SUCCESS != status) {
1700
+ DEBUG_MSG("get_entry_data_list on array element failed.");
1701
+ return status;
1702
+ }
1703
+
1704
+ array_offset = entry_data_list_to->entry_data.offset_to_next;
1705
+ while (previous->next) {
1706
+ previous = previous->next;
1707
+ }
1708
+ }
1709
+ entry_data_list->entry_data.offset_to_next = array_offset;
1710
+
1711
+ }
1712
+ break;
1713
+ case MMDB_DATA_TYPE_MAP:
1714
+ {
1715
+ uint32_t size = entry_data_list->entry_data.data_size;
1716
+
1717
+ offset = entry_data_list->entry_data.offset_to_next;
1718
+ MMDB_entry_data_list_s *previous = entry_data_list;
1719
+ while (size-- > 0) {
1720
+ MMDB_entry_data_list_s *entry_data_list_to = previous->next =
1721
+ new_entry_data_list();
1722
+ if (NULL == entry_data_list_to) {
1723
+ return MMDB_OUT_OF_MEMORY_ERROR;
1724
+ }
1725
+
1726
+ int status =
1727
+ get_entry_data_list(mmdb, offset, entry_data_list_to,
1728
+ depth);
1729
+ if (MMDB_SUCCESS != status) {
1730
+ DEBUG_MSG("get_entry_data_list on map key failed.");
1731
+ return status;
1732
+ }
1733
+
1734
+ while (previous->next) {
1735
+ previous = previous->next;
1736
+ }
1737
+
1738
+ offset = entry_data_list_to->entry_data.offset_to_next;
1739
+ entry_data_list_to = previous->next =
1740
+ new_entry_data_list();
1741
+
1742
+ if (NULL == entry_data_list_to) {
1743
+ return MMDB_OUT_OF_MEMORY_ERROR;
1744
+ }
1745
+
1746
+ status = get_entry_data_list(mmdb, offset, entry_data_list_to,
1747
+ depth);
1748
+ if (MMDB_SUCCESS != status) {
1749
+ DEBUG_MSG("get_entry_data_list on map element failed.");
1750
+ return status;
1751
+ }
1752
+
1753
+ while (previous->next) {
1754
+ previous = previous->next;
1755
+ }
1756
+ offset = entry_data_list_to->entry_data.offset_to_next;
1757
+ }
1758
+ entry_data_list->entry_data.offset_to_next = offset;
1759
+ }
1760
+ break;
1761
+ default:
1762
+ break;
1763
+ }
1764
+
1765
+ return MMDB_SUCCESS;
1766
+ }
1767
+
1768
+ LOCAL float get_ieee754_float(const uint8_t *restrict p)
1769
+ {
1770
+ volatile float f;
1771
+ uint8_t *q = (void *)&f;
1772
+ /* Windows builds don't use autoconf but we can assume they're all
1773
+ * little-endian. */
1774
+ #if MMDB_LITTLE_ENDIAN || _WIN32
1775
+ q[3] = p[0];
1776
+ q[2] = p[1];
1777
+ q[1] = p[2];
1778
+ q[0] = p[3];
1779
+ #else
1780
+ memcpy(q, p, 4);
1781
+ #endif
1782
+ return f;
1783
+ }
1784
+
1785
+ LOCAL double get_ieee754_double(const uint8_t *restrict p)
1786
+ {
1787
+ volatile double d;
1788
+ uint8_t *q = (void *)&d;
1789
+ #if MMDB_LITTLE_ENDIAN || _WIN32
1790
+ q[7] = p[0];
1791
+ q[6] = p[1];
1792
+ q[5] = p[2];
1793
+ q[4] = p[3];
1794
+ q[3] = p[4];
1795
+ q[2] = p[5];
1796
+ q[1] = p[6];
1797
+ q[0] = p[7];
1798
+ #else
1799
+ memcpy(q, p, 8);
1800
+ #endif
1801
+
1802
+ return d;
1803
+ }
1804
+
1805
+ LOCAL uint32_t get_uint32(const uint8_t *p)
1806
+ {
1807
+ return p[0] * 16777216U + p[1] * 65536 + p[2] * 256 + p[3];
1808
+ }
1809
+
1810
+ LOCAL uint32_t get_uint24(const uint8_t *p)
1811
+ {
1812
+ return p[0] * 65536U + p[1] * 256 + p[2];
1813
+ }
1814
+
1815
+ LOCAL uint32_t get_uint16(const uint8_t *p)
1816
+ {
1817
+ return p[0] * 256U + p[1];
1818
+ }
1819
+
1820
+ LOCAL uint64_t get_uintX(const uint8_t *p, int length)
1821
+ {
1822
+ uint64_t value = 0;
1823
+ while (length-- > 0) {
1824
+ value <<= 8;
1825
+ value += *p++;
1826
+ }
1827
+ return value;
1828
+ }
1829
+
1830
+ LOCAL int32_t get_sintX(const uint8_t *p, int length)
1831
+ {
1832
+ return (int32_t)get_uintX(p, length);
1833
+ }
1834
+
1835
+ LOCAL MMDB_entry_data_list_s *new_entry_data_list(void)
1836
+ {
1837
+ /* We need calloc here in order to ensure that the ->next pointer in the
1838
+ * struct doesn't point to some random address. */
1839
+ return calloc(1, sizeof(MMDB_entry_data_list_s));
1840
+ }
1841
+
1842
+ void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list)
1843
+ {
1844
+ if (entry_data_list == NULL) {
1845
+ return;
1846
+ }
1847
+ if (entry_data_list->next) {
1848
+ MMDB_free_entry_data_list(entry_data_list->next);
1849
+ }
1850
+ free(entry_data_list);
1851
+ }
1852
+
1853
+ void MMDB_close(MMDB_s *const mmdb)
1854
+ {
1855
+ free_mmdb_struct(mmdb);
1856
+ }
1857
+
1858
+ LOCAL void free_mmdb_struct(MMDB_s *const mmdb)
1859
+ {
1860
+ if (!mmdb) {
1861
+ return;
1862
+ }
1863
+
1864
+ if (NULL != mmdb->filename) {
1865
+ FREE_AND_SET_NULL(mmdb->filename);
1866
+ }
1867
+ if (NULL != mmdb->file_content) {
1868
+ #ifdef _WIN32
1869
+ UnmapViewOfFile(mmdb->file_content);
1870
+ /* Winsock is only initialized if open was successful so we only have
1871
+ * to cleanup then. */
1872
+ WSACleanup();
1873
+ #else
1874
+ munmap((void *)mmdb->file_content, mmdb->file_size);
1875
+ #endif
1876
+ }
1877
+
1878
+ if (NULL != mmdb->metadata.database_type) {
1879
+ FREE_AND_SET_NULL(mmdb->metadata.database_type);
1880
+ }
1881
+
1882
+ free_languages_metadata(mmdb);
1883
+ free_descriptions_metadata(mmdb);
1884
+ }
1885
+
1886
+ LOCAL void free_languages_metadata(MMDB_s *mmdb)
1887
+ {
1888
+ if (!mmdb->metadata.languages.count) {
1889
+ return;
1890
+ }
1891
+
1892
+ for (size_t i = 0; i < mmdb->metadata.languages.count; i++) {
1893
+ FREE_AND_SET_NULL(mmdb->metadata.languages.names[i]);
1894
+ }
1895
+ FREE_AND_SET_NULL(mmdb->metadata.languages.names);
1896
+ }
1897
+
1898
+ LOCAL void free_descriptions_metadata(MMDB_s *mmdb)
1899
+ {
1900
+ if (!mmdb->metadata.description.count) {
1901
+ return;
1902
+ }
1903
+
1904
+ for (size_t i = 0; i < mmdb->metadata.description.count; i++) {
1905
+ if (NULL != mmdb->metadata.description.descriptions[i]) {
1906
+ if (NULL !=
1907
+ mmdb->metadata.description.descriptions[i]->language) {
1908
+ FREE_AND_SET_NULL(
1909
+ mmdb->metadata.description.descriptions[i]->language);
1910
+ }
1911
+
1912
+ if (NULL !=
1913
+ mmdb->metadata.description.descriptions[i]->description) {
1914
+ FREE_AND_SET_NULL(
1915
+ mmdb->metadata.description.descriptions[i]->description);
1916
+ }
1917
+ FREE_AND_SET_NULL(mmdb->metadata.description.descriptions[i]);
1918
+ }
1919
+ }
1920
+
1921
+ FREE_AND_SET_NULL(mmdb->metadata.description.descriptions);
1922
+ }
1923
+
1924
+ const char *MMDB_lib_version(void)
1925
+ {
1926
+ return PACKAGE_VERSION;
1927
+ }
1928
+
1929
+ int MMDB_dump_entry_data_list(FILE *const stream,
1930
+ MMDB_entry_data_list_s *const entry_data_list,
1931
+ int indent)
1932
+ {
1933
+ int status;
1934
+ dump_entry_data_list(stream, entry_data_list, indent, &status);
1935
+ return status;
1936
+ }
1937
+
1938
+ LOCAL MMDB_entry_data_list_s *dump_entry_data_list(
1939
+ FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent,
1940
+ int *status)
1941
+ {
1942
+ switch (entry_data_list->entry_data.type) {
1943
+ case MMDB_DATA_TYPE_MAP:
1944
+ {
1945
+ uint32_t size = entry_data_list->entry_data.data_size;
1946
+
1947
+ print_indentation(stream, indent);
1948
+ fprintf(stream, "{\n");
1949
+ indent += 2;
1950
+
1951
+ for (entry_data_list = entry_data_list->next;
1952
+ size && entry_data_list; size--) {
1953
+
1954
+ if (MMDB_DATA_TYPE_UTF8_STRING !=
1955
+ entry_data_list->entry_data.type) {
1956
+ *status = MMDB_INVALID_DATA_ERROR;
1957
+ return NULL;
1958
+ }
1959
+ char *key =
1960
+ mmdb_strndup(
1961
+ (char *)entry_data_list->entry_data.utf8_string,
1962
+ entry_data_list->entry_data.data_size);
1963
+ if (NULL == key) {
1964
+ *status = MMDB_OUT_OF_MEMORY_ERROR;
1965
+ return NULL;
1966
+ }
1967
+
1968
+ print_indentation(stream, indent);
1969
+ fprintf(stream, "\"%s\": \n", key);
1970
+ free(key);
1971
+
1972
+ entry_data_list = entry_data_list->next;
1973
+ entry_data_list =
1974
+ dump_entry_data_list(stream, entry_data_list, indent + 2,
1975
+ status);
1976
+
1977
+ if (MMDB_SUCCESS != *status) {
1978
+ return NULL;
1979
+ }
1980
+ }
1981
+
1982
+ indent -= 2;
1983
+ print_indentation(stream, indent);
1984
+ fprintf(stream, "}\n");
1985
+ }
1986
+ break;
1987
+ case MMDB_DATA_TYPE_ARRAY:
1988
+ {
1989
+ uint32_t size = entry_data_list->entry_data.data_size;
1990
+
1991
+ print_indentation(stream, indent);
1992
+ fprintf(stream, "[\n");
1993
+ indent += 2;
1994
+
1995
+ for (entry_data_list = entry_data_list->next;
1996
+ size && entry_data_list; size--) {
1997
+ entry_data_list =
1998
+ dump_entry_data_list(stream, entry_data_list, indent,
1999
+ status);
2000
+ if (MMDB_SUCCESS != *status) {
2001
+ return NULL;
2002
+ }
2003
+ }
2004
+
2005
+ indent -= 2;
2006
+ print_indentation(stream, indent);
2007
+ fprintf(stream, "]\n");
2008
+ }
2009
+ break;
2010
+ case MMDB_DATA_TYPE_UTF8_STRING:
2011
+ {
2012
+ char *string =
2013
+ mmdb_strndup((char *)entry_data_list->entry_data.utf8_string,
2014
+ entry_data_list->entry_data.data_size);
2015
+ if (NULL == string) {
2016
+ *status = MMDB_OUT_OF_MEMORY_ERROR;
2017
+ return NULL;
2018
+ }
2019
+ print_indentation(stream, indent);
2020
+ fprintf(stream, "\"%s\" <utf8_string>\n", string);
2021
+ free(string);
2022
+ entry_data_list = entry_data_list->next;
2023
+ }
2024
+ break;
2025
+ case MMDB_DATA_TYPE_BYTES:
2026
+ {
2027
+ char *hex_string =
2028
+ bytes_to_hex((uint8_t *)entry_data_list->entry_data.bytes,
2029
+ entry_data_list->entry_data.data_size);
2030
+ if (NULL == hex_string) {
2031
+ *status = MMDB_OUT_OF_MEMORY_ERROR;
2032
+ return NULL;
2033
+ }
2034
+
2035
+ print_indentation(stream, indent);
2036
+ fprintf(stream, "%s <bytes>\n", hex_string);
2037
+ free(hex_string);
2038
+
2039
+ entry_data_list = entry_data_list->next;
2040
+ }
2041
+ break;
2042
+ case MMDB_DATA_TYPE_DOUBLE:
2043
+ print_indentation(stream, indent);
2044
+ fprintf(stream, "%f <double>\n",
2045
+ entry_data_list->entry_data.double_value);
2046
+ entry_data_list = entry_data_list->next;
2047
+ break;
2048
+ case MMDB_DATA_TYPE_FLOAT:
2049
+ print_indentation(stream, indent);
2050
+ fprintf(stream, "%f <float>\n",
2051
+ entry_data_list->entry_data.float_value);
2052
+ entry_data_list = entry_data_list->next;
2053
+ break;
2054
+ case MMDB_DATA_TYPE_UINT16:
2055
+ print_indentation(stream, indent);
2056
+ fprintf(stream, "%u <uint16>\n", entry_data_list->entry_data.uint16);
2057
+ entry_data_list = entry_data_list->next;
2058
+ break;
2059
+ case MMDB_DATA_TYPE_UINT32:
2060
+ print_indentation(stream, indent);
2061
+ fprintf(stream, "%u <uint32>\n", entry_data_list->entry_data.uint32);
2062
+ entry_data_list = entry_data_list->next;
2063
+ break;
2064
+ case MMDB_DATA_TYPE_BOOLEAN:
2065
+ print_indentation(stream, indent);
2066
+ fprintf(stream, "%s <boolean>\n",
2067
+ entry_data_list->entry_data.boolean ? "true" : "false");
2068
+ entry_data_list = entry_data_list->next;
2069
+ break;
2070
+ case MMDB_DATA_TYPE_UINT64:
2071
+ print_indentation(stream, indent);
2072
+ fprintf(stream, "%" PRIu64 " <uint64>\n",
2073
+ entry_data_list->entry_data.uint64);
2074
+ entry_data_list = entry_data_list->next;
2075
+ break;
2076
+ case MMDB_DATA_TYPE_UINT128:
2077
+ print_indentation(stream, indent);
2078
+ #if MMDB_UINT128_IS_BYTE_ARRAY
2079
+ char *hex_string =
2080
+ bytes_to_hex((uint8_t *)entry_data_list->entry_data.uint128, 16);
2081
+ if (NULL == hex_string) {
2082
+ *status = MMDB_OUT_OF_MEMORY_ERROR;
2083
+ return NULL;
2084
+ }
2085
+ fprintf(stream, "0x%s <uint128>\n", hex_string);
2086
+ free(hex_string);
2087
+ #else
2088
+ uint64_t high = entry_data_list->entry_data.uint128 >> 64;
2089
+ uint64_t low = (uint64_t)entry_data_list->entry_data.uint128;
2090
+ fprintf(stream, "0x%016" PRIX64 "%016" PRIX64 " <uint128>\n", high,
2091
+ low);
2092
+ #endif
2093
+ entry_data_list = entry_data_list->next;
2094
+ break;
2095
+ case MMDB_DATA_TYPE_INT32:
2096
+ print_indentation(stream, indent);
2097
+ fprintf(stream, "%d <int32>\n", entry_data_list->entry_data.int32);
2098
+ entry_data_list = entry_data_list->next;
2099
+ break;
2100
+ default:
2101
+ *status = MMDB_INVALID_DATA_ERROR;
2102
+ return NULL;
2103
+ }
2104
+
2105
+ *status = MMDB_SUCCESS;
2106
+ return entry_data_list;
2107
+ }
2108
+
2109
+ LOCAL void print_indentation(FILE *stream, int i)
2110
+ {
2111
+ char buffer[1024];
2112
+ int size = i >= 1024 ? 1023 : i;
2113
+ memset(buffer, 32, size);
2114
+ buffer[size] = '\0';
2115
+ fputs(buffer, stream);
2116
+ }
2117
+
2118
+ LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size)
2119
+ {
2120
+ char *hex_string;
2121
+ MAYBE_CHECK_SIZE_OVERFLOW(size, SIZE_MAX / 2 - 1, NULL);
2122
+
2123
+ hex_string = malloc((size * 2) + 1);
2124
+ if (NULL == hex_string) {
2125
+ return NULL;
2126
+ }
2127
+
2128
+ for (uint32_t i = 0; i < size; i++) {
2129
+ sprintf(hex_string + (2 * i), "%02X", bytes[i]);
2130
+ }
2131
+
2132
+ return hex_string;
2133
+ }
2134
+
2135
+ const char *MMDB_strerror(int error_code)
2136
+ {
2137
+ switch (error_code) {
2138
+ case MMDB_SUCCESS:
2139
+ return "Success (not an error)";
2140
+ case MMDB_FILE_OPEN_ERROR:
2141
+ return "Error opening the specified MaxMind DB file";
2142
+ case MMDB_CORRUPT_SEARCH_TREE_ERROR:
2143
+ return "The MaxMind DB file's search tree is corrupt";
2144
+ case MMDB_INVALID_METADATA_ERROR:
2145
+ return "The MaxMind DB file contains invalid metadata";
2146
+ case MMDB_IO_ERROR:
2147
+ return "An attempt to read data from the MaxMind DB file failed";
2148
+ case MMDB_OUT_OF_MEMORY_ERROR:
2149
+ return "A memory allocation call failed";
2150
+ case MMDB_UNKNOWN_DATABASE_FORMAT_ERROR:
2151
+ return
2152
+ "The MaxMind DB file is in a format this library can't handle (unknown record size or binary format version)";
2153
+ case MMDB_INVALID_DATA_ERROR:
2154
+ return
2155
+ "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)";
2156
+ case MMDB_INVALID_LOOKUP_PATH_ERROR:
2157
+ return
2158
+ "The lookup path contained an invalid value (like a negative integer for an array index)";
2159
+ case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR:
2160
+ return
2161
+ "The lookup path does not match the data (key that doesn't exist, array index bigger than the array, expected array or map where none exists)";
2162
+ case MMDB_INVALID_NODE_NUMBER_ERROR:
2163
+ return
2164
+ "The MMDB_read_node function was called with a node number that does not exist in the search tree";
2165
+ case MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR:
2166
+ return
2167
+ "You attempted to look up an IPv6 address in an IPv4-only database";
2168
+ default:
2169
+ return "Unknown error code";
2170
+ }
2171
+ }