github.com/influx6/npkg@v0.8.8/nnet/location.go (about)

     1  package nnet
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"net/http"
     9  
    10  	"github.com/influx6/npkg/nerror"
    11  	"github.com/influx6/npkg/nunsafe"
    12  	"xojoc.pw/useragent"
    13  )
    14  
    15  type Agent struct {
    16  	*useragent.UserAgent
    17  	Browser string
    18  }
    19  
    20  func ParseAgent(agentString string) (*Agent, error) {
    21  	var agentInfo = useragent.Parse(agentString)
    22  	if agentInfo == nil {
    23  		return nil, nerror.New("failed to parse agent")
    24  	}
    25  	return &Agent{
    26  		Browser:   agentInfo.Name,
    27  		UserAgent: agentInfo,
    28  	}, nil
    29  }
    30  
    31  type Locations []Location
    32  
    33  func (l Locations) Find(ip string) (Location, error) {
    34  	for _, lt := range l {
    35  		if lt.IPIsInLocation(ip) {
    36  			return lt, nil
    37  		}
    38  	}
    39  	return Location{}, nerror.New("not found")
    40  }
    41  
    42  type Location struct {
    43  	Type          string  `json:"type_"`
    44  	Street        string  `json:"street"`
    45  	ContinentCode string  `json:"continent_code"`
    46  	ContinentName string  `json:"continent_name"`
    47  	City          string  `json:"city"`
    48  	State         string  `json:"state"`
    49  	Postal        string  `json:"postal"`
    50  	Zip           string  `json:"zip"`
    51  	Zipcode       string  `json:"zip_code"`
    52  	CountryCode   string  `json:"country_code"`
    53  	CountryName   string  `json:"country_name"`
    54  	RegionCode    string  `json:"region_code"`
    55  	RegionName    string  `json:"region_name"`
    56  	Timezone      string  `json:"time_zone"`
    57  	Latitude      float64 `json:"latitude"`
    58  	Longitude     float64 `json:"longitude"`
    59  	MetroCode     string  `json:"metro_code"`
    60  	AreaCode      string  `json:"area_code"`
    61  	FromIP        string  `json:"from_ip"`
    62  	ToIP          string  `json:"to_ip"`
    63  	FromIPNumeric string  `json:"from_ip_numeric"`
    64  	ToIPNumeric   string  `json:"to_ip_numeric"`
    65  }
    66  
    67  func (l Location) IPIsInLocation(ip string) bool {
    68  	var fromIP = net.ParseIP(l.FromIP)
    69  	if fromIP == nil {
    70  		return false
    71  	}
    72  	var toIP = net.ParseIP(l.ToIP)
    73  	if toIP == nil {
    74  		return false
    75  	}
    76  	var targetIP = net.ParseIP(ip)
    77  	if toIP == nil {
    78  		return false
    79  	}
    80  	return IsTargetBetween(targetIP, fromIP, toIP)
    81  }
    82  
    83  type LocationService interface {
    84  	Get(ip string) (Location, error)
    85  }
    86  
    87  // DudLocationService returns a default unknown location with
    88  // provided address as ip.
    89  type DudLocationService struct{}
    90  
    91  func (f DudLocationService) Get(address string) (Location, error) {
    92  	var lt Location
    93  	lt.City = "Unknown"
    94  	lt.State = "Unknown"
    95  	lt.CountryName = "Unknown"
    96  	lt.RegionCode = "Unknown"
    97  	lt.RegionName = "Unknown"
    98  	lt.CountryCode = "Unknown"
    99  	lt.Zipcode = "00000"
   100  	return lt, nil
   101  }
   102  
   103  type CampIPService struct {
   104  	Addr string
   105  }
   106  
   107  func (f CampIPService) Get(address string) (Location, error) {
   108  	var lt Location
   109  
   110  	// Use campip service to get a JSON response
   111  	var response, err = http.Get(fmt.Sprintf("%s/%s", f.Addr, address))
   112  	if err != nil {
   113  		return lt, nerror.WrapOnly(err)
   114  	}
   115  	defer response.Body.Close()
   116  
   117  	// response.Body() is a reader type. We have
   118  	// to use ioutil.ReadAll() to read the data
   119  	// in to a byte slice(string)
   120  	var body, berr = ioutil.ReadAll(response.Body)
   121  	if berr != nil {
   122  		return lt, nerror.WrapOnly(berr)
   123  	}
   124  
   125  	// Unmarshal the JSON byte slice to a GeoIP struct
   126  	err = json.Unmarshal(body, &lt)
   127  	if err != nil {
   128  		return lt, nerror.WrapOnly(err).Add("body", nunsafe.Bytes2String(body))
   129  	}
   130  
   131  	return lt, nil
   132  }
   133  
   134  type IPStackService struct {
   135  	Token string
   136  }
   137  
   138  func (f IPStackService) Get(address string) (Location, error) {
   139  	var lt Location
   140  
   141  	// Use freegeoip.net to get a JSON response
   142  	// There is also /xml/ and /csv/ formats available
   143  	var response, err = http.Get(fmt.Sprintf("http://api.ipstack.com/%s?access_key=%s", address, f.Token))
   144  	if err != nil {
   145  		return lt, nerror.WrapOnly(err)
   146  	}
   147  	defer response.Body.Close()
   148  
   149  	// response.Body() is a reader type. We have
   150  	// to use ioutil.ReadAll() to read the data
   151  	// in to a byte slice(string)
   152  	var body, berr = ioutil.ReadAll(response.Body)
   153  	if berr != nil {
   154  		return lt, nerror.WrapOnly(berr)
   155  	}
   156  
   157  	// Unmarshal the JSON byte slice to a GeoIP struct
   158  	err = json.Unmarshal(body, &lt)
   159  	if err != nil {
   160  		return lt, nerror.WrapOnly(err).Add("body", nunsafe.Bytes2String(body))
   161  	}
   162  
   163  	return lt, nil
   164  }