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 topLevelPath, err := findToplevel() 40 if err != nil { 41 return err 42 } 43 44 dirName, err := os.Getwd() 45 if err != nil { 46 return err 47 } 48 49 lib := filepath.Base(dirName) 50 headerPath := filepath.Join(topLevelPath, "include", "openssl", lib+".h") 51 errDir := filepath.Join(topLevelPath, "crypto", "err") 52 dataPath := filepath.Join(errDir, lib+".errordata") 53 54 headerFile, err := os.Open(headerPath) 55 if err != nil { 56 if os.IsNotExist(err) { 57 return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath) 58 } 59 60 return err 61 } 62 63 prefix := strings.ToUpper(lib) 64 functions, reasons, err := parseHeader(prefix, headerFile) 65 headerFile.Close() 66 67 if reset { 68 err = nil 69 functions = make(map[string]int) 70 // Retain any reason codes above reservedReasonCode. 71 newReasons := make(map[string]int) 72 for key, value := range reasons { 73 if value >= reservedReasonCode { 74 newReasons[key] = value 75 } 76 } 77 reasons = newReasons 78 } 79 80 if err != nil { 81 return err 82 } 83 84 dir, err := os.Open(".") 85 if err != nil { 86 return err 87 } 88 defer dir.Close() 89 90 filenames, err := dir.Readdirnames(-1) 91 if err != nil { 92 return err 93 } 94 95 for _, name := range filenames { 96 if !strings.HasSuffix(name, ".c") { 97 continue 98 } 99 100 if err := addFunctionsAndReasons(functions, reasons, name, prefix); err != nil { 101 return err 102 } 103 } 104 105 assignNewValues(functions, -1) 106 assignNewValues(reasons, reservedReasonCode) 107 108 headerFile, err = os.Open(headerPath) 109 if err != nil { 110 return err 111 } 112 defer headerFile.Close() 113 114 newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) 115 if err != nil { 116 return err 117 } 118 defer newHeaderFile.Close() 119 120 if err := writeHeaderFile(newHeaderFile, headerFile, prefix, functions, reasons); err != nil { 121 return err 122 } 123 os.Rename(headerPath+".tmp", headerPath) 124 125 dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 126 if err != nil { 127 return err 128 } 129 130 outputStrings(dataFile, lib, typeFunctions, functions) 131 outputStrings(dataFile, lib, typeReasons, reasons) 132 dataFile.Close() 133 134 return nil 135 } 136 137 func findToplevel() (path string, err error) { 138 path = ".." 139 buildingPath := filepath.Join(path, "BUILDING") 140 141 _, err = os.Stat(buildingPath) 142 if err != nil && os.IsNotExist(err) { 143 path = filepath.Join("..", path) 144 buildingPath = filepath.Join(path, "BUILDING") 145 _, err = os.Stat(buildingPath) 146 } 147 if err != nil { 148 return "", errors.New("Cannot find BUILDING file at the top-level") 149 } 150 return path, nil 151 } 152 153 type assignment struct { 154 key string 155 value int 156 } 157 158 type assignmentsSlice []assignment 159 160 func (a assignmentsSlice) Len() int { 161 return len(a) 162 } 163 164 func (a assignmentsSlice) Less(i, j int) bool { 165 return a[i].value < a[j].value 166 } 167 168 func (a assignmentsSlice) Swap(i, j int) { 169 a[i], a[j] = a[j], a[i] 170 } 171 172 func outputAssignments(w io.Writer, assignments map[string]int) { 173 var sorted assignmentsSlice 174 175 for key, value := range assignments { 176 sorted = append(sorted, assignment{key, value}) 177 } 178 179 sort.Sort(sorted) 180 181 for _, assignment := range sorted { 182 fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value) 183 } 184 } 185 186 func parseDefineLine(line, lib string) (typ int, key string, value int, ok bool) { 187 if !strings.HasPrefix(line, "#define ") { 188 return 189 } 190 191 fields := strings.Fields(line) 192 if len(fields) != 3 { 193 return 194 } 195 196 funcPrefix := lib + "_F_" 197 reasonPrefix := lib + "_R_" 198 199 key = fields[1] 200 switch { 201 case strings.HasPrefix(key, funcPrefix): 202 typ = typeFunctions 203 case strings.HasPrefix(key, reasonPrefix): 204 typ = typeReasons 205 default: 206 return 207 } 208 209 var err error 210 if value, err = strconv.Atoi(fields[2]); err != nil { 211 return 212 } 213 214 ok = true 215 return 216 } 217 218 func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, functions, reasons map[string]int) error { 219 var last []byte 220 var haveLast, sawDefine bool 221 newLine := []byte("\n") 222 223 scanner := bufio.NewScanner(headerFile) 224 for scanner.Scan() { 225 line := scanner.Text() 226 _, _, _, ok := parseDefineLine(line, lib) 227 if ok { 228 sawDefine = true 229 continue 230 } 231 232 if haveLast { 233 w.Write(last) 234 w.Write(newLine) 235 } 236 237 if len(line) > 0 || !sawDefine { 238 last = []byte(line) 239 haveLast = true 240 } else { 241 haveLast = false 242 } 243 sawDefine = false 244 } 245 246 if err := scanner.Err(); err != nil { 247 return err 248 } 249 250 outputAssignments(w, functions) 251 outputAssignments(w, reasons) 252 w.Write(newLine) 253 254 if haveLast { 255 w.Write(last) 256 w.Write(newLine) 257 } 258 259 return nil 260 } 261 262 const ( 263 typeFunctions = iota 264 typeReasons 265 ) 266 267 func outputStrings(w io.Writer, lib string, ty int, assignments map[string]int) { 268 lib = strings.ToUpper(lib) 269 prefixLen := len(lib + "_F_") 270 271 keys := make([]string, 0, len(assignments)) 272 for key := range assignments { 273 keys = append(keys, key) 274 } 275 sort.Strings(keys) 276 277 for _, key := range keys { 278 typeString := "function" 279 if ty == typeReasons { 280 typeString = "reason" 281 } 282 fmt.Fprintf(w, "%s,%s,%d,%s\n", lib, typeString, assignments[key], key[prefixLen:]) 283 } 284 } 285 286 func assignNewValues(assignments map[string]int, reserved int) { 287 // Needs to be in sync with the reason limit in 288 // |ERR_reason_error_string|. 289 max := 99 290 291 for _, value := range assignments { 292 if reserved >= 0 && value >= reserved { 293 continue 294 } 295 if value > max { 296 max = value 297 } 298 } 299 300 max++ 301 302 // Sort the keys, so this script is reproducible. 303 keys := make([]string, 0, len(assignments)) 304 for key, value := range assignments { 305 if value == -1 { 306 keys = append(keys, key) 307 } 308 } 309 sort.Strings(keys) 310 311 for _, key := range keys { 312 if reserved >= 0 && max >= reserved { 313 // If this happens, try passing -reset. Otherwise bump 314 // up reservedReasonCode. 315 panic("Automatically-assigned values exceeded limit!") 316 } 317 assignments[key] = max 318 max++ 319 } 320 } 321 322 func handleDeclareMacro(line, join, macroName string, m map[string]int) { 323 if i := strings.Index(line, macroName); i >= 0 { 324 contents := line[i+len(macroName):] 325 if i := strings.Index(contents, ")"); i >= 0 { 326 contents = contents[:i] 327 args := strings.Split(contents, ",") 328 for i := range args { 329 args[i] = strings.TrimSpace(args[i]) 330 } 331 if len(args) != 2 { 332 panic("Bad macro line: " + line) 333 } 334 token := args[0] + join + args[1] 335 if _, ok := m[token]; !ok { 336 m[token] = -1 337 } 338 } 339 } 340 } 341 342 func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix string) error { 343 file, err := os.Open(filename) 344 if err != nil { 345 return err 346 } 347 defer file.Close() 348 349 reasonPrefix := prefix + "_R_" 350 var currentFunction string 351 352 scanner := bufio.NewScanner(file) 353 for scanner.Scan() { 354 line := scanner.Text() 355 356 if len(line) > 0 && unicode.IsLetter(rune(line[0])) { 357 /* Function start */ 358 fields := strings.Fields(line) 359 for _, field := range fields { 360 if i := strings.Index(field, "("); i != -1 { 361 f := field[:i] 362 // The return type of some functions is 363 // a macro that contains a "(". 364 if f == "STACK_OF" { 365 continue 366 } 367 currentFunction = f 368 for len(currentFunction) > 0 && currentFunction[0] == '*' { 369 currentFunction = currentFunction[1:] 370 } 371 break 372 } 373 } 374 } 375 376 // Do not include cross-module error lines. 377 if strings.Contains(line, "OPENSSL_PUT_ERROR(" + prefix + ",") { 378 functionToken := prefix + "_F_" + currentFunction 379 if _, ok := functions[functionToken]; !ok { 380 functions[functionToken] = -1 381 } 382 } 383 384 handleDeclareMacro(line, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons) 385 handleDeclareMacro(line, "_F_", "OPENSSL_DECLARE_ERROR_FUNCTION(", functions) 386 387 for len(line) > 0 { 388 i := strings.Index(line, prefix + "_") 389 if i == -1 { 390 break 391 } 392 393 line = line[i:] 394 end := strings.IndexFunc(line, func(r rune) bool { 395 return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) 396 }) 397 if end == -1 { 398 end = len(line) 399 } 400 401 var token string 402 token, line = line[:end], line[end:] 403 404 switch { 405 case strings.HasPrefix(token, reasonPrefix): 406 if _, ok := reasons[token]; !ok { 407 reasons[token] = -1 408 } 409 } 410 } 411 } 412 413 return scanner.Err() 414 } 415 416 func parseHeader(lib string, file io.Reader) (functions, reasons map[string]int, err error) { 417 functions = make(map[string]int) 418 reasons = make(map[string]int) 419 420 scanner := bufio.NewScanner(file) 421 for scanner.Scan() { 422 typ, key, value, ok := parseDefineLine(scanner.Text(), lib) 423 if !ok { 424 continue 425 } 426 427 switch typ { 428 case typeFunctions: 429 functions[key] = value 430 case typeReasons: 431 reasons[key] = value 432 default: 433 panic("internal error") 434 } 435 } 436 437 err = scanner.Err() 438 return 439 } 440 441 func main() { 442 flag.Parse() 443 444 if err := makeErrors(*resetFlag); err != nil { 445 fmt.Fprintf(os.Stderr, "%s\n", err) 446 os.Exit(1) 447 } 448 } 449