LCOV - code coverage report
Current view: top level - third_party/openssl/crypto/ocsp - ocsp_ht.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 0 187 0.0 %
Date: 2015-10-10 Functions: 0 14 0.0 %

          Line data    Source code
       1             : /* ocsp_ht.c */
       2             : /*
       3             :  * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
       4             :  * 2006.
       5             :  */
       6             : /* ====================================================================
       7             :  * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
       8             :  *
       9             :  * Redistribution and use in source and binary forms, with or without
      10             :  * modification, are permitted provided that the following conditions
      11             :  * are met:
      12             :  *
      13             :  * 1. Redistributions of source code must retain the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer.
      15             :  *
      16             :  * 2. Redistributions in binary form must reproduce the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer in
      18             :  *    the documentation and/or other materials provided with the
      19             :  *    distribution.
      20             :  *
      21             :  * 3. All advertising materials mentioning features or use of this
      22             :  *    software must display the following acknowledgment:
      23             :  *    "This product includes software developed by the OpenSSL Project
      24             :  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
      25             :  *
      26             :  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
      27             :  *    endorse or promote products derived from this software without
      28             :  *    prior written permission. For written permission, please contact
      29             :  *    licensing@OpenSSL.org.
      30             :  *
      31             :  * 5. Products derived from this software may not be called "OpenSSL"
      32             :  *    nor may "OpenSSL" appear in their names without prior written
      33             :  *    permission of the OpenSSL Project.
      34             :  *
      35             :  * 6. Redistributions of any form whatsoever must retain the following
      36             :  *    acknowledgment:
      37             :  *    "This product includes software developed by the OpenSSL Project
      38             :  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
      39             :  *
      40             :  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
      41             :  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      42             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      43             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
      44             :  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      45             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      46             :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      47             :  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      48             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      49             :  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      50             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      51             :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      52             :  * ====================================================================
      53             :  *
      54             :  * This product includes cryptographic software written by Eric Young
      55             :  * (eay@cryptsoft.com).  This product includes software written by Tim
      56             :  * Hudson (tjh@cryptsoft.com).
      57             :  *
      58             :  */
      59             : 
      60             : #include <stdio.h>
      61             : #include <stdlib.h>
      62             : #include <ctype.h>
      63             : #include <string.h>
      64             : #include "e_os.h"
      65             : #include <openssl/asn1.h>
      66             : #include <openssl/ocsp.h>
      67             : #include <openssl/err.h>
      68             : #include <openssl/buffer.h>
      69             : #ifdef OPENSSL_SYS_SUNOS
      70             : # define strtoul (unsigned long)strtol
      71             : #endif                          /* OPENSSL_SYS_SUNOS */
      72             : 
      73             : /* Stateful OCSP request code, supporting non-blocking I/O */
      74             : 
      75             : /* Opaque OCSP request status structure */
      76             : 
      77             : struct ocsp_req_ctx_st {
      78             :     int state;                  /* Current I/O state */
      79             :     unsigned char *iobuf;       /* Line buffer */
      80             :     int iobuflen;               /* Line buffer length */
      81             :     BIO *io;                    /* BIO to perform I/O with */
      82             :     BIO *mem;                   /* Memory BIO response is built into */
      83             :     unsigned long asn1_len;     /* ASN1 length of response */
      84             :     unsigned long max_resp_len; /* Maximum length of response */
      85             : };
      86             : 
      87             : #define OCSP_MAX_RESP_LENGTH    (100 * 1024)
      88             : #define OCSP_MAX_LINE_LEN       4096;
      89             : 
      90             : /* OCSP states */
      91             : 
      92             : /* If set no reading should be performed */
      93             : #define OHS_NOREAD              0x1000
      94             : /* Error condition */
      95             : #define OHS_ERROR               (0 | OHS_NOREAD)
      96             : /* First line being read */
      97             : #define OHS_FIRSTLINE           1
      98             : /* MIME headers being read */
      99             : #define OHS_HEADERS             2
     100             : /* OCSP initial header (tag + length) being read */
     101             : #define OHS_ASN1_HEADER         3
     102             : /* OCSP content octets being read */
     103             : #define OHS_ASN1_CONTENT        4
     104             : /* First call: ready to start I/O */
     105             : #define OHS_ASN1_WRITE_INIT     (5 | OHS_NOREAD)
     106             : /* Request being sent */
     107             : #define OHS_ASN1_WRITE          (6 | OHS_NOREAD)
     108             : /* Request being flushed */
     109             : #define OHS_ASN1_FLUSH          (7 | OHS_NOREAD)
     110             : /* Completed */
     111             : #define OHS_DONE                (8 | OHS_NOREAD)
     112             : /* Headers set, no final \r\n included */
     113             : #define OHS_HTTP_HEADER         (9 | OHS_NOREAD)
     114             : 
     115             : static int parse_http_line1(char *line);
     116             : 
     117           0 : OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
     118             : {
     119             :     OCSP_REQ_CTX *rctx;
     120           0 :     rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
     121           0 :     if (!rctx)
     122             :         return NULL;
     123           0 :     rctx->state = OHS_ERROR;
     124           0 :     rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
     125           0 :     rctx->mem = BIO_new(BIO_s_mem());
     126           0 :     rctx->io = io;
     127           0 :     rctx->asn1_len = 0;
     128           0 :     if (maxline > 0)
     129           0 :         rctx->iobuflen = maxline;
     130             :     else
     131           0 :         rctx->iobuflen = OCSP_MAX_LINE_LEN;
     132           0 :     rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
     133           0 :     if (!rctx->iobuf || !rctx->mem) {
     134           0 :         OCSP_REQ_CTX_free(rctx);
     135           0 :         return NULL;
     136             :     }
     137             :     return rctx;
     138             : }
     139             : 
     140           0 : void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
     141             : {
     142           0 :     if (rctx->mem)
     143           0 :         BIO_free(rctx->mem);
     144           0 :     if (rctx->iobuf)
     145           0 :         OPENSSL_free(rctx->iobuf);
     146           0 :     OPENSSL_free(rctx);
     147           0 : }
     148             : 
     149           0 : BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
     150             : {
     151           0 :     return rctx->mem;
     152             : }
     153             : 
     154           0 : void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
     155             : {
     156           0 :     if (len == 0)
     157           0 :         rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
     158             :     else
     159           0 :         rctx->max_resp_len = len;
     160           0 : }
     161             : 
     162           0 : int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val)
     163             : {
     164             :     static const char req_hdr[] =
     165             :         "Content-Type: application/ocsp-request\r\n"
     166             :         "Content-Length: %d\r\n\r\n";
     167           0 :     int reqlen = ASN1_item_i2d(val, NULL, it);
     168           0 :     if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0)
     169             :         return 0;
     170           0 :     if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
     171             :         return 0;
     172           0 :     rctx->state = OHS_ASN1_WRITE_INIT;
     173           0 :     return 1;
     174             : }
     175             : 
     176           0 : int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx,
     177             :                           ASN1_VALUE **pval, const ASN1_ITEM *it)
     178             : {
     179             :     int rv, len;
     180             :     const unsigned char *p;
     181             : 
     182           0 :     rv = OCSP_REQ_CTX_nbio(rctx);
     183           0 :     if (rv != 1)
     184             :         return rv;
     185             : 
     186           0 :     len = BIO_get_mem_data(rctx->mem, &p);
     187           0 :     *pval = ASN1_item_d2i(NULL, &p, len, it);
     188           0 :     if (*pval == NULL) {
     189           0 :         rctx->state = OHS_ERROR;
     190           0 :         return 0;
     191             :     }
     192             :     return 1;
     193             : }
     194             : 
     195           0 : int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path)
     196             : {
     197             :     static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
     198             : 
     199           0 :     if (!path)
     200             :         path = "/";
     201             : 
     202           0 :     if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
     203             :         return 0;
     204           0 :     rctx->state = OHS_HTTP_HEADER;
     205           0 :     return 1;
     206             : }
     207             : 
     208           0 : int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
     209             : {
     210           0 :     return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
     211             :                             (ASN1_VALUE *)req);
     212             : }
     213             : 
     214           0 : int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
     215             :                              const char *name, const char *value)
     216             : {
     217           0 :     if (!name)
     218             :         return 0;
     219           0 :     if (BIO_puts(rctx->mem, name) <= 0)
     220             :         return 0;
     221           0 :     if (value) {
     222           0 :         if (BIO_write(rctx->mem, ": ", 2) != 2)
     223             :             return 0;
     224           0 :         if (BIO_puts(rctx->mem, value) <= 0)
     225             :             return 0;
     226             :     }
     227           0 :     if (BIO_write(rctx->mem, "\r\n", 2) != 2)
     228             :         return 0;
     229           0 :     rctx->state = OHS_HTTP_HEADER;
     230           0 :     return 1;
     231             : }
     232             : 
     233           0 : OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
     234             :                                int maxline)
     235             : {
     236             : 
     237             :     OCSP_REQ_CTX *rctx = NULL;
     238           0 :     rctx = OCSP_REQ_CTX_new(io, maxline);
     239           0 :     if (!rctx)
     240             :         return NULL;
     241             : 
     242           0 :     if (!OCSP_REQ_CTX_http(rctx, "POST", path))
     243             :         goto err;
     244             : 
     245           0 :     if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
     246             :         goto err;
     247             : 
     248           0 :     return rctx;
     249             : 
     250             :  err:
     251           0 :     OCSP_REQ_CTX_free(rctx);
     252           0 :     return NULL;
     253             : }
     254             : 
     255             : /*
     256             :  * Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We
     257             :  * need to obtain the numeric code and (optional) informational message.
     258             :  */
     259             : 
     260           0 : static int parse_http_line1(char *line)
     261             : {
     262             :     int retcode;
     263             :     char *p, *q, *r;
     264             :     /* Skip to first white space (passed protocol info) */
     265             : 
     266           0 :     for (p = line; *p && !isspace((unsigned char)*p); p++)
     267           0 :         continue;
     268           0 :     if (!*p) {
     269           0 :         OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
     270           0 :         return 0;
     271             :     }
     272             : 
     273             :     /* Skip past white space to start of response code */
     274           0 :     while (*p && isspace((unsigned char)*p))
     275           0 :         p++;
     276             : 
     277           0 :     if (!*p) {
     278           0 :         OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
     279           0 :         return 0;
     280             :     }
     281             : 
     282             :     /* Find end of response code: first whitespace after start of code */
     283           0 :     for (q = p; *q && !isspace((unsigned char)*q); q++)
     284           0 :         continue;
     285             : 
     286           0 :     if (!*q) {
     287           0 :         OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
     288           0 :         return 0;
     289             :     }
     290             : 
     291             :     /* Set end of response code and start of message */
     292           0 :     *q++ = 0;
     293             : 
     294             :     /* Attempt to parse numeric code */
     295           0 :     retcode = strtoul(p, &r, 10);
     296             : 
     297           0 :     if (*r)
     298             :         return 0;
     299             : 
     300             :     /* Skip over any leading white space in message */
     301           0 :     while (*q && isspace((unsigned char)*q))
     302           0 :         q++;
     303             : 
     304           0 :     if (*q) {
     305             :         /*
     306             :          * Finally zap any trailing white space in message (include CRLF)
     307             :          */
     308             : 
     309             :         /* We know q has a non white space character so this is OK */
     310           0 :         for (r = q + strlen(q) - 1; isspace((unsigned char)*r); r--)
     311           0 :             *r = 0;
     312             :     }
     313           0 :     if (retcode != 200) {
     314           0 :         OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR);
     315           0 :         if (!*q)
     316           0 :             ERR_add_error_data(2, "Code=", p);
     317             :         else
     318           0 :             ERR_add_error_data(4, "Code=", p, ",Reason=", q);
     319             :         return 0;
     320             :     }
     321             : 
     322             :     return 1;
     323             : 
     324             : }
     325             : 
     326           0 : int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx)
     327             : {
     328             :     int i, n;
     329             :     const unsigned char *p;
     330             :  next_io:
     331           0 :     if (!(rctx->state & OHS_NOREAD)) {
     332           0 :         n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
     333             : 
     334           0 :         if (n <= 0) {
     335           0 :             if (BIO_should_retry(rctx->io))
     336             :                 return -1;
     337           0 :             return 0;
     338             :         }
     339             : 
     340             :         /* Write data to memory BIO */
     341             : 
     342           0 :         if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
     343             :             return 0;
     344             :     }
     345             : 
     346           0 :     switch (rctx->state) {
     347             :     case OHS_HTTP_HEADER:
     348             :         /* Last operation was adding headers: need a final \r\n */
     349           0 :         if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
     350           0 :             rctx->state = OHS_ERROR;
     351           0 :             return 0;
     352             :         }
     353           0 :         rctx->state = OHS_ASN1_WRITE_INIT;
     354             : 
     355             :     case OHS_ASN1_WRITE_INIT:
     356           0 :         rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL);
     357           0 :         rctx->state = OHS_ASN1_WRITE;
     358             : 
     359             :     case OHS_ASN1_WRITE:
     360           0 :         n = BIO_get_mem_data(rctx->mem, &p);
     361             : 
     362           0 :         i = BIO_write(rctx->io, p + (n - rctx->asn1_len), rctx->asn1_len);
     363             : 
     364           0 :         if (i <= 0) {
     365           0 :             if (BIO_should_retry(rctx->io))
     366             :                 return -1;
     367           0 :             rctx->state = OHS_ERROR;
     368           0 :             return 0;
     369             :         }
     370             : 
     371           0 :         rctx->asn1_len -= i;
     372             : 
     373           0 :         if (rctx->asn1_len > 0)
     374             :             goto next_io;
     375             : 
     376           0 :         rctx->state = OHS_ASN1_FLUSH;
     377             : 
     378           0 :         (void)BIO_reset(rctx->mem);
     379             : 
     380             :     case OHS_ASN1_FLUSH:
     381             : 
     382           0 :         i = BIO_flush(rctx->io);
     383             : 
     384           0 :         if (i > 0) {
     385           0 :             rctx->state = OHS_FIRSTLINE;
     386           0 :             goto next_io;
     387             :         }
     388             : 
     389           0 :         if (BIO_should_retry(rctx->io))
     390             :             return -1;
     391             : 
     392           0 :         rctx->state = OHS_ERROR;
     393           0 :         return 0;
     394             : 
     395             :     case OHS_ERROR:
     396             :         return 0;
     397             : 
     398             :     case OHS_FIRSTLINE:
     399             :     case OHS_HEADERS:
     400             : 
     401             :         /* Attempt to read a line in */
     402             : 
     403             :  next_line:
     404             :         /*
     405             :          * Due to &%^*$" memory BIO behaviour with BIO_gets we have to check
     406             :          * there's a complete line in there before calling BIO_gets or we'll
     407             :          * just get a partial read.
     408             :          */
     409           0 :         n = BIO_get_mem_data(rctx->mem, &p);
     410           0 :         if ((n <= 0) || !memchr(p, '\n', n)) {
     411           0 :             if (n >= rctx->iobuflen) {
     412           0 :                 rctx->state = OHS_ERROR;
     413           0 :                 return 0;
     414             :             }
     415             :             goto next_io;
     416             :         }
     417           0 :         n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
     418             : 
     419           0 :         if (n <= 0) {
     420           0 :             if (BIO_should_retry(rctx->mem))
     421             :                 goto next_io;
     422           0 :             rctx->state = OHS_ERROR;
     423           0 :             return 0;
     424             :         }
     425             : 
     426             :         /* Don't allow excessive lines */
     427           0 :         if (n == rctx->iobuflen) {
     428           0 :             rctx->state = OHS_ERROR;
     429           0 :             return 0;
     430             :         }
     431             : 
     432             :         /* First line */
     433           0 :         if (rctx->state == OHS_FIRSTLINE) {
     434           0 :             if (parse_http_line1((char *)rctx->iobuf)) {
     435           0 :                 rctx->state = OHS_HEADERS;
     436           0 :                 goto next_line;
     437             :             } else {
     438           0 :                 rctx->state = OHS_ERROR;
     439           0 :                 return 0;
     440             :             }
     441             :         } else {
     442             :             /* Look for blank line: end of headers */
     443           0 :             for (p = rctx->iobuf; *p; p++) {
     444           0 :                 if ((*p != '\r') && (*p != '\n'))
     445             :                     break;
     446             :             }
     447           0 :             if (*p)
     448             :                 goto next_line;
     449             : 
     450           0 :             rctx->state = OHS_ASN1_HEADER;
     451             : 
     452             :         }
     453             : 
     454             :         /* Fall thru */
     455             : 
     456             :     case OHS_ASN1_HEADER:
     457             :         /*
     458             :          * Now reading ASN1 header: can read at least 2 bytes which is enough
     459             :          * for ASN1 SEQUENCE header and either length field or at least the
     460             :          * length of the length field.
     461             :          */
     462           0 :         n = BIO_get_mem_data(rctx->mem, &p);
     463           0 :         if (n < 2)
     464             :             goto next_io;
     465             : 
     466             :         /* Check it is an ASN1 SEQUENCE */
     467           0 :         if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
     468           0 :             rctx->state = OHS_ERROR;
     469           0 :             return 0;
     470             :         }
     471             : 
     472             :         /* Check out length field */
     473           0 :         if (*p & 0x80) {
     474             :             /*
     475             :              * If MSB set on initial length octet we can now always read 6
     476             :              * octets: make sure we have them.
     477             :              */
     478           0 :             if (n < 6)
     479             :                 goto next_io;
     480           0 :             n = *p & 0x7F;
     481             :             /* Not NDEF or excessive length */
     482           0 :             if (!n || (n > 4)) {
     483           0 :                 rctx->state = OHS_ERROR;
     484           0 :                 return 0;
     485             :             }
     486           0 :             p++;
     487           0 :             rctx->asn1_len = 0;
     488           0 :             for (i = 0; i < n; i++) {
     489           0 :                 rctx->asn1_len <<= 8;
     490           0 :                 rctx->asn1_len |= *p++;
     491             :             }
     492             : 
     493           0 :             if (rctx->asn1_len > rctx->max_resp_len) {
     494           0 :                 rctx->state = OHS_ERROR;
     495           0 :                 return 0;
     496             :             }
     497             : 
     498           0 :             rctx->asn1_len += n + 2;
     499             :         } else
     500           0 :             rctx->asn1_len = *p + 2;
     501             : 
     502           0 :         rctx->state = OHS_ASN1_CONTENT;
     503             : 
     504             :         /* Fall thru */
     505             : 
     506             :     case OHS_ASN1_CONTENT:
     507           0 :         n = BIO_get_mem_data(rctx->mem, NULL);
     508           0 :         if (n < (int)rctx->asn1_len)
     509             :             goto next_io;
     510             : 
     511           0 :         rctx->state = OHS_DONE;
     512           0 :         return 1;
     513             : 
     514             :         break;
     515             : 
     516             :     case OHS_DONE:
     517           0 :         return 1;
     518             : 
     519             :     }
     520             : 
     521             :     return 0;
     522             : 
     523             : }
     524             : 
     525           0 : int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
     526             : {
     527           0 :     return OCSP_REQ_CTX_nbio_d2i(rctx,
     528             :                                  (ASN1_VALUE **)presp,
     529             :                                  ASN1_ITEM_rptr(OCSP_RESPONSE));
     530             : }
     531             : 
     532             : /* Blocking OCSP request handler: now a special case of non-blocking I/O */
     533             : 
     534           0 : OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
     535             : {
     536           0 :     OCSP_RESPONSE *resp = NULL;
     537             :     OCSP_REQ_CTX *ctx;
     538             :     int rv;
     539             : 
     540           0 :     ctx = OCSP_sendreq_new(b, path, req, -1);
     541             : 
     542           0 :     if (!ctx)
     543             :         return NULL;
     544             : 
     545             :     do {
     546             :         rv = OCSP_sendreq_nbio(&resp, ctx);
     547           0 :     } while ((rv == -1) && BIO_should_retry(b));
     548             : 
     549           0 :     OCSP_REQ_CTX_free(ctx);
     550             : 
     551           0 :     if (rv)
     552           0 :         return resp;
     553             : 
     554             :     return NULL;
     555             : }

Generated by: LCOV version 1.10