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