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