| // 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 |
| } |