|
@@ -0,0 +1,242 @@
|
|
|
+package grs
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "gopkg.in/jmcvetta/napping.v3"
|
|
|
+ "net"
|
|
|
+ "net/url"
|
|
|
+ "regexp"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ GRS_URL = "http://rest.db.ripe.net/search.json"
|
|
|
+)
|
|
|
+
|
|
|
+type GRS struct {
|
|
|
+
|
|
|
+ Source string `bson:"source" json:"source"`
|
|
|
+ IpFrom string `bson:"ip_from" json:"ip_from"`
|
|
|
+ IpTo string `bson:"ip_to" json:"ip_to"`
|
|
|
+ IpFromRaw []byte `bson:"ip_from_raw" json:"ip_from_raw"`
|
|
|
+ IpToRaw []byte `bson:"ip_to_raw" json:"ip_to_raw"`
|
|
|
+ Description string `bson:"description" json:"description"`
|
|
|
+ Country string `bson:"country" json:"country"`
|
|
|
+ Status string `bson:"status" json:"status"`
|
|
|
+ Cidr string `bson:"cidr" json:"cidr"`
|
|
|
+ Name string `bson:"name" json:"name"`
|
|
|
+ Org string `bson:"org" json:"org"`
|
|
|
+}
|
|
|
+
|
|
|
+type GRSRaw struct {
|
|
|
+ Service Name `json:"service"`
|
|
|
+ Parameters Parameters `json:"parameters"`
|
|
|
+ Objects Objects `json:"objects"`
|
|
|
+ Terms Terms `json:"terms-and-conditions"`
|
|
|
+ ErrorMessages ErrorMessages `json:"errormessages"`
|
|
|
+}
|
|
|
+
|
|
|
+type Name struct {
|
|
|
+ Name string `json:"name"`
|
|
|
+}
|
|
|
+
|
|
|
+type Parameters struct {
|
|
|
+ InverseLookup map[string]interface{} `json:"inverse-lookup"`
|
|
|
+ TypeFilters map[string]interface{} `json:"type-filters"`
|
|
|
+ Flags map[string]interface{} `json:"flags"`
|
|
|
+ QueryStrings QueryString `json:"query-strings"`
|
|
|
+ Sources Sources `json:"sources"`
|
|
|
+}
|
|
|
+
|
|
|
+type QueryString struct {
|
|
|
+ QueryString []Value `json:"query-string"`
|
|
|
+}
|
|
|
+
|
|
|
+type Value struct {
|
|
|
+ Value string `json:"value"`
|
|
|
+}
|
|
|
+
|
|
|
+type Sources struct {
|
|
|
+ Ids []Id `json:"source"`
|
|
|
+}
|
|
|
+
|
|
|
+type Id struct {
|
|
|
+ ID string `json:"id"`
|
|
|
+}
|
|
|
+
|
|
|
+type Objects struct {
|
|
|
+ Objects []Object `json:"object"`
|
|
|
+}
|
|
|
+
|
|
|
+type Object struct {
|
|
|
+ Type string `json:"type"`
|
|
|
+ Link Link `json:"link"`
|
|
|
+ Source Id `json:source"`
|
|
|
+ PrimaryKey Attributes `json:"primary-key"`
|
|
|
+ Attributes Attributes `json:"attributes"`
|
|
|
+ Tags Tags `json:"tags"`
|
|
|
+}
|
|
|
+
|
|
|
+type Link struct {
|
|
|
+ Type string `json:"type"`
|
|
|
+ Href string `json:"href"`
|
|
|
+}
|
|
|
+
|
|
|
+type Attributes struct {
|
|
|
+ Attributes []Attribute `json:"attribute"`
|
|
|
+}
|
|
|
+
|
|
|
+type Attribute struct {
|
|
|
+ Link Link `json:"link"`
|
|
|
+ Name string `json:"name"`
|
|
|
+ Value string `json:"value"`
|
|
|
+ ReferencedType string `json:"referenced-type"`
|
|
|
+}
|
|
|
+
|
|
|
+type Tags struct {
|
|
|
+ Tags []Tag `json:"tag"`
|
|
|
+}
|
|
|
+
|
|
|
+type Tag struct {
|
|
|
+ ID string `json:"id"`
|
|
|
+ Data string `json:"data"`
|
|
|
+}
|
|
|
+
|
|
|
+type Terms struct {
|
|
|
+ Type string `json:"type"`
|
|
|
+ Href string `json:"href"`
|
|
|
+}
|
|
|
+
|
|
|
+type ErrorMessages struct {
|
|
|
+ ErrorMessage []ErrorMessage `json:"errormessage"`
|
|
|
+}
|
|
|
+
|
|
|
+type ErrorMessage struct {
|
|
|
+ Severity string `json:"severity"`
|
|
|
+ Text string `json:"text"`
|
|
|
+}
|
|
|
+
|
|
|
+func Lookup(ip string) (GRS, error) {
|
|
|
+
|
|
|
+ var data GRSRaw
|
|
|
+
|
|
|
+ args := url.Values{}
|
|
|
+ args.Set("query-string", ip)
|
|
|
+ args.Add("source", "ripe-grs")
|
|
|
+ args.Add("source", "arin-grs")
|
|
|
+ args.Add("source", "apnic-grs")
|
|
|
+ args.Add("source", "lacnic-grs")
|
|
|
+ args.Add("source", "afrinic-grs")
|
|
|
+
|
|
|
+ resp, err := napping.Get(GRS_URL, &args, &data, nil)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return GRS{}, errors.New(fmt.Sprint("Failed to load data from ", resp.Url, ": ", err))
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.Status() != 200 {
|
|
|
+ return GRS{}, errors.New(fmt.Sprint("Failed to load data from GRS server: ", resp.Status()))
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(data.ErrorMessages.ErrorMessage) > 0 {
|
|
|
+ return GRS{}, errors.New(fmt.Sprint("No GRS data for ", ip, ": ", data.ErrorMessages.ErrorMessage[0].Text))
|
|
|
+ }
|
|
|
+
|
|
|
+ grs := GRS{}
|
|
|
+ var grs_inetnum *Object
|
|
|
+ var grs_route *Object
|
|
|
+ var inetnum string
|
|
|
+
|
|
|
+ for i, e := range data.Objects.Objects {
|
|
|
+ switch e.Type {
|
|
|
+ case "inetnum":
|
|
|
+ grs_inetnum = &data.Objects.Objects[i]
|
|
|
+ case "inet6num":
|
|
|
+ grs_inetnum = &data.Objects.Objects[i]
|
|
|
+ case "route":
|
|
|
+ grs_route = &data.Objects.Objects[i]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if grs_inetnum != nil {
|
|
|
+ for _, e := range grs_inetnum.Attributes.Attributes {
|
|
|
+ switch e.Name {
|
|
|
+ case "inetnum":
|
|
|
+ inetnum = e.Value
|
|
|
+ case "inet6num":
|
|
|
+ inetnum = e.Value
|
|
|
+ case "netname":
|
|
|
+ grs.Name = e.Value
|
|
|
+ case "descr":
|
|
|
+ grs.Description = e.Value
|
|
|
+ case "country":
|
|
|
+ grs.Country = e.Value
|
|
|
+ case "status":
|
|
|
+ grs.Status = e.Value
|
|
|
+ case "org":
|
|
|
+ grs.Org = e.Value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if grs_route != nil {
|
|
|
+ for _, e := range grs_route.Attributes.Attributes {
|
|
|
+ switch e.Name {
|
|
|
+ case "route":
|
|
|
+ grs.Cidr = e.Value
|
|
|
+ case "descr":
|
|
|
+ grs.Description = e.Value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var ip_from net.IP
|
|
|
+ var ip_to net.IP
|
|
|
+
|
|
|
+ if inetnum != "" {
|
|
|
+
|
|
|
+ _, network, err := net.ParseCIDR(inetnum)
|
|
|
+
|
|
|
+ if err == nil {
|
|
|
+ ip_from, ip_to = networkRange(network)
|
|
|
+ grs.IpFrom = ip_from.String()
|
|
|
+ grs.IpTo = ip_to.String()
|
|
|
+ if grs.Cidr == "" {
|
|
|
+ grs.Cidr = network.String()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ reg := regexp.MustCompile(`^\s*(\d+\.\d+\.\d+\.\d+)\s*-\s*(\d+\.\d+\.\d+\.\d+)\s*$`)
|
|
|
+ matches := reg.FindAllStringSubmatch(inetnum, -1)
|
|
|
+
|
|
|
+ if matches != nil && matches[0] != nil && matches[0][1] != "" && matches[0][2] != "" {
|
|
|
+ grs.IpFrom = matches[0][1]
|
|
|
+ grs.IpTo = matches[0][2]
|
|
|
+
|
|
|
+ ip_from = net.ParseIP(grs.IpFrom)
|
|
|
+ ip_to = net.ParseIP(grs.IpTo)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ grs.IpFromRaw = ip_from.To16()
|
|
|
+ grs.IpToRaw = ip_to.To16()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return grs, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func networkRange(network *net.IPNet) (net.IP, net.IP) {
|
|
|
+ netIP := network.IP.To16()
|
|
|
+ firstIP := netIP.Mask(network.Mask)
|
|
|
+ lastIP := net.ParseIP("::").To16()
|
|
|
+ for i := 0; i < len(lastIP); i++ {
|
|
|
+ lastIP[i] = netIP[i] | ^network.Mask[i]
|
|
|
+ }
|
|
|
+ return firstIP, lastIP
|
|
|
+}
|