├── .gitignore ├── AUTHORS ├── BSDmakefile ├── COPYRIGHT ├── GNUmakefile ├── HISTORY ├── README.md ├── TODO ├── make.sh ├── rockspec ├── lua-geoip-0.1-1.rockspec ├── lua-geoip-0.1.1-1.rockspec ├── lua-geoip-0.1.2-1.rockspec ├── lua-geoip-0.2-1.rockspec └── lua-geoip-scm-1.rockspec ├── src ├── city.c ├── country.c ├── database.c ├── database.h ├── lua-geoip.c └── lua-geoip.h └── test └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*~ 3 | \#*\# 4 | .\#*\# 5 | 6 | .DS_Store 7 | .project 8 | .settings 9 | 10 | *.o 11 | *.so 12 | 13 | *.dat 14 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | lua-geoip bindings authors: 2 | --------------------------- 3 | 4 | Alexander Gladysh 5 | Vladimir Dronnikov 6 | 7 | Contributors: 8 | ------------- 9 | 10 | Lorenzo Pistone 11 | Mike Trinkala 12 | Vadim A. Misbakh-Soloviov 13 | Marcin Deranek 14 | Frédéric VANNIÈRE 15 | -------------------------------------------------------------------------------- /BSDmakefile: -------------------------------------------------------------------------------- 1 | LUA_IMPL ?= lua5.1 2 | DESTDIR ?= / 3 | CFLAGS ?= -O2 -fPIC -DPIC 4 | PKG_CONFIG ?= pkg-config 5 | CC ?= gcc 6 | INSTALL ?= install 7 | LUA_CMOD_DIR ?= $(shell $(PKG_CONFIG) $(LUA_IMPL) --variable INSTALL_CMOD) 8 | 9 | CF += $(CFLAGS) -Werror -pedantic -std=c99 -Isrc 10 | LF += $(LDFLAGS) -shared -lGeoIP 11 | 12 | all: prepare geoip.so geoip/country.so geoip/city.so 13 | 14 | prepare: 15 | @mkdir -p geoip 16 | 17 | geoip.so: src/database.o src/lua-geoip.o 18 | $(CC) $(LF) $^ -o $@ 19 | 20 | geoip/country.so: src/database.o src/country.o 21 | $(CC) $(LF) $^ -o $@ 22 | 23 | geoip/city.so: src/database.o src/city.o 24 | $(CC) $(LF) $^ -o $@ 25 | 26 | .c.o: 27 | $(CC) $(CF) -c $^ -o $@ 28 | 29 | clean: 30 | @rm -f geoip.so geoip/country.so geoip/city.so 31 | @rm -f src/*.o 32 | @rm -rf geoip 33 | 34 | install: all 35 | $(INSTALL) -d $(DESTDIR/)$(LUA_CMOD_DIR)/geoip 36 | $(INSTALL) geoip/* $(DESTDIR)/$(LUA_CMOD_DIR)/geoip 37 | $(INSTALL) geoip.so $(DESTDIR)/$(LUA_CMOD_DIR) 38 | 39 | uninstall: 40 | @rm -f $(LUA_CMOD_DIR)/geoip.so 41 | @rm -rf $(LUA_CMOD_DIR)/geoip 42 | 43 | .SUFFIXES: .c .o .so 44 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | lua-geoip bindings license 2 | -------------------------- 3 | 4 | lua-geoip bindings module code is licensed under the terms of the MIT license 5 | reproduced below. This means that lua-geoip is free software and can be used 6 | for both academic and commercial purposes at absolutely no cost. 7 | 8 | Note that MaxMind GeoIP library (not included) is licensed under LGPL 2.1, 9 | and MaxMind GeoIP data (also not included) is licensed under a custom "database" 10 | license. 11 | 12 | =============================================================================== 13 | 14 | Copyright (C) 2011-2017 lua-geoip bindings authors 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in 24 | all copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 32 | THE SOFTWARE. 33 | 34 | =============================================================================== 35 | 36 | (end of COPYRIGHT) 37 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | LUA_IMPL ?= lua5.1 2 | DESTDIR ?= / 3 | PKG_CONFIG ?= pkg-config 4 | CC ?= gcc 5 | CFLAGS ?= -O2 -fPIC -DPIC $(shell $(PKG_CONFIG) $(LUA_IMPL) --cflags) 6 | INSTALL ?= install 7 | LUA_CMOD_DIR ?= $(shell $(PKG_CONFIG) $(LUA_IMPL) --variable INSTALL_CMOD) 8 | 9 | CF += $(CFLAGS) -Werror -pedantic -std=c99 -Isrc 10 | LF += $(LDFLAGS) -shared -lGeoIP 11 | 12 | all: prepare geoip.so geoip/country.so geoip/city.so 13 | 14 | prepare: 15 | @mkdir -p geoip 16 | 17 | geoip.so: src/database.o src/lua-geoip.o 18 | geoip/country.so: src/database.o src/country.o 19 | geoip/city.so: src/database.o src/city.o 20 | 21 | .c.o: 22 | $(CC) $(CF) -c $^ -o $@ 23 | 24 | %.so: 25 | $(CC) $(LF) $^ -o $@ 26 | 27 | clean: 28 | @rm -f geoip.so geoip/country.so geoip/city.so 29 | @rm -f src/*.o 30 | @rm -rf geoip 31 | 32 | install: all 33 | $(INSTALL) -d $(DESTDIR)/$(LUA_CMOD_DIR)/geoip 34 | $(INSTALL) geoip/* $(DESTDIR)/$(LUA_CMOD_DIR)/geoip 35 | $(INSTALL) geoip.so $(DESTDIR)/$(LUA_CMOD_DIR) 36 | 37 | uninstall: 38 | @rm -f $(LUA_CMOD_DIR)/geoip.so 39 | @rm -rf $(LUA_CMOD_DIR)/geoip 40 | 41 | .SUFFIXES: .c .o .so 42 | -------------------------------------------------------------------------------- /HISTORY: -------------------------------------------------------------------------------- 1 | Version 0.2 (2017-05-10) 2 | ======================== 3 | 4 | * IPv6 Lookup support for country 5 | * Lua5.2/5.3 compatibility 6 | * Remove the stderr capture and use the GEOIP_SILENCE flag instead 7 | * added makefiles 8 | 9 | Version 0.1.2 (2012-03-10) 10 | ========================== 11 | 12 | * GEOIP_MEMORY_CACHE is now a default option when opening a DB. 13 | * Fixed some compiler warnings. 14 | * Improved tests. 15 | 16 | Version 0.1.1 (2011-03-09) 17 | ========================== 18 | 19 | * Made code more compatible with C89. 20 | * Charset parameter no longer ignored in `city.open()` and `country.open()`. 21 | 22 | Version 0.1 (2011-03-07) 23 | ======================== 24 | 25 | Initial release. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-geoip — bindings for MaxMind GeoIP library 2 | ============================================== 3 | 4 | See the copyright information in the file named `COPYRIGHT`. 5 | 6 | ## API 7 | 8 | * `require 'geoip'` 9 | 10 | ### Enums 11 | 12 | #### DB types 13 | 14 | * `geoip.COUNTRY` 15 | * `geoip.COUNTRY_V6` 16 | * `geoip.REGION_REV0` 17 | * `geoip.REGION_REV1` 18 | * `geoip.REGION` = `geoip.REGION_REV1` 19 | * `geoip.CITY_REV0` 20 | * `geoip.CITY_REV1` 21 | * `geoip.CITY` = `geoip.CITY_REV1` 22 | * `geoip.ORG` 23 | * `geoip.ISP` 24 | * `geoip.PROXY` 25 | * `geoip.ASNUM` 26 | * `geoip.NETSPEED` 27 | * `geoip.DOMAIN` 28 | 29 | #### Open flags 30 | 31 | * `geoip.STANDARD` 32 | * `geoip.MEMORY_CACHE` 33 | * `geoip.CHECK_CACHE` 34 | * `geoip.INDEX_CACHE` 35 | * `geoip.MMAP_CACHE` 36 | 37 | #### Charsets 38 | 39 | * `geoip.ISO_8859_1` 40 | * `geoip.UTF8` 41 | 42 | TODO: Document further. Meanwhile, see tests. 43 | 44 | ## Where to get stuff? 45 | 46 | ### On Debian / Ubuntu Using PPA: 47 | 48 | MaxMind provides a PPA for recent version of Ubuntu. To add the PPA to your 49 | APT sources, run: 50 | 51 | $ sudo add-apt-repository ppa:maxmind/ppa 52 | $ sudo apt-get update 53 | 54 | Then install the packages by running: 55 | 56 | $ sudo apt-get install geoip-database # GeoLite Country only 57 | $ sudo apt-get install libgeoip-dev 58 | 59 | ### Raw 60 | 61 | $ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz 62 | $ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz 63 | 64 | ### C library 65 | 66 | http://www.maxmind.com/app/c 67 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO: 2 | ----- 3 | 4 | -- Document current API. 5 | -- Fix file headers. 6 | -- Run splint all over it, and fix errors. 7 | -- Generalize copy-pasted DB handling code 8 | -- Support GEOIP_COUNTRY_EDITION_V6 somehow (separate db type?) 9 | -- consider removing db type constants from binding 10 | -- Support regions. 11 | -- Bind whole API. 12 | -- Capture not only stderr, but stdin as well, 13 | libgeoip spams there as well. 14 | -- Write better tests. 15 | -- Open by DB type leaks 18KB+ (that's how libgeoip written). 16 | -- Add a luajit.ffi binding 17 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | echo "----> Going pedantic all over the source" 6 | 7 | echo "--> c89..." 8 | gcc -O2 -fPIC -I/usr/include/lua5.1 -c src/*.c -Isrc/ -Wall --pedantic -Werror --std=c89 -fms-extensions 9 | 10 | echo "--> c99..." 11 | gcc -O2 -fPIC -I/usr/include/lua5.1 -c src/*.c -Isrc/ -Wall --pedantic -Werror --std=c99 -fms-extensions 12 | 13 | echo "--> c++98..." 14 | gcc -xc++ -O2 -fPIC -I/usr/include/lua5.1 -c src/*.c -Isrc/ -Wall --pedantic -Werror --std=c++98 15 | 16 | echo "----> Making rock" 17 | sudo luarocks make rockspec/lua-geoip-scm-1.rockspec 18 | -------------------------------------------------------------------------------- /rockspec/lua-geoip-0.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-geoip" 2 | version = "0.1-1" 3 | source = { 4 | url = "git://github.com/agladysh/lua-geoip.git", 5 | branch = "v0.1" 6 | } 7 | external_dependencies = { 8 | GEOIP = { 9 | header = "GeoIP.h", 10 | } 11 | } 12 | description = { 13 | summary = "Bindings for MaxMind's GeoIP library", 14 | homepage = "http://github.com/agladysh/lua-geoip", 15 | license = "MIT/X11", 16 | maintainer = "Alexander Gladysh " 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | geoip = { 25 | sources = { 26 | "src/lua-geoip.c", 27 | "src/database.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | }, 32 | libraries = { "GeoIP" } 33 | }, 34 | ["geoip.country"] = { 35 | sources = { 36 | "src/database.c", 37 | "src/country.c" 38 | }, 39 | incdirs = { 40 | "src/" 41 | }, 42 | libraries = { "GeoIP" } 43 | }, 44 | ["geoip.city"] = { 45 | sources = { 46 | "src/database.c", 47 | "src/city.c" 48 | }, 49 | incdirs = { 50 | "src/" 51 | }, 52 | libraries = { "GeoIP" } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rockspec/lua-geoip-0.1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-geoip" 2 | version = "0.1.1-1" 3 | source = { 4 | url = "git://github.com/agladysh/lua-geoip.git", 5 | branch = "v0.1.1" 6 | } 7 | external_dependencies = { 8 | GEOIP = { 9 | header = "GeoIP.h", 10 | } 11 | } 12 | description = { 13 | summary = "Bindings for MaxMind's GeoIP library", 14 | homepage = "http://github.com/agladysh/lua-geoip", 15 | license = "MIT/X11", 16 | maintainer = "Alexander Gladysh " 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | geoip = { 25 | sources = { 26 | "src/lua-geoip.c", 27 | "src/database.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | }, 32 | libraries = { "GeoIP" } 33 | }, 34 | ["geoip.country"] = { 35 | sources = { 36 | "src/database.c", 37 | "src/country.c" 38 | }, 39 | incdirs = { 40 | "src/" 41 | }, 42 | libraries = { "GeoIP" } 43 | }, 44 | ["geoip.city"] = { 45 | sources = { 46 | "src/database.c", 47 | "src/city.c" 48 | }, 49 | incdirs = { 50 | "src/" 51 | }, 52 | libraries = { "GeoIP" } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rockspec/lua-geoip-0.1.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-geoip" 2 | version = "0.1.2-1" 3 | source = { 4 | url = "git://github.com/agladysh/lua-geoip.git", 5 | branch = "v0.1.2" 6 | } 7 | external_dependencies = { 8 | GEOIP = { 9 | header = "GeoIP.h", 10 | } 11 | } 12 | description = { 13 | summary = "Bindings for MaxMind's GeoIP library", 14 | homepage = "http://github.com/agladysh/lua-geoip", 15 | license = "MIT/X11", 16 | maintainer = "Alexander Gladysh " 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | geoip = { 25 | sources = { 26 | "src/lua-geoip.c", 27 | "src/database.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | }, 32 | libraries = { "GeoIP" } 33 | }, 34 | ["geoip.country"] = { 35 | sources = { 36 | "src/database.c", 37 | "src/country.c" 38 | }, 39 | incdirs = { 40 | "src/" 41 | }, 42 | libraries = { "GeoIP" } 43 | }, 44 | ["geoip.city"] = { 45 | sources = { 46 | "src/database.c", 47 | "src/city.c" 48 | }, 49 | incdirs = { 50 | "src/" 51 | }, 52 | libraries = { "GeoIP" } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rockspec/lua-geoip-0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-geoip" 2 | version = "0.2-1" 3 | source = { 4 | url = "git://github.com/agladysh/lua-geoip.git", 5 | branch = "v0.2" 6 | } 7 | external_dependencies = { 8 | GEOIP = { 9 | header = "GeoIP.h", 10 | } 11 | } 12 | description = { 13 | summary = "Bindings for MaxMind's GeoIP library", 14 | homepage = "http://github.com/agladysh/lua-geoip", 15 | license = "MIT/X11", 16 | maintainer = "Alexander Gladysh " 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | geoip = { 25 | sources = { 26 | "src/lua-geoip.c", 27 | "src/database.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | }, 32 | libraries = { "GeoIP" } 33 | }, 34 | ["geoip.country"] = { 35 | sources = { 36 | "src/database.c", 37 | "src/country.c" 38 | }, 39 | incdirs = { 40 | "src/" 41 | }, 42 | libraries = { "GeoIP" } 43 | }, 44 | ["geoip.city"] = { 45 | sources = { 46 | "src/database.c", 47 | "src/city.c" 48 | }, 49 | incdirs = { 50 | "src/" 51 | }, 52 | libraries = { "GeoIP" } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rockspec/lua-geoip-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-geoip" 2 | version = "scm-1" 3 | source = { 4 | url = "git://github.com/agladysh/lua-geoip.git", 5 | branch = "master" 6 | } 7 | external_dependencies = { 8 | GEOIP = { 9 | header = "GeoIP.h", 10 | } 11 | } 12 | description = { 13 | summary = "Bindings for MaxMind's GeoIP library", 14 | homepage = "http://github.com/agladysh/lua-geoip", 15 | license = "MIT/X11", 16 | maintainer = "Alexander Gladysh " 17 | } 18 | dependencies = { 19 | "lua >= 5.1" 20 | } 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | geoip = { 25 | sources = { 26 | "src/lua-geoip.c", 27 | "src/database.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | }, 32 | libraries = { "GeoIP" } 33 | }, 34 | ["geoip.country"] = { 35 | sources = { 36 | "src/database.c", 37 | "src/country.c" 38 | }, 39 | incdirs = { 40 | "src/" 41 | }, 42 | libraries = { "GeoIP" } 43 | }, 44 | ["geoip.city"] = { 45 | sources = { 46 | "src/database.c", 47 | "src/city.c" 48 | }, 49 | incdirs = { 50 | "src/" 51 | }, 52 | libraries = { "GeoIP" } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/city.c: -------------------------------------------------------------------------------- 1 | /* 2 | * city.c: Bindings for MaxMind's GeoIP library 3 | * See copyright information in file COPYRIGHT. 4 | */ 5 | 6 | #include 7 | 8 | #include "lua-geoip.h" 9 | #include "database.h" 10 | 11 | #define LUAGEOIP_CITY_VERSION "lua-geoip.city 0.2" 12 | #define LUAGEOIP_CITY_COPYRIGHT "Copyright (C) 2011-2017, lua-geoip authors" 13 | #define LUAGEOIP_CITY_DESCRIPTION \ 14 | "Bindings for MaxMind's GeoIP library (city database)" 15 | 16 | static GeoIP * check_city_db(lua_State * L, int idx) 17 | { 18 | int type = 0; 19 | luageoip_DB * pDB = (luageoip_DB *)luaL_checkudata(L, idx, LUAGEOIP_CITY_MT); 20 | if (pDB == NULL) 21 | { 22 | lua_pushstring(L, "lua-geoip error: city db is null"); 23 | return NULL; 24 | } 25 | 26 | if (pDB->pGeoIP == NULL) 27 | { 28 | lua_pushstring(L, "lua-geoip error: attempted to use closed city db"); 29 | return NULL; 30 | } 31 | 32 | type = GeoIP_database_edition(pDB->pGeoIP); 33 | if ( 34 | type != GEOIP_CITY_EDITION_REV0 && 35 | type != GEOIP_CITY_EDITION_REV1 36 | ) 37 | { 38 | lua_pushstring(L, "lua-geoip error: object is not a city db"); 39 | return NULL; 40 | } 41 | 42 | return pDB->pGeoIP; 43 | } 44 | 45 | /* TODO: Generalize copy-paste with country code */ 46 | static int push_city_info( 47 | lua_State * L, 48 | int first_arg_idx, 49 | GeoIPRecord * pRecord 50 | ) 51 | { 52 | static const int NUM_OPTS = 13; 53 | static const char * const opts[] = 54 | { 55 | /* order is important! */ 56 | /* 0 */ "country_code", 57 | /* 1 */ "country_code3", 58 | /* 2 */ "country_name", 59 | /* 3 */ "region", 60 | /* 4 */ "city", 61 | /* 5 */ "postal_code", 62 | /* 6 */ "latitude", 63 | /* 7 */ "longitude", 64 | /* 8 */ "metro_code", 65 | /* 9 */ "dma_code", 66 | /* 10 */ "area_code", 67 | /* 11 */ "charset", 68 | /* 12 */ "continent_code", 69 | NULL 70 | }; 71 | 72 | int nargs = lua_gettop(L) - first_arg_idx + 1; 73 | int need_all = (nargs == 0); 74 | 75 | int i = 0; 76 | 77 | if (pRecord == NULL) 78 | { 79 | lua_pushnil(L); 80 | lua_pushliteral(L, "not found"); 81 | return 2; 82 | } 83 | 84 | if (need_all) 85 | { 86 | nargs = NUM_OPTS; 87 | lua_newtable(L); 88 | } 89 | 90 | for (i = 0; i < nargs; ++i) 91 | { 92 | int idx = (need_all) 93 | ? i 94 | : luaL_checkoption(L, first_arg_idx + i, NULL, opts) 95 | ; 96 | 97 | /* TODO: Ugly */ 98 | switch (idx) 99 | { 100 | case 0: /* "country_code" */ 101 | lua_pushstring(L, pRecord->country_code); 102 | break; 103 | 104 | case 1: /* "country_code3" */ 105 | lua_pushstring(L, pRecord->country_code3); 106 | break; 107 | 108 | case 2: /* "country_name" */ 109 | lua_pushstring(L, pRecord->country_name); 110 | break; 111 | 112 | case 3: /* "region" */ 113 | lua_pushstring(L, pRecord->region); 114 | break; 115 | 116 | case 4: /* "city" */ 117 | lua_pushstring(L, pRecord->city); 118 | break; 119 | 120 | case 5: /* "postal_code" */ 121 | lua_pushstring(L, pRecord->postal_code); 122 | break; 123 | 124 | case 6: /* "latitude" */ 125 | lua_pushnumber(L, pRecord->latitude); 126 | break; 127 | 128 | case 7: /* "longitude" */ 129 | lua_pushnumber(L, pRecord->longitude); 130 | break; 131 | 132 | case 8: /* "metro_code" */ 133 | lua_pushinteger(L, pRecord->metro_code); 134 | break; 135 | 136 | case 9: /* "dma_code" */ 137 | lua_pushinteger(L, pRecord->dma_code); 138 | break; 139 | 140 | case 10: /* "area_code" */ 141 | lua_pushinteger(L, pRecord->area_code); 142 | break; 143 | 144 | case 11: /* "charset" */ 145 | lua_pushinteger(L, pRecord->charset); 146 | break; 147 | 148 | case 12: /* "continent_code" */ 149 | lua_pushstring(L, pRecord->continent_code); 150 | break; 151 | 152 | default: 153 | /* Hint: Did you synchronize switch cases with opts array? */ 154 | return luaL_error(L, "lua-geoip error: bad implementation"); 155 | } 156 | 157 | if (need_all) 158 | { 159 | lua_setfield(L, -2, opts[i]); 160 | } 161 | } 162 | 163 | GeoIPRecord_delete(pRecord); 164 | 165 | return (need_all) ? 1 : nargs; 166 | } 167 | 168 | /* TODO: Remove copy-paste below! */ 169 | 170 | static int lcity_query_by_name(lua_State * L) 171 | { 172 | GeoIP * pGeoIP = check_city_db(L, 1); 173 | const char * name = luaL_checkstring(L, 2); 174 | 175 | if (pGeoIP == NULL) 176 | { 177 | return lua_error(L); /* Error message already on stack */ 178 | } 179 | 180 | return push_city_info( 181 | L, 3, GeoIP_record_by_name(pGeoIP, name) 182 | ); 183 | } 184 | 185 | static int lcity_query_by_addr(lua_State * L) 186 | { 187 | GeoIP * pGeoIP = check_city_db(L, 1); 188 | const char * addr = luaL_checkstring(L, 2); 189 | 190 | if (pGeoIP == NULL) 191 | { 192 | return lua_error(L); /* Error message already on stack */ 193 | } 194 | 195 | return push_city_info( 196 | L, 3, GeoIP_record_by_addr(pGeoIP, addr) 197 | ); 198 | } 199 | 200 | static int lcity_query_by_ipnum(lua_State * L) 201 | { 202 | GeoIP * pGeoIP = check_city_db(L, 1); 203 | lua_Integer ipnum = luaL_checkinteger(L, 2); /* Hoping that value would fit */ 204 | 205 | if (pGeoIP == NULL) 206 | { 207 | return lua_error(L); /* Error message already on stack */ 208 | } 209 | 210 | return push_city_info( 211 | L, 3, GeoIP_record_by_ipnum(pGeoIP, ipnum) 212 | ); 213 | } 214 | 215 | static int lcity_charset(lua_State * L) 216 | { 217 | GeoIP * pGeoIP = check_city_db(L, 1); 218 | if (pGeoIP == NULL) 219 | { 220 | return lua_error(L); /* Error message already on stack */ 221 | } 222 | 223 | lua_pushinteger(L, GeoIP_charset(pGeoIP)); 224 | 225 | return 1; 226 | } 227 | 228 | static int lcity_set_charset(lua_State * L) 229 | { 230 | GeoIP * pGeoIP = check_city_db(L, 1); 231 | int charset = luaL_checkint(L, 2); 232 | 233 | if (pGeoIP == NULL) 234 | { 235 | return lua_error(L); /* Error message already on stack */ 236 | } 237 | 238 | GeoIP_set_charset(pGeoIP, charset); 239 | 240 | return 0; 241 | } 242 | 243 | static int lcity_close(lua_State * L) 244 | { 245 | luageoip_DB * pDB = (luageoip_DB *)luaL_checkudata(L, 1, LUAGEOIP_CITY_MT); 246 | 247 | if (pDB && pDB->pGeoIP != NULL) 248 | { 249 | GeoIP_delete(pDB->pGeoIP); 250 | pDB->pGeoIP = NULL; 251 | } 252 | 253 | return 0; 254 | } 255 | 256 | #define lcity_gc lcity_close 257 | 258 | static int lcity_tostring(lua_State * L) 259 | { 260 | GeoIP * pGeoIP = check_city_db(L, 1); 261 | if (pGeoIP == NULL) 262 | { 263 | return lua_error(L); /* Error message already on stack */ 264 | } 265 | 266 | lua_pushstring(L, GeoIP_database_info(pGeoIP)); 267 | 268 | return 1; 269 | } 270 | 271 | static const luaL_Reg M[] = 272 | { 273 | { "query_by_name", lcity_query_by_name }, 274 | { "query_by_addr", lcity_query_by_addr }, 275 | { "query_by_ipnum", lcity_query_by_ipnum }, 276 | 277 | { "charset", lcity_charset }, 278 | { "set_charset", lcity_set_charset }, 279 | { "close", lcity_close }, 280 | { "__gc", lcity_gc }, 281 | { "__tostring", lcity_tostring }, 282 | 283 | { NULL, NULL } 284 | }; 285 | 286 | static int lcity_open(lua_State * L) 287 | { 288 | static const int allowed_types[] = 289 | { 290 | GEOIP_CITY_EDITION_REV0, 291 | GEOIP_CITY_EDITION_REV1 292 | }; 293 | 294 | return luageoip_common_open_db( 295 | L, 296 | M, 297 | GEOIP_CITY_EDITION_REV1, 298 | GEOIP_MEMORY_CACHE | GEOIP_SILENCE, 299 | LUAGEOIP_CITY_MT, 300 | 0, /* all flags allowed */ 301 | 2, 302 | allowed_types 303 | ); 304 | } 305 | 306 | /* Lua module API */ 307 | static const struct luaL_Reg R[] = 308 | { 309 | { "open", lcity_open }, 310 | 311 | { NULL, NULL } 312 | }; 313 | 314 | #ifdef __cplusplus 315 | extern "C" { 316 | #endif 317 | 318 | LUALIB_API int luaopen_geoip_city(lua_State * L) 319 | { 320 | /* 321 | * Register module 322 | */ 323 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 324 | luaL_register(L, "geoip.city", R); 325 | #else 326 | lua_newtable(L); 327 | luaL_setfuncs(L, R, 0); 328 | #endif 329 | 330 | /* 331 | * Register module information 332 | */ 333 | lua_pushliteral(L, LUAGEOIP_CITY_VERSION); 334 | lua_setfield(L, -2, "_VERSION"); 335 | 336 | lua_pushliteral(L, LUAGEOIP_CITY_COPYRIGHT); 337 | lua_setfield(L, -2, "_COPYRIGHT"); 338 | 339 | lua_pushliteral(L, LUAGEOIP_CITY_DESCRIPTION); 340 | lua_setfield(L, -2, "_DESCRIPTION"); 341 | 342 | return 1; 343 | } 344 | 345 | #ifdef __cplusplus 346 | } 347 | #endif 348 | -------------------------------------------------------------------------------- /src/country.c: -------------------------------------------------------------------------------- 1 | /* 2 | * country.c: Bindings for MaxMind's GeoIP library 3 | * See copyright information in file COPYRIGHT. 4 | */ 5 | 6 | #include "lua-geoip.h" 7 | #include "database.h" 8 | 9 | #define LUAGEOIP_COUNTRY_VERSION "lua-geoip.country 0.2" 10 | #define LUAGEOIP_COUNTRY_COPYRIGHT \ 11 | "Copyright (C) 2011-2017, lua-geoip authors" 12 | #define LUAGEOIP_COUNTRY_DESCRIPTION \ 13 | "Bindings for MaxMind's GeoIP library (country database)" 14 | 15 | static GeoIP * check_country_db(lua_State * L, int idx) 16 | { 17 | int type = 0; 18 | luageoip_DB * pDB = (luageoip_DB *)luaL_checkudata( 19 | L, 20 | idx, 21 | LUAGEOIP_COUNTRY_MT 22 | ); 23 | if (pDB == NULL) 24 | { 25 | lua_pushstring(L, "lua-geoip error: country db is null"); 26 | return NULL; 27 | } 28 | 29 | if (pDB->pGeoIP == NULL) 30 | { 31 | lua_pushstring(L, "lua-geoip error: attempted to use closed country db"); 32 | return NULL; 33 | } 34 | 35 | type = GeoIP_database_edition(pDB->pGeoIP); 36 | if ( 37 | type != GEOIP_COUNTRY_EDITION && 38 | type != GEOIP_COUNTRY_EDITION_V6 39 | ) 40 | { 41 | lua_pushstring(L, "lua-geoip error: object is not a country db"); 42 | return NULL; 43 | } 44 | 45 | return pDB->pGeoIP; 46 | } 47 | 48 | /* TODO: Handle when id 0? */ 49 | static int push_country_info(lua_State * L, int first_arg_idx, int id) 50 | { 51 | static const int NUM_OPTS = 5; 52 | static const char * const opts[] = 53 | { 54 | /* order is important! */ 55 | /* 0 */ "id", 56 | /* 1 */ "code", 57 | /* 2 */ "code3", 58 | /* 3 */ "continent", 59 | /* 4 */ "name", 60 | NULL 61 | }; 62 | 63 | int nargs = lua_gettop(L) - first_arg_idx + 1; 64 | int need_all = (nargs == 0); 65 | 66 | int i = 0; 67 | 68 | if (need_all) 69 | { 70 | nargs = NUM_OPTS; 71 | lua_newtable(L); 72 | } 73 | 74 | for (i = 0; i < nargs; ++i) 75 | { 76 | int idx = (need_all) 77 | ? i 78 | : luaL_checkoption(L, first_arg_idx + i, NULL, opts) 79 | ; 80 | 81 | /* TODO: Ugly */ 82 | switch (idx) 83 | { 84 | case 0: /* id */ 85 | lua_pushinteger(L, id); 86 | break; 87 | 88 | case 1: /* code */ 89 | lua_pushstring(L, GeoIP_code_by_id(id)); 90 | break; 91 | 92 | case 2: /* code3 */ 93 | lua_pushstring(L, GeoIP_code3_by_id(id)); 94 | break; 95 | 96 | case 3: /* continent */ 97 | lua_pushstring(L, GeoIP_continent_by_id(id)); 98 | break; 99 | 100 | case 4: /* name */ 101 | lua_pushstring(L, GeoIP_name_by_id(id)); 102 | break; 103 | 104 | default: 105 | /* Hint: Did you synchronize switch cases with opts array? */ 106 | return luaL_error(L, "lua-geoip error: bad implementation"); 107 | } 108 | 109 | if (need_all) 110 | { 111 | lua_setfield(L, -2, opts[i]); 112 | } 113 | } 114 | 115 | return (need_all) ? 1 : nargs; 116 | } 117 | 118 | /* TODO: Remove copy-paste below! */ 119 | 120 | static int lcountry_query_by_name(lua_State * L) 121 | { 122 | GeoIP * pGeoIP = check_country_db(L, 1); 123 | const char * name = luaL_checkstring(L, 2); 124 | 125 | if (pGeoIP == NULL) 126 | { 127 | return lua_error(L); /* Error message already on stack */ 128 | } 129 | 130 | return push_country_info( 131 | L, 3, GeoIP_id_by_name(pGeoIP, name) 132 | ); 133 | } 134 | 135 | static int lcountry_query_by_addr(lua_State * L) 136 | { 137 | GeoIP * pGeoIP = check_country_db(L, 1); 138 | const char * addr = luaL_checkstring(L, 2); 139 | 140 | if (pGeoIP == NULL) 141 | { 142 | return lua_error(L); /* Error message already on stack */ 143 | } 144 | 145 | return push_country_info( 146 | L, 3, GeoIP_id_by_addr(pGeoIP, addr) 147 | ); 148 | } 149 | 150 | static int lcountry_query_by_addr6(lua_State * L) 151 | { 152 | GeoIP * pGeoIP = check_country_db(L, 1); 153 | const char * addr = luaL_checkstring(L, 2); 154 | 155 | if (pGeoIP == NULL) 156 | { 157 | return lua_error(L); /* Error message already on stack */ 158 | } 159 | 160 | return push_country_info( 161 | L, 3, GeoIP_id_by_addr_v6(pGeoIP, addr) 162 | ); 163 | } 164 | 165 | static int lcountry_query_by_ipnum(lua_State * L) 166 | { 167 | GeoIP * pGeoIP = check_country_db(L, 1); 168 | lua_Integer ipnum = luaL_checkinteger(L, 2); /* Hoping that value would fit */ 169 | 170 | if (pGeoIP == NULL) 171 | { 172 | return lua_error(L); /* Error message already on stack */ 173 | } 174 | 175 | return push_country_info( 176 | L, 3, GeoIP_id_by_ipnum(pGeoIP, ipnum) 177 | ); 178 | } 179 | 180 | static int lcountry_charset(lua_State * L) 181 | { 182 | GeoIP * pGeoIP = check_country_db(L, 1); 183 | if (pGeoIP == NULL) 184 | { 185 | return lua_error(L); /* Error message already on stack */ 186 | } 187 | 188 | lua_pushinteger(L, GeoIP_charset(pGeoIP)); 189 | 190 | return 1; 191 | } 192 | 193 | static int lcountry_set_charset(lua_State * L) 194 | { 195 | GeoIP * pGeoIP = check_country_db(L, 1); 196 | int charset = luaL_checkint(L, 2); 197 | 198 | if (pGeoIP == NULL) 199 | { 200 | return lua_error(L); /* Error message already on stack */ 201 | } 202 | 203 | GeoIP_set_charset(pGeoIP, charset); 204 | 205 | return 0; 206 | } 207 | 208 | static int lcountry_close(lua_State * L) 209 | { 210 | luageoip_DB * pDB = (luageoip_DB *)luaL_checkudata(L, 1, LUAGEOIP_COUNTRY_MT); 211 | 212 | if (pDB && pDB->pGeoIP != NULL) 213 | { 214 | GeoIP_delete(pDB->pGeoIP); 215 | pDB->pGeoIP = NULL; 216 | } 217 | 218 | return 0; 219 | } 220 | 221 | #define lcountry_gc lcountry_close 222 | 223 | static int lcountry_tostring(lua_State * L) 224 | { 225 | GeoIP * pGeoIP = check_country_db(L, 1); 226 | if (pGeoIP == NULL) 227 | { 228 | return lua_error(L); /* Error message already on stack */ 229 | } 230 | 231 | lua_pushstring(L, GeoIP_database_info(pGeoIP)); 232 | 233 | return 1; 234 | } 235 | 236 | static const luaL_Reg M[] = 237 | { 238 | { "query_by_name", lcountry_query_by_name }, 239 | { "query_by_addr", lcountry_query_by_addr }, 240 | { "query_by_ipnum", lcountry_query_by_ipnum }, 241 | { "query_by_addr6", lcountry_query_by_addr6 }, 242 | 243 | { "charset", lcountry_charset }, 244 | { "set_charset", lcountry_set_charset }, 245 | { "close", lcountry_close }, 246 | { "__gc", lcountry_gc }, 247 | { "__tostring", lcountry_tostring }, 248 | 249 | { NULL, NULL } 250 | }; 251 | 252 | static int lcountry_open(lua_State * L) 253 | { 254 | static const int allowed_types[] = 255 | { 256 | GEOIP_COUNTRY_EDITION, 257 | GEOIP_COUNTRY_EDITION_V6 258 | }; 259 | 260 | return luageoip_common_open_db( 261 | L, 262 | M, 263 | GEOIP_COUNTRY_EDITION, 264 | GEOIP_MEMORY_CACHE | GEOIP_SILENCE, 265 | LUAGEOIP_COUNTRY_MT, 266 | GEOIP_INDEX_CACHE, /* not allowed */ 267 | 2, 268 | allowed_types 269 | ); 270 | } 271 | 272 | /* Lua module API */ 273 | static const struct luaL_Reg R[] = 274 | { 275 | { "open", lcountry_open }, 276 | 277 | { NULL, NULL } 278 | }; 279 | 280 | #ifdef __cplusplus 281 | extern "C" { 282 | #endif 283 | 284 | LUALIB_API int luaopen_geoip_country(lua_State * L) 285 | { 286 | /* 287 | * Register module 288 | */ 289 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 290 | luaL_register(L, "geoip.country", R); 291 | #else 292 | lua_newtable(L); 293 | luaL_setfuncs(L, R, 0); 294 | #endif 295 | 296 | /* 297 | * Register module information 298 | */ 299 | lua_pushliteral(L, LUAGEOIP_COUNTRY_VERSION); 300 | lua_setfield(L, -2, "_VERSION"); 301 | 302 | lua_pushliteral(L, LUAGEOIP_COUNTRY_COPYRIGHT); 303 | lua_setfield(L, -2, "_COPYRIGHT"); 304 | 305 | lua_pushliteral(L, LUAGEOIP_COUNTRY_DESCRIPTION); 306 | lua_setfield(L, -2, "_DESCRIPTION"); 307 | 308 | return 1; 309 | } 310 | 311 | #ifdef __cplusplus 312 | } 313 | #endif 314 | -------------------------------------------------------------------------------- /src/database.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "lua-geoip.h" 5 | #include "database.h" 6 | 7 | int luageoip_common_open_db( 8 | lua_State * L, 9 | const luaL_Reg * M, 10 | int default_type, 11 | int default_flags, 12 | const char * mt_name, 13 | unsigned int bad_flags, 14 | size_t num_allowed_types, 15 | const int * allowed_types 16 | ) 17 | { 18 | /* First argument is checked later */ 19 | int flags = luaL_optint(L, 2, default_flags); 20 | int charset = luaL_optint(L, 3, GEOIP_CHARSET_UTF8); 21 | 22 | GeoIP * pGeoIP = NULL; 23 | luageoip_DB * pResult = NULL; 24 | 25 | int error_reported = 0; 26 | 27 | if (bad_flags && (flags & bad_flags) == bad_flags) 28 | { 29 | /* TODO: Or is it concrete DB file problem? */ 30 | return luaL_error( 31 | L, 32 | "%s error: can't open db with these flags", 33 | mt_name 34 | ); 35 | } 36 | 37 | if (lua_isnoneornil(L, 1)) 38 | { 39 | pGeoIP = GeoIP_open_type(default_type, flags); 40 | } 41 | else 42 | { 43 | const char * filename = luaL_checkstring(L, 1); 44 | pGeoIP = GeoIP_open(filename, flags); 45 | } 46 | 47 | if (pGeoIP) 48 | { 49 | int type = GeoIP_database_edition(pGeoIP); 50 | int found = 0; 51 | size_t i = 0; 52 | 53 | for (i = 0; i < num_allowed_types; ++i) 54 | { 55 | if (type == allowed_types[i]) 56 | { 57 | found = 1; 58 | break; 59 | } 60 | } 61 | 62 | if (!found) 63 | { 64 | lua_pushnil(L); 65 | lua_pushfstring( 66 | L, 67 | "%s error: unexpected db type in that file (%s)", 68 | mt_name, 69 | GeoIP_database_info(pGeoIP) 70 | ); 71 | error_reported = 1; 72 | 73 | GeoIP_delete(pGeoIP); 74 | pGeoIP = NULL; 75 | } 76 | } 77 | 78 | if (pGeoIP == NULL) 79 | { 80 | if (!error_reported) 81 | { 82 | lua_pushnil(L); 83 | lua_pushfstring( 84 | L, 85 | "%s error: failed to open database file", 86 | mt_name 87 | ); 88 | error_reported = 1; 89 | } 90 | 91 | return 2; /* nil and error message already on stack */ 92 | } 93 | 94 | GeoIP_set_charset(pGeoIP, charset); 95 | 96 | pResult = (luageoip_DB *)lua_newuserdata(L, sizeof(luageoip_DB)); 97 | pResult->pGeoIP = pGeoIP; 98 | 99 | if (luaL_newmetatable(L, mt_name)) 100 | { 101 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 102 | luaL_register(L, NULL, M); 103 | #else 104 | luaL_setfuncs(L, M, 0); 105 | #endif 106 | lua_pushvalue(L, -1); 107 | lua_setfield(L, -2, "__index"); 108 | } 109 | 110 | lua_setmetatable(L, -2); 111 | 112 | return 1; 113 | } 114 | -------------------------------------------------------------------------------- /src/database.h: -------------------------------------------------------------------------------- 1 | /* 2 | * database.h: Bindings for MaxMind's GeoIP library 3 | * See copyright information in file COPYRIGHT. 4 | */ 5 | 6 | #ifndef LUAGEOIP_DATABASE_H_ 7 | #define LUAGEOIP_DATABASE_H_ 8 | 9 | #define LUAGEOIP_COUNTRY_MT "lua-geoip.db.country" 10 | #define LUAGEOIP_CITY_MT "lua-geoip.db.city" 11 | 12 | int luageoip_common_open_db( 13 | lua_State * L, 14 | const luaL_Reg * M, 15 | int default_type, 16 | int default_flags, 17 | const char * mt_name, 18 | unsigned int bad_flags, 19 | size_t num_allowed_types, 20 | const int * allowed_types 21 | ); 22 | 23 | #endif /* LUAGEOIP_DATABASE_H_ */ 24 | -------------------------------------------------------------------------------- /src/lua-geoip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lua-geoip.c: Bindings for MaxMind's GeoIP library 3 | * See copyright information in file COPYRIGHT. 4 | */ 5 | 6 | #define LUAGEOIP_VERSION "lua-geoip 0.2" 7 | #define LUAGEOIP_COPYRIGHT "Copyright (C) 2011-2017, lua-geoip authors" 8 | #define LUAGEOIP_DESCRIPTION "Bindings for MaxMind's GeoIP library" 9 | 10 | #include "lua-geoip.h" 11 | 12 | typedef struct luageoip_Enum 13 | { 14 | const char * name; 15 | const int value; 16 | } luageoip_Enum; 17 | 18 | static const struct luageoip_Enum Options[] = 19 | { 20 | { "STANDARD", GEOIP_STANDARD }, 21 | { "MEMORY_CACHE", GEOIP_MEMORY_CACHE }, 22 | { "CHECK_CACHE", GEOIP_CHECK_CACHE }, 23 | { "INDEX_CACHE", GEOIP_INDEX_CACHE }, 24 | { "MMAP_CACHE", GEOIP_MMAP_CACHE }, 25 | { NULL, 0 } 26 | }; 27 | 28 | static const struct luageoip_Enum DBTypes[] = 29 | { 30 | { "COUNTRY", GEOIP_COUNTRY_EDITION }, /* Note that this is not an alias */ 31 | { "COUNTRY_V6",GEOIP_COUNTRY_EDITION_V6 }, 32 | 33 | { "REGION_REV0", GEOIP_REGION_EDITION_REV0 }, 34 | { "REGION_REV1", GEOIP_REGION_EDITION_REV1 }, 35 | { "REGION", GEOIP_REGION_EDITION_REV1 }, /* Alias */ 36 | 37 | { "CITY_REV0", GEOIP_CITY_EDITION_REV0 }, 38 | { "CITY_REV1", GEOIP_CITY_EDITION_REV1 }, 39 | { "CITY", GEOIP_CITY_EDITION_REV1 }, /* Alias */ 40 | 41 | { "ORG", GEOIP_ORG_EDITION }, 42 | { "ISP", GEOIP_ISP_EDITION }, 43 | { "PROXY", GEOIP_PROXY_EDITION }, 44 | { "ASNUM", GEOIP_ASNUM_EDITION }, 45 | { "NETSPEED", GEOIP_NETSPEED_EDITION }, 46 | { "DOMAIN", GEOIP_DOMAIN_EDITION }, 47 | 48 | { NULL, 0 } 49 | }; 50 | 51 | static const struct luageoip_Enum Charsets[] = 52 | { 53 | { "ISO_8859_1", GEOIP_CHARSET_ISO_8859_1 }, 54 | { "UTF8", GEOIP_CHARSET_UTF8 }, 55 | { NULL, 0 } 56 | }; 57 | 58 | static void reg_enum(lua_State * L, const luageoip_Enum * e) 59 | { 60 | for ( ; e->name; ++e) 61 | { 62 | lua_pushinteger(L, e->value); 63 | lua_setfield(L, -2, e->name); 64 | } 65 | } 66 | 67 | static int lcode_by_id(lua_State * L) 68 | { 69 | int id = luaL_checkint(L, 1); 70 | lua_pushstring(L, GeoIP_code_by_id(id)); 71 | return 1; 72 | } 73 | 74 | static int lcode3_by_id(lua_State * L) 75 | { 76 | int id = luaL_checkint(L, 1); 77 | lua_pushstring(L, GeoIP_code3_by_id(id)); 78 | return 1; 79 | } 80 | 81 | static int lname_by_id(lua_State * L) 82 | { 83 | int id = luaL_checkint(L, 1); 84 | lua_pushstring(L, GeoIP_name_by_id(id)); 85 | return 1; 86 | } 87 | 88 | static int lcontinent_by_id(lua_State * L) 89 | { 90 | int id = luaL_checkint(L, 1); 91 | lua_pushstring(L, GeoIP_continent_by_id(id)); 92 | return 1; 93 | } 94 | 95 | static int lid_by_code(lua_State * L) 96 | { 97 | const char * country = luaL_checkstring(L, 1); 98 | lua_pushinteger(L, GeoIP_id_by_code(country)); 99 | return 1; 100 | } 101 | 102 | static int lregion_name_by_code(lua_State * L) 103 | { 104 | const char * country_code = luaL_checkstring(L, 1); 105 | const char * region_code = luaL_checkstring(L, 2); 106 | lua_pushstring(L, GeoIP_region_name_by_code(country_code, region_code)); 107 | return 1; 108 | } 109 | 110 | static int ltime_zone_by_country_and_region(lua_State * L) 111 | { 112 | const char * country_code = luaL_checkstring(L, 1); 113 | const char * region_code = luaL_checkstring(L, 2); 114 | lua_pushstring( 115 | L, 116 | GeoIP_time_zone_by_country_and_region(country_code, region_code) 117 | ); 118 | return 1; 119 | } 120 | 121 | /* Lua module API */ 122 | static const struct luaL_Reg R[] = 123 | { 124 | { "code_by_id", lcode_by_id }, 125 | { "code3_by_id", lcode3_by_id }, 126 | { "name_by_id", lname_by_id }, 127 | { "continent_by_id", lcontinent_by_id }, 128 | { "id_by_code", lid_by_code }, 129 | { "region_name_by_code", lregion_name_by_code }, 130 | { "time_zone_by_country_and_region", ltime_zone_by_country_and_region }, 131 | 132 | { NULL, NULL } 133 | }; 134 | 135 | #ifdef __cplusplus 136 | extern "C" { 137 | #endif 138 | 139 | LUALIB_API int luaopen_geoip(lua_State * L) 140 | { 141 | /* 142 | * Register module 143 | */ 144 | 145 | #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 146 | luaL_register(L, "geoip", R); 147 | #else 148 | lua_newtable(L); 149 | luaL_setfuncs(L, R, 0); 150 | #endif 151 | 152 | /* 153 | * Register module information 154 | */ 155 | lua_pushliteral(L, LUAGEOIP_VERSION); 156 | lua_setfield(L, -2, "_VERSION"); 157 | 158 | lua_pushliteral(L, LUAGEOIP_COPYRIGHT); 159 | lua_setfield(L, -2, "_COPYRIGHT"); 160 | 161 | lua_pushliteral(L, LUAGEOIP_DESCRIPTION); 162 | lua_setfield(L, -2, "_DESCRIPTION"); 163 | 164 | /* 165 | * Register enums 166 | */ 167 | reg_enum(L, Options); 168 | reg_enum(L, DBTypes); 169 | reg_enum(L, Charsets); 170 | 171 | return 1; 172 | } 173 | 174 | #ifdef __cplusplus 175 | } 176 | #endif 177 | -------------------------------------------------------------------------------- /src/lua-geoip.h: -------------------------------------------------------------------------------- 1 | /* 2 | * lua-geoip.h: Bindings for MaxMind's GeoIP library 3 | * See copyright information in file COPYRIGHT. 4 | */ 5 | 6 | #ifndef LUAGEOIP_LUA_GEOIP_H_ 7 | #define LUAGEOIP_LUA_GEOIP_H_ 8 | 9 | #if defined (__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | #ifndef luaL_checkint 17 | #define luaL_checkint(L,n) luaL_checkinteger(L,n) 18 | #endif 19 | 20 | #ifndef luaL_optint 21 | #define luaL_optint(L,n,s) luaL_optinteger(L,n,s) 22 | #endif 23 | 24 | #if defined (__cplusplus) 25 | } 26 | #endif 27 | 28 | #include 29 | #include 30 | 31 | typedef struct luageoip_DB 32 | { 33 | GeoIP * pGeoIP; 34 | } luageoip_DB; 35 | 36 | #endif /* LUAGEOIP_LUA_GEOIP_H */ 37 | -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | -- TODO: Scrap these hacks and write a proper test suite. 2 | 3 | pcall(require, 'luarocks.require') 4 | 5 | local socket = require 'socket' 6 | 7 | local geoip = require 'geoip' 8 | local geoip_country = require 'geoip.country' 9 | local geoip_city = require 'geoip.city' 10 | 11 | local geoip_country_filename = select(1, ...) or "./GeoIP.dat" 12 | local geoip_city_filename = select(2, ...) or "./GeoLiteCity.dat" 13 | local geoip_country6_filename = select(3, ...) or "./GeoIPv6.dat" 14 | 15 | print("TESTING lua-geoip") 16 | print("") 17 | print("VERSION: ", assert(geoip._VERSION)) 18 | print("DESCRIPTION: ", assert(geoip._DESCRIPTION)) 19 | print("COPYRIGHT: ", assert(geoip._COPYRIGHT)) 20 | print("") 21 | print("VERSION: ", assert(geoip_country._VERSION)) 22 | print("DESCRIPTION: ", assert(geoip_country._DESCRIPTION)) 23 | print("COPYRIGHT: ", assert(geoip_country._COPYRIGHT)) 24 | print("") 25 | print("VERSION: ", assert(geoip_city._VERSION)) 26 | print("DESCRIPTION: ", assert(geoip_city._DESCRIPTION)) 27 | print("COPYRIGHT: ", assert(geoip_city._COPYRIGHT)) 28 | print("") 29 | 30 | -- Check that required files exist 31 | -- See README on info on how to get them 32 | assert(io.open(geoip_country_filename, "r")):close() 33 | assert(io.open(geoip_city_filename, "r")):close() 34 | assert(io.open(geoip_country6_filename, "r")):close() 35 | 36 | do 37 | local id = assert(geoip.id_by_code('RU')) 38 | 39 | assert(geoip.code_by_id(id) == 'RU') 40 | assert(geoip.code3_by_id(id) == 'RUS') 41 | assert(geoip.name_by_id(id) == 'Russian Federation') 42 | 43 | -- Depends on libgeoip version o_O 44 | assert(geoip.continent_by_id(id) == 'EU' or geoip.continent_by_id(id) == 'AS') 45 | 46 | assert(geoip.region_name_by_code('RU', '77') == "Tver'") -- WTF? MSK? 47 | assert(geoip.time_zone_by_country_and_region('RU', '77') == 'Europe/Moscow') 48 | end 49 | 50 | do 51 | assert(geoip_country.open("./BADFILENAME") == nil) 52 | 53 | assert(pcall(geoip_country.open, nil, geoip.INDEX_CACHE) == false) 54 | 55 | --assert(geoip_country.open(nil, 2 ^ 10) == nil) -- TODO: This should fail 56 | --assert(geoip_country.open(nil, nil, -1) == nil) -- TODO: This should fail 57 | 58 | assert(geoip_country.open(geoip_city_filename) == nil) 59 | end 60 | 61 | do 62 | assert(geoip_city.open("./BADFILENAME") == nil) 63 | 64 | --assert(geoip_city.open(nil, 2 ^ 10) == nil) -- TODO: This should fail 65 | --assert(geoip_city.open(nil, nil, -1) == nil) -- TODO: This should fail 66 | 67 | assert(geoip_city.open(geoip_country_filename) == nil) 68 | end 69 | 70 | do 71 | local flags = 72 | { 73 | geoip.STANDARD; 74 | geoip.MEMORY_CACHE; 75 | geoip.CHECK_CACHE; 76 | geoip.INDEX_CACHE; 77 | geoip.MMAP_CACHE; 78 | } 79 | 80 | for _, flag in ipairs(flags) do 81 | if flag ~= geoip.INDEX_CACHE then 82 | assert(geoip_country.open(nil, flag)):close() 83 | assert(geoip_country.open(geoip_country_filename, flag)):close() 84 | end 85 | assert(geoip_city.open(geoip_city_filename, flag)):close() 86 | end 87 | end 88 | 89 | do 90 | local geodb = assert( 91 | geoip_country.open(geoip_country_filename) 92 | ) 93 | geodb:close() 94 | geodb:close() 95 | end 96 | 97 | do 98 | local geodb = assert( 99 | geoip_city.open(geoip_city_filename) 100 | ) 101 | geodb:close() 102 | geodb:close() 103 | end 104 | 105 | do 106 | local check_country = function(db, method, arg) 107 | local id = assert(db[method](db, arg, "id")) 108 | assert(type(id) == "number") 109 | 110 | local expected = 111 | { 112 | id = id; 113 | code = assert(geoip.code_by_id(id)); 114 | code3 = assert(geoip.code3_by_id(id)); 115 | name = assert(geoip.name_by_id(id)); 116 | continent = assert(geoip.continent_by_id(id)); 117 | } 118 | 119 | local all = assert(db[method](db, arg)) 120 | 121 | local keys = { } 122 | for k, v in pairs(expected) do 123 | assert(all[k] == expected[k]) 124 | assert(db[method](db, arg, k) == expected[k]) 125 | keys[#keys + 1] = k 126 | end 127 | 128 | local r = { db[method](db, arg, unpack(keys)) } 129 | assert(#r == #keys) 130 | for i = 1, #keys do 131 | assert(r[i] == expected[keys[i]]) 132 | end 133 | end 134 | 135 | local apack = function(...) 136 | return select("#", ...), { ... } 137 | end 138 | 139 | local check_city = function(db, method, arg) 140 | local keys = 141 | { 142 | "country_code"; 143 | "country_code3"; 144 | "country_name"; 145 | "region"; 146 | "city"; 147 | "postal_code"; 148 | "latitude"; 149 | "longitude"; 150 | "metro_code"; 151 | "dma_code"; 152 | "area_code"; 153 | "charset"; 154 | "continent_code"; 155 | } 156 | 157 | local all = assert(db[method](db, arg)) 158 | 159 | local nret, r = apack(db[method](db, arg, unpack(keys))) 160 | assert(nret == #keys) 161 | 162 | for i = 1, #keys do 163 | assert(r[i] == all[keys[i]]) 164 | assert(r[i] == db[method](db, arg, keys[i])) 165 | end 166 | end 167 | 168 | local geodb_country = assert(geoip_country.open(geoip_country_filename)) 169 | local geodb_city = assert(geoip_city.open(geoip_city_filename)) 170 | 171 | local checkers = 172 | { 173 | [geodb_country] = check_country; 174 | [geodb_city] = check_city; 175 | } 176 | 177 | for _, geodb in ipairs { geodb_country, geodb_city } do 178 | local checker = checkers[geodb] 179 | 180 | checker(geodb, "query_by_name", "google-public-dns-a.google.com") 181 | checker(geodb, "query_by_addr", "8.8.8.8") 182 | checker(geodb, "query_by_ipnum", 134744072) -- 8.8.8.8 183 | end 184 | 185 | geodb_country:close() 186 | geodb_city:close() 187 | end 188 | 189 | -- Country IPv6 Edition 190 | do 191 | local geodb_country6 = assert(geoip_country.open(geoip_country6_filename, geoip.MEMORY_CACHE, geoip.COUNTRY_V6)) 192 | 193 | res = geodb_country6:query_by_addr6("2a01:e0c:1::1") 194 | assert(res.code == "FR") 195 | 196 | res = geodb_country6:query_by_addr6("2a03:2880:f127:83:face:b00c:0:25de") 197 | assert(res.code == "US") 198 | geodb_country6:close() 199 | end 200 | 201 | -- TODO: Test two different DBs open in parallel work properly 202 | 203 | local profiles = 204 | { 205 | { 206 | name = "country"; 207 | module = geoip_country; 208 | file = geoip_country_filename; 209 | field = "id"; 210 | }; 211 | { 212 | name = "city"; 213 | module = geoip_city; 214 | file = geoip_city_filename; 215 | field = "country_code"; 216 | }; 217 | } 218 | 219 | for i = 1, #profiles do 220 | local p = profiles[i] 221 | 222 | local geodb = assert(p.module.open(p.file)) 223 | 224 | do 225 | print(p.name, "profiling ipnum queries") 226 | 227 | local num_queries = 1e5 228 | 229 | local cases = { } 230 | for i = 1, num_queries do 231 | cases[i] = math.random(0x7FFFFFFF) 232 | end 233 | 234 | local time_start = socket.gettime() 235 | for i = 1, num_queries do 236 | if i % 1e4 == 0 then 237 | print("#", i, "of", num_queries) 238 | end 239 | local result, err = geodb:query_by_ipnum(cases[i], p.field) 240 | if not result and err ~= "not found" then 241 | error(err) 242 | end 243 | end 244 | 245 | print( 246 | p.name, 247 | num_queries / (socket.gettime() - time_start), 248 | "ipnum queries per second" 249 | ) 250 | print() 251 | end 252 | 253 | do 254 | print(p.name, "profiling addr queries") -- slow due to dns resolution 255 | 256 | local num_queries = 1e5 257 | 258 | local cases = { } 259 | for i = 1, num_queries do 260 | cases[i] = ('%d.%d.%d.%d'):format( 261 | math.random(255), 262 | math.random(255), 263 | math.random(255), 264 | math.random(255) 265 | ) 266 | end 267 | 268 | local time_start = socket.gettime() 269 | for i = 1, num_queries do 270 | if i % 1e4 == 0 then 271 | print("#", i, "of", num_queries) 272 | end 273 | local result, err = geodb:query_by_name(cases[i], p.field) 274 | if not result and err ~= "not found" then 275 | error(err) 276 | end 277 | end 278 | 279 | print( 280 | p.name, 281 | num_queries / (socket.gettime() - time_start), 282 | "addr queries per second" 283 | ) 284 | print() 285 | end 286 | 287 | do 288 | print(p.name, "profiling name queries") 289 | 290 | local num_queries = 500 -- slow due to dns resolution 291 | 292 | local time_start = socket.gettime() 293 | for i = 1, num_queries do 294 | if i % 50 == 0 then 295 | print("#", i, "of", num_queries) 296 | end 297 | assert(geodb:query_by_name("ya.ru", p.field)) 298 | end 299 | 300 | print( 301 | p.name, 302 | num_queries / (socket.gettime() - time_start), 303 | "name queries per second" 304 | ) 305 | print() 306 | end 307 | 308 | geodb:close() 309 | end 310 | 311 | print("") 312 | print("OK") 313 | --------------------------------------------------------------------------------