├── .gitignore ├── LICENSE ├── README.md ├── demo.gif └── geodig.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | .notes 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geodig 2 | Command line tool for looking up Geolocation info for an ip address. 3 | 4 | ## Database 5 | This product includes GeoLite data created by MaxMind, available from [http://www.maxmind.com/](http://www.maxmind.com). 6 | 7 | ## Demo 8 | 9 | ![](demo.gif) 10 | 11 | ## Build 12 | ``` 13 | $ go build -o geodig geodig.go 14 | ``` 15 | 16 | ## Install using Homebrew 17 | 18 | ``` 19 | $ brew tap dutchcoders/homebrew-geodig 20 | $ brew install geodig 21 | ``` 22 | 23 | ## Examples 24 | 25 | Get location for ip address 26 | ``` 27 | $geodig 192.30.252.131 28 | United States (San Francisco)% 29 | ``` 30 | 31 | Ip addresses can be piped, for use with log files 32 | ``` 33 | $echo 192.30.252.131|geodig 34 | United States (San Francisco)% 35 | ``` 36 | 37 | Analyzing log files 38 | ``` 39 | $curl http://#####.###/logs/access.log | awk '{print $1}' | sort | uniq | go run geodig.go --format "(country)\n"| sort | uniq 40 | Afghanistan 41 | Australia 42 | Belarus 43 | Bulgaria 44 | Canada 45 | China 46 | Finland 47 | France 48 | Germany 49 | India 50 | Indonesia 51 | Ireland 52 | Israel 53 | Netherlands 54 | Poland 55 | Romania 56 | Russia 57 | Rwanda 58 | Spain 59 | Thailand 60 | Ukraine 61 | United Kingdom 62 | United States 63 | ``` 64 | 65 | Creating a shell alias 66 | ``` 67 | $alias geodig='go run geodig.go --format "(country)\n"' 68 | ``` 69 | 70 | ## Contributions 71 | 72 | Contributions are welcome. 73 | 74 | ## Creators 75 | 76 | **Remco Verhoef** 77 | - 78 | - 79 | 80 | ## Copyright and license 81 | 82 | Code and documentation copyright 2011-2014 Remco Verhoef. 83 | 84 | Code released under [the MIT license](LICENSE). 85 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dutchcoders/geodig/606ce456b3ea58dc8e116178d5475a9042e136b5/demo.gif -------------------------------------------------------------------------------- /geodig.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "compress/gzip" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "net/http" 11 | "os" 12 | "os/user" 13 | "path" 14 | "strings" 15 | 16 | "github.com/oschwald/maxminddb-golang" 17 | ) 18 | 19 | var reader *maxminddb.Reader 20 | var verbose bool 21 | var format string 22 | 23 | type geoIPResult struct { 24 | Location struct { 25 | Longitude float64 `maxminddb:"longitude"` 26 | Latitude float64 `maxminddb:"latitude"` 27 | } `maxminddb:"location"` 28 | City struct { 29 | Names map[string]string `maxminddb:"names"` 30 | } `maxminddb:"city"` 31 | Country struct { 32 | IsoCode string `maxminddb:"iso_code"` 33 | Names map[string]string `maxminddb:"names"` 34 | } `maxminddb:"country"` 35 | } 36 | 37 | // create ~/.geodig folder, and download geolite db 38 | func init() { 39 | var err error 40 | var usr *user.User 41 | if usr, err = user.Current(); err != nil { 42 | panic(err) 43 | } 44 | 45 | geodig_path := path.Join(usr.HomeDir, ".geodig") 46 | 47 | _, err = os.Stat(geodig_path) 48 | 49 | switch { 50 | case err == nil: 51 | break 52 | case os.IsNotExist(err): 53 | if err = os.Mkdir(geodig_path, 0755); err != nil { 54 | panic(err) 55 | } 56 | default: 57 | panic(err) 58 | 59 | } 60 | 61 | db_path := path.Join(geodig_path, "GeoLite2-City.mmdb") 62 | 63 | _, err = os.Stat(db_path) 64 | if os.IsNotExist(err) { 65 | if verbose { 66 | fmt.Println("Downloading database") 67 | } 68 | 69 | if err = download(geoLiteURL, db_path); err != nil { 70 | fmt.Println(err) 71 | os.Exit(1) 72 | } 73 | } 74 | 75 | if verbose { 76 | fmt.Printf("Using database %s.\n", db_path) 77 | } 78 | 79 | reader, err = maxminddb.Open(db_path) 80 | } 81 | 82 | func help() { 83 | fmt.Println("No ip addresses") 84 | } 85 | 86 | func download(url string, dest string) error { 87 | client := &http.Client{} 88 | 89 | req, err := http.NewRequest("GET", geoLiteURL, nil) 90 | if err != nil { 91 | return err 92 | } 93 | 94 | var resp *http.Response 95 | if resp, err = client.Do(req); err != nil { 96 | return err 97 | } 98 | 99 | defer resp.Body.Close() 100 | 101 | gzf, err := gzip.NewReader(resp.Body) 102 | if err != nil { 103 | return err 104 | } 105 | defer gzf.Close() 106 | 107 | f, err := os.Create(dest) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | defer f.Close() 113 | 114 | _, err = io.Copy(f, gzf) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | return nil 120 | } 121 | 122 | const geoLiteURL = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz" 123 | 124 | func main() { 125 | flag.StringVar(&format, "format", "(country) ((city))", "format") 126 | flag.BoolVar(&verbose, "verbose", false, "verbose") 127 | flag.Parse() 128 | 129 | format = strings.Replace(format, "\\n", "\n", -1) 130 | 131 | fi, err := os.Stdin.Stat() 132 | if err != nil { 133 | panic(err) 134 | } 135 | 136 | var args = flag.Args() 137 | 138 | if fi.Mode()&os.ModeNamedPipe > 0 { 139 | scanner := bufio.NewScanner(os.Stdin) 140 | 141 | for scanner.Scan() { 142 | args = append(args, scanner.Text()) 143 | } 144 | 145 | if err := scanner.Err(); err != nil { 146 | panic(err) 147 | } 148 | } 149 | 150 | if len(args) == 0 { 151 | help() 152 | os.Exit(1) 153 | } 154 | 155 | for _, arg := range args { 156 | arg = strings.Trim(arg, "\n\t ") 157 | var addr net.IP 158 | addr = net.ParseIP(arg) 159 | if addr == nil { 160 | if ips, err := net.LookupIP(arg); err != nil { 161 | fmt.Fprintf(os.Stderr, "%s is not a valid ip address or host %s.\n", arg, err) 162 | continue 163 | } else { 164 | addr = ips[0] 165 | } 166 | } 167 | 168 | var result geoIPResult 169 | 170 | var err error 171 | if err = reader.Lookup(addr, &result); err != nil { 172 | panic(err) 173 | } 174 | 175 | var p string 176 | p = format 177 | p = strings.Replace(p, "(ip)", addr.String(), -1) 178 | p = strings.Replace(p, "(country)", result.Country.Names["en"], -1) 179 | p = strings.Replace(p, "(city)", result.City.Names["en"], -1) 180 | p = strings.Replace(p, "(lat)", fmt.Sprintf("%f", result.Location.Latitude), -1) 181 | p = strings.Replace(p, "(long)", fmt.Sprintf("%f", result.Location.Longitude), -1) 182 | 183 | fmt.Print(p) 184 | } 185 | } 186 | --------------------------------------------------------------------------------