Question

I want to call this function from OpenSSL library defined with prototy below:

X509 *d2i_X509(X509 **px, const unsigned char **in, int len);

The second parameter in is defined as a const unsigned char ** because:

  • d2i_X509 won't modify the data in the buffer pointed to by *in
  • *in will be incremented by the amount of data parsed from the buffer.

Now this prototype is a problem, because as far as I see the function can't be called the following way which as far as I understand the API is the normal way to call it (error management code removed to make things simpler):

unsigned char buffer[2048];
unsigned char * end = buffer;
unsigned char * p = buffer;

end += read(fd, p, 2048);
certlen1 = parseint(&p);
X509 * cert1 = d2i_x509(NULL, &p, certlen1);
certlen2 = parseint(&p);
X509 * cert2 = d2i_x509(NULL, &p, certlen2);

If I try to compile the above code, it says:

error: invalid conversion from 'unsigned char**' to 'const unsigned char**'

I understand the rationale of the message (explained here for instance), but in this particular case this rationale does not apply as the function states in it's documentation it will only increment the value of *in, never assign an unrelated pointer to it.

I can't change type of p to const unsigned char * because the code above is actually simplified, the pointer is hidden behind some abstract IO object performing both reading and writing. The good point with the previous IO object is that is keeps symmetric reading and writing code together, it is possible to use two separate pointers for reading and writing in my IO object, but overall benefice of doing that is really small (if not negative) and add much syntaxic noise. Also having that kind of deep internal change in an IO object forced by some external unrelated API call, looks really stretching.

A solution could be to perform a cast or even to not use the returned pointer value at all (as I have the size parameter), but it does not feels right either. It seems either the prototype of d2i_X509 does not contains enough information, or that the const checking rule of the compiler is too strict in such case.

How should such API be called from C++ ? For now I just will use a cast as I feel it is the lesser evil, but is there any better way ?

Was it helpful?

Solution

First, the += in this code doesn't make much sense:

p += read(fd, p, 2048);

You're modifying p to point just past the data you just read, so d2i_x509 will be reading junk.

I'd do it like this:

unsigned char buffer[2048];
int n = read(fd, buffer, 2048);
if (n < 0) ..error..;
const unsigned char * p = buffer;
X509 * cert1 = d2i_x509(NULL, &p, certlen1);
X509 * cert2 = d2i_x509(NULL, &p, certlen2);
int used = p - buffer;  // # of bytes actually parsed by the two calls

Any future changes can be done to buffer directly.

Edit:

Ok, then replace this:

X509 * cert1 = d2i_x509(NULL, &p, certlen1);

with this:

{
  const unsigned char *q = p;
  X509 * cert1 = d2i_x509(NULL, &q, certlen1);
  p += q - p;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top