123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- package grs
- import (
- "errors"
- "fmt"
- "net"
- "net/url"
- "regexp"
- "time"
- "gopkg.in/jmcvetta/napping.v3"
- "gopkg.in/mgo.v2/bson"
- )
- const (
- GRS_URL = "http://rest.db.ripe.net/search.json"
- )
- 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"`
- }
- 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 map[string]interface{}
- 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]
- }
- }
- // Inetnum fields
- //
- 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
- }
- }
- }
- // Route fields
- //
- 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
- }
- // Calculates the first and last IP addresses in an IPNet
- 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
- }
|