Implement geoip related functionality in gooni
This commit is contained in:
		
							parent
							
								
									75d639d771
								
							
						
					
					
						commit
						6ba779b156
					
				
							
								
								
									
										19
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| @ -103,6 +103,18 @@ | |||||||
|   packages = ["."] |   packages = ["."] | ||||||
|   revision = "b8bc1bf767474819792c23f32d8286a45736f1c6" |   revision = "b8bc1bf767474819792c23f32d8286a45736f1c6" | ||||||
| 
 | 
 | ||||||
|  | [[projects]] | ||||||
|  |   name = "github.com/oschwald/geoip2-golang" | ||||||
|  |   packages = ["."] | ||||||
|  |   revision = "7118115686e16b77967cdbf55d1b944fe14ad312" | ||||||
|  |   version = "v1.2.1" | ||||||
|  | 
 | ||||||
|  | [[projects]] | ||||||
|  |   name = "github.com/oschwald/maxminddb-golang" | ||||||
|  |   packages = ["."] | ||||||
|  |   revision = "c5bec84d1963260297932a1b7a1753c8420717a7" | ||||||
|  |   version = "v1.3.0" | ||||||
|  | 
 | ||||||
| [[projects]] | [[projects]] | ||||||
|   name = "github.com/pkg/errors" |   name = "github.com/pkg/errors" | ||||||
|   packages = ["."] |   packages = ["."] | ||||||
| @ -164,7 +176,10 @@ | |||||||
| [[projects]] | [[projects]] | ||||||
|   branch = "master" |   branch = "master" | ||||||
|   name = "golang.org/x/sys" |   name = "golang.org/x/sys" | ||||||
|   packages = ["unix"] |   packages = [ | ||||||
|  |     "unix", | ||||||
|  |     "windows" | ||||||
|  |   ] | ||||||
|   revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" |   revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" | ||||||
| 
 | 
 | ||||||
| [[projects]] | [[projects]] | ||||||
| @ -186,6 +201,6 @@ | |||||||
| [solve-meta] | [solve-meta] | ||||||
|   analyzer-name = "dep" |   analyzer-name = "dep" | ||||||
|   analyzer-version = 1 |   analyzer-version = 1 | ||||||
|   inputs-digest = "cfd4567b0cd553bc4594b7f8bebcb91be475dfd112fd8740f65b5ba4a8253181" |   inputs-digest = "46860a32f649dbb2e01b285c0d5581078ba4b28a21e21175d47ccb2725a9c9fb" | ||||||
|   solver-name = "gps-cdcl" |   solver-name = "gps-cdcl" | ||||||
|   solver-version = 1 |   solver-version = 1 | ||||||
|  | |||||||
| @ -61,3 +61,7 @@ required = ["github.com/shuLhan/go-bindata/go-bindata"] | |||||||
| [[constraint]] | [[constraint]] | ||||||
|   name = "github.com/shuLhan/go-bindata" |   name = "github.com/shuLhan/go-bindata" | ||||||
|   version = "3.3.0" |   version = "3.3.0" | ||||||
|  | 
 | ||||||
|  | [[constraint]] | ||||||
|  |   name = "github.com/oschwald/geoip2-golang" | ||||||
|  |   version = "1.2.1" | ||||||
|  | |||||||
							
								
								
									
										212
									
								
								utils/geoip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								utils/geoip.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,212 @@ | |||||||
|  | package utils | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"archive/tar" | ||||||
|  | 	"compress/gzip" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"math/rand" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oschwald/geoip2-golang" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // LocationInfo contains location information | ||||||
|  | type LocationInfo struct { | ||||||
|  | 	IP          string | ||||||
|  | 	ASN         uint | ||||||
|  | 	NetworkName string | ||||||
|  | 	CountryCode string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // XXX consider integration with: https://updates.maxmind.com/app/update_getfilename?product_id=GeoLite2-ASN | ||||||
|  | var geoipFiles = map[string]string{ | ||||||
|  | 	"GeoLite2-ASN.mmdb":     "http://geolite.maxmind.com/download/geoip/database/GeoLite2-ASN.tar.gz", | ||||||
|  | 	"GeoLite2-Country.mmdb": "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DownloadGeoIPDatabaseFiles into the target directory | ||||||
|  | func DownloadGeoIPDatabaseFiles(dir string) error { | ||||||
|  | 	for filename, url := range geoipFiles { | ||||||
|  | 		dstPath := filepath.Join(dir, filename) | ||||||
|  | 
 | ||||||
|  | 		// Download the file to a temporary location | ||||||
|  | 		out, err := ioutil.TempFile(os.TempDir(), "maxmind") | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "failed to create temporary directory") | ||||||
|  | 		} | ||||||
|  | 		resp, err := http.Get(url) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "failed to fetch URL") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		_, err = io.Copy(out, resp.Body) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "failed to copy response body") | ||||||
|  | 		} | ||||||
|  | 		out.Close() | ||||||
|  | 		resp.Body.Close() | ||||||
|  | 
 | ||||||
