geoip2_c 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }