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 	"flag"
     22 	"fmt"
     23 	"io"
     24 	"os"
     25 	"sort"
     26 	"strconv"
     27 	"strings"
     28 )
     29 
     30 var 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.
     34 var 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.
     71 type 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 
     83 func 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.
     91 const offsetMask = 0x7fff
     92 
     93 func (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.
    118 type keySlice []uint32
    119 
    120 func (ks keySlice) Len() int {
    121 	return len(ks)
    122 }
    123 
    124 func (ks keySlice) Less(i, j int) bool {
    125 	return (ks[i] >> 15) < (ks[j] >> 15)
    126 }
    127 
    128 func (ks keySlice) Swap(i, j int) {
    129 	ks[i], ks[j] = ks[j], ks[i]
    130 }
    131 
    132 func (st *stringList) buildList() []uint32 {
    133 	sort.Sort(keySlice(st.entries))
    134 	return st.entries
    135 }
    136 
    137 type stringWriter interface {
    138 	io.Writer
    139 	WriteString(string) (int, error)
    140 }
    141 
    142 func (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 
    168 type errorData struct {
    169 	reasons    *stringList
    170 	libraryMap map[string]uint32
    171 }
    172 
    173 func (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 
    222 func 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