|  | 		// Extract the tar.gz file | ||||||
|  | 
 | ||||||
|  | 		f, err := os.Open(out.Name()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "failed to read file") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		gzf, err := gzip.NewReader(f) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return errors.Wrap(err, "failed to create gzip reader") | ||||||
|  | 		} | ||||||
|  | 		tarReader := tar.NewReader(gzf) | ||||||
|  | 
 | ||||||
|  | 		// Look inside of the tar for the file we need | ||||||
|  | 		for { | ||||||
|  | 			header, err := tarReader.Next() | ||||||
|  | 			if err == io.EOF { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			if err != nil { | ||||||
|  | 				return errors.Wrap(err, "error extracting tar.gz") | ||||||
|  | 			} | ||||||
|  | 			name := header.Name | ||||||
|  | 			if filepath.Base(name) == filename { | ||||||
|  | 				outFile, err := os.Create(dstPath) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return errors.Wrap(err, "error creating file") | ||||||
|  | 				} | ||||||
|  | 				if _, err := io.Copy(outFile, tarReader); err != nil { | ||||||
|  | 					return errors.Wrap(err, "error reading file from tar") | ||||||
|  | 				} | ||||||
|  | 				outFile.Close() | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		f.Close() | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // LookupLocation resolves an IP to a location according to the Maxmind DB | ||||||
|  | func LookupLocation(dbPath string, ipStr string) (LocationInfo, error) { | ||||||
|  | 	loc := LocationInfo{} | ||||||
|  | 	fmt.Printf("Opening %s", filepath.Join(dbPath, "GeoLite2-ASN.mmdb")) | ||||||
|  | 
 | ||||||
|  | 	asnDB, err := geoip2.Open(filepath.Join(dbPath, "GeoLite2-ASN.mmdb")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return loc, errors.Wrap(err, "failed to open ASN db") | ||||||
|  | 	} | ||||||
|  | 	defer asnDB.Close() | ||||||
|  | 
 | ||||||
|  | 	countryDB, err := geoip2.Open(filepath.Join(dbPath, "GeoLite2-Country.mmdb")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return loc, errors.Wrap(err, "failed to open country db") | ||||||
|  | 	} | ||||||
|  | 	defer countryDB.Close() | ||||||
|  | 
 | ||||||
|  | 	ip := net.ParseIP(ipStr) | ||||||
|  | 
 | ||||||
|  | 	asn, err := asnDB.ASN(ip) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return loc, err | ||||||
|  | 	} | ||||||
|  | 	country, err := countryDB.Country(ip) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return loc, err | ||||||
|  | 	} | ||||||
|  | 	loc.ASN = asn.AutonomousSystemNumber | ||||||
|  | 	loc.NetworkName = asn.AutonomousSystemOrganization | ||||||
|  | 	loc.CountryCode = country.Country.IsoCode | ||||||
|  | 
 | ||||||
|  | 	return loc, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type avastResponse struct { | ||||||
|  | 	IP string `json:"ip"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func avastLookup() (string, error) { | ||||||
|  | 	var parsed = new(avastResponse) | ||||||
|  | 
 | ||||||
|  | 	resp, err := http.Get("https://ip-info.ff.avast.com/v1/info") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to perform request") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to read response body") | ||||||
|  | 	} | ||||||
|  | 	err = json.Unmarshal([]byte(body), &parsed) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to parse json") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return parsed.IP, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func akamaiLookup() (string, error) { | ||||||
|  | 	// This is a domain fronted request to akamai | ||||||
|  | 	client := &http.Client{} | ||||||
|  | 	req, err := http.NewRequest("GET", "https://a248.e.akamai.net/", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	req.Host = "whatismyip.akamai.com" | ||||||
|  | 	resp, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to perform request") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to read response body") | ||||||
|  | 	} | ||||||
|  | 	return string(body), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type lookupFunc func() (string, error) | ||||||
|  | 
 | ||||||
|  | var lookupServices = []lookupFunc{ | ||||||
|  | 	avastLookup, | ||||||
|  | 	akamaiLookup, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IPLookup gets the users IP address from a IP lookup service | ||||||
|  | func IPLookup() (string, error) { | ||||||
|  | 	rand.Seed(time.Now().Unix()) | ||||||
|  | 
 | ||||||
|  | 	retries := 3 | ||||||
|  | 	for retries > 0 { | ||||||
|  | 		lookup := lookupServices[rand.Intn(len(lookupServices))] | ||||||
|  | 		ipStr, err := lookup() | ||||||
|  | 		if err == nil { | ||||||
|  | 			return ipStr, nil | ||||||
|  | 		} | ||||||
|  | 		retries-- | ||||||
|  | 	} | ||||||
|  | 	return "", errors.New("exceeded maximum retries") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GeoIPLookup does a geoip lookup and returns location information | ||||||
|  | func GeoIPLookup(dbPath string) (*LocationInfo, error) { | ||||||
|  | 	ipStr, err := IPLookup() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	location, err := LookupLocation(dbPath, ipStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &location, nil | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user