// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build ignore //go:generate go run gen.go // This program generates internet protocol constants and tables by // reading IANA protocol registries. package main import ( "bytes" "encoding/xml" "fmt" "go/format" "io" "io/ioutil" "net/http" "os" "strconv" "strings" ) var registries = []struct { url string parse func(io.Writer, io.Reader) error }{ { "https://www.iana.org/assignments/dscp-registry/dscp-registry.xml", parseDSCPRegistry, }, { "https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml", parseProtocolNumbers, }, { "https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml", parseAddrFamilyNumbers, }, } func main() { var bb bytes.Buffer fmt.Fprintf(&bb, "// go generate gen.go\n") fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n") fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n") fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n") for _, r := range registries { resp, err := http.Get(r.url) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) os.Exit(1) } if err := r.parse(&bb, resp.Body); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Fprintf(&bb, "\n") } b, err := format.Source(bb.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := ioutil.WriteFile("const.go", b, 0644); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func parseDSCPRegistry(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var dr dscpRegistry if err := dec.Decode(&dr); err != nil { return err } fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated) fmt.Fprintf(w, "const (\n") for _, dr := range dr.escapeDSCP() { fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value) fmt.Fprintf(w, "// %s\n", dr.OrigName) } for _, er := range dr.escapeECN() { fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value) fmt.Fprintf(w, "// %s\n", er.OrigDescr) } fmt.Fprintf(w, ")\n") return nil } type dscpRegistry struct { XMLName xml.Name `xml:"registry"` Title string `xml:"title"` Updated string `xml:"updated"` Note string `xml:"note"` Registries []struct { Title string `xml:"title"` Registries []struct { Title string `xml:"title"` Records []struct { Name string `xml:"name"` Space string `xml:"space"` } `xml:"record"` } `xml:"registry"` Records []struct { Value string `xml:"value"` Descr string `xml:"description"` } `xml:"record"` } `xml:"registry"` } type canonDSCPRecord struct { OrigName string Name string Value int } func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord { var drs []canonDSCPRecord for _, preg := range drr.Registries { if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") { continue } for _, reg := range preg.Registries { if !strings.Contains(reg.Title, "Pool 1 Codepoints") { continue } drs = make([]canonDSCPRecord, len(reg.Records)) sr := strings.NewReplacer( "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, dr := range reg.Records { s := strings.TrimSpace(dr.Name) drs[i].OrigName = s drs[i].Name = sr.Replace(s) n, err := strconv.ParseUint(dr.Space, 2, 8) if err != nil { continue } drs[i].Value = int(n) << 2 } } } return drs } type canonECNRecord struct { OrigDescr string Descr string Value int } func (drr *dscpRegistry) escapeECN() []canonECNRecord { var ers []canonECNRecord for _, reg := range drr.Registries { if !strings.Contains(reg.Title, "ECN Field") { continue } ers = make([]canonECNRecord, len(reg.Records)) sr := strings.NewReplacer( "Capable", "", "Not-ECT", "", "ECT(1)", "", "ECT(0)", "", "CE", "", "(", "", ")", "", "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, er := range reg.Records { s := strings.TrimSpace(er.Descr) ers[i].OrigDescr = s ss := strings.Split(s, " ") if len(ss) > 1 { ers[i].Descr = strings.Join(ss[1:], " ") } else { ers[i].Descr = ss[0] } ers[i].Descr = sr.Replace(er.Descr) n, err := strconv.ParseUint(er.Value, 2, 8) if err != nil { continue } ers[i].Value = int(n) } } return ers } func parseProtocolNumbers(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var pn protocolNumbers if err := dec.Decode(&pn); err != nil { return err } prs := pn.escape() prs = append([]canonProtocolRecord{{ Name: "IP", Descr: "IPv4 encapsulation, pseudo protocol number", Value: 0, }}, prs...) fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated) fmt.Fprintf(w, "const (\n") for _, pr := range prs { if pr.Name == "" { continue } fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value) s := pr.Descr if s == "" { s = pr.OrigName } fmt.Fprintf(w, "// %s\n", s) } fmt.Fprintf(w, ")\n") return nil } type protocolNumbers struct { XMLName xml.Name `xml:"registry"` Title string `xml:"title"` Updated string `xml:"updated"` RegTitle string `xml:"registry>title"` Note string `xml:"registry>note"` Records []struct { Value string `xml:"value"` Name string `xml:"name"` Descr string `xml:"description"` } `xml:"registry>record"` } type canonProtocolRecord struct { OrigName string Name string Descr string Value int } func (pn *protocolNumbers) escape() []canonProtocolRecord { prs := make([]canonProtocolRecord, len(pn.Records)) sr := strings.NewReplacer( "-in-", "in", "-within-", "within", "-over-", "over", "+", "P", "-", "", "/", "", ".", "", " ", "", ) for i, pr := range pn.Records { if strings.Contains(pr.Name, "Deprecated") || strings.Contains(pr.Name, "deprecated") { continue } prs[i].OrigName = pr.Name s := strings.TrimSpace(pr.Name) switch pr.Name { case "ISIS over IPv4": prs[i].Name = "ISIS" case "manet": prs[i].Name = "MANET" default: prs[i].Name = sr.Replace(s) } ss := strings.Split(pr.Descr, "\n") for i := range ss { ss[i] = strings.TrimSpace(ss[i]) } if len(ss) > 1 { prs[i].Descr = strings.Join(ss, " ") } else { prs[i].Descr = ss[0] } prs[i].Value, _ = strconv.Atoi(pr.Value) } return prs } func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var afn addrFamilylNumbers if err := dec.Decode(&afn); err != nil { return err } afrs := afn.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated) fmt.Fprintf(w, "const (\n") for _, afr := range afrs { if afr.Name == "" { continue } fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value) fmt.Fprintf(w, "// %s\n", afr.Descr) } fmt.Fprintf(w, ")\n") return nil } type addrFamilylNumbers struct { XMLName xml.Name `xml:"registry"` Title string `xml:"title"` Updated string `xml:"updated"` RegTitle string `xml:"registry>title"` Note string `xml:"registry>note"` Records []struct { Value string `xml:"value"` Descr string `xml:"description"` } `xml:"registry>record"` } type canonAddrFamilyRecord struct { Name string Descr string Value int } func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord { afrs := make([]canonAddrFamilyRecord, len(afn.Records)) sr := strings.NewReplacer( "IP version 4", "IPv4", "IP version 6", "IPv6", "Identifier", "ID", "-", "", "-", "", "/", "", ".", "", " ", "", ) for i, afr := range afn.Records { if strings.Contains(afr.Descr, "Unassigned") || strings.Contains(afr.Descr, "Reserved") { continue } afrs[i].Descr = afr.Descr s := strings.TrimSpace(afr.Descr) switch s { case "IP (IP version 4)": afrs[i].Name = "IPv4" case "IP6 (IP version 6)": afrs[i].Name = "IPv6" case "AFI for L2VPN information": afrs[i].Name = "L2VPN" case "E.164 with NSAP format subaddress": afrs[i].Name = "E164withSubaddress" case "MT IP: Multi-Topology IP version 4": afrs[i].Name = "MTIPv4" case "MAC/24": afrs[i].Name = "MACFinal24bits" case "MAC/40": afrs[i].Name = "MACFinal40bits" case "IPv6/64": afrs[i].Name = "IPv6Initial64bits" default: n := strings.Index(s, "(") if n > 0 { s = s[:n] } n = strings.Index(s, ":") if n > 0 { s = s[:n] } afrs[i].Name = sr.Replace(s) } afrs[i].Value, _ = strconv.Atoi(afr.Value) } return afrs }