The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Crypt::OpenSSL::CA::Inline::C - A bag of XS and Inline::C tricks

SYNOPSIS

  package Crypt::OpenSSL::CA::Foo;

  use Crypt::OpenSSL::CA::Inline::C <<"C_CODE_SAMPLE";
  #include <openssl/x509.h>

  static
  SV* mysub() {
    // Your C code here
  }

  C_CODE_SAMPLE

  # Then maybe some Perl...

  use Crypt::OpenSSL::CA::Inline::C <<"MORE_C_CODE";

  static
  void another() {
    // ...
  }

  MORE_C_CODE

  use Crypt::OpenSSL::CA::Inline::C "__END__";

DESCRIPTION

This documentation is only useful for people who want to hack Crypt::OpenSSL::CA. It is of no interest for people who just want to use the module.

This package provides Inline::C goodness to Crypt::OpenSSL::CA during development, plus a few tricks of our own. The idiom in "SYNOPSIS", used throughout the source code of Crypt::OpenSSL::CA, recaps them all; noteworthy points are:

the static-newline trick

Because the C language doesn't have namespaces, we don't want symbols named e.g. new appearing in the .so's symbol tables: they could clash with other symbols defined by Perl, or with each other. Therefore we have to declare them static, but doing this in the naïve way would cause Inline::C to purposefully not bind them with Perl... The winning trick is to put the static word alone on its line, as demonstrated.

the "__END__" pragma

The code in Crypt::OpenSSL::CA must use the following pragma to signal that it won't attempt to add any Inline::C code after this point:

   use Crypt::OpenSSL::CA::Inline::C "__END__";

Standard Library

In addition to the standard library available to XS C code described in Inline::C, perlxstut, perlguts and perlapi, C code that compiles itself through Crypt::OpenSSL::CA::Inline::C has access to the following C functions:

static inline char* char0_value(SV* string)

Returns the string value of a Perl SV, making sure that it exists and is zero-terminated beforehand. If string is undef, returns the empty string (not NULL; see "char0_value_or_null"). See "Working with SVs" in perlguts, look for the word "Nevertheless" - I'm pretty sure there is a macro in Perl's convenience stuff that does exactly that already, but I don't know it...

static inline char* char0_value_or_null(SV* string)

Like "char0_value", except that NULL is returned if string is undef.

static inline SV* perl_wrap(class, pointer)

Creates read-only SV containing the integral value of pointer, blesses it into class class and returns it as a SV*. The return value is an adequate Perl wrapper to stand for pointer, as demonstrated in "Object Oriented Inline" in Inline::C-Cookbook.

perl_unwrap(class, typename, SV*)

The reverse of "perl_wrap". Given a "perl_wrap"ped SV*, asserts that it actually contains an object blessed in class class (lest it croaks), extracts the pointer within same, casts it into typename and returns it. This is a macro instead of a static inline, so as to be able to perform the polymorphic cast.

static inline SV* openssl_string_to_SV(char* string)

Copies over string to a newly-allocated SV* Perl scalar, and then frees string using OPENSSL_free(). Used to transfer ownership of strings from OpenSSL to Perl, and thereby ensure proper memory management.

Note to Crypt::OpenSSL::CA hackers: if string is on an OpenSSL static buffer instead of having been allocated by OpenSSL, this will SEGV in trying to free() string that was not malloc()'d; in this case you want to use "XSRETURN_PV" in perlapi instead or some such. Check the OpenSSL documentation carefully, and make use of "leaks_bytes_ok" in Crypt::OpenSSL::CA::Test to ascertain experimentally that your code doesn't leak memory.

static inline SV* openssl_buf_to_SV(char* string, int length)

Like "openssl_string_to_SV" except that the length is specified, which allows for string to not contain null characters or not be zero-terminated. Use this form e.g. for ASN.1 buffers returned by i2d_foobar OpenSSL functions.

static inline SV* BIO_mem_to_SV(BIO *bio)

This inline function is intended to be used to return scalar values (e.g. PEM strings and RSA moduli) constructed by OpenSSL. Should be invoked thusly, after having freed all temporary resources except *bio:

   return BIO_mem_to_SV(bio);

BIO_mem_to_SV() turns bio into a Perl scalar and returns it, or croak()s trying (hence the requirement not to have any outstanding memory resources allocated in the caller). Regardless of the outcome, bio will be BIO_free()d.

static void ensure_openssl_stuff_loaded()

Ensures that various stuff is loaded inside OpenSSL, such as ERR_load_crypto_strings(), OpenSSL_add_all_digests() and all that jazz. After this function returns, $Crypt::OpenSSL::CA::openssl_stuff_loaded will be 1. Calling it several times has no effect.

static void sslcroak(char *format, ...)

Like "croak" in perlapi, except that a blessed exception of class Crypt::OpenSSL::CA::Error is generated. The OpenSSL error stack, if any, gets recorded as an array reference inside the exception structure.

Note to Crypt::OpenSSL::CA hackers: please select the appropriate routine between sslcroak and croak, depending on whether the current error condition is being caused by OpenSSL or not; in this way callers are able to discriminate errors. Also, don't be fooled into thinking that sslcroak (or, for that matter, croak) is the same thing in C and in Perl! Because calling sslcroak will return control directly to Perl without running any C code, any and all temporary variables that have been allocated from C will fail to be de-allocated, thereby causing a memory leak.

