blob: 458c9e2badec4c60f1718f16b09ef2776d6ef7d2 [file] [log] [blame]
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +02001package provider
2
3// Support for the ARIN IRR.
4// ARIN is special. We have to query them via whois. It's also not the same
5// whois as you usually see. And also not many autnums (even big players like
6// AS15169 and AS112) have IRR entries at all.
7
8import (
9 "context"
10 "fmt"
11 "strings"
12
13 "code.hackerspace.pl/hscloud/bgpwtf/cccampix/irr/whois"
14 pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020015 "google.golang.org/grpc/codes"
16 "google.golang.org/grpc/status"
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020017)
18
19const ARINWhois = "rr.arin.net:43"
20
21type arin struct {
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020022 sem chan struct{}
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020023}
24
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020025func NewARIN(limit int) Provider {
26 return &arin{
27 sem: make(chan struct{}, limit),
28 }
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020029}
30
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020031func (a *arin) Query(ctx context.Context, asn uint64) (*pb.IRRQueryResponse, error) {
32 a.sem <- struct{}{}
33 defer func() {
34 <-a.sem
35 }()
36
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020037 data, err := whois.Query(ctx, ARINWhois, fmt.Sprintf("AS%d", asn))
38 if err != nil {
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020039 return nil, status.Errorf(codes.Unavailable, "could not contact ARIN IRR: %v", err)
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020040 }
41
42 lines := strings.Split(data, "\n")
43
44 // Convert possibly 'continued' RPSL entries into single-line entries.
45 // eg.
46 // import: from AS6083
47 // action pref=10;
48 // accept ANY
49 // into
50 // 'import', 'from AS6083, action pref=10; accept ANY'
51
52 attrs := []rpslRawAttribute{}
53 for _, line := range lines {
54 if strings.HasPrefix(strings.TrimSpace(line), "%") {
55 // Comment
56 continue
57 }
58 if strings.TrimSpace(line) == "" {
59 // Empty line
60 continue
61 }
62
63 if strings.HasPrefix(line, " ") {
64 // Continuation
65 if len(attrs) < 1 {
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020066 return nil, status.Errorf(codes.Unavailable, "unparseable IRR, continuation with no previous atribute name: %q", line)
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020067 }
68
69 attrs[len(attrs)-1].value += " " + strings.TrimSpace(line)
70 } else {
71 parts := strings.SplitN(line, ":", 2)
72 if len(parts) != 2 {
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020073 return nil, status.Errorf(codes.Unavailable, "unparseable IRR, line with no attribute key: %q", line)
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020074 }
75 name := strings.TrimSpace(parts[0])
76 value := strings.TrimSpace(parts[1])
77 attrs = append(attrs, rpslRawAttribute{
78 name: name,
79 value: value,
80 })
81 }
82 }
83
Sergiusz Bazanski2316ac02019-08-03 23:49:19 +020084 if len(attrs) == 0 {
85 return nil, status.Errorf(codes.NotFound, "no such ASN")
86 }
87
Sergiusz Bazanski6eaaaf92019-08-02 01:25:31 +020088 return &pb.IRRQueryResponse{
89 Source: pb.IRRQueryResponse_SOURCE_ARIN,
90 Attributes: parseAttributes(attrs),
91 }, nil
92}