Ver Fonte

- added ASN loading

Tobias Begalke há 7 anos atrás
pai
commit
3236d41631
1 ficheiros alterados com 309 adições e 29 exclusões
  1. 309 29
      grs.go

+ 309 - 29
grs.go

@@ -3,35 +3,44 @@ package grs
 import (
 	"errors"
 	"fmt"
+	"log"
 	"net"
 	"net/url"
 	"regexp"
+	"strings"
 	"time"
 
-	"gopkg.in/jmcvetta/napping.v3"
-	"gopkg.in/mgo.v2/bson"
+	napping "gopkg.in/jmcvetta/napping.v3"
 )
 
 const (
-	GRS_URL = "http://rest.db.ripe.net/search.json"
+	// GrsURL contains the RIPE REST API URL
+	GrsURL = "http://rest.db.ripe.net/search.json"
 )
 
+// GRS contains whois data from the ripe API
 type GRS struct {
-	ID          bson.ObjectId `bson:"_id,omitempty" json:"id"`
-	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"`
-	CreatedAt   time.Time     `bson:"created_at" json:"created_at"`
+	//ID           bson.ObjectId `bson:"_id,omitempty" json:"id"`
+	ID           string       `bson:"_id" json:"_id"`
+	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"`
+	NetMaskBits  int          `bson:"netmask_bits" json:"netmask_bits"`
+	Name         string       `bson:"name" json:"name"`
+	Org          string       `bson:"org" json:"org"`
+	ASN          string       `bson:"asn" json:"asn"`
+	CreatedAt    time.Time    `bson:"created_at" json:"created_at"`
+	ModifiedAt   time.Time    `bson:"modified_at" json:"modified_at"`
+	Organization Organization `bson:"organization" json:"organization"`
 }
 
