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//go:build ignore 16 17package main 18 19import ( 20 "bufio" 21 "errors" 22 "flag" 23 "fmt" 24 "io" 25 "os" 26 "path/filepath" 27 "sort" 28 "strconv" 29 "strings" 30) 31 32// ssl.h reserves values 1000 and above for error codes corresponding to 33// alerts. If automatically assigned reason codes exceed this value, this script 34// will error. This must be kept in sync with SSL_AD_REASON_OFFSET in ssl.h. 35const reservedReasonCode = 1000 36 37var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch") 38 39type libraryInfo struct { 40 sourceDirs []string 41 headerName string 42} 43 44func getLibraryInfo(lib string) libraryInfo { 45 var info libraryInfo 46 if lib == "ssl" { 47 info.sourceDirs = []string{"ssl"} 48 } else { 49 info.sourceDirs = []string{ 50 filepath.Join("crypto", lib), 51 filepath.Join("crypto", lib+"_extra"), 52 filepath.Join("crypto", "fipsmodule", lib), 53 } 54 } 55 info.headerName = lib + ".h" 56 57 if lib == "evp" { 58 info.headerName = "evp_errors.h" 59 info.sourceDirs = append(info.sourceDirs, filepath.Join("crypto", "hpke")) 60 } 61 62 return info 63} 64 65func makeErrors(lib string, reset bool) error { 66 topLevelPath, err := findToplevel() 67 if err != nil { 68 return err 69 } 70 71 info := getLibraryInfo(lib) 72 73 headerPath := filepath.Join(topLevelPath, "include", "openssl", info.headerName) 74 errDir := filepath.Join(topLevelPath, "crypto", "err") 75 dataPath := filepath.Join(errDir, lib+".errordata") 76 77 headerFile, err := os.Open(headerPath) 78 if err != nil { 79 if os.IsNotExist(err) { 80 return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath) 81 } 82 83 return err 84 } 85 86 prefix := strings.ToUpper(lib) 87 reasons, err := parseHeader(prefix, headerFile) 88 headerFile.Close() 89 90 if reset { 91 err = nil 92 // Retain any reason codes above reservedReasonCode. 93 newReasons := make(map[string]int) 94 for key, value := range reasons { 95 if value >= reservedReasonCode { 96 newReasons[key] = value 97 } 98 } 99 reasons = newReasons 100 } 101 102 if err != nil { 103 return err 104 } 105 106 for _, sourceDir := range info.sourceDirs { 107 fullPath := filepath.Join(topLevelPath, sourceDir) 108 dir, err := os.Open(fullPath) 109 if err != nil { 110 if os.IsNotExist(err) { 111 // Some directories in the search path may not exist. 112 continue 113 } 114 return err 115 } 116 defer dir.Close() 117 filenames, err := dir.Readdirnames(-1) 118 if err != nil { 119 return err 120 } 121 122 for _, name := range filenames { 123 if !strings.HasSuffix(name, ".c") && !strings.HasSuffix(name, ".cc") { 124 continue 125 } 126 127 if err := addReasons(reasons, filepath.Join(fullPath, name), prefix); err != nil { 128 return err 129 } 130 } 131 } 132 133 assignNewValues(reasons, reservedReasonCode) 134 135 headerFile, err = os.Open(headerPath) 136 if err != nil { 137 return err 138 } 139 defer headerFile.Close() 140 141 newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) 142 if err != nil { 143 return err 144 } 145 defer newHeaderFile.Close() 146 147 if err := writeHeaderFile(newHeaderFile, headerFile, prefix, reasons); err != nil { 148 return err 149 } 150 // Windows forbids renaming an open file. 151 headerFile.Close() 152 newHeaderFile.Close() 153 if err := os.Rename(headerPath+".tmp", headerPath); err != nil { 154 return err 155 } 156 157 dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 158 if err != nil { 159 return err 160 } 161 162 outputStrings(dataFile, lib, reasons) 163 dataFile.Close() 164 165 return nil 166} 167 168func findToplevel() (path string, err error) { 169 path = "." 170 buildingPath := filepath.Join(path, "BUILDING.md") 171 172 _, err = os.Stat(buildingPath) 173 for i := 0; i < 2 && err != nil && os.IsNotExist(err); i++ { 174 if i == 0 { 175 path = ".." 176 } else { 177 path = filepath.Join("..", path) 178 } 179 buildingPath = filepath.Join(path, "BUILDING.md") 180 _, err = os.Stat(buildingPath) 181 } 182 if err != nil { 183 return "", errors.New("Cannot find BUILDING.md file at the top-level") 184 } 185 return path, nil 186} 187 188type assignment struct { 189 key string 190 value int 191} 192 193func outputAssignments(w io.Writer, assignments map[string]int) { 194 sorted := make([]assignment, 0, len(assignments)) 195 for key, value := range assignments { 196 sorted = append(sorted, assignment{key, value}) 197 } 198 199 sort.Slice(sorted, func(i, j int) bool { return sorted[i].value < sorted[j].value }) 200 201 for _, assignment := range sorted { 202 fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value) 203 } 204} 205 206func parseDefineLine(line, lib string) (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 key = fields[1] 217 if !strings.HasPrefix(key, lib+"_R_") { 218 return 219 } 220 221 var err error 222 if value, err = strconv.Atoi(fields[2]); err != nil { 223 return 224 } 225 226 ok = true 227 return 228} 229 230func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, reasons map[string]int) error { 231 var last []byte 232 var haveLast, sawDefine bool 233 newLine := []byte("\n") 234 235 scanner := bufio.NewScanner(headerFile) 236 for scanner.Scan() { 237 line := scanner.Text() 238 _, _, ok := parseDefineLine(line, lib) 239 if ok { 240 sawDefine = true 241 continue 242 } 243 244 if haveLast { 245 w.Write(last) 246 w.Write(newLine) 247 } 248 249 if len(line) > 0 || !sawDefine { 250 last = []byte(line) 251 haveLast = true 252 } else { 253 haveLast = false 254 } 255 sawDefine = false 256 } 257 258 if err := scanner.Err(); err != nil { 259 return err 260 } 261 262 outputAssignments(w, reasons) 263 w.Write(newLine) 264 265 if haveLast { 266 w.Write(last) 267 w.Write(newLine) 268 } 269 270 return nil 271} 272 273func outputStrings(w io.Writer, lib string, assignments map[string]int) { 274 lib = strings.ToUpper(lib) 275 prefixLen := len(lib + "_R_") 276 277 keys := make([]string, 0, len(assignments)) 278 for key := range assignments { 279 keys = append(keys, key) 280 } 281 sort.Strings(keys) 282 283 for _, key := range keys { 284 fmt.Fprintf(w, "%s,%d,%s\n", lib, assignments[key], key[prefixLen:]) 285 } 286} 287 288func assignNewValues(assignments map[string]int, reserved int) { 289 // Needs to be in sync with the reason limit in 290 // |ERR_reason_error_string|. 291 max := 99 292 293 for _, value := range assignments { 294 if reserved >= 0 && value >= reserved { 295 continue 296 } 297 if value > max { 298 max = value 299 } 300 } 301 302 max++ 303 304 // Sort the keys, so this script is reproducible. 305 keys := make([]string, 0, len(assignments)) 306 for key, value := range assignments { 307 if value == -1 { 308 keys = append(keys, key) 309 } 310 } 311 sort.Strings(keys) 312 313 for _, key := range keys { 314 if reserved >= 0 && max >= reserved { 315 // If this happens, try passing -reset. Otherwise bump 316 // up reservedReasonCode. 317 panic("Automatically-assigned values exceeded limit!") 318 } 319 assignments[key] = max 320 max++ 321 } 322} 323 324func handleDeclareMacro(line, join, macroName string, m map[string]int) { 325 if i := strings.Index(line, macroName); i >= 0 { 326 contents := line[i+len(macroName):] 327 if i := strings.Index(contents, ")"); i >= 0 { 328 contents = contents[:i] 329 args := strings.Split(contents, ",") 330 for i := range args { 331 args[i] = strings.TrimSpace(args[i]) 332 } 333 if len(args) != 2 { 334 panic("Bad macro line: " + line) 335 } 336 token := args[0] + join + args[1] 337 if _, ok := m[token]; !ok { 338 m[token] = -1 339 } 340 } 341 } 342} 343 344func addReasons(reasons map[string]int, filename, prefix string) error { 345 file, err := os.Open(filename) 346 if err != nil { 347 return err 348 } 349 defer file.Close() 350 351 reasonPrefix := prefix + "_R_" 352 353 scanner := bufio.NewScanner(file) 354 for scanner.Scan() { 355 line := scanner.Text() 356 357 handleDeclareMacro(line, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons) 358 359 for len(line) > 0 { 360 i := strings.Index(line, prefix+"_") 361 if i == -1 { 362 break 363 } 364 365 line = line[i:] 366 end := strings.IndexFunc(line, func(r rune) bool { 367 return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) 368 }) 369 if end == -1 { 370 end = len(line) 371 } 372 373 var token string 374 token, line = line[:end], line[end:] 375 376 switch { 377 case strings.HasPrefix(token, reasonPrefix): 378 if _, ok := reasons[token]; !ok { 379 reasons[token] = -1 380 } 381 } 382 } 383 } 384 385 return scanner.Err() 386} 387 388func parseHeader(lib string, file io.Reader) (reasons map[string]int, err error) { 389 reasons = make(map[string]int) 390 391 scanner := bufio.NewScanner(file) 392 for scanner.Scan() { 393 key, value, ok := parseDefineLine(scanner.Text(), lib) 394 if !ok { 395 continue 396 } 397 398 reasons[key] = value 399 } 400 401 err = scanner.Err() 402 return 403} 404 405func main() { 406 flag.Parse() 407 if flag.NArg() == 0 { 408 fmt.Fprintf(os.Stderr, "Usage: make_errors.go LIB [LIB2...]\n") 409 os.Exit(1) 410 } 411 412 for _, lib := range flag.Args() { 413 if err := makeErrors(lib, *resetFlag); err != nil { 414 fmt.Fprintf(os.Stderr, "Error generating errors for %q: %s\n", lib, err) 415 os.Exit(1) 416 } 417 } 418} 419