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