204 lines
5.2 KiB
Go
204 lines
5.2 KiB
Go
//+build ignore
|
|
|
|
//go:generate go run gen.go
|
|
// This program generates STUN parameters: methods, attributes and error codes by reading IANA registry.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"go/format"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
func main() {
|
|
err := generate("http://www.iana.org/assignments/stun-parameters/stun-parameters.xml", "registry.go")
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func loadRegistry(url string) (*Registry, error) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r := &Registry{}
|
|
err = xml.Unmarshal(b, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name := regexp.MustCompile("^([\\w-]+)(.*was\\s+([\\w-]+))?")
|
|
for _, reg := range r.Registry {
|
|
for _, r := range reg.Records {
|
|
m := name.FindStringSubmatch(r.Description)
|
|
if m != nil {
|
|
r.Value = strings.ToLower(r.Value)
|
|
r.Name = m[1]
|
|
if r.Name == "Reserved" && m[3] != "" {
|
|
r.Name = m[3]
|
|
r.Deprecated = true
|
|
}
|
|
r.Ref.Data = strings.TrimPrefix(r.Ref.Data, "rfc")
|
|
r.Ref.Data = strings.TrimPrefix(r.Ref.Data, "RFC-")
|
|
}
|
|
}
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
type Record struct {
|
|
Value string `xml:"value"`
|
|
Description string `xml:"description"`
|
|
Name string
|
|
Deprecated bool
|
|
Ref struct {
|
|
Type string `xml:"type,attr"`
|
|
Data string `xml:"data,attr"`
|
|
} `xml:"xref"`
|
|
}
|
|
|
|
func (r *Record) IsValid() bool {
|
|
return r.Name != "Reserved" && r.Name != "Unassigned" && (r.Ref.Type == "rfc" || r.Ref.Type == "draft")
|
|
}
|
|
|
|
type Registry struct {
|
|
Title string `xml:"title"`
|
|
Updated string `xml:"updated"`
|
|
Registry []struct {
|
|
Id string `xml:"id,attr"`
|
|
Records []*Record `xml:"record"`
|
|
} `xml:"registry"`
|
|
}
|
|
|
|
func (reg *Registry) GetRecords(id string) []*Record {
|
|
for _, it := range reg.Registry {
|
|
if it.Id == id {
|
|
return it.Records
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func generate(url, file string) error {
|
|
reg, err := loadRegistry(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b := &bytes.Buffer{}
|
|
fmt.Fprintf(b, "package stun\n\n")
|
|
fmt.Fprintf(b, "// Do not edit. This file is generated by 'go generate gen.go'\n")
|
|
fmt.Fprintf(b, "// This file provides STUN parameters managed by the Internet Assigned Numbers Authority (IANA).\n")
|
|
fmt.Fprintf(b, "// %s, Updated: %s.\n\n", reg.Title, reg.Updated)
|
|
|
|
genMethods(reg.GetRecords("stun-parameters-2"), b)
|
|
genAttributes(reg.GetRecords("stun-parameters-4"), b)
|
|
genErrors(reg.GetRecords("stun-parameters-6"), b)
|
|
|
|
src, err := format.Source(b.Bytes())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = ioutil.WriteFile(file, src, 0644); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func genMethods(records []*Record, b *bytes.Buffer) error {
|
|
c, m := &bytes.Buffer{}, &bytes.Buffer{}
|
|
ref := ""
|
|
for _, it := range records {
|
|
if it.IsValid() {
|
|
if ref == it.Ref.Data {
|
|
fmt.Fprintf(c, "Method%v uint16 = %s\n", it.Name, it.Value)
|
|
} else {
|
|
ref = it.Ref.Data
|
|
fmt.Fprintf(c, "Method%v uint16 = %s // RFC %s\n", it.Name, it.Value, ref)
|
|
}
|
|
fmt.Fprintf(m, "Method%v: \"%s\",\n", it.Name, it.Name)
|
|
}
|
|
}
|
|
fmt.Fprintf(b, "// STUN methods.\n")
|
|
fmt.Fprintf(b, "const (\n%s)\n", c.Bytes())
|
|
fmt.Fprintf(b, "// STUN method names.\n")
|
|
fmt.Fprintf(b, "var methodNames = map[uint16]string{\n%s}\n", m.Bytes())
|
|
return nil
|
|
}
|
|
|
|
func genAttributes(records []*Record, b *bytes.Buffer) error {
|
|
c, d, m := &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}
|
|
ref := ""
|
|
for _, it := range records {
|
|
if it.IsValid() {
|
|
a := c
|
|
if it.Deprecated {
|
|
a = d
|
|
}
|
|
v := strings.Replace(it.Name, "_", "-", -1)
|
|
n := strings.Replace(v, "-", " ", -1)
|
|
parts := strings.Fields(n)
|
|
for i, s := range parts {
|
|
switch s {
|
|
case "", "ID":
|
|
default:
|
|
r, n := utf8.DecodeRuneInString(s)
|
|
s = string(unicode.ToUpper(r)) + strings.ToLower(s[n:])
|
|
}
|
|
parts[i] = s
|
|
}
|
|
n = strings.Join(parts, "")
|
|
if ref == it.Ref.Data || it.Deprecated {
|
|
fmt.Fprintf(a, "Attr%v uint16 = %s\n", n, it.Value)
|
|
} else {
|
|
ref = it.Ref.Data
|
|
fmt.Fprintf(a, "Attr%v uint16 = %s // RFC %s\n", n, it.Value, ref)
|
|
}
|
|
fmt.Fprintf(m, "Attr%v: \"%s\",\n", n, v)
|
|
}
|
|
}
|
|
fmt.Fprintf(b, "// STUN attributes.\n")
|
|
fmt.Fprintf(b, "const (\n%s)\n", c.Bytes())
|
|
fmt.Fprintf(b, "// Deprecated: For backwards compatibility only.\n")
|
|
fmt.Fprintf(b, "const (\n%s)\n", d.Bytes())
|
|
fmt.Fprintf(b, "// STUN attribute names.\n")
|
|
fmt.Fprintf(b, "var attrNames = map[uint16]string{\n%s}\n", m.Bytes())
|
|
return nil
|
|
}
|
|
|
|
func genErrors(records []*Record, b *bytes.Buffer) error {
|
|
c, m := &bytes.Buffer{}, &bytes.Buffer{}
|
|
ref := ""
|
|
for _, it := range records {
|
|
if it.IsValid() {
|
|
n := strings.Replace(strings.Title(it.Description), " ", "", -1)
|
|
if ref == it.Ref.Data {
|
|
fmt.Fprintf(c, "Code%v int = %s\n", n, it.Value)
|
|
} else {
|
|
ref = it.Ref.Data
|
|
fmt.Fprintf(c, "Code%v int = %s // RFC %s\n", n, it.Value, ref)
|
|
}
|
|
fmt.Fprintf(m, "Code%v: \"%s\",\n", n, it.Description)
|
|
}
|
|
}
|
|
fmt.Fprintf(b, "// STUN error codes.\n")
|
|
fmt.Fprintf(b, "const (\n%s)\n", c.Bytes())
|
|
fmt.Fprintf(b, "// STUN error texts.\n")
|
|
fmt.Fprintf(b, "var errorText = map[int]string{\n%s}\n", m.Bytes())
|
|
return nil
|
|
}
|