1 // Copyright (c) 2014, 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 "errors" 20 "flag" 21 "fmt" 22 "io" 23 "os" 24 "path/filepath" 25 "sort" 26 "strconv" 27 "strings" 28 "unicode" 29 ) 30 31 // ssl.h reserves values 1000 and above for error codes corresponding to 32 // alerts. If automatically assigned reason codes exceed this value, this script 33 // will error. This must be kept in sync with SSL_AD_REASON_OFFSET in ssl.h. 34 const reservedReasonCode = 1000 35 36 var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch") 37 38 func makeErrors(reset bool) error { 39 dirName, err := os.Getwd() 40 if err != nil { 41 return err 42 } 43 44 lib := filepath.Base(dirName) 45 headerPath, err := findHeader(lib + ".h") 46 if err != nil { 47 return err 48 } 49 sourcePath := lib + "_error.c" 50 51 headerFile, err := os.Open(headerPath) 52 if err != nil { 53 if os.IsNotExist(err) { 54 return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath) 55 } 56 57 return err 58 } 59 60 prefix := strings.ToUpper(lib) 61 functions, reasons, err := parseHeader(prefix, headerFile) 62 headerFile.Close() 63 64 if reset { 65 err = nil 66 functions = make(map[string]int) 67 // Retain any reason codes above reservedReasonCode. 68 newReasons := make(map[string]int) 69 for key, value := range reasons { 70 if value >= reservedReasonCode { 71 newReasons[key] = value 72 } 73 } 74 reasons = newReasons 75 } 76 77 if err != nil { 78 return err 79 } 80 81 dir, err := os.Open(".") 82 if err != nil { 83 return err 84 } 85 defer dir.Close() 86 87 filenames, err := dir.Readdirnames(-1) 88 if err != nil { 89 return err 90 } 91 92 for _, name := range filenames { 93 if !strings.HasSuffix(name, ".c") || name == sourcePath { 94 continue 95 } 96 97 if err := addFunctionsAndReasons(functions, reasons, name, prefix); err != nil { 98 return err 99 } 100 } 101 102 assignNewValues(functions, -1) 103 assignNewValues(reasons, reservedReasonCode) 104 105 headerFile, err = os.Open(headerPath) 106 if err != nil { 107 return err 108 } 109 defer headerFile.Close() 110 111 newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) 112 if err != nil { 113 return err 114 } 115 defer newHeaderFile.Close() 116 117 if err := writeHeaderFile(newHeaderFile, headerFile, prefix, functions, reasons); err != nil { 118 return err 119 } 120 os.Rename(headerPath+".tmp", headerPath) 121 122 sourceFile, err := os.OpenFile(sourcePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 123 if err != nil { 124 return err 125 } 126 defer sourceFile.Close() 127 128 fmt.Fprintf(sourceFile, `/* Copyright (c) 2014, Google Inc. 129 * 130 * Permission to use, copy, modify, and/or distribute this software for any 131 * purpose with or without fee is hereby granted, provided that the above 132 * copyright notice and this permission notice appear in all copies. 133 * 134 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 135 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 136 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 137 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 138 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 139 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 140 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 141 142 #include <openssl/err.h> 143 144 #include <openssl/%s.h> 145 146 const ERR_STRING_DATA %s_error_string_data[] = { 147 `, lib, prefix) 148 outputStrings(sourceFile, lib, typeFunctions, functions) 149 outputStrings(sourceFile, lib, typeReasons, reasons) 150 151 sourceFile.WriteString(" {0, NULL},\n};\n") 152 153 return nil 154 } 155 156 func findHeader(basename string) (path string, err error) { 157 includeDir := filepath.Join("..", "include") 158 159 fi, err := os.Stat(includeDir) 160 if err != nil && os.IsNotExist(err) { 161 includeDir = filepath.Join("..", includeDir) 162 fi, err = os.Stat(includeDir) 163 } 164 if err != nil { 165 return "", errors.New("cannot find path to include directory") 166 } 167 if !fi.IsDir() { 168 return "", errors.New("include node is not a directory") 169 } 170 return filepath.Join(includeDir, "openssl", basename), nil 171 } 172 173 type assignment struct { 174 key string 175 value int 176 } 177 178 type assignmentsSlice []assignment 179 180 func (a assignmentsSlice) Len() int { 181 return len(a) 182 } 183 184 func (a assignmentsSlice) Less(i, j int) bool { 185 return a[i].value < a[j].value 186 } 187 188 func (a assignmentsSlice) Swap(i, j int) { 189 a[i], a[j] = a[j], a[i] 190 } 191 192 func outputAssignments(w io.Writer, assignments map[string]int) { 193 var sorted assignmentsSlice 194 195 for key, value := range assignments { 196 sorted = append(sorted, assignment{key, value}) 197 } 198 199 sort.Sort(sorted) 200 201 for _, assignment := range sorted { 202 fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value) 203 } 204 } 205 206 func parseDefineLine(line, lib string) (typ int, key string, value int, ok bool) { 207 if !strings.HasPrefix(line, "#define ") { 208 return 209 } 210 211 fields := strings.Fields(line) 212 if len(fields) != 3 { 213 return 214 } 215 216 funcPrefix := lib + "_F_" 217 reasonPrefix := lib + "_R_" 218 219 key = fields[1] 220 switch { 221 case strings.HasPrefix(key, funcPrefix): 222 typ = typeFunctions 223 case strings.HasPrefix(key, reasonPrefix): 224 typ = typeReasons 225 default: 226 return 227 } 228 229 var err error 230 if value, err = strconv.Atoi(fields[2]); err != nil { 231 return 232 } 233 234 ok = true 235 return 236 } 237 238 func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, functions, reasons map[string]int) error { 239 var last []byte 240 var haveLast, sawDefine bool 241 newLine := []byte("\n") 242 243 scanner := bufio.NewScanner(headerFile) 244 for scanner.Scan() { 245 line := scanner.Text() 246 _, _, _, ok := parseDefineLine(line, lib) 247 if ok { 248 sawDefine = true 249 continue 250 } 251 252 if haveLast { 253 w.Write(last) 254 w.Write(newLine) 255 } 256 257 if len(line) > 0 || !sawDefine { 258 last = []byte(line) 259 haveLast = true 260 } else { 261 haveLast = false 262 } 263 sawDefine = false 264 } 265 266 if err := scanner.Err(); err != nil { 267 return err 268 } 269 270 outputAssignments(w, functions) 271 outputAssignments(w, reasons) 272 w.Write(newLine) 273 274 if haveLast { 275 w.Write(last) 276 w.Write(newLine) 277 } 278 279 return nil 280 } 281 282 const ( 283 typeFunctions = iota 284 typeReasons 285 ) 286 287 func outputStrings(w io.Writer, lib string, ty int, assignments map[string]int) { 288 lib = strings.ToUpper(lib) 289 prefixLen := len(lib + "_F_") 290 291 keys := make([]string, 0, len(assignments)) 292 for key := range assignments { 293 keys = append(keys, key) 294 } 295 sort.Strings(keys) 296 297 for _, key := range keys { 298 var pack string 299 if ty == typeFunctions { 300 pack = key + ", 0" 301 } else { 302 pack = "0, " + key 303 } 304 305 fmt.Fprintf(w, " {ERR_PACK(ERR_LIB_%s, %s), \"%s\"},\n", lib, pack, key[prefixLen:]) 306 } 307 } 308 309 func assignNewValues(assignments map[string]int, reserved int) { 310 max := 99 311 312 for _, value := range assignments { 313 if reserved >= 0 && value >= reserved { 314 continue 315 } 316 if value > max { 317 max = value 318 } 319 } 320 321 max++ 322 323 for key, value := range assignments { 324 if value == -1 { 325 if reserved >= 0 && max >= reserved { 326 // If this happens, try passing 327 // -reset. Otherwise bump up reservedReasonCode. 328 panic("Automatically-assigned values exceeded limit!") 329 } 330 assignments[key] = max 331 max++ 332 } 333 } 334 } 335 336 func handleDeclareMacro(line, join, macroName string, m map[string]int) { 337 if i := strings.Index(line, macroName); i >= 0 { 338 contents := line[i+len(macroName):] 339 if i := strings.Index(contents, ")"); i >= 0 { 340 contents = contents[:i] 341 args := strings.Split(contents, ",") 342 for i := range args { 343 args[i] = strings.TrimSpace(args[i]) 344 } 345 if len(args) != 2 { 346 panic("Bad macro line: " + line) 347 } 348 token := args[0] + join + args[1] 349 if _, ok := m[token]; !ok { 350 m[token] = -1 351 } 352 } 353 } 354 } 355 356 func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix string) error { 357 file, err := os.Open(filename) 358 if err != nil { 359 return err 360 } 361 defer file.Close() 362 363 prefix += "_" 364 reasonPrefix := prefix + "R_" 365 var currentFunction string 366 367 scanner := bufio.NewScanner(file) 368 for scanner.Scan() { 369 line := scanner.Text() 370 371 if len(line) > 0 && unicode.IsLetter(rune(line[0])) { 372 /* Function start */ 373 fields := strings.Fields(line) 374 for _, field := range fields { 375 if i := strings.Index(field, "("); i != -1 { 376 f := field[:i] 377 // The return type of some functions is 378 // a macro that contains a "(". 379 if f == "STACK_OF" { 380 continue 381 } 382 currentFunction = f 383 for len(currentFunction) > 0 && currentFunction[0] == '*' { 384 currentFunction = currentFunction[1:] 385 } 386 break 387 } 388 } 389 } 390 391 if strings.Contains(line, "OPENSSL_PUT_ERROR(") { 392 functionToken := prefix + "F_" + currentFunction 393 if _, ok := functions[functionToken]; !ok { 394 functions[functionToken] = -1 395 } 396 } 397 398 handleDeclareMacro(line, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons) 399 handleDeclareMacro(line, "_F_", "OPENSSL_DECLARE_ERROR_FUNCTION(", functions) 400 401 for len(line) > 0 { 402 i := strings.Index(line, prefix) 403 if i == -1 { 404 break 405 } 406 407 line = line[i:] 408 end := strings.IndexFunc(line, func(r rune) bool { 409 return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) 410 }) 411 if end == -1 { 412 end = len(line) 413 } 414 415 var token string 416 token, line = line[:end], line[end:] 417 418 switch { 419 case strings.HasPrefix(token, reasonPrefix): 420 if _, ok := reasons[token]; !ok { 421 reasons[token] = -1 422 } 423 } 424 } 425 } 426 427 return scanner.Err() 428 } 429 430 func parseHeader(lib string, file io.Reader) (functions, reasons map[string]int, err error) { 431 functions = make(map[string]int) 432 reasons = make(map[string]int) 433 434 scanner := bufio.NewScanner(file) 435 for scanner.Scan() { 436 typ, key, value, ok := parseDefineLine(scanner.Text(), lib) 437 if !ok { 438 continue 439 } 440 441 switch typ { 442 case typeFunctions: 443 functions[key] = value 444 case typeReasons: 445 reasons[key] = value 446 default: 447 panic("internal error") 448 } 449 } 450 451 err = scanner.Err() 452 return 453 } 454 455 func main() { 456 flag.Parse() 457 458 if err := makeErrors(*resetFlag); err != nil { 459 fmt.Fprintf(os.Stderr, "%s\n", err) 460 os.Exit(1) 461 } 462 } 463