• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2015, Google Inc.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//go:build ignore
16
17package main
18
19import (
20	"bufio"
21	"bytes"
22	"errors"
23	"flag"
24	"fmt"
25	"io"
26	"os"
27	"sort"
28	"strconv"
29	"strings"
30)
31
32var verbose = flag.Bool("verbose", false, "If true, prints a status message at the end.")
33
34// libraryNames must be kept in sync with the enum in err.h. The generated code
35// will contain static assertions to enforce this.
36var libraryNames = []string{
37	"NONE",
38	"SYS",
39	"BN",
40	"RSA",
41	"DH",
42	"EVP",
43	"BUF",
44	"OBJ",
45	"PEM",
46	"DSA",
47	"X509",
48	"ASN1",
49	"CONF",
50	"CRYPTO",
51	"EC",
52	"SSL",
53	"BIO",
54	"PKCS7",
55	"PKCS8",
56	"X509V3",
57	"RAND",
58	"ENGINE",
59	"OCSP",
60	"UI",
61	"COMP",
62	"ECDSA",
63	"ECDH",
64	"HMAC",
65	"DIGEST",
66	"CIPHER",
67	"HKDF",
68	"TRUST_TOKEN",
69	"USER",
70}
71
72// stringList is a map from uint32 -> string which can output data for a sorted
73// list as C literals.
74type stringList struct {
75	// entries is an array of keys and offsets into |stringData|. The
76	// offsets are in the bottom 15 bits of each uint32 and the key is the
77	// top 17 bits.
78	entries []uint32
79	// internedStrings contains the same strings as are in |stringData|,
80	// but allows for easy deduplication. It maps a string to its offset in
81	// |stringData|.
82	internedStrings map[string]uint32
83	stringData      []byte
84}
85
86func newStringList() *stringList {
87	return &stringList{
88		internedStrings: make(map[string]uint32),
89	}
90}
91
92// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a
93// uint32 in entries.
94const offsetMask = 0x7fff
95
96func (st *stringList) Add(key uint32, value string) error {
97	if key&offsetMask != 0 {
98		return errors.New("need bottom 15 bits of the key for the offset")
99	}
100	offset, ok := st.internedStrings[value]
101	if !ok {
102		offset = uint32(len(st.stringData))
103		if offset&offsetMask != offset {
104			return errors.New("stringList overflow")
105		}
106		st.stringData = append(st.stringData, []byte(value)...)
107		st.stringData = append(st.stringData, 0)
108		st.internedStrings[value] = offset
109	}
110
111	for _, existing := range st.entries {
112		if existing>>15 == key>>15 {
113			panic("duplicate entry")
114		}
115	}
116	st.entries = append(st.entries, key|offset)
117	return nil
118}
119
120func (st *stringList) buildList() []uint32 {
121	sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) })
122	return st.entries
123}
124
125type stringWriter interface {
126	io.Writer
127	WriteString(string) (int, error)
128}
129
130func (st *stringList) WriteTo(out stringWriter, name string) {
131	list := st.buildList()
132	if *verbose {
133		fmt.Fprintf(os.Stderr, "%s: %d bytes of list and %d bytes of string data.\n", name, 4*len(list), len(st.stringData))
134	}
135
136	values := "kOpenSSL" + name + "Values"
137	out.WriteString("const uint32_t " + values + "[] = {\n")
138	for _, v := range list {
139		fmt.Fprintf(out, "    0x%x,\n", v)
140	}
141	out.WriteString("};\n\n")
142	out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n")
143
144	stringData := "kOpenSSL" + name + "StringData"
145	out.WriteString("const char " + stringData + "[] =\n    \"")
146	for i, c := range st.stringData {
147		if c == 0 {
148			out.WriteString("\\0\"\n    \"")
149			continue
150		}
151		out.Write(st.stringData[i : i+1])
152	}
153	out.WriteString("\";\n\n")
154}
155
156type errorData struct {
157	reasons    *stringList
158	libraryMap map[string]uint32
159}
160
161func (e *errorData) readErrorDataFile(filename string) error {
162	inFile, err := os.Open(filename)
163	if err != nil {
164		return err
165	}
166	defer inFile.Close()
167
168	scanner := bufio.NewScanner(inFile)
169	comma := []byte(",")
170
171	lineNo := 0
172	for scanner.Scan() {
173		lineNo++
174
175		line := scanner.Bytes()
176		if len(line) == 0 {
177			continue
178		}
179		parts := bytes.Split(line, comma)
180		if len(parts) != 3 {
181			return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts))
182		}
183		libNum, ok := e.libraryMap[string(parts[0])]
184		if !ok {
185			return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename)
186		}
187		if libNum >= 64 {
188			return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename)
189		}
190		key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */)
191		if err != nil {
192			return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err)
193		}
194		if key >= 2048 {
195			return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename)
196		}
197		value := string(parts[2])
198
199		listKey := libNum<<26 | uint32(key)<<15
200
201		err = e.reasons.Add(listKey, value)
202		if err != nil {
203			return err
204		}
205	}
206
207	return scanner.Err()
208}
209
210func main() {
211	flag.Parse()
212
213	e := &errorData{
214		reasons:    newStringList(),
215		libraryMap: make(map[string]uint32),
216	}
217	for i, name := range libraryNames {
218		e.libraryMap[name] = uint32(i) + 1
219	}
220
221	cwd, err := os.Open(".")
222	if err != nil {
223		panic(err)
224	}
225	names, err := cwd.Readdirnames(-1)
226	if err != nil {
227		panic(err)
228	}
229
230	sort.Strings(names)
231	for _, name := range names {
232		if !strings.HasSuffix(name, ".errordata") {
233			continue
234		}
235		if err := e.readErrorDataFile(name); err != nil {
236			panic(err)
237		}
238	}
239
240	out := os.Stdout
241
242	out.WriteString(`/* Copyright (c) 2015, Google Inc.
243 *
244 * Permission to use, copy, modify, and/or distribute this software for any
245 * purpose with or without fee is hereby granted, provided that the above
246 * copyright notice and this permission notice appear in all copies.
247 *
248 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
249 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
250 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
251 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
252 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
253 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
254 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
255
256 /* This file was generated by err_data_generate.go. */
257
258#include <openssl/base.h>
259#include <openssl/err.h>
260
261#include <assert.h>
262
263`)
264
265	for i, name := range libraryNames {
266		fmt.Fprintf(out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1)
267	}
268	fmt.Fprintf(out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1)
269	out.WriteString("\n")
270
271	e.reasons.WriteTo(out, "Reason")
272}
273