blob: 5fd75e27108bd320a27a1b318c378a8e0baf718a [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001// 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
15package main
16
17import (
18 "bufio"
Adam Langley4c921e12014-07-14 15:28:14 -070019 "errors"
Adam Langley95c29f32014-06-20 12:00:00 -070020 "flag"
21 "fmt"
22 "io"
23 "os"
24 "path/filepath"
25 "sort"
26 "strconv"
27 "strings"
28 "unicode"
29)
30
David Benjamin2e521212014-07-16 14:37:51 -040031// 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.
34const reservedReasonCode = 1000
35
Adam Langley95c29f32014-06-20 12:00:00 -070036var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch")
37
38func makeErrors(reset bool) error {
39 dirName, err := os.Getwd()
40 if err != nil {
41 return err
42 }
43
44 lib := filepath.Base(dirName)
Adam Langley4c921e12014-07-14 15:28:14 -070045 headerPath, err := findHeader(lib + ".h")
46 if err != nil {
47 return err
48 }
Adam Langley95c29f32014-06-20 12:00:00 -070049 sourcePath := lib + "_error.c"
50
51 headerFile, err := os.Open(headerPath)
52 if err != nil {
53 if os.IsNotExist(err) {
Adam Langley4c921e12014-07-14 15:28:14 -070054 return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath)
Adam Langley95c29f32014-06-20 12:00:00 -070055 }
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)
David Benjamin2e521212014-07-16 14:37:51 -040067 // 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
Adam Langley95c29f32014-06-20 12:00:00 -070075 }
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 {
HÃ¥vard Mollandfd003032014-08-20 14:58:27 +020093 if !strings.HasSuffix(name, ".c") || name == sourcePath {
Adam Langley95c29f32014-06-20 12:00:00 -070094 continue
95 }
96
97 if err := addFunctionsAndReasons(functions, reasons, name, prefix); err != nil {
98 return err
99 }
100 }
101
David Benjamin2e521212014-07-16 14:37:51 -0400102 assignNewValues(functions, -1)
103 assignNewValues(reasons, reservedReasonCode)
Adam Langley95c29f32014-06-20 12:00:00 -0700104
105 headerFile, err = os.Open(headerPath)
106 if err != nil {
107 return err
108 }
109 defer headerFile.Close()
110
Adam Langley4c921e12014-07-14 15:28:14 -0700111 newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
Adam Langley95c29f32014-06-20 12:00:00 -0700112 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 }
Adam Langley4c921e12014-07-14 15:28:14 -0700120 os.Rename(headerPath+".tmp", headerPath)
Adam Langley95c29f32014-06-20 12:00:00 -0700121
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
Adam Langley4c921e12014-07-14 15:28:14 -0700144#include <openssl/%s.h>
Adam Langley95c29f32014-06-20 12:00:00 -0700145
146const 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
Adam Langley4c921e12014-07-14 15:28:14 -0700156func 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
Adam Langley95c29f32014-06-20 12:00:00 -0700173type assignment struct {
174 key string
175 value int
176}
177
178type assignmentsSlice []assignment
179
180func (a assignmentsSlice) Len() int {
181 return len(a)
182}
183
184func (a assignmentsSlice) Less(i, j int) bool {
185 return a[i].value < a[j].value
186}
187
188func (a assignmentsSlice) Swap(i, j int) {
189 a[i], a[j] = a[j], a[i]
190}
191
192func 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
206func 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
238func 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
282const (
283 typeFunctions = iota
284 typeReasons
285)
286
287func 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
David Benjamin2e521212014-07-16 14:37:51 -0400309func assignNewValues(assignments map[string]int, reserved int) {
Adam Langley95c29f32014-06-20 12:00:00 -0700310 max := 99
311
312 for _, value := range assignments {
David Benjamin2e521212014-07-16 14:37:51 -0400313 if reserved >= 0 && value >= reserved {
314 continue
315 }
Adam Langley95c29f32014-06-20 12:00:00 -0700316 if value > max {
317 max = value
318 }
319 }
320
321 max++
322
323 for key, value := range assignments {
324 if value == -1 {
David Benjamin2e521212014-07-16 14:37:51 -0400325 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 }
Adam Langley95c29f32014-06-20 12:00:00 -0700330 assignments[key] = max
331 max++
332 }
333 }
334}
335
336func handleDeclareMacro(line, join, macroName string, m map[string]int) {
337 if i := strings.Index(line, macroName); i >= 0 {
Adam Langley4c921e12014-07-14 15:28:14 -0700338 contents := line[i+len(macroName):]
Adam Langley95c29f32014-06-20 12:00:00 -0700339 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
356func 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
430func 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
455func 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}