blob: 458c9e2badec4c60f1718f16b09ef2776d6ef7d2 [file] [log] [blame]
package provider
// Support for the ARIN IRR.
// ARIN is special. We have to query them via whois. It's also not the same
// whois as you usually see. And also not many autnums (even big players like
// AS15169 and AS112) have IRR entries at all.
import (
"context"
"fmt"
"strings"
"code.hackerspace.pl/hscloud/bgpwtf/cccampix/irr/whois"
pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const ARINWhois = "rr.arin.net:43"
type arin struct {
sem chan struct{}
}
func NewARIN(limit int) Provider {
return &arin{
sem: make(chan struct{}, limit),
}
}
func (a *arin) Query(ctx context.Context, asn uint64) (*pb.IRRQueryResponse, error) {
a.sem <- struct{}{}
defer func() {
<-a.sem
}()
data, err := whois.Query(ctx, ARINWhois, fmt.Sprintf("AS%d", asn))
if err != nil {
return nil, status.Errorf(codes.Unavailable, "could not contact ARIN IRR: %v", err)
}
lines := strings.Split(data, "\n")
// Convert possibly 'continued' RPSL entries into single-line entries.
// eg.
// import: from AS6083
// action pref=10;
// accept ANY
// into
// 'import', 'from AS6083, action pref=10; accept ANY'
attrs := []rpslRawAttribute{}
for _, line := range lines {
if strings.HasPrefix(strings.TrimSpace(line), "%") {
// Comment
continue
}
if strings.TrimSpace(line) == "" {
// Empty line
continue
}
if strings.HasPrefix(line, " ") {
// Continuation
if len(attrs) < 1 {
return nil, status.Errorf(codes.Unavailable, "unparseable IRR, continuation with no previous atribute name: %q", line)
}
attrs[len(attrs)-1].value += " " + strings.TrimSpace(line)
} else {
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
return nil, status.Errorf(codes.Unavailable, "unparseable IRR, line with no attribute key: %q", line)
}
name := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
attrs = append(attrs, rpslRawAttribute{
name: name,
value: value,
})
}
}
if len(attrs) == 0 {
return nil, status.Errorf(codes.NotFound, "no such ASN")
}
return &pb.IRRQueryResponse{
Source: pb.IRRQueryResponse_SOURCE_ARIN,
Attributes: parseAttributes(attrs),
}, nil
}