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