2

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 ?

Community
  • 1
  • 1
kriss
  • 23,497
  • 17
  • 97
  • 116

1 Answers1

1

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;
}
Keith Randall
  • 22,985
  • 2
  • 35
  • 54
  • you are technically correct but completely besides the point, while overly simplyfing my actual code, I have overdone it a bit. Actually I have a second pointer 'end' which is incremented by reading and certlen1 and certlen2 are parsed and extracted using p which starts at buffer and is incremented along the way. Also p is stored in some opaque object performing both reading and writing on buffers and (checking boundaries). While possible making internal p const is a real burden without clear benefice. In others words the suggested proposal would be possible if it was toy, not a real program. – kriss Sep 07 '12 at 22:17
  • I'm not really sure it's better than the cast (looks like some way to foul the const checking system), but yes it's definitely a possible solution to avoid casting. – kriss Sep 08 '12 at 19:39