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