ngx_stream_geoip2_module.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /*
  2. * Copyright (C) Lee Valentine <lee@leev.net>
  3. * Copyright (C) Andrei Belov <defanator@gmail.com>
  4. *
  5. * Based on nginx's 'ngx_stream_geoip_module.c' by Igor Sysoev
  6. */
  7. #include <ngx_config.h>
  8. #include <ngx_core.h>
  9. #include <ngx_stream.h>
  10. #include <maxminddb.h>
  11. typedef struct {
  12. MMDB_s mmdb;
  13. MMDB_lookup_result_s result;
  14. time_t last_check;
  15. time_t last_change;
  16. time_t check_interval;
  17. #if (NGX_HAVE_INET6)
  18. uint8_t address[16];
  19. #else
  20. unsigned long address;
  21. #endif
  22. ngx_queue_t queue;
  23. } ngx_stream_geoip2_db_t;
  24. typedef struct {
  25. ngx_queue_t databases;
  26. } ngx_stream_geoip2_conf_t;
  27. typedef struct {
  28. ngx_stream_geoip2_db_t *database;
  29. const char **lookup;
  30. ngx_str_t default_value;
  31. ngx_stream_complex_value_t source;
  32. } ngx_stream_geoip2_ctx_t;
  33. typedef struct {
  34. ngx_stream_geoip2_db_t *database;
  35. ngx_str_t metavalue;
  36. } ngx_stream_geoip2_metadata_t;
  37. static ngx_int_t ngx_stream_geoip2_variable(ngx_stream_session_t *s,
  38. ngx_stream_variable_value_t *v, uintptr_t data);
  39. static ngx_int_t ngx_stream_geoip2_metadata(ngx_stream_session_t *s,
  40. ngx_stream_variable_value_t *v, uintptr_t data);
  41. static void *ngx_stream_geoip2_create_conf(ngx_conf_t *cf);
  42. static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
  43. void *conf);
  44. static char *ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy,
  45. void *conf);
  46. static char *ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd,
  47. void *conf);
  48. static char *ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy,
  49. void *conf);
  50. static char *ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf,
  51. ngx_stream_geoip2_db_t *database);
  52. static char *ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf,
  53. ngx_stream_geoip2_db_t *database);
  54. static void ngx_stream_geoip2_cleanup(void *data);
  55. static ngx_int_t ngx_stream_geoip2_init(ngx_conf_t *cf);
  56. #define FORMAT(fmt, ...) do { \
  57. p = ngx_palloc(s->connection->pool, NGX_OFF_T_LEN); \
  58. if (p == NULL) { \
  59. return NGX_ERROR; \
  60. } \
  61. v->len = ngx_sprintf(p, fmt, __VA_ARGS__) - p; \
  62. v->data = p; \
  63. } while (0)
  64. static ngx_command_t ngx_stream_geoip2_commands[] = {
  65. { ngx_string("geoip2"),
  66. NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
  67. ngx_stream_geoip2,
  68. NGX_STREAM_MAIN_CONF_OFFSET,
  69. 0,
  70. NULL },
  71. ngx_null_command
  72. };
  73. static ngx_stream_module_t ngx_stream_geoip2_module_ctx = {
  74. NULL, /* preconfiguration */
  75. ngx_stream_geoip2_init, /* postconfiguration */
  76. ngx_stream_geoip2_create_conf, /* create main configuration */
  77. NULL, /* init main configuration */
  78. NULL, /* create server configuration */
  79. NULL /* merge server configuration */
  80. };
  81. ngx_module_t ngx_stream_geoip2_module = {
  82. NGX_MODULE_V1,
  83. &ngx_stream_geoip2_module_ctx, /* module context */
  84. ngx_stream_geoip2_commands, /* module directives */
  85. NGX_STREAM_MODULE, /* module type */
  86. NULL, /* init master */
  87. NULL, /* init module */
  88. NULL, /* init process */
  89. NULL, /* init thread */
  90. NULL, /* exit thread */
  91. NULL, /* exit process */
  92. NULL, /* exit master */
  93. NGX_MODULE_V1_PADDING
  94. };
  95. static ngx_int_t
  96. ngx_stream_geoip2_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
  97. uintptr_t data)
  98. {
  99. int mmdb_error;
  100. u_char *p;
  101. ngx_str_t val;
  102. ngx_addr_t addr;
  103. MMDB_entry_data_s entry_data;
  104. ngx_stream_geoip2_ctx_t *geoip2 = (ngx_stream_geoip2_ctx_t *) data;
  105. ngx_stream_geoip2_db_t *database = geoip2->database;
  106. #if (NGX_HAVE_INET6)
  107. uint8_t address[16], *addressp = address;
  108. #else
  109. unsigned long address;
  110. #endif
  111. if (geoip2->source.value.len > 0) {
  112. if (ngx_stream_complex_value(s, &geoip2->source, &val) != NGX_OK) {
  113. goto not_found;
  114. }
  115. if (ngx_parse_addr(s->connection->pool, &addr, val.data, val.len) != NGX_OK) {
  116. goto not_found;
  117. }
  118. } else {
  119. addr.sockaddr = s->connection->sockaddr;
  120. addr.socklen = s->connection->socklen;
  121. }
  122. switch (addr.sockaddr->sa_family) {
  123. case AF_INET:
  124. #if (NGX_HAVE_INET6)
  125. ngx_memset(addressp, 0, 12);
  126. ngx_memcpy(addressp + 12, &((struct sockaddr_in *)
  127. addr.sockaddr)->sin_addr.s_addr, 4);
  128. break;
  129. case AF_INET6:
  130. ngx_memcpy(addressp, &((struct sockaddr_in6 *)
  131. addr.sockaddr)->sin6_addr.s6_addr, 16);
  132. #else
  133. address = ((struct sockaddr_in *)addr.sockaddr)->sin_addr.s_addr;
  134. #endif
  135. break;
  136. default:
  137. goto not_found;
  138. }
  139. #if (NGX_HAVE_INET6)
  140. if (ngx_memcmp(&address, &database->address, sizeof(address)) != 0) {
  141. #else
  142. if (address != database->address) {
  143. #endif
  144. memcpy(&database->address, &address, sizeof(address));
  145. database->result = MMDB_lookup_sockaddr(&database->mmdb,
  146. addr.sockaddr, &mmdb_error);
  147. if (mmdb_error != MMDB_SUCCESS) {
  148. goto not_found;
  149. }
  150. }
  151. if (!database->result.found_entry
  152. || MMDB_aget_value(&database->result.entry, &entry_data, geoip2->lookup)
  153. != MMDB_SUCCESS)
  154. {
  155. goto not_found;
  156. }
  157. if (!entry_data.has_data) {
  158. goto not_found;
  159. }
  160. switch (entry_data.type) {
  161. case MMDB_DATA_TYPE_BOOLEAN:
  162. FORMAT("%d", entry_data.boolean);
  163. break;
  164. case MMDB_DATA_TYPE_UTF8_STRING:
  165. v->len = entry_data.data_size;
  166. v->data = ngx_pnalloc(s->connection->pool, v->len);
  167. if (v->data == NULL) {
  168. return NGX_ERROR;
  169. }
  170. ngx_memcpy(v->data, (u_char *) entry_data.utf8_string, v->len);
  171. break;
  172. case MMDB_DATA_TYPE_BYTES:
  173. v->len = entry_data.data_size;
  174. v->data = ngx_pnalloc(s->connection->pool, v->len);
  175. if (v->data == NULL) {
  176. return NGX_ERROR;
  177. }
  178. ngx_memcpy(v->data, (u_char *) entry_data.bytes, v->len);
  179. break;
  180. case MMDB_DATA_TYPE_FLOAT:
  181. FORMAT("%.5f", entry_data.float_value);
  182. break;
  183. case MMDB_DATA_TYPE_DOUBLE:
  184. FORMAT("%.5f", entry_data.double_value);
  185. break;
  186. case MMDB_DATA_TYPE_UINT16:
  187. FORMAT("%uD", entry_data.uint16);
  188. break;
  189. case MMDB_DATA_TYPE_UINT32:
  190. FORMAT("%uD", entry_data.uint32);
  191. break;
  192. case MMDB_DATA_TYPE_INT32:
  193. FORMAT("%D", entry_data.int32);
  194. break;
  195. case MMDB_DATA_TYPE_UINT64:
  196. FORMAT("%uL", entry_data.uint64);
  197. break;
  198. case MMDB_DATA_TYPE_UINT128: ;
  199. #if MMDB_UINT128_IS_BYTE_ARRAY
  200. uint8_t *val = (uint8_t *) entry_data.uint128;
  201. FORMAT("0x%02x%02x%02x%02x%02x%02x%02x%02x"
  202. "%02x%02x%02x%02x%02x%02x%02x%02x",
  203. val[0], val[1], val[2], val[3],
  204. val[4], val[5], val[6], val[7],
  205. val[8], val[9], val[10], val[11],
  206. val[12], val[13], val[14], val[15]);
  207. #else
  208. mmdb_uint128_t val = entry_data.uint128;
  209. FORMAT("0x%016uxL%016uxL",
  210. (uint64_t) (val >> 64), (uint64_t) val);
  211. #endif
  212. break;
  213. default:
  214. goto not_found;
  215. }
  216. v->valid = 1;
  217. v->no_cacheable = 0;
  218. v->not_found = 0;
  219. return NGX_OK;
  220. not_found:
  221. if (geoip2->default_value.len > 0) {
  222. v->data = geoip2->default_value.data;
  223. v->len = geoip2->default_value.len;
  224. v->valid = 1;
  225. v->no_cacheable = 0;
  226. v->not_found = 0;
  227. return NGX_OK;
  228. }
  229. v->not_found = 1;
  230. return NGX_OK;
  231. }
  232. static ngx_int_t
  233. ngx_stream_geoip2_metadata(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
  234. uintptr_t data)
  235. {
  236. ngx_stream_geoip2_metadata_t *metadata = (ngx_stream_geoip2_metadata_t *) data;
  237. ngx_stream_geoip2_db_t *database = metadata->database;
  238. u_char *p;
  239. if (ngx_strncmp(metadata->metavalue.data, "build_epoch", 11) == 0) {
  240. FORMAT("%uL", database->mmdb.metadata.build_epoch);
  241. } else if (ngx_strncmp(metadata->metavalue.data, "last_check", 10) == 0) {
  242. FORMAT("%T", database->last_check);
  243. } else if (ngx_strncmp(metadata->metavalue.data, "last_change", 11) == 0) {
  244. FORMAT("%T", database->last_change);
  245. } else {
  246. v->not_found = 1;
  247. return NGX_OK;
  248. }
  249. v->valid = 1;
  250. v->no_cacheable = 0;
  251. v->not_found = 0;
  252. return NGX_OK;
  253. }
  254. static void *
  255. ngx_stream_geoip2_create_conf(ngx_conf_t *cf)
  256. {
  257. ngx_pool_cleanup_t *cln;
  258. ngx_stream_geoip2_conf_t *conf;
  259. conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_conf_t));
  260. if (conf == NULL) {
  261. return NULL;
  262. }
  263. cln = ngx_pool_cleanup_add(cf->pool, 0);
  264. if (cln == NULL) {
  265. return NULL;
  266. }
  267. ngx_queue_init(&conf->databases);
  268. cln->handler = ngx_stream_geoip2_cleanup;
  269. cln->data = conf;
  270. return conf;
  271. }
  272. static char *
  273. ngx_stream_geoip2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  274. {
  275. int status;
  276. char *rv;
  277. ngx_str_t *value;
  278. ngx_conf_t save;
  279. ngx_stream_geoip2_db_t *database;
  280. ngx_stream_geoip2_conf_t *gcf = conf;
  281. ngx_queue_t *q;
  282. value = cf->args->elts;
  283. if (value[1].data && value[1].data[0] != '/') {
  284. if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
  285. return NGX_CONF_ERROR;
  286. }
  287. }
  288. if (!ngx_queue_empty(&gcf->databases)) {
  289. for (q = ngx_queue_head(&gcf->databases);
  290. q != ngx_queue_sentinel(&gcf->databases);
  291. q = ngx_queue_next(q))
  292. {
  293. database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue);
  294. if (ngx_strcmp(value[1].data, database->mmdb.filename) == 0) {
  295. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  296. "Duplicate GeoIP2 mmdb - %V", &value[1]);
  297. return NGX_CONF_ERROR;
  298. }
  299. }
  300. }
  301. database = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_db_t));
  302. if (database == NULL) {
  303. return NGX_CONF_ERROR;
  304. }
  305. ngx_queue_insert_tail(&gcf->databases, &database->queue);
  306. database->last_check = database->last_change = ngx_time();
  307. status = MMDB_open((char *) value[1].data, MMDB_MODE_MMAP, &database->mmdb);
  308. if (status != MMDB_SUCCESS) {
  309. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  310. "MMDB_open(\"%V\") failed - %s", &value[1],
  311. MMDB_strerror(status));
  312. return NGX_CONF_ERROR;
  313. }
  314. save = *cf;
  315. cf->handler = ngx_stream_geoip2_parse_config;
  316. cf->handler_conf = (void *) database;
  317. rv = ngx_conf_parse(cf, NULL);
  318. *cf = save;
  319. return rv;
  320. }
  321. static char *
  322. ngx_stream_geoip2_parse_config(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  323. {
  324. ngx_stream_geoip2_db_t *database;
  325. ngx_str_t *value;
  326. time_t interval;
  327. value = cf->args->elts;
  328. if (value[0].data[0] == '$') {
  329. return ngx_stream_geoip2_add_variable(cf, dummy, conf);
  330. }
  331. if (value[0].len == 11
  332. && ngx_strncmp(value[0].data, "auto_reload", 11) == 0) {
  333. if ((int) cf->args->nelts != 2) {
  334. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  335. "invalid number of arguments for auto_reload");
  336. return NGX_CONF_ERROR;
  337. }
  338. interval = ngx_parse_time(&value[1], true);
  339. if (interval == (time_t) NGX_ERROR) {
  340. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  341. "invalid interval for auto_reload \"%V\"",
  342. value[1]);
  343. return NGX_CONF_ERROR;
  344. }
  345. database = (ngx_stream_geoip2_db_t *) conf;
  346. database->check_interval = interval;
  347. return NGX_CONF_OK;
  348. }
  349. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  350. "invalid setting \"%V\"", &value[0]);
  351. return NGX_CONF_ERROR;
  352. }
  353. static char *
  354. ngx_stream_geoip2_add_variable(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  355. {
  356. ngx_stream_geoip2_db_t *database;
  357. ngx_str_t *value;
  358. int nelts;
  359. value = cf->args->elts;
  360. if (value[0].data[0] != '$') {
  361. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  362. "invalid variable name \"%V\"", &value[0]);
  363. return NGX_CONF_ERROR;
  364. }
  365. value[0].len--;
  366. value[0].data++;
  367. nelts = (int) cf->args->nelts;
  368. database = (ngx_stream_geoip2_db_t *) conf;
  369. if (nelts > 0 && value[1].len == 8 && ngx_strncmp(value[1].data, "metadata", 8) == 0) {
  370. return ngx_stream_geoip2_add_variable_metadata(cf, database);
  371. }
  372. return ngx_stream_geoip2_add_variable_geodata(cf, database);
  373. }
  374. static char *
  375. ngx_stream_geoip2_add_variable_metadata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database)
  376. {
  377. ngx_stream_geoip2_metadata_t *metadata;
  378. ngx_str_t *value, name;
  379. ngx_stream_variable_t *var;
  380. metadata = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_metadata_t));
  381. if (metadata == NULL) {
  382. return NGX_CONF_ERROR;
  383. }
  384. value = cf->args->elts;
  385. name = value[0];
  386. metadata->database = database;
  387. metadata->metavalue = value[2];
  388. var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
  389. if (var == NULL) {
  390. return NGX_CONF_ERROR;
  391. }
  392. var->get_handler = ngx_stream_geoip2_metadata;
  393. var->data = (uintptr_t) metadata;
  394. return NGX_CONF_OK;
  395. }
  396. static char *
  397. ngx_stream_geoip2_add_variable_geodata(ngx_conf_t *cf, ngx_stream_geoip2_db_t *database)
  398. {
  399. ngx_stream_geoip2_ctx_t *geoip2;
  400. ngx_stream_compile_complex_value_t ccv;
  401. ngx_str_t *value, name, source;
  402. ngx_stream_variable_t *var;
  403. int i, nelts, idx;
  404. geoip2 = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip2_ctx_t));
  405. if (geoip2 == NULL) {
  406. return NGX_CONF_ERROR;
  407. }
  408. geoip2->database = database;
  409. ngx_str_null(&source);
  410. value = cf->args->elts;
  411. name = value[0];
  412. nelts = (int) cf->args->nelts;
  413. idx = 1;
  414. if (nelts > idx) {
  415. for (i = idx; i < nelts; i++) {
  416. if (ngx_strnstr(value[idx].data, "=", value[idx].len) == NULL) {
  417. break;
  418. }
  419. if (value[idx].len > 8 && ngx_strncmp(value[idx].data, "default=", 8) == 0) {
  420. if (geoip2->default_value.len > 0) {
  421. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  422. "default has already been declared for \"$%V\"", &name);
  423. return NGX_CONF_ERROR;
  424. }
  425. geoip2->default_value.len = value[idx].len - 8;
  426. geoip2->default_value.data = value[idx].data + 8;
  427. } else if (value[idx].len > 7 && ngx_strncmp(value[idx].data, "source=", 7) == 0) {
  428. if (source.len > 0) {
  429. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  430. "source has already been declared for \"$%V\"", &name);
  431. return NGX_CONF_ERROR;
  432. }
  433. source.len = value[idx].len - 7;
  434. source.data = value[idx].data + 7;
  435. if (source.data[0] != '$') {
  436. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  437. "invalid source variable name \"%V\"", &source);
  438. return NGX_CONF_ERROR;
  439. }
  440. ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
  441. ccv.cf = cf;
  442. ccv.value = &source;
  443. ccv.complex_value = &geoip2->source;
  444. if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  445. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  446. "unable to compile \"%V\" for \"$%V\"", &source, &name);
  447. return NGX_CONF_ERROR;
  448. }
  449. } else {
  450. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  451. "invalid setting \"%V\" for \"$%V\"", &value[idx], &name);
  452. return NGX_CONF_ERROR;
  453. }
  454. idx++;
  455. }
  456. }
  457. var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
  458. if (var == NULL) {
  459. return NGX_CONF_ERROR;
  460. }
  461. geoip2->lookup = ngx_pcalloc(cf->pool,
  462. sizeof(const char *) * (cf->args->nelts - (idx - 1)));
  463. if (geoip2->lookup == NULL) {
  464. return NGX_CONF_ERROR;
  465. }
  466. for (i = idx; i < nelts; i++) {
  467. geoip2->lookup[i - idx] = (char *) value[i].data;
  468. }
  469. geoip2->lookup[i - idx] = NULL;
  470. var->get_handler = ngx_stream_geoip2_variable;
  471. var->data = (uintptr_t) geoip2;
  472. return NGX_CONF_OK;
  473. }
  474. static void
  475. ngx_stream_geoip2_cleanup(void *data)
  476. {
  477. ngx_queue_t *q;
  478. ngx_stream_geoip2_db_t *database;
  479. ngx_stream_geoip2_conf_t *gcf = data;
  480. while (!ngx_queue_empty(&gcf->databases)) {
  481. q = ngx_queue_head(&gcf->databases);
  482. ngx_queue_remove(q);
  483. database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue);
  484. MMDB_close(&database->mmdb);
  485. }
  486. }
  487. static ngx_int_t
  488. ngx_stream_geoip2_log_handler(ngx_stream_session_t *s)
  489. {
  490. int status;
  491. MMDB_s tmpdb;
  492. ngx_queue_t *q;
  493. ngx_file_info_t fi;
  494. ngx_stream_geoip2_db_t *database;
  495. ngx_stream_geoip2_conf_t *gcf;
  496. ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  497. "geoip2 stream log handler");
  498. gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip2_module);
  499. if (ngx_queue_empty(&gcf->databases)) {
  500. return NGX_OK;
  501. }
  502. for (q = ngx_queue_head(&gcf->databases);
  503. q != ngx_queue_sentinel(&gcf->databases);
  504. q = ngx_queue_next(q))
  505. {
  506. database = ngx_queue_data(q, ngx_stream_geoip2_db_t, queue);
  507. if (database->check_interval == 0) {
  508. continue;
  509. }
  510. if ((database->last_check + database->check_interval)
  511. > ngx_time())
  512. {
  513. continue;
  514. }
  515. database->last_check = ngx_time();
  516. if (ngx_file_info(database->mmdb.filename, &fi) == NGX_FILE_ERROR) {
  517. ngx_log_error(NGX_LOG_EMERG, s->connection->log, ngx_errno,
  518. ngx_file_info_n " \"%s\" failed",
  519. database->mmdb.filename);
  520. continue;
  521. }
  522. if (ngx_file_mtime(&fi) <= database->last_change) {
  523. continue;
  524. }
  525. /* do the reload */
  526. ngx_memzero(&tmpdb, sizeof(MMDB_s));
  527. status = MMDB_open(database->mmdb.filename, MMDB_MODE_MMAP, &tmpdb);
  528. if (status != MMDB_SUCCESS) {
  529. ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  530. "MMDB_open(\"%s\") failed to reload - %s",
  531. database->mmdb.filename, MMDB_strerror(status));
  532. continue;
  533. }
  534. database->last_change = ngx_file_mtime(&fi);
  535. MMDB_close(&database->mmdb);
  536. database->mmdb = tmpdb;
  537. ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
  538. "Reload MMDB \"%s\"",
  539. database->mmdb.filename);
  540. }
  541. return NGX_OK;
  542. }
  543. static ngx_int_t
  544. ngx_stream_geoip2_init(ngx_conf_t *cf)
  545. {
  546. ngx_stream_handler_pt *h;
  547. ngx_stream_core_main_conf_t *cmcf;
  548. cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
  549. h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers);
  550. if (h == NULL) {
  551. return NGX_ERROR;
  552. }
  553. *h = ngx_stream_geoip2_log_handler;
  554. return NGX_OK;
  555. }