Read only the AS's objects in RIPE, APNIC and AFRINIC answers (closes #168)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
APNIC and RIPE start their answer for an AS with the as-block object of the block the AS is in. The parser kept the first value of each field across the whole answer, so it stored the block's dates as the AS's. The parser now reads an answer from RIPE, APNIC or AFRINIC object by object. It takes the fields from the aut-num object and the organisation object it names, and leaves out every other object. The irt object's address therefore no longer joins the organisation's. ARIN and LACNIC answers are parsed as before. Model: opus-5-5
This commit was merged in pull request #175.
This commit is contained in:
+83
-18
@@ -29,10 +29,18 @@ const maxAnswerSize = 1 << 20
|
||||
|
||||
// Parsing constants.
|
||||
const (
|
||||
keyValueParts = 2 // Expected parts when splitting "key: value"
|
||||
lacnicDateFormatLen = 8 // Length of YYYYMMDD date format
|
||||
)
|
||||
|
||||
// The classes of the two objects that describe an AS in an answer from
|
||||
// RIPE, APNIC or AFRINIC, and the key of the aut-num object's line that
|
||||
// gives the ID of the AS's organisation object.
|
||||
const (
|
||||
autNumClass = "aut-num"
|
||||
organisationClass = "organisation"
|
||||
orgKey = "org"
|
||||
)
|
||||
|
||||
// WHOIS server addresses.
|
||||
const (
|
||||
whoisServerIANA = "whois.iana.org:43"
|
||||
@@ -318,7 +326,9 @@ func (c *Client) parseIANAReferral(response string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// parseResponse extracts ASN information from a WHOIS response.
|
||||
// parseResponse extracts ASN information from a WHOIS response. Of an
|
||||
// answer from RIPE, APNIC or AFRINIC it reads only the lines asObjectLines
|
||||
// returns. Of an answer from ARIN or LACNIC it reads every line.
|
||||
func (c *Client) parseResponse(asn int, rir, response string) *ASNInfo {
|
||||
info := &ASNInfo{
|
||||
ASN: asn,
|
||||
@@ -327,26 +337,15 @@ func (c *Client) parseResponse(asn int, rir, response string) *ASNInfo {
|
||||
}
|
||||
|
||||
lines := strings.Split(response, "\n")
|
||||
if rir == RIRRIPE || rir == RIRAPNIC || rir == RIRAFRNIC {
|
||||
lines = asObjectLines(lines)
|
||||
}
|
||||
|
||||
var addressLines []string
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "%") ||
|
||||
strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Split on first colon
|
||||
parts := strings.SplitN(line, ":", keyValueParts)
|
||||
if len(parts) != keyValueParts {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(strings.ToLower(parts[0]))
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
if value == "" {
|
||||
key, value, ok := splitField(line)
|
||||
if !ok || value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -373,6 +372,72 @@ func (c *Client) parseResponse(asn int, rir, response string) *ASNInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
// splitField splits a "key: value" line of a WHOIS answer into its key, in
|
||||
// lower case, and its value. The third result is false for a line that
|
||||
// holds no field: a blank line, a comment line or a line without a colon.
|
||||
func splitField(line string) (string, string, bool) {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "%") || strings.HasPrefix(line, "#") {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
key, value, ok := strings.Cut(line, ":")
|
||||
|
||||
return strings.ToLower(strings.TrimSpace(key)), strings.TrimSpace(value), ok
|
||||
}
|
||||
|
||||
// asObjectLines returns the lines of the aut-num object in an answer from
|
||||
// RIPE, APNIC or AFRINIC, followed by the lines of the organisation object
|
||||
// whose ID the aut-num object gives on its "org:" line. The answer's other
|
||||
// objects are left out. RIPE and APNIC start their answer with the
|
||||
// as-block object of the block the AS is in, whose dates are not the AS's.
|
||||
//
|
||||
// These answers are in RPSL. A blank line ends an object. An object's first
|
||||
// line has the object's class as its key and the object's ID as its value,
|
||||
// as in "organisation: ORG-APNI1-AP".
|
||||
func asObjectLines(lines []string) []string {
|
||||
var autNum []string
|
||||
|
||||
// The lines of each organisation object, by its ID.
|
||||
organisations := make(map[string][]string)
|
||||
|
||||
// The class and ID of the object the line is in, "" between objects.
|
||||
var class, id string
|
||||
|
||||
// The ID of the organisation object the aut-num object refers to.
|
||||
var orgID string
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
class = ""
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
key, value, ok := splitField(line)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if class == "" {
|
||||
class, id = key, value
|
||||
}
|
||||
|
||||
switch class {
|
||||
case autNumClass:
|
||||
autNum = append(autNum, line)
|
||||
|
||||
if key == orgKey {
|
||||
orgID = value
|
||||
}
|
||||
case organisationClass:
|
||||
organisations[id] = append(organisations[id], line)
|
||||
}
|
||||
}
|
||||
|
||||
return append(autNum, organisations[orgID]...)
|
||||
}
|
||||
|
||||
// setResponseField stores the value of one "key: value" line of a WHOIS
|
||||
// response in info, keeping the first value seen for each field.
|
||||
func (c *Client) setResponseField(info *ASNInfo, key, value string) {
|
||||
|
||||
Reference in New Issue
Block a user