123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- #! /usr/bin/env perl
- # Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
- #
- # Licensed under the Apache License 2.0 (the "License"). You may not use
- # this file except in compliance with the License. You can obtain a copy
- # in the file LICENSE in the source distribution or at
- # https://www.openssl.org/source/license.html
- use strict;
- no strict 'refs'; # To be able to use strings as function refs
- use OpenSSL::Test;
- use OpenSSL::Test::Utils;
- use Errno qw(:POSIX);
- use POSIX qw(:limits_h strerror);
- use Data::Dumper;
- setup('test_errstr');
- # In a cross compiled situation, there are chances that our
- # application is linked against different C libraries than
- # perl, and may thereby get different error messages for the
- # same error.
- # The safest is not to test under such circumstances.
- plan skip_all => 'This is unsupported for cross compiled configurations'
- if config('CROSS_COMPILE');
- # The same can be said when compiling OpenSSL with mingw configuration
- # on Windows when built with msys perl. Similar problems are also observed
- # in MSVC builds, depending on the perl implementation used.
- plan skip_all => 'This is unsupported on MSYS/MinGW or MSWin32'
- if $^O eq 'msys' or $^O eq 'MSWin32';
- plan skip_all => 'OpenSSL is configured "no-autoerrinit" or "no-err"'
- if disabled('autoerrinit') || disabled('err');
- # OpenSSL constants found in <openssl/err.h>
- use constant ERR_SYSTEM_FLAG => INT_MAX + 1;
- use constant ERR_LIB_OFFSET => 23; # Offset of the "library" errcode section
- # OpenSSL "library" numbers
- use constant ERR_LIB_NONE => 1;
- # We use Errno::EXPORT_OK as a list of known errno values on the current
- # system. libcrypto's ERR should either use the same string as perl, or if
- # it was outside the range that ERR looks at, ERR gives the reason string
- # "reason(nnn)", where nnn is the errno number.
- plan tests => scalar @Errno::EXPORT_OK
- +1 # Checking that error 128 gives 'reason(128)'
- +1 # Checking that error 0 gives the library name
- +1; # Check trailing whitespace is removed.
- # Test::More:ok() has a sub prototype, which means we need to use the '&ok'
- # syntax to force it to accept a list as a series of arguments.
- foreach my $errname (@Errno::EXPORT_OK) {
- # The error names are perl constants, which are implemented as functions
- # returning the numeric value of that name.
- my $errcode = "Errno::$errname"->();
- SKIP: {
- # On most systems, there is no E macro for errcode zero in <errno.h>,
- # which means that it seldom comes up here. However, reports indicate
- # that some platforms do have an E macro for errcode zero.
- # With perl, errcode zero is a bit special. Perl consistently gives
- # the empty string for that one, while the C strerror() may give back
- # something else. The easiest way to deal with that possible mismatch
- # is to skip this errcode.
- skip "perl error strings and ssystem error strings for errcode 0 differ", 1
- if $errcode == 0;
- # On some systems (for example Hurd), there are negative error codes.
- # These are currently unsupported in OpenSSL error reports.
- skip "negative error codes are not supported in OpenSSL", 1
- if $errcode < 0;
- &ok(match_syserr_reason($errcode));
- }
- }
- # OpenSSL library 1 is the "unknown" library
- &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 256,
- "reason(256)"));
- # Reason code 0 of any library gives the library name as reason
- &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 0,
- "unknown library"));
- &ok(match_any("Trailing whitespace \n\t", "?", ( "Trailing whitespace" )));
- exit 0;
- # For an error string "error:xxxxxxxx:lib:func:reason", this returns
- # the following array:
- #
- # ( "xxxxxxxx", "lib", "func", "reason" )
- sub split_error {
- # Limit to 5 items, in case the reason contains a colon
- my @erritems = split /:/, $_[0], 5;
- # Remove the first item, which is always "error"
- shift @erritems;
- return @erritems;
- }
- # Compares the first argument as string to each of the arguments 3 and on,
- # and returns an array of two elements:
- # 0: True if the first argument matched any of the others, otherwise false
- # 1: A string describing the test
- # The returned array can be used as the arguments to Test::More::ok()
- sub match_any {
- my $first = shift;
- my $desc = shift;
- my @strings = @_;
- # ignore trailing whitespace
- $first =~ s/\s+$//;
- if (scalar @strings > 1) {
- $desc = "match '$first' ($desc) with one of ( '"
- . join("', '", @strings) . "' )";
- } else {
- $desc = "match '$first' ($desc) with '$strings[0]'";
- }
- return ( scalar(
- grep { ref $_ eq 'Regexp' ? $first =~ $_ : $first eq $_ }
- @strings
- ) > 0,
- $desc );
- }
- sub match_opensslerr_reason {
- my $errcode = shift;
- my @strings = @_;
- my $errcode_hex = sprintf "%x", $errcode;
- my $reason =
- ( run(app([ qw(openssl errstr), $errcode_hex ]), capture => 1) )[0];
- $reason =~ s|\R$||;
- $reason = ( split_error($reason) )[3];
- return match_any($reason, $errcode_hex, @strings);
- }
- sub match_syserr_reason {
- my $errcode = shift;
- my @strings = ();
- # The POSIX reason string
- push @strings, eval {
- # Set $! to the error number...
- local $! = $errcode;
- # ... and $! will give you the error string back
- $!
- };
- # Occasionally, we get an error code that is simply not translatable
- # to POSIX semantics on VMS, and we get an error string saying so.
- push @strings, qr/^non-translatable vms error code:/ if $^O eq 'VMS';
- # The OpenSSL fallback string
- push @strings, "reason($errcode)";
- return match_opensslerr_reason(ERR_SYSTEM_FLAG | $errcode, @strings);
- }
|