├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── config ├── ipdb ├── ipdb.c └── ipdb.h ├── ngx_http_ipdb_module.c └── t ├── ipdb.t ├── ipdb_ext.t └── ipiptest.ipdb /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | os: linux 4 | 5 | compiler: 6 | - gcc 7 | 8 | addons: 9 | apt: 10 | packages: 11 | - cpanminus 12 | - lcov 13 | - libjson-c-dev 14 | 15 | install: 16 | - gem install coveralls-lcov 17 | - git clone https://github.com/nginx/nginx.git ../nginx 18 | - git clone https://github.com/nginx/nginx-tests.git ../nginx-tests 19 | - rm -f ../nginx-tests/*.t 20 | - cp ./t/* ../nginx-tests/ 21 | 22 | before_script: 23 | - lcov --directory ../nginx --zerocounters 24 | 25 | script: 26 | - ls -ltra 27 | - cd ../nginx 28 | - ls -ltra 29 | - ./auto/configure --prefix=/tmp/nginx --add-module=../ngx_http_ipdb_module 30 | --with-cc-opt="-Wno-unused-result -fprofile-arcs -ftest-coverage" --with-ld-opt="-lgcov" 31 | - make -j 32 | - make install 33 | - cd ../nginx-tests 34 | - ls -ltra 35 | - TEST_NGINX_BINARY=/tmp/nginx/sbin/nginx prove ipdb.t 36 | - cd ../nginx 37 | - sudo lcov --compat-libtool --directory ./objs/addon/ngx_http_ipdb_module/ --capture 38 | --output-file ./coverage.info --base-directory . 39 | - ls -ltra 40 | 41 | after_success: 42 | - coveralls-lcov ./coverage.info 43 | - genhtml -s -o /tmp/ipdb_html coverage.info 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | 5 | [![travis-ci](https://travis-ci.org/vislee/ngx_http_ipdb_module.svg?branch=master)](https://travis-ci.org/vislee/ngx_http_ipdb_module) 6 | [![Coverage Status](https://coveralls.io/repos/github/vislee/ngx_http_ipdb_module/badge.svg?branch=master)](https://coveralls.io/github/vislee/ngx_http_ipdb_module?branch=master) 7 | 8 | ngx_http_ipdb_module - creates variables with values depending on the client IP address or specifies address, using the precompiled [ipip.net](https://www.ipip.net) [ipdb](https://www.ipip.net/ipdb/test). 9 | 10 | Table of Contents 11 | ================= 12 | * [Name](#name) 13 | * [Status](#status) 14 | * [Install](#install) 15 | * [Example Configuration](#example-configuration) 16 | * [Directives](#directives) 17 | * [ipdb](#ipdb) 18 | * [ipdb_language](#ipdb_language) 19 | * [ipdb_proxy](#ipdb_proxy) 20 | * [ipdb_proxy_recursive](#ipdb_proxy_recursive) 21 | * [ipdb_specifies_addr](#ipdb_specifies_addr) 22 | * [Variable](#variable) 23 | * [$ipdb_country_name](#ipdb_country_name) 24 | * [$ipdb_region_name](#ipdb_region_name) 25 | * [$ipdb_city_name](#ipdb_city_name) 26 | * [$ipdb_owner_domain](#ipdb_owner_domain) 27 | * [$ipdb_isp_domain](#ipdb_isp_domain) 28 | * [$ipdb_latitude](#ipdb_latitude) 29 | * [$ipdb_longitude](#ipdb_longitude) 30 | * [$ipdb_timezone](#ipdb_timezone) 31 | * [$ipdb_utc_offset](#ipdb_utc_offset) 32 | * [$ipdb_china_admin_code](#ipdb_china_admin_code) 33 | * [$ipdb_idd_code](#ipdb_idd_code) 34 | * [$ipdb_country_code](#ipdb_country_code) 35 | * [$ipdb_continent_code](#ipdb_continent_code) 36 | * [$ipdb_idc](#ipdb_idc) 37 | * [$ipdb_base_station](#ipdb_base_station) 38 | * [$ipdb_country_code3](#ipdb_country_code3) 39 | * [$ipdb_european_union](#ipdb_european_union) 40 | * [$ipdb_currency_code](#ipdb_currency_code) 41 | * [$ipdb_currency_name](#ipdb_currency_name) 42 | * [$ipdb_anycast](#ipdb_anycast) 43 | * [$ipdb_raw](#ipdb_raw) 44 | * [TODO](#todo) 45 | * [Author](#author) 46 | * [Copyright and License](#copyright-and-license) 47 | * [See Also](#see-also) 48 | 49 | 50 | Status 51 | ====== 52 | The module is currently in active development. 53 | 54 | [Back to TOC](#table-of-contents) 55 | 56 | Install 57 | ======= 58 | 59 | ```sh 60 | # install json-c lib 61 | # centos 62 | yum install json-c-devel -y 63 | #or mac OSX 64 | brew install json-c 65 | 66 | configure --prefix=/usr/local/nginx --add-module=./github.com/vislee/ngx_http_ipdb_module 67 | # or dynamic compile 68 | configure --prefix=/usr/local/nginx --add-dynamic-module=./github.com/vislee/ngx_http_ipdb_module --with-compat 69 | ``` 70 | 71 | The following information is success: 72 | 73 | >> checking for json-c library ... found 74 | >> 75 | >> \+ ngx_http_ipdb_module was configured 76 | 77 | 78 | [Back to TOC](#table-of-contents) 79 | 80 | Example Configuration 81 | ==================== 82 | 83 | ```nginx 84 | 85 | # load_module ./modules/ngx_http_ipdb_module.so; 86 | 87 | http { 88 | include mime.types; 89 | default_type application/octet-stream; 90 | 91 | ...... 92 | 93 | ipdb /tmp/nginx/conf/ipiptest.ipdb; 94 | ipdb_language CN; 95 | ipdb_proxy 127.0.0.1; 96 | ipdb_proxy_recursive on; 97 | 98 | server { 99 | listen 8090; 100 | server_name localhost; 101 | 102 | ...... 103 | 104 | location / { 105 | # ipdb_specifies_addr $http_addr; 106 | # ipdb_language EN; 107 | 108 | return 200 "country_name:$ipdb_country_name, raw_info:$ipdb_raw"; 109 | } 110 | } 111 | } 112 | 113 | ``` 114 | 115 | [Back to TOC](#table-of-contents) 116 | 117 | TODO 118 | ========== 119 | 120 | + add variable 121 | 122 | [Back to TOC](#table-of-contents) 123 | 124 | Directives 125 | ========== 126 | 127 | ipdb 128 | ---- 129 | **syntax:** *ipdb file;* 130 | 131 | **default:** *-* 132 | 133 | **context:** *http* 134 | 135 | Specifies a database. 136 | 137 | ipdb_language 138 | ------------- 139 | **syntax:** *ipdb_language EN|CN;* 140 | 141 | **default:** *EN* 142 | 143 | **context:** *http,server,location* 144 | 145 | set variable language. 146 | 147 | ipdb_proxy 148 | ---------- 149 | **syntax:** *ipdb_proxy address|CIDR;* 150 | 151 | **default:** *-* 152 | 153 | **context:** *http* 154 | 155 | Defines trusted addresses. Just like [`geoip_proxy`](http://nginx.org/en/docs/http/ngx_http_geoip_module.html#geoip_proxy). 156 | 157 | ipdb_proxy_recursive 158 | -------------------- 159 | **syntax:** *ipdb_proxy_recursive on|off;* 160 | 161 | **default:** *off* 162 | 163 | **context:** *http* 164 | 165 | Is recursive search. Just like [`geoip_proxy_recursive`](http://nginx.org/en/docs/http/ngx_http_geoip_module.html#geoip_proxy_recursive) 166 | 167 | ipdb_specifies_addr 168 | ------------------- 169 | **syntax:** *ipdb_specifies_addr address;* 170 | 171 | **default:** *-* 172 | 173 | **context:** *http,server,location* 174 | 175 | Specifies the address. The address can contain text, variables. 176 | 177 | [Back to TOC](#table-of-contents) 178 | 179 | 180 | Variable 181 | ======== 182 | 183 | ipdb_country_name 184 | ---------------- 185 | 186 | $ipdb_country_name - country name, for example, "中国", "China" 187 | 188 | ipdb_region_name 189 | ---------------- 190 | 191 | $ipdb_region_name - country region name, for example, "内蒙古","Nei Mongol", "北京", "Beijing" 192 | 193 | ipdb_city_name 194 | -------------- 195 | 196 | $ipdb_city_name - city name, for example, "呼和浩特", "Hohhot", "北京", "Beijing" 197 | 198 | 199 | ipdb_owner_domain 200 | ----------------- 201 | 202 | 203 | ipdb_isp_domain 204 | --------------- 205 | 206 | $ipdb_isp_domain - ISP name, for example, "电信", "ChinaTelecom" 207 | 208 | 209 | ipdb_latitude 210 | ------------- 211 | 212 | 213 | ipdb_longitude 214 | --------------- 215 | 216 | 217 | ipdb_timezone 218 | -------------- 219 | 220 | 221 | ipdb_utc_offset 222 | ---------------- 223 | 224 | 225 | ipdb_china_admin_code 226 | --------------------- 227 | 228 | 229 | ipdb_idd_code 230 | ------------- 231 | 232 | 233 | ipdb_country_code 234 | ----------------- 235 | 236 | 237 | ipdb_continent_code 238 | ------------------- 239 | 240 | 241 | ipdb_idc 242 | -------- 243 | 244 | 245 | ipdb_base_station 246 | ----------------- 247 | 248 | 249 | ipdb_country_code3 250 | ------------------ 251 | 252 | 253 | ipdb_european_union 254 | ------------------- 255 | 256 | 257 | ipdb_currency_code 258 | ------------------- 259 | 260 | 261 | ipdb_currency_name 262 | ------------------ 263 | 264 | 265 | ipdb_anycast 266 | ------------ 267 | 268 | 269 | ipdb_raw 270 | -------- 271 | 272 | $ipdb_raw - raw info, for example, "中国\t内蒙古\t呼和浩特","China\tNei Mongol\tHohhot" 273 | 274 | *NOTE:* If you need to get multiple variables, use this `$ipdb_raw`. 275 | 276 | [Back to TOC](#table-of-contents) 277 | 278 | Author 279 | ====== 280 | 281 | wenqiang li(vislee) 282 | 283 | [Back to TOC](#table-of-contents) 284 | 285 | Copyright and License 286 | ===================== 287 | 288 | This module is licensed under the [GPL](http://www.gnu.org/licenses/licenses.en.html) license. 289 | 290 | Copyright (C) 2018-2019, by vislee. 291 | 292 | All rights reserved. 293 | 294 | [Back to TOC](#table-of-contents) 295 | 296 | 297 | See Also 298 | ======== 299 | 300 | + [ngx_http_geoip_module](http://nginx.org/en/docs/http/ngx_http_geoip_module.html#geoip_proxy) 301 | + [github.com/ipipdotnet/ipdb-c](https://github.com/ipipdotnet/ipdb-c) -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | _HTTP_IPDB_SRCS="\ 2 | $ngx_addon_dir/ngx_http_ipdb_module.c \ 3 | $ngx_addon_dir/ipdb/ipdb.c \ 4 | " 5 | _HTTP_JSON_LIB="-ljson-c" 6 | ngx_addon_name=ngx_http_ipdb_module 7 | 8 | ngx_feature="json-c library" 9 | ngx_feature_name="NGX_JSON_C" 10 | ngx_feature_run=no 11 | ngx_feature_incs="#include " 12 | ngx_feature_path= 13 | ngx_feature_libs="$_HTTP_JSON_LIB" 14 | ngx_feature_test="json_c_version_num()" 15 | . auto/feature 16 | 17 | if [ $ngx_found = yes ]; then 18 | 19 | if test -n "$ngx_module_link"; then 20 | ngx_module_type=HTTP 21 | ngx_module_name=$ngx_addon_name 22 | ngx_module_srcs="$_HTTP_IPDB_SRCS" 23 | ngx_module_libs="$_HTTP_JSON_LIB" 24 | . auto/module 25 | else 26 | HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" 27 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $_HTTP_IPDB_SRCS" 28 | CORE_LIBS="$CORE_LIBS $_HTTP_JSON_LIB" 29 | HTTP_INCS="$HTTP_INCS $ngx_addon_dir" 30 | fi 31 | 32 | else 33 | echo "warning: $ngx_addon_name invalid. please yum install json-c" 34 | fi 35 | -------------------------------------------------------------------------------- /ipdb/ipdb.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 11/14/18. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ipdb.h" 10 | 11 | int is_big_endian(void) { 12 | union { 13 | uint32_t i; 14 | char c[4]; 15 | } e = {0x01000000}; 16 | 17 | return e.c[0]; 18 | } 19 | 20 | unsigned int l2b(unsigned int x) { 21 | return (((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00) | ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000)); 22 | } 23 | 24 | ipdb_meta_data *parse_meta_data(const char *meta_json) { 25 | int i = 0; 26 | 27 | ipdb_meta_data *meta_data = (ipdb_meta_data *) malloc(sizeof(ipdb_meta_data)); 28 | if (meta_data == NULL) { 29 | return NULL; 30 | } 31 | 32 | memset(meta_data, 0, sizeof(ipdb_meta_data)); 33 | 34 | json_object *obj = json_tokener_parse(meta_json); 35 | json_object *value = NULL; 36 | 37 | if (json_object_object_get_ex(obj, "node_count", &value)) { 38 | meta_data->node_count = json_object_get_int(value); 39 | } 40 | 41 | if (json_object_object_get_ex(obj, "total_size", &value)) { 42 | meta_data->total_size = json_object_get_int(value); 43 | } 44 | 45 | if (json_object_object_get_ex(obj, "build", &value)){ 46 | meta_data->build_time = json_object_get_int64(value); 47 | } 48 | 49 | if (json_object_object_get_ex(obj, "ip_version", &value)) { 50 | meta_data->ip_version = (short) json_object_get_int(value); 51 | } 52 | 53 | if (json_object_object_get_ex(obj, "fields", &value)) { 54 | meta_data->fields_length = json_object_array_length(value); 55 | } 56 | 57 | meta_data->fields = (char **) malloc(sizeof(char *) * meta_data->fields_length); 58 | if (meta_data->fields == NULL) { 59 | return NULL; 60 | } 61 | for (i = 0; i < meta_data->fields_length; ++i) { 62 | json_object *it = json_object_array_get_idx(value, i); 63 | meta_data->fields[i] = malloc(sizeof(char) * json_object_get_string_len(it) + 1); 64 | strcpy(meta_data->fields[i], json_object_get_string(it)); 65 | } 66 | 67 | if (json_object_object_get_ex(obj, "languages", &value)) { 68 | meta_data->language_length = json_object_object_length(value); 69 | meta_data->language = (ipdb_meta_data_language *) malloc( 70 | sizeof(ipdb_meta_data_language) * meta_data->language_length); 71 | if (meta_data->language == NULL) { 72 | return NULL; 73 | } 74 | 75 | struct json_object_iterator language = json_object_iter_begin(value); 76 | for (i = 0; i < meta_data->language_length; ++i) { 77 | strcpy(meta_data->language[i].name, json_object_iter_peek_name(&language)); 78 | struct json_object *it = json_object_iter_peek_value(&language); 79 | meta_data->language[i].offset = json_object_get_int(it); 80 | json_object_iter_next(&language); 81 | } 82 | json_object_iter_end(value); 83 | } 84 | 85 | return meta_data; 86 | } 87 | 88 | int ipdb_read_node(ipdb_reader *reader, int node, int index) { 89 | int off = node * 8 + index * 4; 90 | int tar = *(int *) &reader->data[off]; 91 | return l2b((unsigned int) tar); 92 | } 93 | 94 | int ipdb_reader_new(const char *file, ipdb_reader **reader) { 95 | FILE *fd = fopen(file, "rb"); 96 | if (!fd) { 97 | return ErrFileSize; 98 | } 99 | *reader = malloc(sizeof(ipdb_reader)); 100 | ipdb_reader *rd = *reader; 101 | 102 | fseek(fd, 0, SEEK_END); 103 | long fsize = ftell(fd); 104 | fseek(fd, 0, SEEK_SET); 105 | unsigned int meta_length = 0; 106 | fread(&meta_length, sizeof(meta_length), 1, fd); 107 | meta_length = is_big_endian() ? meta_length : l2b(meta_length); 108 | 109 | char *meta_json = (char *) malloc(meta_length + 1); 110 | meta_json[meta_length] = 0; 111 | fread(meta_json, sizeof(char), meta_length, fd); 112 | rd->meta = parse_meta_data(meta_json); 113 | free(meta_json); 114 | if (rd->meta == NULL || rd->meta->language_length == 0 || rd->meta->fields_length == 0) { 115 | return ErrMetaData; 116 | } 117 | 118 | if (fsize != (4 + meta_length + rd->meta->total_size)) { 119 | return ErrFileSize; 120 | } 121 | rd->file_size = (int) fsize; 122 | int data_len = (int) fsize - 4 - meta_length; 123 | rd->data = (unsigned char *) malloc(sizeof(unsigned char) * data_len); 124 | fread(rd->data, sizeof(unsigned char), (size_t) data_len, fd); 125 | rd->data_size = data_len; 126 | 127 | int node = 0; 128 | int i = 0; 129 | for (i = 0; i < 96 && node < rd->meta->node_count; ++i) { 130 | if (i >= 80) { 131 | node = ipdb_read_node(rd, node, 1); 132 | } else { 133 | node = ipdb_read_node(rd, node, 0); 134 | } 135 | } 136 | rd->v4offset = node; 137 | 138 | fclose(fd); 139 | return ErrNoErr; 140 | } 141 | 142 | void ipdb_reader_free(ipdb_reader **reader) { 143 | int i = 0; 144 | if ((*reader)->meta) { 145 | ipdb_meta_data *meta_data = (*reader)->meta; 146 | for (i = 0; i < meta_data->fields_length; ++i) { 147 | free(meta_data->fields[i]); 148 | } 149 | free(meta_data->fields); 150 | free(meta_data->language); 151 | free(meta_data); 152 | } 153 | if ((*reader)->data) { 154 | free((*reader)->data); 155 | } 156 | free(*reader); 157 | *reader = 0; 158 | } 159 | 160 | int ipdb_reader_is_ipv4_support(ipdb_reader *reader) { 161 | return (((int) reader->meta->ip_version) & IPv4) == IPv4; 162 | 163 | } 164 | 165 | int ipdb_reader_is_ipv6_support(ipdb_reader *reader) { 166 | return (((int) reader->meta->ip_version) & IPv6) == IPv6; 167 | 168 | } 169 | 170 | int ipdb_resolve(ipdb_reader *reader, int node, const char **bytes) { 171 | int resolved = node - reader->meta->node_count + reader->meta->node_count * 8; 172 | if (resolved >= reader->file_size) { 173 | return ErrDatabaseError; 174 | } 175 | 176 | int size = (reader->data[resolved] << 8) | reader->data[resolved + 1]; 177 | if ((resolved + 2 + size) > reader->data_size) { 178 | return ErrDatabaseError; 179 | } 180 | *bytes = (const char *) reader->data + resolved + 2; 181 | return ErrNoErr; 182 | } 183 | 184 | int ipdb_search(ipdb_reader *reader, const u_char *ip, int bit_count, int *node) { 185 | int i = 0; 186 | 187 | *node = 0; 188 | 189 | if (bit_count == 32) { 190 | *node = reader->v4offset; 191 | } else { 192 | *node = 0; 193 | } 194 | 195 | for (i = 0; i < bit_count; ++i) { 196 | if (*node > reader->meta->node_count) { 197 | break; 198 | } 199 | 200 | *node = ipdb_read_node(reader, *node, 201 | ((0xFF & ((int) ip[i >> 3])) >> (unsigned int) (7 - (i % 8))) & 1); 202 | } 203 | 204 | if (*node > reader->meta->node_count) { 205 | return ErrNoErr; 206 | } 207 | return ErrDataNotExists; 208 | } 209 | 210 | int ipdb_find0(ipdb_reader *reader, const char *addr, const char **body) { 211 | int node = 0; 212 | int err; 213 | struct in_addr addr4; 214 | struct in6_addr addr6; 215 | if (inet_pton(AF_INET, addr, &addr4)) { 216 | if (!ipdb_reader_is_ipv4_support(reader)) { 217 | return ErrNoSupportIPv4; 218 | } 219 | err = ipdb_search(reader, (const u_char *) &addr4.s_addr, 32, &node); 220 | if (err != ErrNoErr) { 221 | return err; 222 | } 223 | } else if (inet_pton(AF_INET6, addr, &addr6)) { 224 | if (!ipdb_reader_is_ipv6_support(reader)) { 225 | return ErrNoSupportIPv6; 226 | } 227 | err = ipdb_search(reader, (const u_char *) &addr6.s6_addr, 128, &node); 228 | if (err != ErrNoErr) { 229 | return err; 230 | } 231 | } else { 232 | return ErrIPFormat; 233 | } 234 | err = ipdb_resolve(reader, node, body); 235 | return err; 236 | } 237 | 238 | int ipdb_find1(ipdb_reader *reader, const char *addr, const char *language, char *body) { 239 | int err, i; 240 | off_t off = -1; 241 | for (i = 0; i < reader->meta->language_length; ++i) { 242 | if (strcmp(language, reader->meta->language[i].name) == 0) { 243 | off = reader->meta->language[i].offset; 244 | break; 245 | } 246 | } 247 | if (off == -1) { 248 | return ErrNoSupportLanguage; 249 | } 250 | const char *content; 251 | err = ipdb_find0(reader, addr, &content); 252 | if (err != ErrNoErr) { 253 | return err; 254 | } 255 | size_t p = 0, o = 0, s = 0, e = 0; 256 | size_t len = reader->meta->fields_length; 257 | 258 | while (*(content + p)) { 259 | if (*(content + p) == '\t') { 260 | ++o; 261 | } 262 | if ((!e) && o == off + len) { 263 | e = p; 264 | } 265 | ++p; 266 | if (off && (!s) && (off_t)o == off) { 267 | s = p; 268 | } 269 | } 270 | if (!e) e = p; 271 | if (off + len > o + 1) { 272 | err = ErrDatabaseError; 273 | } else { 274 | strncpy(body, content + s, e - s); 275 | body[e - s] = 0; 276 | } 277 | return err; 278 | } 279 | 280 | int ipdb_reader_find(ipdb_reader *reader, const char *addr, const char *language, char *body) { 281 | return ipdb_find1(reader, addr, language, body); 282 | } -------------------------------------------------------------------------------- /ipdb/ipdb.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by root on 11/14/18. 3 | // 4 | 5 | #ifndef IPDB_C_IPDB_H 6 | #define IPDB_C_IPDB_H 7 | 8 | #define IPv4 0x01 9 | #define IPv6 0x02 10 | 11 | #define ErrNoErr 0 //No error. 12 | #define ErrFileSize 1 //"IP Database file size error." 13 | #define ErrMetaData 2 //"IP Database metadata error." 14 | //#define ErrReadFull 3 //"IP Database ReadFull error." 15 | 16 | #define ErrDatabaseError 4 //"database error" 17 | 18 | #define ErrIPFormat 5 //"Query IP Format error." 19 | 20 | #define ErrNoSupportLanguage 6 //"language not support" 21 | #define ErrNoSupportIPv4 7 //"IPv4 not support" 22 | #define ErrNoSupportIPv6 8 //"IPv6 not support" 23 | 24 | #define ErrDataNotExists 9 //"data is not exists" 25 | 26 | typedef struct ipdb_meta_data_language { 27 | char name[8]; 28 | int offset; 29 | } ipdb_meta_data_language; 30 | 31 | typedef struct ipdb_meta_data { 32 | int node_count; 33 | int total_size; 34 | short ip_version; 35 | long build_time; 36 | ipdb_meta_data_language *language; 37 | int language_length; 38 | char **fields; 39 | int fields_length; 40 | } ipdb_meta_data; 41 | 42 | typedef struct ipdb_reader { 43 | ipdb_meta_data *meta; 44 | int v4offset; 45 | int file_size; 46 | int data_size; 47 | unsigned char *data; 48 | } ipdb_reader; 49 | 50 | int ipdb_reader_new(const char *file, ipdb_reader **reader); 51 | 52 | void ipdb_reader_free(ipdb_reader **reader); 53 | 54 | int ipdb_reader_is_ipv4_support(ipdb_reader *reader); 55 | 56 | int ipdb_reader_is_ipv6_support(ipdb_reader *reader); 57 | 58 | int ipdb_reader_find(ipdb_reader *reader, const char *addr, const char *language, char *body); 59 | 60 | int ipdb_search(ipdb_reader *reader, const u_char *ip, int bit_count, int *node); 61 | int ipdb_resolve(ipdb_reader *reader, int node, const char **bytes); 62 | #endif //IPDB_C_IPDB_H 63 | -------------------------------------------------------------------------------- /ngx_http_ipdb_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) vislee 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ipdb/ipdb.h" 10 | 11 | #define NGX_IPDB_country_name 0 12 | #define NGX_IPDB_region_name 1 13 | #define NGX_IPDB_city_name 2 14 | #define NGX_IPDB_owner_domain 3 15 | #define NGX_IPDB_isp_domain 4 16 | #define NGX_IPDB_latitude 5 17 | #define NGX_IPDB_longitude 6 18 | #define NGX_IPDB_timezone 7 19 | 20 | #define NGX_IPDB_utc_offset 8 21 | #define NGX_IPDB_china_admin_code 9 22 | #define NGX_IPDB_idd_code 10 23 | #define NGX_IPDB_country_code 11 24 | #define NGX_IPDB_continent_code 12 25 | #define NGX_IPDB_idc 13 26 | #define NGX_IPDB_base_station 14 27 | #define NGX_IPDB_country_code3 15 28 | #define NGX_IPDB_european_union 16 29 | #define NGX_IPDB_currency_code 17 30 | #define NGX_IPDB_currency_name 18 31 | #define NGX_IPDB_anycast 19 32 | 33 | // all item 34 | #define NGX_IPDB_raw 20 35 | 36 | typedef struct { 37 | ipdb_reader *ipdb; 38 | ngx_array_t *proxies; /* array of ngx_cidr_t */ 39 | ngx_flag_t proxy_recursive; 40 | } ngx_http_ipdb_main_conf_t; 41 | 42 | typedef struct ngx_http_ipdb_loc_conf_s { 43 | ngx_str_t lang; 44 | ngx_str_t spec_addr; 45 | ngx_http_complex_value_t key; 46 | } ngx_http_ipdb_loc_conf_t; 47 | 48 | 49 | static char *ngx_http_ipdb_language(ngx_conf_t *cf, void *post, void *data); 50 | static ngx_int_t ngx_http_ipdb_add_variables(ngx_conf_t *cf); 51 | static void *ngx_http_ipdb_create_main_conf(ngx_conf_t *cf); 52 | static char *ngx_http_ipdb_init_main_conf(ngx_conf_t *cf, void *conf); 53 | static void *ngx_http_ipdb_create_loc_conf(ngx_conf_t *cf); 54 | static char *ngx_http_ipdb_merge_loc_conf(ngx_conf_t *cf, 55 | void *parent, void *child); 56 | static void ngx_http_ipdb_cleanup(void *data); 57 | static char *ngx_http_ipdb_open(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 58 | static char *ngx_http_ipdb_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 59 | static ngx_int_t ngx_http_ipdb_variable(ngx_http_request_t *r, 60 | ngx_http_variable_value_t *v, uintptr_t data); 61 | 62 | 63 | static ngx_conf_post_handler_pt ngx_http_ipdb_language_p = 64 | ngx_http_ipdb_language; 65 | 66 | 67 | static ngx_command_t ngx_http_ipdb_commands[] = { 68 | 69 | { ngx_string("ipdb"), 70 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 71 | ngx_http_ipdb_open, 72 | NGX_HTTP_MAIN_CONF_OFFSET, 73 | 0, 74 | NULL }, 75 | 76 | { ngx_string("ipdb_language"), 77 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 78 | ngx_conf_set_str_slot, 79 | NGX_HTTP_LOC_CONF_OFFSET, 80 | offsetof(ngx_http_ipdb_loc_conf_t, lang), 81 | &ngx_http_ipdb_language_p }, 82 | 83 | { ngx_string("ipdb_proxy"), 84 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 85 | ngx_http_ipdb_proxy, 86 | NGX_HTTP_MAIN_CONF_OFFSET, 87 | 0, 88 | NULL }, 89 | 90 | { ngx_string("ipdb_proxy_recursive"), 91 | NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, 92 | ngx_conf_set_flag_slot, 93 | NGX_HTTP_MAIN_CONF_OFFSET, 94 | offsetof(ngx_http_ipdb_main_conf_t, proxy_recursive), 95 | NULL }, 96 | 97 | { ngx_string("ipdb_specifies_addr"), 98 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 99 | ngx_conf_set_str_slot, 100 | NGX_HTTP_LOC_CONF_OFFSET, 101 | offsetof(ngx_http_ipdb_loc_conf_t, spec_addr), 102 | NULL }, 103 | 104 | ngx_null_command 105 | }; 106 | 107 | 108 | static ngx_http_module_t ngx_http_ipdb_module_ctx = { 109 | ngx_http_ipdb_add_variables, /* preconfiguration */ 110 | NULL, /* postconfiguration */ 111 | 112 | ngx_http_ipdb_create_main_conf, /* create main configuration */ 113 | ngx_http_ipdb_init_main_conf, /* init main configuration */ 114 | 115 | NULL, /* create server configuration */ 116 | NULL, /* merge server configuration */ 117 | 118 | ngx_http_ipdb_create_loc_conf, /* create location configuration */ 119 | ngx_http_ipdb_merge_loc_conf /* merge location configuration */ 120 | }; 121 | 122 | 123 | ngx_module_t ngx_http_ipdb_module = { 124 | NGX_MODULE_V1, 125 | &ngx_http_ipdb_module_ctx, /* module context */ 126 | ngx_http_ipdb_commands, /* module directives */ 127 | NGX_HTTP_MODULE, /* module type */ 128 | NULL, /* init master */ 129 | NULL, /* init module */ 130 | NULL, /* init process */ 131 | NULL, /* init thread */ 132 | NULL, /* exit thread */ 133 | NULL, /* exit process */ 134 | NULL, /* exit master */ 135 | NGX_MODULE_V1_PADDING 136 | }; 137 | 138 | 139 | static ngx_http_variable_t ngx_http_ipdb_vars[] = { 140 | 141 | { ngx_string("ipdb_country_name"), NULL, 142 | ngx_http_ipdb_variable, 143 | NGX_IPDB_country_name, 0, 0 }, 144 | 145 | { ngx_string("ipdb_region_name"), NULL, 146 | ngx_http_ipdb_variable, 147 | NGX_IPDB_region_name, 0, 0 }, 148 | 149 | { ngx_string("ipdb_city_name"), NULL, 150 | ngx_http_ipdb_variable, 151 | NGX_IPDB_city_name, 0, 0 }, 152 | 153 | { ngx_string("ipdb_owner_domain"), NULL, 154 | ngx_http_ipdb_variable, 155 | NGX_IPDB_owner_domain, 0, 0 }, 156 | 157 | { ngx_string("ipdb_isp_domain"), NULL, 158 | ngx_http_ipdb_variable, 159 | NGX_IPDB_isp_domain, 0, 0 }, 160 | 161 | { ngx_string("ipdb_latitude"), NULL, 162 | ngx_http_ipdb_variable, 163 | NGX_IPDB_latitude, 0, 0 }, 164 | 165 | { ngx_string("ipdb_longitude"), NULL, 166 | ngx_http_ipdb_variable, 167 | NGX_IPDB_longitude, 0, 0 }, 168 | 169 | { ngx_string("ipdb_timezone"), NULL, 170 | ngx_http_ipdb_variable, 171 | NGX_IPDB_timezone, 0, 0 }, 172 | 173 | { ngx_string("ipdb_utc_offset"), NULL, 174 | ngx_http_ipdb_variable, 175 | NGX_IPDB_utc_offset, 0, 0 }, 176 | 177 | { ngx_string("ipdb_china_admin_code"), NULL, 178 | ngx_http_ipdb_variable, 179 | NGX_IPDB_china_admin_code, 0, 0 }, 180 | 181 | { ngx_string("ipdb_idd_code"), NULL, 182 | ngx_http_ipdb_variable, 183 | NGX_IPDB_idd_code, 0, 0 }, 184 | 185 | { ngx_string("ipdb_country_code"), NULL, 186 | ngx_http_ipdb_variable, 187 | NGX_IPDB_country_code, 0, 0 }, 188 | 189 | { ngx_string("ipdb_continent_code"), NULL, 190 | ngx_http_ipdb_variable, 191 | NGX_IPDB_continent_code, 0, 0 }, 192 | 193 | { ngx_string("ipdb_idc"), NULL, 194 | ngx_http_ipdb_variable, 195 | NGX_IPDB_idc, 0, 0 }, 196 | 197 | { ngx_string("ipdb_base_station"), NULL, 198 | ngx_http_ipdb_variable, 199 | NGX_IPDB_base_station, 0, 0 }, 200 | 201 | { ngx_string("ipdb_country_code3"), NULL, 202 | ngx_http_ipdb_variable, 203 | NGX_IPDB_country_code3, 0, 0 }, 204 | 205 | { ngx_string("ipdb_european_union"), NULL, 206 | ngx_http_ipdb_variable, 207 | NGX_IPDB_european_union, 0, 0 }, 208 | 209 | { ngx_string("ipdb_currency_code"), NULL, 210 | ngx_http_ipdb_variable, 211 | NGX_IPDB_currency_code, 0, 0 }, 212 | 213 | { ngx_string("ipdb_currency_name"), NULL, 214 | ngx_http_ipdb_variable, 215 | NGX_IPDB_currency_name, 0, 0 }, 216 | 217 | { ngx_string("ipdb_anycast"), NULL, 218 | ngx_http_ipdb_variable, 219 | NGX_IPDB_anycast, 0, 0 }, 220 | 221 | { ngx_string("ipdb_raw"), NULL, 222 | ngx_http_ipdb_variable, 223 | NGX_IPDB_raw, 0, 0 }, 224 | 225 | #if (nginx_version >= 1013004) 226 | ngx_http_null_variable 227 | #else 228 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 229 | #endif 230 | }; 231 | 232 | 233 | static char * 234 | ngx_http_ipdb_language(ngx_conf_t *cf, void *post, void *data) 235 | { 236 | ngx_str_t *lang = data; 237 | 238 | if (ngx_strcmp(lang->data, "EN") == 0 239 | || ngx_strcmp(lang->data, "CN") == 0) { 240 | 241 | return NGX_CONF_OK; 242 | } 243 | 244 | return NGX_CONF_ERROR; 245 | } 246 | 247 | 248 | static ngx_int_t 249 | ngx_http_ipdb_add_variables(ngx_conf_t *cf) 250 | { 251 | ngx_http_variable_t *var, *v; 252 | 253 | for (v = ngx_http_ipdb_vars; v->name.len; v++) { 254 | var = ngx_http_add_variable(cf, &v->name, v->flags); 255 | if (var == NULL) { 256 | return NGX_ERROR; 257 | } 258 | 259 | var->get_handler = v->get_handler; 260 | var->data = v->data; 261 | } 262 | 263 | return NGX_OK; 264 | } 265 | 266 | 267 | static void * 268 | ngx_http_ipdb_create_main_conf(ngx_conf_t *cf) 269 | { 270 | ngx_pool_cleanup_t *cln; 271 | ngx_http_ipdb_main_conf_t *conf; 272 | 273 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ipdb_main_conf_t)); 274 | if (conf == NULL) { 275 | return NULL; 276 | } 277 | 278 | conf->proxy_recursive = NGX_CONF_UNSET; 279 | 280 | cln = ngx_pool_cleanup_add(cf->pool, 0); 281 | if (cln == NULL) { 282 | return NULL; 283 | } 284 | 285 | cln->handler = ngx_http_ipdb_cleanup; 286 | cln->data = conf; 287 | 288 | return conf; 289 | } 290 | 291 | 292 | static char * 293 | ngx_http_ipdb_init_main_conf(ngx_conf_t *cf, void *conf) 294 | { 295 | ngx_http_ipdb_main_conf_t *imcf = conf; 296 | 297 | ngx_conf_init_value(imcf->proxy_recursive, 0); 298 | 299 | return NGX_CONF_OK; 300 | } 301 | 302 | 303 | static void * 304 | ngx_http_ipdb_create_loc_conf(ngx_conf_t *cf) 305 | { 306 | ngx_http_ipdb_loc_conf_t *ilcf; 307 | 308 | ilcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ipdb_loc_conf_t)); 309 | if (ilcf == NULL) { 310 | return NULL; 311 | } 312 | 313 | return ilcf; 314 | } 315 | 316 | 317 | static char * 318 | ngx_http_ipdb_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 319 | { 320 | ngx_http_compile_complex_value_t ccv; 321 | 322 | ngx_http_ipdb_loc_conf_t *prev = parent; 323 | ngx_http_ipdb_loc_conf_t *conf = child; 324 | 325 | ngx_conf_merge_str_value(conf->lang, prev->lang, "EN"); 326 | ngx_conf_merge_str_value(conf->spec_addr, prev->spec_addr, ""); 327 | 328 | if (conf->spec_addr.len > 0) { 329 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 330 | 331 | ccv.cf = cf; 332 | ccv.value = &conf->spec_addr; 333 | ccv.complex_value = &conf->key; 334 | 335 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 336 | return NGX_CONF_ERROR; 337 | } 338 | } 339 | 340 | return NGX_CONF_OK; 341 | } 342 | 343 | 344 | static void 345 | ngx_http_ipdb_cleanup(void *data) 346 | { 347 | ngx_http_ipdb_main_conf_t *imcf = data; 348 | 349 | if (imcf->ipdb) { 350 | ipdb_reader_free(&imcf->ipdb); 351 | } 352 | } 353 | 354 | 355 | static char * 356 | ngx_http_ipdb_open(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 357 | { 358 | ngx_int_t err; 359 | ngx_http_ipdb_main_conf_t *imcf = conf; 360 | 361 | ngx_str_t *value; 362 | 363 | if (imcf->ipdb) { 364 | return "is duplicate"; 365 | } 366 | 367 | value = cf->args->elts; 368 | 369 | err = ipdb_reader_new((char *) value[1].data, &imcf->ipdb); 370 | 371 | if (err || imcf->ipdb == NULL) { 372 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 373 | "ipdb_reader_new(\"%V\") failed", &value[1]); 374 | 375 | return NGX_CONF_ERROR; 376 | } 377 | 378 | return NGX_CONF_OK; 379 | } 380 | 381 | static ngx_int_t 382 | ngx_http_ipdb_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) 383 | { 384 | ngx_int_t rc; 385 | 386 | if (ngx_strcmp(net->data, "255.255.255.255") == 0) { 387 | cidr->family = AF_INET; 388 | cidr->u.in.addr = 0xffffffff; 389 | cidr->u.in.mask = 0xffffffff; 390 | 391 | return NGX_OK; 392 | } 393 | 394 | rc = ngx_ptocidr(net, cidr); 395 | 396 | if (rc == NGX_ERROR) { 397 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); 398 | return NGX_ERROR; 399 | } 400 | 401 | if (rc == NGX_DONE) { 402 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 403 | "low address bits of %V are meaningless", net); 404 | } 405 | 406 | return NGX_OK; 407 | } 408 | 409 | static char * 410 | ngx_http_ipdb_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 411 | { 412 | ngx_http_ipdb_main_conf_t *imcf = conf; 413 | 414 | ngx_str_t *value; 415 | ngx_cidr_t cidr, *c; 416 | 417 | value = cf->args->elts; 418 | 419 | if (ngx_http_ipdb_cidr_value(cf, &value[1], &cidr) != NGX_OK) { 420 | return NGX_CONF_ERROR; 421 | } 422 | 423 | if (imcf->proxies == NULL) { 424 | imcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t)); 425 | if (imcf->proxies == NULL) { 426 | return NGX_CONF_ERROR; 427 | } 428 | } 429 | 430 | c = ngx_array_push(imcf->proxies); 431 | if (c == NULL) { 432 | return NGX_CONF_ERROR; 433 | } 434 | 435 | *c = cidr; 436 | 437 | return NGX_CONF_OK; 438 | } 439 | 440 | 441 | // "a\tb\t\tc" 442 | // "" 443 | static char * 444 | ngx_http_ipdb_get_index_item(char *v, ngx_int_t idx) 445 | { 446 | char *p, *q; 447 | ngx_int_t n = 0; 448 | 449 | if (v == NULL) return NULL; 450 | 451 | if (idx >= NGX_IPDB_raw) return v; 452 | 453 | p = v; 454 | q = p; 455 | 456 | while (*p) { 457 | 458 | if (*p == '\t') { 459 | 460 | if (idx == n) { 461 | *p = 0; 462 | return q; 463 | } 464 | 465 | q = p + 1; 466 | ++n; 467 | } 468 | 469 | ++p; 470 | } 471 | 472 | if (*p == 0 && idx == n) { 473 | return q; 474 | } 475 | 476 | return NULL; 477 | } 478 | 479 | 480 | static ngx_int_t 481 | ngx_http_ipdb_item_by_addr(ipdb_reader *reader, ngx_addr_t *addr, 482 | const char *lang, char *body) 483 | { 484 | int node = 0; 485 | ngx_int_t err, i; 486 | off_t off = -1; 487 | size_t p = 0, o = 0, s = 0, e = 0; 488 | size_t len = reader->meta->fields_length; 489 | const char *content; 490 | 491 | for (i = 0; i < reader->meta->language_length; ++i) { 492 | if (ngx_strcmp(lang, reader->meta->language[i].name) == 0) { 493 | off = reader->meta->language[i].offset; 494 | break; 495 | } 496 | } 497 | 498 | if (off == -1) { 499 | return ErrNoSupportLanguage; 500 | } 501 | 502 | if (addr->sockaddr->sa_family == AF_INET) { 503 | if (!ipdb_reader_is_ipv4_support(reader)) { 504 | return ErrNoSupportIPv4; 505 | } 506 | 507 | struct sockaddr_in *sin; 508 | 509 | sin = (struct sockaddr_in *) addr->sockaddr; 510 | // sin->sin_addr.s_addr 511 | err = ipdb_search(reader, (const u_char *)&sin->sin_addr.s_addr, 32, &node); 512 | if (err != ErrNoErr) { 513 | return err; 514 | } 515 | } 516 | #if (NGX_HAVE_INET6) 517 | else if (addr->sockaddr->sa_family == AF_INET6) { 518 | if (!ipdb_reader_is_ipv6_support(reader)) { 519 | return ErrNoSupportIPv6; 520 | } 521 | 522 | struct in6_addr *inaddr6; 523 | inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr; 524 | err = ipdb_search(reader, (const u_char *)&inaddr6->s6_addr, 128, &node); 525 | if (err != ErrNoErr) { 526 | return err; 527 | } 528 | } 529 | #endif 530 | else { 531 | return ErrIPFormat; 532 | } 533 | 534 | err = ipdb_resolve(reader, node, &content); 535 | if (err) { 536 | return err; 537 | } 538 | 539 | while (*(content + p)) { 540 | if (*(content + p) == '\t') { 541 | ++o; 542 | } 543 | if ((!e) && o == off + len) { 544 | e = p; 545 | } 546 | ++p; 547 | if (off && (!s) && (off_t)o == off) { 548 | s = p; 549 | } 550 | } 551 | if (!e) e = p; 552 | if (off + len > o + 1) { 553 | err = ErrDatabaseError; 554 | } else { 555 | strncpy(body, content + s, e - s); 556 | body[e - s] = 0; 557 | } 558 | 559 | return err; 560 | } 561 | 562 | static ngx_int_t 563 | ngx_http_ipdb_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 564 | uintptr_t data) 565 | { 566 | size_t len; 567 | char *p, *t; 568 | char body[512]; 569 | ngx_int_t err; 570 | ngx_str_t spec_addr; 571 | ngx_addr_t addr; 572 | 573 | #if (nginx_version >= 1023000) 574 | ngx_table_elt_t *xfwd; 575 | #else 576 | ngx_array_t *xfwd; 577 | #endif 578 | 579 | ngx_http_ipdb_main_conf_t *imcf; 580 | ngx_http_ipdb_loc_conf_t *ilcf; 581 | 582 | #if (NGX_DEBUG) 583 | ngx_str_t debug; 584 | #endif 585 | 586 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 587 | "ngx_http_ipdb_variable"); 588 | 589 | imcf = ngx_http_get_module_main_conf(r, ngx_http_ipdb_module); 590 | 591 | if (imcf == NULL || imcf->ipdb == NULL) { 592 | goto not_found; 593 | } 594 | 595 | ilcf = ngx_http_get_module_loc_conf(r, ngx_http_ipdb_module); 596 | 597 | if (ilcf == NULL || ilcf->lang.data == NULL) { 598 | goto not_found; 599 | } 600 | 601 | if (ilcf->key.value.len > 0) { 602 | if (ngx_http_complex_value(r, &ilcf->key, &spec_addr) != NGX_OK) { 603 | goto not_found; 604 | } 605 | 606 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 607 | "ngx_http_ipdb_variable, spec_addr: \"%V\"", &spec_addr); 608 | 609 | t = ngx_pcalloc(r->pool, spec_addr.len + 1); 610 | if (t == NULL) { 611 | goto not_found; 612 | } 613 | 614 | ngx_memcpy(t, spec_addr.data, spec_addr.len); 615 | t[spec_addr.len] = 0; 616 | 617 | err = ipdb_reader_find(imcf->ipdb, (const char*)t, 618 | (const char *)ilcf->lang.data, body); 619 | 620 | ngx_pfree(r->pool, t); 621 | 622 | } else { 623 | addr.sockaddr = r->connection->sockaddr; 624 | addr.socklen = r->connection->socklen; 625 | 626 | #if (nginx_version >= 1023000) 627 | xfwd = r->headers_in.x_forwarded_for; 628 | #else 629 | xfwd = &r->headers_in.x_forwarded_for; 630 | #endif 631 | 632 | if ( 633 | #if (nginx_version >= 1023000) 634 | xfwd != NULL 635 | #else 636 | xfwd->nelts > 0 637 | #endif 638 | && imcf->proxies != NULL) { 639 | (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, 640 | imcf->proxies, imcf->proxy_recursive); 641 | } 642 | 643 | err = ngx_http_ipdb_item_by_addr(imcf->ipdb, &addr, 644 | (const char *)ilcf->lang.data, body); 645 | } 646 | 647 | if (err) { 648 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 649 | "ngx_http_ipdb_variable, ipdb find error: %d", err); 650 | 651 | goto not_found; 652 | } 653 | 654 | #if (NGX_DEBUG) 655 | debug.len = ngx_strlen(body); 656 | debug.data = (u_char *)body; 657 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 658 | "ngx_http_ipdb_variable, item: \"%V\"", &debug); 659 | #endif 660 | 661 | p = ngx_http_ipdb_get_index_item(body, data); 662 | if (p == NULL) { 663 | goto not_found; 664 | } 665 | len = ngx_strlen(p); 666 | 667 | #if (NGX_DEBUG) 668 | debug.len = len; 669 | debug.data = (u_char *)p; 670 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 671 | "ngx_http_ipdb_variable, %l res: \"%V\"", data, &debug); 672 | #endif 673 | 674 | v->data = ngx_pnalloc(r->pool, len); 675 | if (v->data == NULL) { 676 | return NGX_ERROR; 677 | } 678 | 679 | ngx_memcpy(v->data, p, len); 680 | 681 | v->len = len; 682 | v->valid = 1; 683 | v->no_cacheable = 0; 684 | v->not_found = 0; 685 | 686 | return NGX_OK; 687 | 688 | not_found: 689 | 690 | v->not_found = 1; 691 | 692 | return NGX_OK; 693 | } 694 | -------------------------------------------------------------------------------- /t/ipdb.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # (C) vislee 4 | 5 | # Tests for http ipdb module. 6 | 7 | ############################################################################### 8 | 9 | use warnings; 10 | use strict; 11 | 12 | use Test::More; 13 | 14 | use Socket qw/ CRLF /; 15 | 16 | BEGIN { use FindBin; chdir($FindBin::Bin); } 17 | 18 | use lib 'lib'; 19 | use Test::Nginx; 20 | 21 | ############################################################################### 22 | 23 | select STDERR; $| = 1; 24 | select STDOUT; $| = 1; 25 | 26 | my $t = Test::Nginx->new(); 27 | 28 | $t->write_file_expand('nginx.conf', <<'EOF'); 29 | 30 | %%TEST_GLOBALS%% 31 | 32 | daemon off; 33 | 34 | # load_module /tmp/nginx/modules/ngx_http_ipdb_module.so; 35 | 36 | events { 37 | } 38 | 39 | http { 40 | %%TEST_GLOBALS_HTTP%% 41 | 42 | ipdb ./ipiptest.ipdb; 43 | ipdb_language CN; 44 | ipdb_proxy 127.0.0.1; 45 | ipdb_proxy 192.168.0.1/24; 46 | ipdb_proxy 255.255.255.255; 47 | ipdb_proxy_recursive on; 48 | 49 | server { 50 | listen 127.0.0.1:8080; 51 | server_name localhost; 52 | 53 | #set_real_ip_from 127.0.0.1; 54 | #real_ip_header X-Forwarded-For; 55 | 56 | location /test/ { 57 | return 200 testipdb; 58 | } 59 | 60 | location /country/ { 61 | return 200 $ipdb_country_name; 62 | } 63 | 64 | location /ipv6/country/ { 65 | return 200 null$ipdb_country_name; 66 | } 67 | 68 | location /region/ { 69 | return 200 $ipdb_region_name; 70 | } 71 | 72 | location /city/ { 73 | return 200 $ipdb_city_name; 74 | } 75 | 76 | location /isp/ { 77 | return 200 null$ipdb_isp_domain; 78 | } 79 | 80 | location /spec/country { 81 | ipdb_specifies_addr $arg_addr; 82 | ipdb_language CN; 83 | return 200 $ipdb_country_name; 84 | } 85 | 86 | location /spec/region { 87 | ipdb_specifies_addr $http_addr; 88 | return 200 $ipdb_region_name; 89 | } 90 | 91 | location /spec/city/ { 92 | ipdb_specifies_addr $http_addr; 93 | return 200 $ipdb_city_name; 94 | 95 | location /spec/city/args { 96 | ipdb_specifies_addr $arg_addr; 97 | return 200 $ipdb_city_name; 98 | } 99 | 100 | } 101 | 102 | 103 | location /raw/ { 104 | ipdb_specifies_addr $http_addr; 105 | return 200 $ipdb_raw; 106 | } 107 | } 108 | 109 | } 110 | 111 | EOF 112 | 113 | $t->try_run('no ipdb')->plan(14); 114 | 115 | ############################################################################### 116 | like(http(<new(); 27 | 28 | $t->write_file_expand('nginx.conf', <<'EOF'); 29 | 30 | %%TEST_GLOBALS%% 31 | 32 | daemon off; 33 | 34 | # load_module /tmp/nginx/modules/ngx_http_ipdb_module.so; 35 | 36 | events { 37 | } 38 | 39 | http { 40 | %%TEST_GLOBALS_HTTP%% 41 | 42 | ipdb ./ipip.ipdb; 43 | ipdb_language CN; 44 | 45 | server { 46 | listen 127.0.0.1:8080; 47 | server_name localhost; 48 | 49 | location /spec/owner_domain { 50 | ipdb_specifies_addr $arg_addr; 51 | ipdb_language CN; 52 | return 200 $ipdb_owner_domain; 53 | } 54 | 55 | location /spec/xitude { 56 | ipdb_specifies_addr $arg_addr; 57 | ipdb_language CN; 58 | return 200 "$ipdb_latitude,$ipdb_longitude"; 59 | } 60 | 61 | location /spec/timezone { 62 | ipdb_specifies_addr $arg_addr; 63 | ipdb_language CN; 64 | return 200 "$ipdb_timezone"; 65 | } 66 | 67 | location /spec/utc_offset { 68 | ipdb_specifies_addr $arg_addr; 69 | ipdb_language CN; 70 | return 200 "$ipdb_utc_offset"; 71 | } 72 | 73 | location /spec/china_admin_code { 74 | ipdb_specifies_addr $arg_addr; 75 | ipdb_language CN; 76 | return 200 "$ipdb_china_admin_code"; 77 | } 78 | 79 | location /spec/idd_code { 80 | ipdb_specifies_addr $arg_addr; 81 | ipdb_language CN; 82 | return 200 "$ipdb_idd_code"; 83 | } 84 | 85 | location /spec/country_code { 86 | ipdb_specifies_addr $arg_addr; 87 | ipdb_language CN; 88 | return 200 "$ipdb_country_code"; 89 | } 90 | 91 | location /spec/continent_code { 92 | ipdb_specifies_addr $arg_addr; 93 | ipdb_language CN; 94 | return 200 "$ipdb_continent_code"; 95 | } 96 | 97 | location /spec/idc { 98 | ipdb_specifies_addr $arg_addr; 99 | ipdb_language CN; 100 | return 200 "$ipdb_idc"; 101 | } 102 | 103 | location /spec/currency_name { 104 | ipdb_specifies_addr $arg_addr; 105 | ipdb_language CN; 106 | return 200 "$ipdb_currency_name"; 107 | } 108 | 109 | location /spec/country_code3 { 110 | ipdb_specifies_addr $arg_addr; 111 | ipdb_language CN; 112 | return 200 "$ipdb_country_code3"; 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | EOF 120 | 121 | $t->try_run('no ipdb')->plan(11); 122 | 123 | ############################################################################### 124 | like(http(<