Internally, sslcroak works by invoking "_ssl_croak_callback" several times, using a rough equivalent of the following pseudo-code:

  _sslcroak_callback("-message", $formattedstring);
  _sslcroak_callback("-openssl", $openssl_errorstring_1);
  _sslcroak_callback("-openssl", $openssl_errorstring_2);
  ...
  _sslcroak_callback("DONE");

where $formattedstring is the sprintf-formatted version of the arguments passed to sslcroak, and the OpenSSL error strings are retrieved using ERR_get_error(3) and ERR_error_string(3).

static ASN1_TIME* parse_RFC3280_time(char* datetime, char** errmsg, char* sslerrmsg)

Parses datetime, a date in "Zulu" format (that is, yyyymmddhhmmssZ, with a literal Z at the end), and returns a newly-allocated ASN1_TIME* structure utilizing a utcTime encoding for dates in the year 2049 or before and generalizedTime for dates in 2050 and after. RFC3280 dictates that this convention should apply to most date-related fields in X509 certificates and CRLs (as per sections 4.1.2.5 for certificate validity periods, and 5.1.2.4 through 5.1.2.6 for CRL validity periods and certificate revocation times). By contrast, the invalidityDate CRL revocation reason extension is always in generalizedTime and this function should not be used there.

If there is an error, NULL is returned, and one (and only one) of *errmsg and *sslerrmsg is set to an error string, provided that they are not NULL. Caller should thereafter call croak or "sslcroak" respectively.

static ASN1_TIME* parse_RFC3280_time_or_croak(char* datetime)

Like "parse_RFC3280_time" except that it handles its errors itself and will therefore never return NULL. The caller should not have an outstanding temporary variable that must be freed before it returns, or a memory leak will be created; if this is the case, use the more clunky "parse_serial" form instead.

static ASN1_INTEGER* parse_serial (char* hexserial, char** errmsg, char** sslerrmsg)

Parses hexserial, a lowercase, hexadecimal string that starts with "0x", and returns it as a newly-allocated ASN1_INTEGER structure that must be freed by caller (with ASN1_INTEGER_free) when done with it. If there is an error, NULL is returned, and one (and only one) of *errmsg and *sslerrmsg is set to an error string, provided that they are not NULL. Caller should thereafter call croak or "sslcroak" respectively.

static ASN1_INTEGER* parse_serial_or_croak(char* hexserial)

Like "parse_serial" except that it handles its errors itself and will therefore never return NULL. The caller should not have an outstanding temporary variable that must be freed before it returns, or a memory leak will be created; if this is the case, use the more clunky "parse_serial" form instead.

INTERNALS

The use Crypt::OpenSSL::CA::Inline::C idiom described in "SYNOPSIS" is implemented in terms of Inline.

%c_code

A lexical variable that "import" uses to accumulate all the C code submitted by Crypt::OpenSSL::CA. Keys are package names, and values are snippets of C.

import()

Called whenever one of the use Crypt::OpenSSL::CA::Inline::C "foo" pragmas (listed in "SYNOPSIS") is seen by Perl; performs the actual magic of the module. Stashes everything into "%code", and invokes "compile_everything" at the end.

compile_everything()

Called when "the "__END__" pragma" is seen. Invokes "import" in Inline once for every package (that is, every key in "%code"), with the following tweaks:

  • Inline::C is configured to compile with all warnings turned into errors (i.e. -Wall -Werror) and to link with the OpenSSL libraries;

  • the C code that implements the "Standard Library" is prepended to the caller-provided C code.

full_debugging

Returns true iff the environment variable FULL_DEBUGGING is set. This causes the C code to be compiled without optimization, allowing gdb to dump symbols of static functions with only one call site (which comprises most of the C code in Crypt::OpenSSL::CA). Also, the temporary build files are left intact if FULL_DEBUGGING is set.

Developpers, please note that in the absence of FULL_DEBUGGING, the default compiler flags are -g -O2, still allowing for a range of debugging strategies. FULL_DEBUGGING should therefore only be set on a one-shot basis by developpers who have a specific need for it.

installed_version

Returns what the source code of this module will look like (with POD and everything) after it is installed. The installed version is a dud stub; its "import" method only loads the XS DLL, and it is no longer possible to alter the C code once the module has been installed. The upside is that in thanks to that, Inline is a dependency only at compile time.

ENVIRONMENT VARIABLES

FULL_DEBUGGING

See "full_debugging"

BUILD_OPENSSL_INCLUDEDIR

Contains the path to the OpenSSL header files; passed on to "INC" in Inline::C by "compile_everything" in order to clue the compiler into finding them.

BUILD_OPENSSL_LIBDIR

Contains the path to the OpenSSL libraries; passed on to "LIBS" in Inline::C by "compile_everything" in order to clue the linker into finding them.

SEE ALSO

Inline::C, perlxstut, perlguts, perlapi.

TEST SUITE

3 POD Errors

The following errors were encountered while parsing the POD:

Around line 62:

Non-ASCII character seen before =encoding in 'naïve'. Assuming UTF-8

Around line 662:

You forgot a '=back' before '=head1'

Around line 945:

=cut found outside a pod block. Skipping to next block.