123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- package grs
- import (
- "errors"
- "fmt"
- "log"
- "net"
- "net/url"
- "regexp"
- "strings"
- "time"
- napping "gopkg.in/jmcvetta/napping.v3"
- )
- const (
- // 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"`
- 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"`
- Objects Objects `json:"objects"`
- Terms Terms `json:"terms-and-conditions"`
- 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"`
- Flags map[string]interface{} `json:"flags"`
- QueryStrings QueryString `json:"query-strings"`
- 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"`
- }
- 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"`
- }
- 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"`
- }
- // 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
- 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(GrsURL, &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 grs_org *Object
- var inetnum string
- ymdRegexp := regexp.MustCompile(`^\d+$`)
- 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]
- case "organisation":
- grs_org = &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
- 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
- }
- }
- }
- // 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
- case "origin":
- grs.ASN = e.Value
- }
- }
- }
- var ipFrom net.IP
- var ipTo net.IP
- if inetnum != "" {
- _, network, err := net.ParseCIDR(inetnum)
- if err == nil {
- 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*$`)
- 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]
- 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 = 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
- }
- // 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
- }
- 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
- }
|