+// GRSRaw contains the raw GRS response from the ripe API server
 type GRSRaw struct {
 	Service       Name          `json:"service"`
 	Parameters    Parameters    `json:"parameters"`
@@ -40,10 +49,12 @@ type GRSRaw struct {
 	ErrorMessages ErrorMessages `json:"errormessages"`
 }
 
+// Name contains the GRS name
 type Name struct {
 	Name string `json:"name"`
 }
 
+// Parameters contains all API query parameters
 type Parameters struct {
 	InverseLookup map[string]interface{} `json:"inverse-lookup"`
 	TypeFilters   map[string]interface{} `json:"type-filters"`
@@ -52,10 +63,12 @@ type Parameters struct {
 	Sources       Sources                `json:"sources"`
 }
 
+// QueryString contains a list of query-strings for API requests
 type QueryString struct {
 	QueryString []Value `json:"query-string"`
 }
 
+// Value contains a GRS value
 type Value struct {
 	Value string `json:"value"`
 }
@@ -64,6 +77,34 @@ type Sources struct {
 	Ids []Id `json:"source"`
 }
 
+type AutNum struct {
+	Source       string
+	ID           string
+	Name         string
+	Org          string
+	AdminContact string
+	TechContact  string
+	Status       string
+	Notify       []string
+	Maintainer   []string
+	Created      time.Time
+	Modified     time.Time
+	Organization Organization
+}
+
+type Organization struct {
+	// ID           string
+	Name         string
+	Type         string
+	Address      []string
+	Phone        string
+	Fax          string
+	Email        string
+	Organization string
+	AbuseEmail   string
+	Source       string
+}
+
 type Id struct {
 	ID string `json:"id"`
 }
@@ -75,7 +116,7 @@ type Objects struct {
 type Object struct {
 	Type       string     `json:"type"`
 	Link       Link       `json:"link"`
-	Source     Id         `json:source"`
+	Source     Id         `json:"source"`
 	PrimaryKey Attributes `json:"primary-key"`
 	Attributes Attributes `json:"attributes"`
 	Tags       Tags       `json:"tags"`
@@ -120,6 +161,143 @@ type ErrorMessage struct {
 	Text     string `json:"text"`
 }
 
+// LookupAutNum looks up the Autonomous System Number (ASN) using the RIPE API and returns an AutNum object
+func LookupAutNum(id string) (AutNum, error) {
+	asID := id
+	asID = strings.TrimPrefix(asID, "MNT-")
+	asID = strings.TrimPrefix(asID, "AS")
+	asID = fmt.Sprintf("AS%s", id)
+
+	args := url.Values{}
+	args.Set("query-string", id)
+	args.Add("type-filter", "aut-num")
+	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")
+
+	var data GRSRaw
+
+	resp, err := napping.Get(GrsURL, &args, &data, nil)
+
+	if err != nil {
+		return AutNum{}, errors.New(fmt.Sprint("Failed to load data from ", resp.Url, ": ", err))
+	}
+
+	if resp.Status() != 200 {
+		return AutNum{}, errors.New(fmt.Sprint("Failed to load data from GRS server: ", resp.Status()))
+	}
+
+	if len(data.ErrorMessages.ErrorMessage) > 0 {
+		return AutNum{}, errors.New(fmt.Sprint("No GRS data for ", id, ": ", data.ErrorMessages.ErrorMessage[0].Text))
+	}
+	if err != nil {
+		return AutNum{}, err
+	}
+
+	var aut AutNum
+
+	var autnum *Object
+	var org *Object
+
+	ymdRegexp := regexp.MustCompile(`^\d+$`)
+
+	for i, e := range data.Objects.Objects {
+		switch e.Type {
+		case "aut-num":
+			autnum = &data.Objects.Objects[i]
+		case "organisation":
+			org = &data.Objects.Objects[i]
+		}
+	}
+
+	if autnum != nil {
+		for _, e := range autnum.Attributes.Attributes {
+			switch e.Name {
+			case "aut-num":
+				aut.ID = e.Value
+			case "as-name":
+				aut.Name = e.Value
+			case "org":
+				aut.Org = e.Value
+			case "admin-c":
+				aut.AdminContact = e.Value
+			case "tech-c":
+				aut.TechContact = e.Value
+			case "status":
+				aut.Status = e.Value
+			case "mnt-by":
+				aut.Maintainer = append(aut.Maintainer, e.Value)
+			case "source":
+				aut.Source = e.Value
+			case "created":
+				if ymdRegexp.MatchString(e.Value) {
+					t, err := time.Parse("20060102", e.Value)
+					if err != nil {
+						log.Printf("%s: %s\n", e.Value, err)
+						break
+					}
+					aut.Created = t
+					break
+				}
+
+				t, err := time.Parse(time.RFC3339, e.Value)
+				if err != nil {
+					log.Printf("%s: %s\n", e.Value, err)
+					break
+				}
+				aut.Created = t
+			case "last-modified":
+				if ymdRegexp.MatchString(e.Value) {
+					t, err := time.Parse("20060102", e.Value)
+					if err != nil {
+						log.Printf("%s: %s\n", e.Value, err)
+						break
+					}
+					aut.Modified = t
+					break
+				}
+
+				t, err := time.Parse(time.RFC3339, e.Value)
+				if err != nil {
+					log.Printf("%s: %s\n", e.Value, err)
+					break
+				}
+				aut.Modified = t
+			}
+		}
+	}
+
+	if org != nil {
+		o := Organization{}
+
+		for _, e := range org.Attributes.Attributes {
+			switch e.Name {
+			case "organisation":
+				o.Organization = e.Value
+			case "org-name":
+				o.Name = e.Value
+			case "org-type":
+				o.Type = e.Value
+			case "address":
+				o.Address = append(o.Address, e.Value)
+			case "phone":
+				o.Phone = e.Value
+			case "abuse-mailbox":
+				o.AbuseEmail = e.Value
+			case "e-mail":
+				o.Email = e.Value
+			}
+		}
+
+		aut.Organization = o
+	}
+
+	return aut, nil
+}
+
+// Lookup loads GRS data from the RIPE REST API and returns a GRS object
 func Lookup(ip string) (GRS, error) {
 	// var data map[string]interface{}
 	var data GRSRaw
@@ -132,7 +310,7 @@ func Lookup(ip string) (GRS, error) {
 	args.Add("source", "lacnic-grs")
 	args.Add("source", "afrinic-grs")
 
-	resp, err := napping.Get(GRS_URL, &args, &data, nil)
+	resp, err := napping.Get(GrsURL, &args, &data, nil)
 
 	if err != nil {
 		return GRS{}, errors.New(fmt.Sprint("Failed to load data from ", resp.Url, ": ", err))
@@ -149,8 +327,11 @@ func Lookup(ip string) (GRS, error) {
 	grs := GRS{}
 	var grs_inetnum *Object
 	var grs_route *Object
+	var grs_org *Object
 	var inetnum string
 
+	ymdRegexp := regexp.MustCompile(`^\d+$`)
+
 	for i, e := range data.Objects.Objects {
 		switch e.Type {
 		case "inetnum":
@@ -159,6 +340,8 @@ func Lookup(ip string) (GRS, error) {
 			grs_inetnum = &data.Objects.Objects[i]
 		case "route":
 			grs_route = &data.Objects.Objects[i]
+		case "organisation":
+			grs_org = &data.Objects.Objects[i]
 		}
 	}
 
@@ -181,6 +364,42 @@ func Lookup(ip string) (GRS, error) {
 				grs.Status = e.Value
 			case "org":
 				grs.Org = e.Value
+			case "source":
+				grs.Source = e.Value
+			case "created":
+				if ymdRegexp.MatchString(e.Value) {
+					t, err := time.Parse("20060102", e.Value)
+					if err != nil {
+						log.Printf("%s: %s\n", e.Value, err)
+						break
+					}
+					grs.CreatedAt = t
+					break
+				}
+
+				t, err := time.Parse(time.RFC3339, e.Value)
+				if err != nil {
+					log.Printf("%s: %s\n", e.Value, err)
+					break
+				}
+				grs.CreatedAt = t
+			case "last-modified":
+				if ymdRegexp.MatchString(e.Value) {
+					t, err := time.Parse("20060102", e.Value)
+					if err != nil {
+						log.Printf("%s: %s\n", e.Value, err)
+						break
+					}
+					grs.ModifiedAt = t
+					break
+				}
+
+				t, err := time.Parse(time.RFC3339, e.Value)
+				if err != nil {
+					log.Printf("%s: %s\n", e.Value, err)
+					break
+				}
+				grs.ModifiedAt = t
 			}
 		}
 	}
@@ -190,27 +409,32 @@ func Lookup(ip string) (GRS, error) {
 	if grs_route != nil {
 		for _, e := range grs_route.Attributes.Attributes {
 			switch e.Name {
-			case "route":
-				grs.Cidr = e.Value
+			//case "route":
+			//	grs.Cidr = e.Value
 			case "descr":
 				grs.Description = e.Value
+			case "origin":
+				grs.ASN = e.Value
 			}
 		}
 	}
 
-	var ip_from net.IP
-	var ip_to net.IP
+	var ipFrom net.IP
+	var ipTo 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()
+			ipFrom, ipTo = networkRange(network)
+			grs.IpFrom = ipFrom.String()
+			grs.IpTo = ipTo.String()
 			if grs.Cidr == "" {
 				grs.Cidr = network.String()
+
+				_, numBits := network.Mask.Size()
+				grs.NetMaskBits = numBits
 			}
 		} else {
 			reg := regexp.MustCompile(`^\s*(\d+\.\d+\.\d+\.\d+)\s*-\s*(\d+\.\d+\.\d+\.\d+)\s*$`)
@@ -220,15 +444,47 @@ func Lookup(ip string) (GRS, error) {
 				grs.IpFrom = matches[0][1]
 				grs.IpTo = matches[0][2]
 
-				ip_from = net.ParseIP(grs.IpFrom)
-				ip_to = net.ParseIP(grs.IpTo)
+				ipFrom = net.ParseIP(grs.IpFrom)
+				ipTo = net.ParseIP(grs.IpTo)
+
+				_, masklen := cidr(ipFrom, ipTo)
+
+				grs.NetMaskBits = masklen
+				grs.Cidr = fmt.Sprintf("%s/%d", grs.IpFrom, masklen)
 
 			}
 
 		}
-		grs.IpFromRaw = ip_from.To16()
-		grs.IpToRaw = ip_to.To16()
 
+		grs.IpFromRaw = ipFrom.To16()
+		grs.IpToRaw = ipTo.To16()
+	}
+
+	if grs_org != nil {
+		org := Organization{}
+		for _, e := range grs_org.Attributes.Attributes {
+			switch e.Name {
+			case "organisation":
+				org.Organization = e.Value
+			case "org-name":
+				org.Name = e.Value
+			case "org-type":
+				org.Type = e.Value
+			case "address":
+				org.Address = append(org.Address, e.Value)
+			case "e-mail":
+				org.Email = e.Value
+			case "abuse-mailbox":
+				org.AbuseEmail = e.Value
+			case "phone":
+				org.Phone = e.Value
+			case "fax":
+				org.Fax = e.Value
+			case "source":
+				org.Source = e.Value
+			}
+		}
+		grs.Organization = org
 	}
 
 	return grs, nil
@@ -244,3 +500,27 @@ func networkRange(network *net.IPNet) (net.IP, net.IP) {
 	}
 	return firstIP, lastIP
 }
+
+func cidr(first, last net.IP) (net.IPMask, int) {
+
+	// Is the address IPv4?
+	a := first.To4()
+	b := last.To4()
+	if a == nil || b == nil {
+		a = first
+		b = last
+	}
+
+	l := len(a)
+	m := make([]byte, l)
+
+	for i := 0; i < l; i++ {
+		msk := a[i] ^ b[i]
+		m[i] = ^msk
+	}
+
+	ipmask := net.IPMask(m)
+	masklen, _ := ipmask.Size()
+
+	return ipmask, masklen
+}