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