.ctags.d is previous, include it in our tarballs
[openssl.git] / test / recipes / 02-test_errstr.t
1 #! /usr/bin/env perl
2 # Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the Apache License 2.0 (the "License").  You may not use
5 # this file except in compliance with the License.  You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
8
9 use strict;
10 no strict 'refs';               # To be able to use strings as function refs
11 use OpenSSL::Test;
12 use OpenSSL::Test::Utils;
13 use Errno qw(:POSIX);
14 use POSIX qw(:limits_h strerror);
15
16 use Data::Dumper;
17
18 setup('test_errstr');
19
20 # In a cross compiled situation, there are chances that our
21 # application is linked against different C libraries than
22 # perl, and may thereby get different error messages for the
23 # same error.
24 # The safest is not to test under such circumstances.
25 plan skip_all => 'This is unsupported for cross compiled configurations'
26     if config('CROSS_COMPILE');
27
28 # The same can be said when compiling OpenSSL with mingw configuration
29 # on Windows when built with msys perl.  Similar problems are also observed
30 # in MSVC builds, depending on the perl implementation used.
31 plan skip_all => 'This is unsupported on MSYS/MinGW or MSWin32'
32     if $^O eq 'msys' or $^O eq 'MSWin32';
33
34 plan skip_all => 'OpenSSL is configured "no-autoerrinit" or "no-err"'
35     if disabled('autoerrinit') || disabled('err');
36
37 # OpenSSL constants found in <openssl/err.h>
38 use constant ERR_SYSTEM_FLAG => INT_MAX + 1;
39 use constant ERR_LIB_OFFSET => 23; # Offset of the "library" errcode section
40
41 # OpenSSL "library" numbers
42 use constant ERR_LIB_NONE => 1;
43
44 # We use Errno::EXPORT_OK as a list of known errno values on the current
45 # system.  libcrypto's ERR should either use the same string as perl, or if
46 # it was outside the range that ERR looks at, ERR gives the reason string
47 # "reason(nnn)", where nnn is the errno number.
48
49 plan tests => scalar @Errno::EXPORT_OK
50     +1                          # Checking that error 128 gives 'reason(128)'
51     +1                          # Checking that error 0 gives the library name
52     +1;                         # Check trailing whitespace is removed.
53
54 # Test::More:ok() has a sub prototype, which means we need to use the '&ok'
55 # syntax to force it to accept a list as a series of arguments.
56
57 foreach my $errname (@Errno::EXPORT_OK) {
58     # The error names are perl constants, which are implemented as functions
59     # returning the numeric value of that name.
60     my $errcode = "Errno::$errname"->();
61
62   SKIP: {
63       # On most systems, there is no E macro for errcode zero in <errno.h>,
64       # which means that it seldom comes up here.  However, reports indicate
65       # that some platforms do have an E macro for errcode zero.
66       # With perl, errcode zero is a bit special.  Perl consistently gives
67       # the empty string for that one, while the C strerror() may give back
68       # something else.  The easiest way to deal with that possible mismatch
69       # is to skip this errcode.
70       skip "perl error strings and ssystem error strings for errcode 0 differ", 1
71           if $errcode == 0;
72
73       &ok(match_syserr_reason($errcode));
74     }
75 }
76
77 # OpenSSL library 1 is the "unknown" library
78 &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 256,
79                             "reason(256)"));
80 # Reason code 0 of any library gives the library name as reason
81 &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET |   0,
82                             "unknown library"));
83 &ok(match_any("Trailing whitespace  \n\t", "?", ( "Trailing whitespace" )));
84
85 exit 0;
86
87 # For an error string "error:xxxxxxxx:lib:func:reason", this returns
88 # the following array:
89 #
90 # ( "xxxxxxxx", "lib", "func", "reason" )
91 sub split_error {
92     # Limit to 5 items, in case the reason contains a colon
93     my @erritems = split /:/, $_[0], 5;
94
95     # Remove the first item, which is always "error"
96     shift @erritems;
97
98     return @erritems;
99 }
100
101 # Compares the first argument as string to each of the arguments 3 and on,
102 # and returns an array of two elements:
103 # 0:  True if the first argument matched any of the others, otherwise false
104 # 1:  A string describing the test
105 # The returned array can be used as the arguments to Test::More::ok()
106 sub match_any {
107     my $first = shift;
108     my $desc = shift;
109     my @strings = @_;
110
111     # ignore trailing whitespace
112     $first =~ s/\s+$//;
113
114     if (scalar @strings > 1) {
115         $desc = "match '$first' ($desc) with one of ( '"
116             . join("', '", @strings) . "' )";
117     } else {
118         $desc = "match '$first' ($desc) with '$strings[0]'";
119     }
120
121     return ( scalar( grep { $first eq $_ } @strings ) > 0,
122              $desc );
123 }
124
125 sub match_opensslerr_reason {
126     my $errcode = shift;
127     my @strings = @_;
128
129     my $errcode_hex = sprintf "%x", $errcode;
130     my $reason =
131         ( run(app([ qw(openssl errstr), $errcode_hex ]), capture => 1) )[0];
132     $reason =~ s|\R$||;
133     $reason = ( split_error($reason) )[3];
134
135     return match_any($reason, $errcode, @strings);
136 }
137
138 sub match_syserr_reason {
139     my $errcode = shift;
140
141     my @strings = ();
142     # The POSIX reason string
143     push @strings, eval {
144           # Set $! to the error number...
145           local $! = $errcode;
146           # ... and $! will give you the error string back
147           $!
148     };
149     # The OpenSSL fallback string
150     push @strings, "reason($errcode)";
151
152     return match_opensslerr_reason(ERR_SYSTEM_FLAG | $errcode, @strings);
153 }