summaryrefslogtreecommitdiff
path: root/doc/libmaxminddb.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/libmaxminddb.md')
-rw-r--r--doc/libmaxminddb.md889
1 files changed, 889 insertions, 0 deletions
diff --git a/doc/libmaxminddb.md b/doc/libmaxminddb.md
new file mode 100644
index 0000000..e6de9d5
--- /dev/null
+++ b/doc/libmaxminddb.md
@@ -0,0 +1,889 @@
+# NAME
+
+libmaxminddb - a library for working with MaxMind DB files
+
+# SYNOPSIS
+
+```c
+#include <maxminddb.h>
+
+int MMDB_open(
+ const char *const filename,
+ uint32_t flags,
+ MMDB_s *const mmdb);
+void MMDB_close(MMDB_s *const mmdb);
+
+MMDB_lookup_result_s MMDB_lookup_string(
+ MMDB_s *const mmdb,
+ const char *const ipstr,
+ int *const gai_error,
+ int *const mmdb_error);
+MMDB_lookup_result_s MMDB_lookup_sockaddr(
+ MMDB_s *const mmdb,
+ const struct sockaddr *const
+ sockaddr,
+ int *const mmdb_error);
+
+int MMDB_get_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ ...);
+int MMDB_vget_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ va_list va_path);
+int MMDB_aget_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ const char *const *const path);
+
+int MMDB_get_entry_data_list(
+ MMDB_entry_s *start,
+ MMDB_entry_data_list_s **const entry_data_list);
+void MMDB_free_entry_data_list(
+ MMDB_entry_data_list_s *const entry_data_list);
+int MMDB_get_metadata_as_entry_data_list(
+ MMDB_s *const mmdb,
+ MMDB_entry_data_list_s **const entry_data_list);
+int MMDB_dump_entry_data_list(
+ FILE *const stream,
+ MMDB_entry_data_list_s *const entry_data_list,
+ int indent);
+
+int MMDB_read_node(
+ MMDB_s *const mmdb,
+ uint32_t node_number,
+ MMDB_search_node_s *const node);
+
+const char *MMDB_lib_version(void);
+const char *MMDB_strerror(int error_code);
+
+typedef struct MMDB_lookup_result_s {
+ bool found_entry;
+ MMDB_entry_s entry;
+ uint16_t netmask;
+} MMDB_lookup_result_s;
+
+typedef struct MMDB_entry_data_s {
+ bool has_data;
+ union {
+ uint32_t pointer;
+ const char *utf8_string;
+ double double_value;
+ const uint8_t *bytes;
+ uint16_t uint16;
+ uint32_t uint32;
+ int32_t int32;
+ uint64_t uint64;
+ {mmdb_uint128_t or uint8_t[16]} uint128;
+ bool boolean;
+ float float_value;
+ };
+ ...
+ uint32_t data_size;
+ uint32_t type;
+} MMDB_entry_data_s;
+
+typedef struct MMDB_entry_data_list_s {
+ MMDB_entry_data_s entry_data;
+ struct MMDB_entry_data_list_s *next;
+} MMDB_entry_data_list_s;
+```
+
+# DESCRIPTION
+
+The libmaxminddb library provides functions for working MaxMind DB files. See
+http://maxmind.github.io/MaxMind-DB/ for the MaxMind DB format specification.
+The database and results are all represented by different data structures.
+Databases are opened by calling `MMDB_open()`. You can look up IP addresses as
+a string with `MMDB_lookup_string()` or as a pointer to a `sockaddr`
+structure with `MMDB_lookup_sockaddr()`.
+
+If the lookup finds the IP address in the database, it returns a
+`MMDB_lookup_result_s` structure. If that structure indicates that the database
+has data for the IP, there are a number of functions that can be used to fetch
+that data. These include `MMDB_get_value()` and `MMDB_get_entry_data_list()`.
+See the function documentation below for more details.
+
+When you are done with the database handle you should call `MMDB_close()`.
+
+All publicly visible functions, structures, and macros begin with "MMDB_".
+
+# DATA STRUCTURES
+
+All data structures exported by this library's `maxminddb.h` header are
+typedef'd in the form `typedef struct foo_s { ... } foo_s` so you can refer to
+them without the `struct` prefix.
+
+This library provides the following data structures:
+
+## `MMDB_s`
+
+This is the handle for a MaxMind DB file. We only document some of this
+structure's fields intended for public use. All other fields are subject to
+change and are intended only for internal use.
+
+```c
+typedef struct MMDB_s {
+ uint32_t flags;
+ const char *filename;
+ ...
+ MMDB_metadata_s metadata;
+} MMDB_s;
+```
+
+* `uint32_t flags` - the flags this database was opened with. See the
+ `MMDB_open()` documentation for more details.
+* `const char *filename` - the name of the file which was opened, as passed to
+ `MMDB_open()`.
+* `MMDB_metadata_s metadata` - the metadata for the database.
+
+## `MMDB_metadata_s` and `MMDB_description_s`
+
+This structure can be retrieved from the `MMDB_s` structure. It contains the
+metadata read from the database file. Note that you may find it more convenient
+to access this metadata by calling `MMDB_get_metadata_as_entry_data_list()`
+instead.
+
+```c
+typedef struct MMDB_metadata_s {
+ uint32_t node_count;
+ uint16_t record_size;
+ uint16_t ip_version;
+ const char *database_type;
+ struct {
+ size_t count;
+ const char **names;
+ } languages;
+ uint16_t binary_format_major_version;
+ uint16_t binary_format_minor_version;
+ uint64_t build_epoch;
+ struct {
+ size_t count;
+ MMDB_description_s **descriptions;
+ } description;
+} MMDB_metadata_s;
+
+typedef struct MMDB_description_s {
+ const char *language;
+ const char *description;
+} MMDB_description_s;
+```
+
+These structures should be mostly self-explanatory.
+
+The `ip_version` member should always be `4` or `6`. The
+`binary_format_major_version` should always be `2`.
+
+There is no requirement that the database metadata include languages or
+descriptions, so the `count` for these parts of the metadata can be zero. All
+of the other `MMDB_metadata_s` fields should be populated.
+
+## `MMDB_lookup_result_s`
+
+This structure is returned as the result of looking up an IP address.
+
+```c
+typedef struct MMDB_lookup_result_s {
+ bool found_entry;
+ MMDB_entry_s entry;
+ uint16_t netmask;
+} MMDB_lookup_result_s;
+```
+
+If the `found_entry` member is false then the other members of this structure
+do not contain meaningful values. Always check that `found_entry` is true
+first.
+
+The `entry` member is used to look up the data associated with the IP address.
+
+The `netmask` member tells you what subnet the IP address belongs to in this
+database. For example, if you look up the address `1.1.1.1` in an IPv4 database
+and the returned `netmask` is 16, then the address is part of the `1.1.0.0/16`
+subnet.
+
+If the database is an IPv6 database, the returned netmask is always an IPv6
+prefix length (from 0-128), even if that database *also* contains IPv4
+networks. If you look up an IPv4 address and would like to turn the netmask
+into an IPv4 netmask value, you can simply subtract `96` from the value.
+
+## `MMDB_result_s`
+
+You don't really need to dig around in this structure. You'll get this from a
+`MMDB_lookup_result_s` structure and pass it to various functions.
+
+## `MMDB_entry_data_s`
+
+This structure is used to return a single data section entry for an IP. These
+entries can in turn point to other entries, as is the case for things like maps
+and arrays. Some members of this structure are not documented as they are only
+for internal use.
+
+```c
+typedef struct MMDB_entry_data_s {
+ bool has_data;
+ union {
+ uint32_t pointer;
+ const char *utf8_string;
+ double double_value;
+ const uint8_t *bytes;
+ uint16_t uint16;
+ uint32_t uint32;
+ int32_t int32;
+ uint64_t uint64;
+ {mmdb_uint128_t or uint8_t[16]} uint128;
+ bool boolean;
+ float float_value;
+ };
+ ...
+ uint32_t data_size;
+ uint32_t type;
+} MMDB_entry_data_s;
+```
+
+The `has_data` member is true if data was found for a given lookup. See
+`MMDB_get_value()` for more details. If this member is false then none of the
+other values in the structure are meaningful.
+
+The union at the beginning of the structure defines the actual data. To
+determine which union member is populated you should look at the `type` member.
+The `pointer` member of the union should never be populated in any data
+returned by the API. Pointers should always be resolved internally.
+
+The `data_size` member is only relevant for `utf8_string` and `bytes` data.
+`utf8_string` is not null terminated and `data_size` _must_ be used to
+determine its length.
+
+The `type` member can be compared to one of the `MMDB_DTYPE_*` macros.
+
+### 128-bit Integers
+
+The handling of `uint128` data depends on how your platform supports 128-bit
+integers, if it does so at all. With GCC 4.4 and 4.5 we can write `unsigned
+int __attribute__ ((__mode__ (TI)))`. With newer versions of GCC (4.6+) and
+clang (3.2+) we can simply write "unsigned __int128".
+
+In order to work around these differences, this library defines an
+`mmdb_uint128_t` type. This type is defined in the `maxminddb.h` header so you
+can use it in your own code.
+
+With older compilers, we can't use an integer so we instead use a 16 byte
+array of `uint8_t` values. This is the raw data from the database.
+
+This library provides a public macro `MMDB_UINT128_IS_BYTE_ARRAY` macro. If
+this is true (1), then `uint128` values are returned as a byte array, if it is
+false then they are returned as a `mmdb_uint128_t` integer.
+
+### Data Type Macros
+
+This library provides a macro for every data type defined by the MaxMind DB
+spec.
+
+* `MMDB_DATA_TYPE_UTF8_STRING`
+* `MMDB_DATA_TYPE_DOUBLE`
+* `MMDB_DATA_TYPE_BYTES`
+* `MMDB_DATA_TYPE_UINT16`
+* `MMDB_DATA_TYPE_UINT32`
+* `MMDB_DATA_TYPE_MAP`
+* `MMDB_DATA_TYPE_INT32`
+* `MMDB_DATA_TYPE_UINT64`
+* `MMDB_DATA_TYPE_UINT128`
+* `MMDB_DATA_TYPE_ARRAY`
+* `MMDB_DATA_TYPE_BOOLEAN`
+* `MMDB_DATA_TYPE_FLOAT`
+
+There are also a few types that are for internal use only:
+
+* `MMDB_DATA_TYPE_EXTENDED`
+* `MMDB_DATA_TYPE_POINTER`
+* `MMDB_DATA_TYPE_CONTAINER`
+* `MMDB_DATA_TYPE_END_MARKER`
+
+If you see one of these in returned data then something has gone very wrong.
+The database is damaged or was generated incorrectly or there is a bug in the
+libmaxminddb code.
+
+### Pointer Values and `MMDB_close()`
+
+The `utf8_string`, `bytes`, and (maybe) the `uint128` members of this structure
+are all pointers directly into the database's data section. This can either be
+a `malloc`'d or `mmap`'d block of memory. In either case, these pointers will
+become invalid after `MMDB_close()` is called.
+
+If you need to refer to this data after that time you should copy the data
+with an appropriate function (`strdup`, `memcpy`, etc.).
+
+## `MMDB_entry_data_list_s`
+
+This structure encapsulates a linked list of `MMDB_entry_data_s` structures.
+
+```c
+typedef struct MMDB_entry_data_list_s {
+ MMDB_entry_data_s entry_data;
+ struct MMDB_entry_data_list_s *next;
+} MMDB_entry_data_list_s;
+```
+
+This structure lets you look at entire map or array data entry by iterating
+over the linked list.
+
+## `MMDB_search_node_s`
+
+This structure encapsulates the two records in a search node. This is really
+only useful if you want to write code that iterates over the entire search
+tree as opposed to looking up a specific IP address.
+
+```c
+typedef struct MMDB_search_node_s {
+ uint64_t left_record;
+ uint64_t right_record;
+ uint8_t left_record_type;
+ uint8_t right_record_type;
+ MMDB_entry_s left_record_entry;
+ MMDB_entry_s right_record_entry;
+} MMDB_search_node_s;
+```
+
+The two record types will take one of the following values:
+
+* `MMDB_RECORD_TYPE_SEARCH_NODE` - The record points to the next search node.
+* `MMDB_RECORD_TYPE_EMPTY` - The record is a placeholder that indicates there
+ is no data for the IP address. The search should end here.
+* `MMDB_RECORD_TYPE_DATA` - The record is for data in the data section of the
+ database. Use the entry for the record when looking up the data for the
+ record.
+* `MMDB_RECORD_TYPE_INVALID` - The record is invalid. Either an invalid node
+ was looked up or the database is corrupt.
+
+The `MMDB_entry_s` for the record is only valid if the type is
+`MMDB_RECORD_TYPE_DATA`. Attempts to use an entry for other record types will
+result in an error or invalid data.
+
+# STATUS CODES
+
+This library returns (or populates) status codes for many functions. These
+status codes are:
+
+* `MMDB_SUCCESS` - everything worked
+* `MMDB_FILE_OPEN_ERROR` - there was an error trying to open the MaxMind DB
+ file.
+* `MMDB_IO_ERROR` - an IO operation failed. Check `errno` for more details.
+* `MMDB_CORRUPT_SEARCH_TREE_ERROR` - looking up an IP address in the search
+ tree gave us an impossible result. The database is damaged or was generated
+ incorrectly or there is a bug in the libmaxminddb code.
+* `MMDB_INVALID_METADATA_ERROR` - something in the database is wrong. This
+ includes missing metadata keys as well as impossible values (like an
+ `ip_version` of 7).
+* `MMDB_UNKNOWN_DATABASE_FORMAT_ERROR` - The database metadata indicates that
+ it's major version is not 2. This library can only handle major version 2.
+* `MMDB_OUT_OF_MEMORY_ERROR` - a memory allocation call (`malloc`, etc.)
+ failed.
+* `MMDB_INVALID_DATA_ERROR` - an entry in the data section contains invalid
+ data. For example, a `uint16` field is claiming to be more than 2 bytes long.
+ The database is probably damaged or was generated incorrectly.
+* `MMDB_INVALID_LOOKUP_PATH_ERROR` - The lookup path passed to
+ `MMDB_get_value`, `MMDB_vget_value`, or `MMDB_aget_value` contains an array
+ offset that is negative integer or an integer larger than LONG_MAX.
+* `MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR` - The lookup path passed to
+ `MMDB_get_value`,`MMDB_vget_value`, or `MMDB_aget_value` does not match the
+ data structure for the entry. There are number of reasons this can
+ happen. The lookup path could include a key not in a map. The lookup path
+ could include an array index larger than an array. It can also happen when
+ the path expects to find a map or array where none exist.
+
+All status codes should be treated as `int` values.
+
+## `MMDB_strerror()`
+
+```c
+const char *MMDB_strerror(int error_code)
+```
+
+This function takes a status code and returns an English string explaining the
+status.
+
+# FUNCTIONS
+
+This library provides the following exported functions:
+
+## `MMDB_open()`
+
+```c
+int MMDB_open(
+ const char *const filename,
+ uint32_t flags,
+ MMDB_s *const mmdb);
+```
+
+This function opens a handle to a MaxMind DB file. Its return value is a
+status code as defined above. Always check this call's return value.
+
+```c
+MMDB_s mmdb;
+int status =
+ MMDB_open("/path/to/file.mmdb", MMDB_MODE_MMAP, &mmdb);
+if (MMDB_SUCCESS != status) { ... }
+...
+MMDB_close(&mmdb);
+```
+
+The `MMDB_s` structure you pass in can be on the stack or allocated from the
+heap. However, if the open is successful it will contain heap-allocated data,
+so you need to close it with `MMDB_close()`. If the status returned is not
+`MMDB_SUCCESS` then this library makes sure that all allocated memory is freed
+before returning.
+
+The flags currently provided are:
+
+* `MMDB_MODE_MMAP` - open the database with `mmap()`.
+
+Passing in other values for `flags` may yield unpredictable results. In the
+future we may add additional flags that you can bitwise-or together with the
+mode, as well as additional modes.
+
+You can also pass `0` as the `flags` value in which case the database will be
+opened with the default flags. However, these defaults may change in future
+releases. The current default is `MMDB_MODE_MMAP`.
+
+## `MMDB_close()`
+
+```c
+void MMDB_close(MMDB_s *const mmdb);
+```
+
+This frees any allocated or mmap'd memory that is held from the `MMDB_s`
+structure. *It does not free the memory allocated for the structure itself!*
+If you allocated the structure from the heap then you are responsible for
+freeing it.
+
+## `MMDB_lookup_string()`
+
+```c
+MMDB_lookup_result_s MMDB_lookup_string(
+ MMDB_s *const mmdb,
+ const char *const ipstr,
+ int *const gai_error,
+ int *const mmdb_error);
+```
+
+This function looks up an IP address that is passed in as a null-terminated
+string. Internally it calls `getaddrinfo()` to resolve the address into a
+binary form. It then calls `MMDB_lookup_sockaddr()` to look the address up in
+the database. If you have already resolved an address you can call
+`MMDB_lookup_sockaddr()` directly, rather than resolving the address twice.
+
+```c
+int gai_error, mmdb_error;
+MMDB_lookup_result_s result =
+ MMDB_lookup_string(&mmdb, "1.2.3.4", &gai_error, &mmdb_error);
+if (0 != gai_error) { ... }
+if (MMDB_SUCCESS != mmdb_error) { ... }
+
+if (result.found_entry) { ... }
+```
+
+This function always returns an `MMDB_lookup_result_s` structure, but you
+should also check the `gai_error` and `mmdb_error` parameters. If either of
+these indicates an error then the returned structure is meaningless.
+
+If no error occurred you still need to make sure that the `found_entry` member
+in the returned result is true. If it's not, this means that the IP address
+does not have an entry in the database.
+
+This function will work with IPv4 addresses even when the database contains
+data for both IPv4 and IPv6 addresses. The IPv4 address will be looked up as
+'::xxx.xxx.xxx.xxx' rather than being remapped to the `::ffff:xxx.xxx.xxx.xxx`
+block allocated for IPv4-mapped IPv6 addresses.
+
+If you pass an IPv6 address to a database with only IPv4 data then the
+`found_entry` member will be false, but the `mmdb_error` status will still be
+`MMDB_SUCCESS`.
+
+## `MMDB_lookup_sockaddr()`
+
+```c
+MMDB_lookup_result_s MMDB_lookup_sockaddr(
+ MMDB_s *const mmdb,
+ const struct sockaddr *const sockaddr,
+ int *const mmdb_error);
+```
+
+This function looks up an IP address that has already been resolved by
+`getaddrinfo()`.
+
+Other than not calling `getaddrinfo()` itself, this function is identical to
+the `MMDB_lookup_string()` function.
+
+```c
+int mmdb_error;
+MMDB_lookup_result_s result =
+ MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
+if (MMDB_SUCCESS != mmdb_error) { ... }
+
+if (result.found_entry) { ... }
+```
+
+## Data Lookup Functions
+
+There are three functions for looking up data associated with an IP address.
+
+```c
+int MMDB_get_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ ...);
+int MMDB_vget_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ va_list va_path);
+int MMDB_aget_value(
+ MMDB_entry_s *const start,
+ MMDB_entry_data_s *const entry_data,
+ const char *const *const path);
+```
+
+The three functions allow three slightly different calling styles, but they
+all do the same thing.
+
+The first parameter is an `MMDB_entry_s` value. In most cases this will come
+from the `MMDB_lookup_result_s` value returned by `MMDB_lookup_string()` or
+`MMDB_lookup_sockaddr()`.
+
+The second parameter is a reference to an `MMDB_entry_data_s` structure. This
+will be populated with the data that is being looked up, if any is found. If
+nothing is found, then the `has_data` member of this structure will be false.
+If `has_data` is true then you can look at the `data_type` member.
+
+The final parameter is a lookup path. The path consists of a set of strings
+representing either map keys (e.g, "city") or array indexes (e.g., "0", "1")
+to use in the lookup. This allow you to navigate a complex data structure. For
+example, given this example:
+
+```js
+{
+ "names": {
+ "en": "Germany",
+ "de": "Deutschland"
+ },
+ "cities": [ "Berlin", "Frankfurt" ]
+}
+```
+
+We could look up the English name with this code:
+
+```c
+MMDB_lookup_result_s result =
+ MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
+MMDB_entry_data_s entry_data;
+int status =
+ MMDB_get_value(&result.entry, &entry_data,
+ "names", "en", NULL);
+if (MMDB_SUCCESS != status) { ... }
+if (entry_data.has_data) { ... }
+```
+
+If we wanted to find the first city the lookup path would be `"cities",
+"0"`. If you don't provide a lookup path at all, you'll get the entry which
+corresponds to the top level map. The lookup path must always end with `NULL`,
+regardless of which function you call.
+
+The `MMDB_get_value` function takes a variable number of arguments. All of the
+arguments after the `MMDB_entry_data_s *` structure pointer are the lookup
+path. The last argument must be `NULL`.
+
+The `MMDB_vget_value` function accepts a `va_list` as the lookup path. The
+last element retrieved by `va_arg()` must be `NULL`.
+
+Finally, the `MMDB_aget_value` accepts an array of strings as the lookup
+path. The last member of this array must be `NULL`.
+
+If you want to get all of the entry data at once you can call
+`MMDB_get_entry_data_list()` instead.
+
+For each of the three functions, the return value is a status code as
+defined above.
+
+## `MMDB_get_entry_data_list()`
+
+```c
+int MMDB_get_entry_data_list(
+ MMDB_entry_s *start,
+ MMDB_entry_data_list_s **const entry_data_list);
+```
+
+This function allows you to get all of the data for a complex data structure
+at once, rather than looking up each piece using repeated calls to
+`MMDB_get_value()`.
+
+```c
+MMDB_lookup_result_s result =
+ MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
+MMDB_entry_data_list_s *entry_data_list, *first;
+int status =
+ MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+if (MMDB_SUCCESS != status) { ... }
+// save this so we can free this data later
+first = entry_data_list;
+
+while (1) {
+ MMDB_entry_data_list_s *next = entry_data_list = entry_data_list->next;
+ if (NULL == next) {
+ break;
+ }
+
+ switch (next->entry_data.type) {
+ case MMDB_DATA_TYPE_MAP: { ... }
+ case MMDB_DATA_TYPE_UTF8_STRING: { ... }
+ ...
+ }
+
+}
+
+MMDB_free_entry_data_list(first);
+```
+
+It's up to you to interpret the `entry_data_list` data structure. The list is
+linked in a depth-first traversal. Let's use this structure as an example:
+
+```js
+{
+ "names": {
+ "en": "Germany",
+ "de": "Deutschland"
+ },
+ "cities": [ "Berlin", "Frankfurt" ]
+}
+```
+
+The list will consist of the following items:
+
+1. MAP - top level map
+2. UTF8_STRING - "names" key
+3. MAP - map for "names" key
+4. UTF8_STRING - "en" key
+5. UTF8_STRING - value for "en" key
+6. UTF8_STRING - "de" key
+7. UTF8_STRING - value for "de" key
+8. UTF8_STRING - "cities" key
+9. ARRAY - value for "cities" key
+10. UTF8_STRING - array[0]
+11. UTF8_STRING - array[1]
+
+The return value of the function is a status code as defined above.
+
+## `MMDB_free_entry_data_list()`
+
+```c
+void MMDB_free_entry_data_list(
+ MMDB_entry_data_list_s *const entry_data_list);
+```
+
+The `MMDB_get_entry_data_list()` and `MMDB_get_metadata_as_entry_data_list()`
+functions will allocate the linked list structure from the heap. Call this
+function to free the `MMDB_entry_data_list_s` structure.
+
+## `MMDB_get_metadata_as_entry_data_list()`
+
+```c
+int MMDB_get_metadata_as_entry_data_list(
+ MMDB_s *const mmdb,
+ MMDB_entry_data_list_s **const entry_data_list);
+```
+
+This function allows you to retrieve the database metadata as a linked list of
+`MMDB_entry_data_list_s` structures. This can be a more convenient way to deal
+with the metadata than using the metadata structure directly.
+
+```c
+ MMDB_entry_data_list_s *entry_data_list, *first;
+ int status =
+ MMDB_get_metadata_as_entry_data_list(&mmdb, &entry_data_list);
+ if (MMDB_SUCCESS != status) { ... }
+ first = entry_data_list;
+ ... // do something with the data
+ MMDB_free_entry_data_list(first);
+```
+
+The return value of the function is a status code as defined above.
+
+## `MMDB_dump_entry_data_list()`
+
+```c
+int MMDB_dump_entry_data_list(
+ FILE *const stream,
+ MMDB_entry_data_list_s *const entry_data_list,
+ int indent);
+```
+
+This function takes a linked list of `MMDB_entry_data_list_s` structures and
+stringifies it to the given `stream`. The `indent` parameter is the starting
+indent level for the generated output. It is incremented for nested data
+structures (maps, array, etc.).
+
+The `stream` must be a file handle (`stdout`, etc). If your platform provides
+something like the GNU `open_memstream()` you can use that to capture the
+output as a string.
+
+The output is formatted in a JSON-ish fashion, but values are marked with their
+data type (except for maps and arrays which are shown with "{}" and "[]"
+respectively).
+
+The specific output format may change in future releases, so you should not
+rely on the specific formatting produced by this function. It is intended to be
+used to show data to users in a readable way and for debugging purposes.
+
+The return value of the function is a status code as defined above.
+
+## `MMDB_read_node()`
+
+```c
+int MMDB_read_node(
+ MMDB_s *const mmdb,
+ uint32_t node_number,
+ MMDB_search_node_s *const node);
+```
+
+This reads a specific node in the search tree. The third argument is a
+reference to an `MMDB_search_node_s` structure that will be populated by this
+function.
+
+The return value is a status code. If you pass a `node_number` that is greater
+than the number of nodes in the database, this function will return
+`MMDB_INVALID_NODE_NUMBER_ERROR`, otherwise it will return `MMDB_SUCCESS`.
+
+The first node in the search tree is always node 0. If you wanted to iterate
+over the whole search tree, you would start by reading node 0 and then
+following the the records that make up this node, based on the type of each
+record. If the type is `MMDB_RECORD_TYPE_SEARCH_NODE` then the record contains
+an integer for the next node to look up.
+
+## `MMDB_lib_version()`
+
+```c
+const char *MMDB_lib_version(void)
+```
+
+This function returns the library version as a string, something like "2.0.0".
+
+# EXAMPLE
+
+```c
+#include <errno.h>
+#include <maxminddb.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ char *filename = argv[1];
+ char *ip_address = argv[2];
+
+ MMDB_s mmdb;
+ int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);
+
+ if (MMDB_SUCCESS != status) {
+ fprintf(stderr, "\n Can't open %s - %s\n",
+ filename, MMDB_strerror(status));
+
+ if (MMDB_IO_ERROR == status) {
+ fprintf(stderr, " IO error: %s\n", strerror(errno));
+ }
+ exit(1);
+ }
+
+ int gai_error, mmdb_error;
+ MMDB_lookup_result_s result =
+ MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error);
+
+ if (0 != gai_error) {
+ fprintf(stderr,
+ "\n Error from getaddrinfo for %s - %s\n\n",
+ ip_address, gai_strerror(gai_error));
+ exit(2);
+ }
+
+ if (MMDB_SUCCESS != mmdb_error) {
+ fprintf(stderr,
+ "\n Got an error from libmaxminddb: %s\n\n",
+ MMDB_strerror(mmdb_error));
+ exit(3);
+ }
+
+ MMDB_entry_data_list_s *entry_data_list = NULL;
+
+ int exit_code = 0;
+ if (result.found_entry) {
+ int status = MMDB_get_entry_data_list(&result.entry,
+ &entry_data_list);
+
+ if (MMDB_SUCCESS != status) {
+ fprintf(
+ stderr,
+ "Got an error looking up the entry data - %s\n",
+ MMDB_strerror(status));
+ exit_code = 4;
+ goto end;
+ }
+
+ if (NULL != entry_data_list) {
+ MMDB_dump_entry_data_list(stdout, entry_data_list, 2);
+ }
+ } else {
+ fprintf(
+ stderr,
+ "\n No entry for this IP address (%s) was found\n\n",
+ ip_address);
+ exit_code = 5;
+ }
+
+ end:
+ MMDB_free_entry_data_list(entry_data_list);
+ MMDB_close(&mmdb);
+ exit(exit_code);
+}
+```
+
+# THREAD SAFETY
+
+This library is thread safe when compiled and linked with a thread-safe
+`malloc` and `free` implementation.
+
+# INSTALLATION AND SOURCE
+
+You can download the latest release of libmaxminddb
+[from GitHub](https://github.com/maxmind/libmaxminddb/releases).
+
+[Our GitHub repo](https://github.com/maxmind/libmaxminddb) is publicly
+available. Please fork it!
+
+# BUG REPORTS AND PULL REQUESTS
+
+Please report all issues to
+[our GitHub issue tracker](https://github.com/maxmind/libmaxminddb/issues). We
+welcome bug reports and pull requests. Please note that pull requests are
+greatly preferred over patches.
+
+# AUTHORS
+
+This library was written by Boris Zentner ([email protected]) and Dave
+
+# COPYRIGHT AND LICENSE
+
+Copyright 2013-2014 MaxMind, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+# SEE ALSO
+
+mmdblookup(1)