maxminddb.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. /* MaxMind, Inc., licenses this file to you under the Apache License, Version
  2. * 2.0 (the "License"); you may not use this file except in compliance with
  3. * the License. You may obtain a copy of the License at
  4. *
  5. * http://www.apache.org/licenses/LICENSE-2.0
  6. *
  7. * Unless required by applicable law or agreed to in writing, software
  8. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. * License for the specific language governing permissions and limitations
  11. * under the License.
  12. */
  13. #include "php_maxminddb.h"
  14. #ifdef HAVE_CONFIG_H
  15. #include "config.h"
  16. #endif
  17. #include <php.h>
  18. #include <zend.h>
  19. #include "Zend/zend_exceptions.h"
  20. #include "Zend/zend_types.h"
  21. #include "ext/spl/spl_exceptions.h"
  22. #include "ext/standard/info.h"
  23. #include <maxminddb.h>
  24. #ifdef ZTS
  25. #include <TSRM.h>
  26. #endif
  27. #define __STDC_FORMAT_MACROS
  28. #include <inttypes.h>
  29. #define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db")
  30. #define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader")
  31. #define PHP_MAXMINDDB_METADATA_NS \
  32. ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata")
  33. #define PHP_MAXMINDDB_READER_EX_NS \
  34. ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException")
  35. #define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv))
  36. typedef size_t strsize_t;
  37. typedef zend_object free_obj_t;
  38. /* For PHP 8 compatibility */
  39. #if PHP_VERSION_ID < 80000
  40. #define PROP_OBJ(zv) (zv)
  41. #else
  42. #define PROP_OBJ(zv) Z_OBJ_P(zv)
  43. #define TSRMLS_C
  44. #define TSRMLS_CC
  45. #define TSRMLS_DC
  46. /* End PHP 8 compatibility */
  47. #endif
  48. #ifndef ZEND_ACC_CTOR
  49. #define ZEND_ACC_CTOR 0
  50. #endif
  51. /* IS_MIXED was added in 2020 */
  52. #ifndef IS_MIXED
  53. #define IS_MIXED IS_UNDEF
  54. #endif
  55. /* ZEND_THIS was added in 7.4 */
  56. #ifndef ZEND_THIS
  57. #define ZEND_THIS (&EX(This))
  58. #endif
  59. typedef struct _maxminddb_obj {
  60. MMDB_s *mmdb;
  61. zend_object std;
  62. } maxminddb_obj;
  63. PHP_FUNCTION(maxminddb);
  64. static int
  65. get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len);
  66. static const MMDB_entry_data_list_s *
  67. handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
  68. zval *z_value TSRMLS_DC);
  69. static const MMDB_entry_data_list_s *
  70. handle_array(const MMDB_entry_data_list_s *entry_data_list,
  71. zval *z_value TSRMLS_DC);
  72. static const MMDB_entry_data_list_s *
  73. handle_map(const MMDB_entry_data_list_s *entry_data_list,
  74. zval *z_value TSRMLS_DC);
  75. static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list,
  76. zval *z_value TSRMLS_DC);
  77. static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
  78. zval *z_value TSRMLS_DC);
  79. static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
  80. zval *z_value TSRMLS_DC);
  81. #define CHECK_ALLOCATED(val) \
  82. if (!val) { \
  83. zend_error(E_ERROR, "Out of memory"); \
  84. return; \
  85. }
  86. static zend_object_handlers maxminddb_obj_handlers;
  87. static zend_class_entry *maxminddb_ce, *maxminddb_exception_ce, *metadata_ce;
  88. static inline maxminddb_obj *
  89. php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) {
  90. return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std));
  91. }
  92. ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_construct, 0, 0, 1)
  93. ZEND_ARG_TYPE_INFO(0, db_file, IS_STRING, 0)
  94. ZEND_END_ARG_INFO()
  95. PHP_METHOD(MaxMind_Db_Reader, __construct) {
  96. char *db_file = NULL;
  97. strsize_t name_len;
  98. zval *_this_zval = NULL;
  99. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
  100. getThis(),
  101. "Os",
  102. &_this_zval,
  103. maxminddb_ce,
  104. &db_file,
  105. &name_len) == FAILURE) {
  106. return;
  107. }
  108. if (0 != php_check_open_basedir(db_file TSRMLS_CC) ||
  109. 0 != access(db_file, R_OK)) {
  110. zend_throw_exception_ex(
  111. spl_ce_InvalidArgumentException,
  112. 0 TSRMLS_CC,
  113. "The file \"%s\" does not exist or is not readable.",
  114. db_file);
  115. return;
  116. }
  117. MMDB_s *mmdb = (MMDB_s *)ecalloc(1, sizeof(MMDB_s));
  118. int const status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb);
  119. if (MMDB_SUCCESS != status) {
  120. zend_throw_exception_ex(
  121. maxminddb_exception_ce,
  122. 0 TSRMLS_CC,
  123. "Error opening database file (%s). Is this a valid "
  124. "MaxMind DB file?",
  125. db_file);
  126. efree(mmdb);
  127. return;
  128. }
  129. maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(ZEND_THIS);
  130. mmdb_obj->mmdb = mmdb;
  131. }
  132. ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
  133. arginfo_maxminddbreader_get, 0, 1, IS_MIXED, 1)
  134. ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
  135. ZEND_END_ARG_INFO()
  136. PHP_METHOD(MaxMind_Db_Reader, get) {
  137. int prefix_len = 0;
  138. get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len);
  139. }
  140. ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
  141. arginfo_maxminddbreader_getWithPrefixLen, 0, 1, IS_ARRAY, 1)
  142. ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
  143. ZEND_END_ARG_INFO()
  144. PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) {
  145. zval record, z_prefix_len;
  146. int prefix_len = 0;
  147. if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, &record, &prefix_len) ==
  148. FAILURE) {
  149. return;
  150. }
  151. array_init(return_value);
  152. add_next_index_zval(return_value, &record);
  153. ZVAL_LONG(&z_prefix_len, prefix_len);
  154. add_next_index_zval(return_value, &z_prefix_len);
  155. }
  156. static int
  157. get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) {
  158. char *ip_address = NULL;
  159. strsize_t name_len;
  160. zval *this_zval = NULL;
  161. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
  162. getThis(),
  163. "Os",
  164. &this_zval,
  165. maxminddb_ce,
  166. &ip_address,
  167. &name_len) == FAILURE) {
  168. return FAILURE;
  169. }
  170. const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(ZEND_THIS);
  171. MMDB_s *mmdb = mmdb_obj->mmdb;
  172. if (NULL == mmdb) {
  173. zend_throw_exception_ex(spl_ce_BadMethodCallException,
  174. 0 TSRMLS_CC,
  175. "Attempt to read from a closed MaxMind DB.");
  176. return FAILURE;
  177. }
  178. struct addrinfo hints = {
  179. .ai_family = AF_UNSPEC,
  180. .ai_flags = AI_NUMERICHOST,
  181. /* We set ai_socktype so that we only get one result back */
  182. .ai_socktype = SOCK_STREAM};
  183. struct addrinfo *addresses = NULL;
  184. int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses);
  185. if (gai_status) {
  186. zend_throw_exception_ex(spl_ce_InvalidArgumentException,
  187. 0 TSRMLS_CC,
  188. "The value \"%s\" is not a valid IP address.",
  189. ip_address);
  190. return FAILURE;
  191. }
  192. if (!addresses || !addresses->ai_addr) {
  193. zend_throw_exception_ex(
  194. spl_ce_InvalidArgumentException,
  195. 0 TSRMLS_CC,
  196. "getaddrinfo was successful but failed to set the addrinfo");
  197. return FAILURE;
  198. }
  199. int sa_family = addresses->ai_addr->sa_family;
  200. int mmdb_error = MMDB_SUCCESS;
  201. MMDB_lookup_result_s result =
  202. MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
  203. freeaddrinfo(addresses);
  204. if (MMDB_SUCCESS != mmdb_error) {
  205. zend_class_entry *ex;
  206. if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) {
  207. ex = spl_ce_InvalidArgumentException;
  208. } else {
  209. ex = maxminddb_exception_ce;
  210. }
  211. zend_throw_exception_ex(ex,
  212. 0 TSRMLS_CC,
  213. "Error looking up %s. %s",
  214. ip_address,
  215. MMDB_strerror(mmdb_error));
  216. return FAILURE;
  217. }
  218. *prefix_len = result.netmask;
  219. if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) {
  220. /* We return the prefix length given the IPv4 address. If there is
  221. no IPv4 subtree, we return a prefix length of 0. */
  222. *prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0;
  223. }
  224. if (!result.found_entry) {
  225. ZVAL_NULL(record);
  226. return SUCCESS;
  227. }
  228. MMDB_entry_data_list_s *entry_data_list = NULL;
  229. int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
  230. if (MMDB_SUCCESS != status) {
  231. zend_throw_exception_ex(maxminddb_exception_ce,
  232. 0 TSRMLS_CC,
  233. "Error while looking up data for %s. %s",
  234. ip_address,
  235. MMDB_strerror(status));
  236. MMDB_free_entry_data_list(entry_data_list);
  237. return FAILURE;
  238. } else if (NULL == entry_data_list) {
  239. zend_throw_exception_ex(
  240. maxminddb_exception_ce,
  241. 0 TSRMLS_CC,
  242. "Error while looking up data for %s. Your database may "
  243. "be corrupt or you have found a bug in libmaxminddb.",
  244. ip_address);
  245. return FAILURE;
  246. }
  247. const MMDB_entry_data_list_s *rv =
  248. handle_entry_data_list(entry_data_list, record TSRMLS_CC);
  249. if (rv == NULL) {
  250. /* We should have already thrown the exception in handle_entry_data_list
  251. */
  252. return FAILURE;
  253. }
  254. MMDB_free_entry_data_list(entry_data_list);
  255. return SUCCESS;
  256. }
  257. ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_void, 0, 0, 0)
  258. ZEND_END_ARG_INFO()
  259. PHP_METHOD(MaxMind_Db_Reader, metadata) {
  260. zval *this_zval = NULL;
  261. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
  262. getThis(),
  263. "O",
  264. &this_zval,
  265. maxminddb_ce) == FAILURE) {
  266. return;
  267. }
  268. const maxminddb_obj *const mmdb_obj =
  269. (maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
  270. if (NULL == mmdb_obj->mmdb) {
  271. zend_throw_exception_ex(spl_ce_BadMethodCallException,
  272. 0 TSRMLS_CC,
  273. "Attempt to read from a closed MaxMind DB.");
  274. return;
  275. }
  276. object_init_ex(return_value, metadata_ce);
  277. MMDB_entry_data_list_s *entry_data_list;
  278. MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list);
  279. zval metadata_array;
  280. const MMDB_entry_data_list_s *rv =
  281. handle_entry_data_list(entry_data_list, &metadata_array TSRMLS_CC);
  282. if (rv == NULL) {
  283. return;
  284. }
  285. MMDB_free_entry_data_list(entry_data_list);
  286. zend_call_method_with_1_params(PROP_OBJ(return_value),
  287. metadata_ce,
  288. &metadata_ce->constructor,
  289. ZEND_CONSTRUCTOR_FUNC_NAME,
  290. NULL,
  291. &metadata_array);
  292. zval_ptr_dtor(&metadata_array);
  293. }
  294. PHP_METHOD(MaxMind_Db_Reader, close) {
  295. zval *this_zval = NULL;
  296. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
  297. getThis(),
  298. "O",
  299. &this_zval,
  300. maxminddb_ce) == FAILURE) {
  301. return;
  302. }
  303. maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
  304. if (NULL == mmdb_obj->mmdb) {
  305. zend_throw_exception_ex(spl_ce_BadMethodCallException,
  306. 0 TSRMLS_CC,
  307. "Attempt to close a closed MaxMind DB.");
  308. return;
  309. }
  310. MMDB_close(mmdb_obj->mmdb);
  311. efree(mmdb_obj->mmdb);
  312. mmdb_obj->mmdb = NULL;
  313. }
  314. static const MMDB_entry_data_list_s *
  315. handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
  316. zval *z_value TSRMLS_DC) {
  317. switch (entry_data_list->entry_data.type) {
  318. case MMDB_DATA_TYPE_MAP:
  319. return handle_map(entry_data_list, z_value TSRMLS_CC);
  320. case MMDB_DATA_TYPE_ARRAY:
  321. return handle_array(entry_data_list, z_value TSRMLS_CC);
  322. case MMDB_DATA_TYPE_UTF8_STRING:
  323. ZVAL_STRINGL(z_value,
  324. entry_data_list->entry_data.utf8_string,
  325. entry_data_list->entry_data.data_size);
  326. break;
  327. case MMDB_DATA_TYPE_BYTES:
  328. ZVAL_STRINGL(z_value,
  329. (char const *)entry_data_list->entry_data.bytes,
  330. entry_data_list->entry_data.data_size);
  331. break;
  332. case MMDB_DATA_TYPE_DOUBLE:
  333. ZVAL_DOUBLE(z_value, entry_data_list->entry_data.double_value);
  334. break;
  335. case MMDB_DATA_TYPE_FLOAT:
  336. ZVAL_DOUBLE(z_value, entry_data_list->entry_data.float_value);
  337. break;
  338. case MMDB_DATA_TYPE_UINT16:
  339. ZVAL_LONG(z_value, entry_data_list->entry_data.uint16);
  340. break;
  341. case MMDB_DATA_TYPE_UINT32:
  342. handle_uint32(entry_data_list, z_value TSRMLS_CC);
  343. break;
  344. case MMDB_DATA_TYPE_BOOLEAN:
  345. ZVAL_BOOL(z_value, entry_data_list->entry_data.boolean);
  346. break;
  347. case MMDB_DATA_TYPE_UINT64:
  348. handle_uint64(entry_data_list, z_value TSRMLS_CC);
  349. break;
  350. case MMDB_DATA_TYPE_UINT128:
  351. handle_uint128(entry_data_list, z_value TSRMLS_CC);
  352. break;
  353. case MMDB_DATA_TYPE_INT32:
  354. ZVAL_LONG(z_value, entry_data_list->entry_data.int32);
  355. break;
  356. default:
  357. zend_throw_exception_ex(maxminddb_exception_ce,
  358. 0 TSRMLS_CC,
  359. "Invalid data type arguments: %d",
  360. entry_data_list->entry_data.type);
  361. return NULL;
  362. }
  363. return entry_data_list;
  364. }
  365. static const MMDB_entry_data_list_s *
  366. handle_map(const MMDB_entry_data_list_s *entry_data_list,
  367. zval *z_value TSRMLS_DC) {
  368. array_init(z_value);
  369. const uint32_t map_size = entry_data_list->entry_data.data_size;
  370. uint32_t i;
  371. for (i = 0; i < map_size && entry_data_list; i++) {
  372. entry_data_list = entry_data_list->next;
  373. char *key = estrndup(entry_data_list->entry_data.utf8_string,
  374. entry_data_list->entry_data.data_size);
  375. if (NULL == key) {
  376. zend_throw_exception_ex(maxminddb_exception_ce,
  377. 0 TSRMLS_CC,
  378. "Invalid data type arguments");
  379. return NULL;
  380. }
  381. entry_data_list = entry_data_list->next;
  382. zval new_value;
  383. entry_data_list =
  384. handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
  385. if (entry_data_list != NULL) {
  386. add_assoc_zval(z_value, key, &new_value);
  387. }
  388. efree(key);
  389. }
  390. return entry_data_list;
  391. }
  392. static const MMDB_entry_data_list_s *
  393. handle_array(const MMDB_entry_data_list_s *entry_data_list,
  394. zval *z_value TSRMLS_DC) {
  395. const uint32_t size = entry_data_list->entry_data.data_size;
  396. array_init(z_value);
  397. uint32_t i;
  398. for (i = 0; i < size && entry_data_list; i++) {
  399. entry_data_list = entry_data_list->next;
  400. zval new_value;
  401. entry_data_list =
  402. handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
  403. if (entry_data_list != NULL) {
  404. add_next_index_zval(z_value, &new_value);
  405. }
  406. }
  407. return entry_data_list;
  408. }
  409. static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list,
  410. zval *z_value TSRMLS_DC) {
  411. uint64_t high = 0;
  412. uint64_t low = 0;
  413. #if MMDB_UINT128_IS_BYTE_ARRAY
  414. int i;
  415. for (i = 0; i < 8; i++) {
  416. high = (high << 8) | entry_data_list->entry_data.uint128[i];
  417. }
  418. for (i = 8; i < 16; i++) {
  419. low = (low << 8) | entry_data_list->entry_data.uint128[i];
  420. }
  421. #else
  422. high = entry_data_list->entry_data.uint128 >> 64;
  423. low = (uint64_t)entry_data_list->entry_data.uint128;
  424. #endif
  425. char *num_str;
  426. spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low);
  427. CHECK_ALLOCATED(num_str);
  428. ZVAL_STRING(z_value, num_str);
  429. efree(num_str);
  430. }
  431. static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
  432. zval *z_value TSRMLS_DC) {
  433. uint32_t val = entry_data_list->entry_data.uint32;
  434. #if LONG_MAX >= UINT32_MAX
  435. ZVAL_LONG(z_value, val);
  436. return;
  437. #else
  438. if (val <= LONG_MAX) {
  439. ZVAL_LONG(z_value, val);
  440. return;
  441. }
  442. char *int_str;
  443. spprintf(&int_str, 0, "%" PRIu32, val);
  444. CHECK_ALLOCATED(int_str);
  445. ZVAL_STRING(z_value, int_str);
  446. efree(int_str);
  447. #endif
  448. }
  449. static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
  450. zval *z_value TSRMLS_DC) {
  451. uint64_t val = entry_data_list->entry_data.uint64;
  452. #if LONG_MAX >= UINT64_MAX
  453. ZVAL_LONG(z_value, val);
  454. return;
  455. #else
  456. if (val <= LONG_MAX) {
  457. ZVAL_LONG(z_value, val);
  458. return;
  459. }
  460. char *int_str;
  461. spprintf(&int_str, 0, "%" PRIu64, val);
  462. CHECK_ALLOCATED(int_str);
  463. ZVAL_STRING(z_value, int_str);
  464. efree(int_str);
  465. #endif
  466. }
  467. static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) {
  468. maxminddb_obj *obj =
  469. php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC);
  470. if (obj->mmdb != NULL) {
  471. MMDB_close(obj->mmdb);
  472. efree(obj->mmdb);
  473. }
  474. zend_object_std_dtor(&obj->std TSRMLS_CC);
  475. }
  476. static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) {
  477. maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj));
  478. zend_object_std_init(&obj->std, type TSRMLS_CC);
  479. object_properties_init(&(obj->std), type);
  480. obj->std.handlers = &maxminddb_obj_handlers;
  481. return &obj->std;
  482. }
  483. /* clang-format off */
  484. static zend_function_entry maxminddb_methods[] = {
  485. PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxminddbreader_construct,
  486. ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
  487. PHP_ME(MaxMind_Db_Reader, close, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
  488. PHP_ME(MaxMind_Db_Reader, get, arginfo_maxminddbreader_get, ZEND_ACC_PUBLIC)
  489. PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxminddbreader_getWithPrefixLen, ZEND_ACC_PUBLIC)
  490. PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
  491. { NULL, NULL, NULL }
  492. };
  493. /* clang-format on */
  494. ZEND_BEGIN_ARG_INFO_EX(arginfo_metadata_construct, 0, 0, 1)
  495. ZEND_ARG_TYPE_INFO(0, metadata, IS_ARRAY, 0)
  496. ZEND_END_ARG_INFO()
  497. PHP_METHOD(MaxMind_Db_Reader_Metadata, __construct) {
  498. zval *object = NULL;
  499. zval *metadata_array = NULL;
  500. zend_long node_count = 0;
  501. zend_long record_size = 0;
  502. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
  503. getThis(),
  504. "Oa",
  505. &object,
  506. metadata_ce,
  507. &metadata_array) == FAILURE) {
  508. return;
  509. }
  510. zval *tmp = NULL;
  511. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  512. "binary_format_major_version",
  513. sizeof("binary_format_major_version") - 1))) {
  514. zend_update_property(metadata_ce,
  515. PROP_OBJ(object),
  516. "binaryFormatMajorVersion",
  517. sizeof("binaryFormatMajorVersion") - 1,
  518. tmp);
  519. }
  520. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  521. "binary_format_minor_version",
  522. sizeof("binary_format_minor_version") - 1))) {
  523. zend_update_property(metadata_ce,
  524. PROP_OBJ(object),
  525. "binaryFormatMinorVersion",
  526. sizeof("binaryFormatMinorVersion") - 1,
  527. tmp);
  528. }
  529. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  530. "build_epoch",
  531. sizeof("build_epoch") - 1))) {
  532. zend_update_property(metadata_ce,
  533. PROP_OBJ(object),
  534. "buildEpoch",
  535. sizeof("buildEpoch") - 1,
  536. tmp);
  537. }
  538. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  539. "database_type",
  540. sizeof("database_type") - 1))) {
  541. zend_update_property(metadata_ce,
  542. PROP_OBJ(object),
  543. "databaseType",
  544. sizeof("databaseType") - 1,
  545. tmp);
  546. }
  547. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  548. "description",
  549. sizeof("description") - 1))) {
  550. zend_update_property(metadata_ce,
  551. PROP_OBJ(object),
  552. "description",
  553. sizeof("description") - 1,
  554. tmp);
  555. }
  556. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  557. "ip_version",
  558. sizeof("ip_version") - 1))) {
  559. zend_update_property(metadata_ce,
  560. PROP_OBJ(object),
  561. "ipVersion",
  562. sizeof("ipVersion") - 1,
  563. tmp);
  564. }
  565. if ((tmp = zend_hash_str_find(
  566. HASH_OF(metadata_array), "languages", sizeof("languages") - 1))) {
  567. zend_update_property(metadata_ce,
  568. PROP_OBJ(object),
  569. "languages",
  570. sizeof("languages") - 1,
  571. tmp);
  572. }
  573. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  574. "record_size",
  575. sizeof("record_size") - 1))) {
  576. zend_update_property(metadata_ce,
  577. PROP_OBJ(object),
  578. "recordSize",
  579. sizeof("recordSize") - 1,
  580. tmp);
  581. if (Z_TYPE_P(tmp) == IS_LONG) {
  582. record_size = Z_LVAL_P(tmp);
  583. }
  584. }
  585. if (record_size != 0) {
  586. zend_update_property_long(metadata_ce,
  587. PROP_OBJ(object),
  588. "nodeByteSize",
  589. sizeof("nodeByteSize") - 1,
  590. record_size / 4);
  591. }
  592. if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
  593. "node_count",
  594. sizeof("node_count") - 1))) {
  595. zend_update_property(metadata_ce,
  596. PROP_OBJ(object),
  597. "nodeCount",
  598. sizeof("nodeCount") - 1,
  599. tmp);
  600. if (Z_TYPE_P(tmp) == IS_LONG) {
  601. node_count = Z_LVAL_P(tmp);
  602. }
  603. }
  604. if (record_size != 0) {
  605. zend_update_property_long(metadata_ce,
  606. PROP_OBJ(object),
  607. "searchTreeSize",
  608. sizeof("searchTreeSize") - 1,
  609. record_size * node_count / 4);
  610. }
  611. }
  612. // clang-format off
  613. static zend_function_entry metadata_methods[] = {
  614. PHP_ME(MaxMind_Db_Reader_Metadata, __construct, arginfo_metadata_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
  615. {NULL, NULL, NULL}
  616. };
  617. // clang-format on
  618. PHP_MINIT_FUNCTION(maxminddb) {
  619. zend_class_entry ce;
  620. INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_EX_NS, NULL);
  621. maxminddb_exception_ce =
  622. zend_register_internal_class_ex(&ce, zend_ce_exception);
  623. INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods);
  624. maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC);
  625. maxminddb_ce->create_object = maxminddb_create_handler;
  626. INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_METADATA_NS, metadata_methods);
  627. metadata_ce = zend_register_internal_class(&ce TSRMLS_CC);
  628. zend_declare_property_null(metadata_ce,
  629. "binaryFormatMajorVersion",
  630. sizeof("binaryFormatMajorVersion") - 1,
  631. ZEND_ACC_PUBLIC);
  632. zend_declare_property_null(metadata_ce,
  633. "binaryFormatMinorVersion",
  634. sizeof("binaryFormatMinorVersion") - 1,
  635. ZEND_ACC_PUBLIC);
  636. zend_declare_property_null(
  637. metadata_ce, "buildEpoch", sizeof("buildEpoch") - 1, ZEND_ACC_PUBLIC);
  638. zend_declare_property_null(metadata_ce,
  639. "databaseType",
  640. sizeof("databaseType") - 1,
  641. ZEND_ACC_PUBLIC);
  642. zend_declare_property_null(
  643. metadata_ce, "description", sizeof("description") - 1, ZEND_ACC_PUBLIC);
  644. zend_declare_property_null(
  645. metadata_ce, "ipVersion", sizeof("ipVersion") - 1, ZEND_ACC_PUBLIC);
  646. zend_declare_property_null(
  647. metadata_ce, "languages", sizeof("languages") - 1, ZEND_ACC_PUBLIC);
  648. zend_declare_property_null(metadata_ce,
  649. "nodeByteSize",
  650. sizeof("nodeByteSize") - 1,
  651. ZEND_ACC_PUBLIC);
  652. zend_declare_property_null(
  653. metadata_ce, "nodeCount", sizeof("nodeCount") - 1, ZEND_ACC_PUBLIC);
  654. zend_declare_property_null(
  655. metadata_ce, "recordSize", sizeof("recordSize") - 1, ZEND_ACC_PUBLIC);
  656. zend_declare_property_null(metadata_ce,
  657. "searchTreeSize",
  658. sizeof("searchTreeSize") - 1,
  659. ZEND_ACC_PUBLIC);
  660. memcpy(&maxminddb_obj_handlers,
  661. zend_get_std_object_handlers(),
  662. sizeof(zend_object_handlers));
  663. maxminddb_obj_handlers.clone_obj = NULL;
  664. maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std);
  665. maxminddb_obj_handlers.free_obj = maxminddb_free_storage;
  666. zend_declare_class_constant_string(maxminddb_ce,
  667. "MMDB_LIB_VERSION",
  668. sizeof("MMDB_LIB_VERSION") - 1,
  669. MMDB_lib_version() TSRMLS_CC);
  670. return SUCCESS;
  671. }
  672. static PHP_MINFO_FUNCTION(maxminddb) {
  673. php_info_print_table_start();
  674. php_info_print_table_row(2, "MaxMind DB Reader", "enabled");
  675. php_info_print_table_row(
  676. 2, "maxminddb extension version", PHP_MAXMINDDB_VERSION);
  677. php_info_print_table_row(
  678. 2, "libmaxminddb library version", MMDB_lib_version());
  679. php_info_print_table_end();
  680. }
  681. zend_module_entry maxminddb_module_entry = {STANDARD_MODULE_HEADER,
  682. PHP_MAXMINDDB_EXTNAME,
  683. NULL,
  684. PHP_MINIT(maxminddb),
  685. NULL,
  686. NULL,
  687. NULL,
  688. PHP_MINFO(maxminddb),
  689. PHP_MAXMINDDB_VERSION,
  690. STANDARD_MODULE_PROPERTIES};
  691. #ifdef COMPILE_DL_MAXMINDDB
  692. ZEND_GET_MODULE(maxminddb)
  693. #endif