Dominique Quatravaux

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:

char0_value

  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"). If string is tainted, that makes no difference; return it, or the empty string, just the same. See "Working with SVs" in perlguts, look for the word "Nevertheless" - I assume there is a macro in Perl's convenience stuff that does exactly that already, but I don't know it...

char0_value_or_null

  static inline char* char0_value_or_null(SV* string);

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

perl_wrap

   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.

openssl_string_to_SV

  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.

openssl_buf_to_SV

   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.

BIO_mem_to_SV

   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.

sslcroak

   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 croak-style error management acts in the same way in C and Perl! Because calling sslcroak (or, for that matter, "croak" in perlapi) 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 "_sslcroak_callback" in Crypt::OpenSSL::CA 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).

parse_RFC3280_time

  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.

parse_RFC3280_time_or_croak

  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_RFC3280_time" form instead.

parse_serial

  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.

parse_serial_or_croak

  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.

BOOT-time effect

Each .so XS module will be fitted with a BOOT section (see "BOOT" in Inline::C which automatically gets executed upon loading it with DynaLoader or XSLoader. The BOOT section is the same for all subpackages in Crypt::OpenSSL::CA; it ensures that various stuff is loaded inside OpenSSL, such as ERR_load_crypto_strings(), OpenSSL_add_all_algorithms() and all that jazz. After the boot code completes, $Crypt::OpenSSL::CA::openssl_stuff_loaded will be 1, so that the following XS modules can skip that when they in turn get loaded.

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 "%c_code", and invokes "compile_everything" at the end.

compile_everything ()

Called when "the "__END__" pragma" is seen. Invokes compile_namespace once for every package (that is, every key in "%c_code"), prepending _c_boilerplate each time.

compile_into ($package, $c_code, %named_options)

Compile $c_code and make its functions available as part of $package's namespace, courtesy to Inline::C magic. Works by invoking "import" in Inline in a tweaked fashion, so as to compile with all warnings turned into errors (i.e. -Wall -Werror) and to link with the OpenSSL libraries. The environment variables are taken into account (see "ENVIRONMENT VARIABLES").

Available named options are:

-boot_section => $c_code

Adds $c_code to the BOOT section of the generated .so module.

full_debugging

Returns true iff the environment variable "FULL_DEBUGGING" is set.

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

Setting this variable to 1 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.

BUILD_OPENSSL_CFLAGS

Contains the CFLAGS to pass so as to compile C code that links against OpenSSL; eg -I/usr/lib/openssl/include or something. Passed on to "INC" in Inline::C by "compile_everything".

BUILD_OPENSSL_LDFLAGS

Contains the LDFLAGS to pass so as to link with the OpenSSL libraries; eg -L/usr/lib/openssl/lib or something. Passed on to "LIBS" in Inline::C by "compile_everything".

SEE ALSO

Inline::C, perlxstut, perlguts, perlapi.