1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build ignore
6
7//go:generate go run gen.go
8
9// This program generates system adaptation constants and types,
10// internet protocol constants and tables by reading template files
11// and IANA protocol registries.
12package main
13
14import (
15	"bytes"
16	"encoding/xml"
17	"fmt"
18	"go/format"
19	"io"
20	"io/ioutil"
21	"net/http"
22	"os"
23	"os/exec"
24	"runtime"
25	"strconv"
26	"strings"
27)
28
29func main() {
30	if err := genzsys(); err != nil {
31		fmt.Fprintln(os.Stderr, err)
32		os.Exit(1)
33	}
34	if err := geniana(); err != nil {
35		fmt.Fprintln(os.Stderr, err)
36		os.Exit(1)
37	}
38}
39
40func genzsys() error {
41	defs := "defs_" + runtime.GOOS + ".go"
42	f, err := os.Open(defs)
43	if err != nil {
44		if os.IsNotExist(err) {
45			return nil
46		}
47		return err
48	}
49	f.Close()
50	cmd := exec.Command("go", "tool", "cgo", "-godefs", defs)
51	b, err := cmd.Output()
52	if err != nil {
53		return err
54	}
55	b, err = format.Source(b)
56	if err != nil {
57		return err
58	}
59	zsys := "zsys_" + runtime.GOOS + ".go"
60	switch runtime.GOOS {
61	case "freebsd", "linux":
62		zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go"
63	}
64	if err := ioutil.WriteFile(zsys, b, 0644); err != nil {
65		return err
66	}
67	return nil
68}
69
70var registries = []struct {
71	url   string
72	parse func(io.Writer, io.Reader) error
73}{
74	{
75		"https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml",
76		parseICMPv6Parameters,
77	},
78}
79
80func geniana() error {
81	var bb bytes.Buffer
82	fmt.Fprintf(&bb, "// go generate gen.go\n")
83	fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
84	fmt.Fprintf(&bb, "package ipv6\n\n")
85	for _, r := range registries {
86		resp, err := http.Get(r.url)
87		if err != nil {
88			return err
89		}
90		defer resp.Body.Close()
91		if resp.StatusCode != http.StatusOK {
92			return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url)
93		}
94		if err := r.parse(&bb, resp.Body); err != nil {
95			return err
96		}
97		fmt.Fprintf(&bb, "\n")
98	}
99	b, err := format.Source(bb.Bytes())
100	if err != nil {
101		return err
102	}
103	if err := ioutil.WriteFile("iana.go", b, 0644); err != nil {
104		return err
105	}
106	return nil
107}
108
109func parseICMPv6Parameters(w io.Writer, r io.Reader) error {
110	dec := xml.NewDecoder(r)
111	var icp icmpv6Parameters
112	if err := dec.Decode(&icp); err != nil {
113		return err
114	}
115	prs := icp.escape()
116	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
117	fmt.Fprintf(w, "const (\n")
118	for _, pr := range prs {
119		if pr.Name == "" {
120			continue
121		}
122		fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value)
123		fmt.Fprintf(w, "// %s\n", pr.OrigName)
124	}
125	fmt.Fprintf(w, ")\n\n")
126	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
127	fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
128	for _, pr := range prs {
129		if pr.Name == "" {
130			continue
131		}
132		fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName))
133	}
134	fmt.Fprintf(w, "}\n")
135	return nil
136}
137
138type icmpv6Parameters struct {
139	XMLName    xml.Name `xml:"registry"`
140	Title      string   `xml:"title"`
141	Updated    string   `xml:"updated"`
142	Registries []struct {
143		Title   string `xml:"title"`
144		Records []struct {
145			Value string `xml:"value"`
146			Name  string `xml:"name"`
147		} `xml:"record"`
148	} `xml:"registry"`
149}
150
151type canonICMPv6ParamRecord struct {
152	OrigName string
153	Name     string
154	Value    int
155}
156
157func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord {
158	id := -1
159	for i, r := range icp.Registries {
160		if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") {
161			id = i
162			break
163		}
164	}
165	if id < 0 {
166		return nil
167	}
168	prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records))
169	sr := strings.NewReplacer(
170		"Messages", "",
171		"Message", "",
172		"ICMP", "",
173		"+", "P",
174		"-", "",
175		"/", "",
176		".", "",
177		" ", "",
178	)
179	for i, pr := range icp.Registries[id].Records {
180		if strings.Contains(pr.Name, "Reserved") ||
181			strings.Contains(pr.Name, "Unassigned") ||
182			strings.Contains(pr.Name, "Deprecated") ||
183			strings.Contains(pr.Name, "Experiment") ||
184			strings.Contains(pr.Name, "experiment") {
185			continue
186		}
187		ss := strings.Split(pr.Name, "\n")
188		if len(ss) > 1 {
189			prs[i].Name = strings.Join(ss, " ")
190		} else {
191			prs[i].Name = ss[0]
192		}
193		s := strings.TrimSpace(prs[i].Name)
194		prs[i].OrigName = s
195		prs[i].Name = sr.Replace(s)
196		prs[i].Value, _ = strconv.Atoi(pr.Value)
197	}
198	return prs
199}
200