The new client has become an independent libcrpyto module in crypto/http/ and * can handle any types of requests and responses (ASN.1-encoded and plain) * does not include potentially busy loops when waiting for responses but * makes use of a new timeout mechanism integrated with socket-based BIO * supports the use of HTTP proxies and TLS, including HTTPS over proxies * supports HTTP redirection via codes 301 and 302 for GET requests * returns more useful diagnostics in various error situations Also adapts - and strongly simplifies - hitherto uses of HTTP in crypto/ocsp/, crypto/x509/x_all.c, apps/lib/apps.c, and apps/{ocsp,s_client,s_server}.c Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com> (Merged from https://github.com/openssl/openssl/pull/10667)master
parent
bcbb30afe2
commit
29f178bddf
@ -0,0 +1,2 @@
|
||||
LIBS=../../libcrypto
|
||||
SOURCE[../../libcrypto]=http_client.c http_err.c http_lib.c
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Generated by util/mkerr.pl DO NOT EDIT
|
||||
* Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/httperr.h>
|
||||
|
||||
#ifndef OPENSSL_NO_ERR
|
||||
|
||||
static const ERR_STRING_DATA HTTP_str_reasons[] = {
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN),
|
||||
"asn1 len exceeds max resp len"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_CONNECT_FAILURE), "connect failure"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_ASN1_LENGTH),
|
||||
"error parsing asn1 length"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_CONTENT_LENGTH),
|
||||
"error parsing content length"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_URL), "error parsing url"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_RECEIVING), "error receiving"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_SENDING), "error sending"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INCONSISTENT_CONTENT_LENGTH),
|
||||
"inconsistent content length"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MAX_RESP_LEN_EXCEEDED),
|
||||
"max resp len exceeded"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_ASN1_ENCODING),
|
||||
"missing asn1 encoding"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_CONTENT_TYPE),
|
||||
"missing content type"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_REDIRECT_LOCATION),
|
||||
"missing redirect location"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP),
|
||||
"redirection from https to http"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_REDIRECTION_NOT_ENABLED),
|
||||
"redirection not enabled"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_RESPONSE_LINE_TOO_LONG),
|
||||
"response line too long"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR),
|
||||
"server response parse error"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_SENT_ERROR), "server sent error"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION),
|
||||
"server sent wrong http version"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_STATUS_CODE_UNSUPPORTED),
|
||||
"status code unsupported"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_TLS_NOT_ENABLED), "tls not enabled"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_TOO_MANY_REDIRECTIONS),
|
||||
"too many redirections"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_UNEXPECTED_CONTENT_TYPE),
|
||||
"unexpected content type"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
int ERR_load_HTTP_strings(void)
|
||||
{
|
||||
#ifndef OPENSSL_NO_ERR
|
||||
if (ERR_reason_error_string(HTTP_str_reasons[0].error) == NULL)
|
||||
ERR_load_strings_const(HTTP_str_reasons);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include <openssl/http.h>
|
||||
#include <openssl/httperr.h>
|
||||
#include <openssl/err.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Parse a URL and split it up into host, port and path components and
|
||||
* whether it indicates SSL/TLS. Return 1 on success, 0 on error.
|
||||
*/
|
||||
|
||||
int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
|
||||
char **ppath, int *pssl)
|
||||
{
|
||||
char *p, *buf;
|
||||
char *host;
|
||||
char *port = "80";
|
||||
|
||||
if (url == NULL) {
|
||||
HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (phost != NULL)
|
||||
*phost = NULL;
|
||||
if (pport != NULL)
|
||||
*pport = NULL;
|
||||
if (ppath != NULL)
|
||||
*ppath = NULL;
|
||||
if (pssl != NULL)
|
||||
*pssl = 0;
|
||||
|
||||
/* dup the buffer since we are going to mess with it */
|
||||
if ((buf = OPENSSL_strdup(url)) == NULL)
|
||||
goto err;
|
||||
|
||||
/* Check for initial colon */
|
||||
p = strchr(buf, ':');
|
||||
if (p == NULL || p - buf > 5 /* strlen("https") */) {
|
||||
p = buf;
|
||||
} else {
|
||||
*(p++) = '\0';
|
||||
|
||||
if (strcmp(buf, "https") == 0) {
|
||||
if (pssl != NULL)
|
||||
*pssl = 1;
|
||||
port = "443";
|
||||
} else if (strcmp(buf, "http") != 0) {
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
/* Check for double slash */
|
||||
if ((p[0] != '/') || (p[1] != '/'))
|
||||
goto parse_err;
|
||||
p += 2;
|
||||
}
|
||||
host = p;
|
||||
|
||||
/* Check for trailing part of path */
|
||||
p = strchr(p, '/');
|
||||
if (ppath != NULL && (*ppath = OPENSSL_strdup(p == NULL ? "/" : p)) == NULL)
|
||||
goto err;
|
||||
if (p != NULL)
|
||||
*p = '\0'; /* Set start of path to 0 so hostname[:port] is valid */
|
||||
|
||||
p = host;
|
||||
if (host[0] == '[') {
|
||||
/* ipv6 literal */
|
||||
host++;
|
||||
p = strchr(host, ']');
|
||||
if (p == NULL)
|
||||
goto parse_err;
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
|
||||
/* Look for optional ':' for port number */
|
||||
if ((p = strchr(p, ':'))) {
|
||||
*p = '\0';
|
||||
port = p + 1;
|
||||
}
|
||||
if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL)
|
||||
goto err;
|
||||
if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL)
|
||||
goto err;
|
||||
|
||||
OPENSSL_free(buf);
|
||||
return 1;
|
||||
|
||||
parse_err:
|
||||
HTTPerr(0, HTTP_R_ERROR_PARSING_URL);
|
||||
|
||||
err:
|
||||
if (ppath != NULL) {
|
||||
OPENSSL_free(*ppath);
|
||||
*ppath = NULL;
|
||||
}
|
||||
if (pport != NULL) {
|
||||
OPENSSL_free(*pport);
|
||||
*pport = NULL;
|
||||
}
|
||||
if (phost != NULL) {
|
||||
OPENSSL_free(*phost);
|
||||
*phost = NULL;
|
||||
}
|
||||
OPENSSL_free(buf);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved.
|
||||
* Copyright Siemens AG 2018-2020
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#ifndef OSSL_CRYPTO_HTTP_LOCAL_H
|
||||
# define OSSL_CRYPTO_HTTP_LOCAL_H
|
||||
|
||||
# include <openssl/ocsp.h>
|
||||
|
||||
/* name aliases for legacy names with name prefix "OCSP_" */
|
||||
typedef OCSP_REQ_CTX OSSL_HTTP_REQ_CTX;
|
||||
/* functions meanwhile only used internally */
|
||||
# define OSSL_HTTP_REQ_CTX_new OCSP_REQ_CTX_new
|
||||
# define OSSL_HTTP_REQ_CTX_free OCSP_REQ_CTX_free
|
||||
# define OSSL_HTTP_REQ_CTX_header OCSP_REQ_CTX_http
|
||||
# define OSSL_HTTP_REQ_CTX_add1_header OCSP_REQ_CTX_add1_header
|
||||
# define OSSL_HTTP_REQ_CTX_i2d OCSP_REQ_CTX_i2d
|
||||
# define OSSL_HTTP_REQ_CTX_nbio OCSP_REQ_CTX_nbio
|
||||
# ifndef OPENSSL_NO_SOCK
|
||||
# define OSSL_HTTP_REQ_CTX_sendreq_d2i OCSP_REQ_CTX_nbio_d2i
|
||||
# endif
|
||||
/* functions that are meanwhile unused */
|
||||
# define OSSL_HTTP_REQ_CTX_get0_mem_bio OCSP_REQ_CTX_get0_mem_bio /* undoc'd */
|
||||
# define OSSL_HTTP_REQ_CTX_set_max_response_length OCSP_set_max_response_length
|
||||
|
||||
BIO *HTTP_asn1_item2bio(const ASN1_ITEM *it, ASN1_VALUE *val);
|
||||
OSSL_HTTP_REQ_CTX *HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int use_http_proxy,
|
||||
const char *server, const char *port,
|
||||
const char *path,
|
||||
const STACK_OF(CONF_VALUE) *headers,
|
||||
const char *content_type, BIO *req_mem,
|
||||
int maxline, unsigned long max_resp_len,
|
||||
int timeout,
|
||||
const char *expected_content_type,
|
||||
int expect_asn1);
|
||||
ASN1_VALUE *HTTP_sendreq_bio(BIO *bio, OSSL_HTTP_bio_cb_t bio_update_fn,
|
||||
void *arg, const char *server, const char *port,
|
||||
const char *path, int use_ssl, int use_proxy,
|
||||
const STACK_OF(CONF_VALUE) *headers,
|
||||
const char *content_type,
|
||||
ASN1_VALUE *req, const ASN1_ITEM *req_it,
|
||||
int maxline, unsigned long max_resp_len,
|
||||
int timeout, const ASN1_ITEM *rsp_it);
|
||||
|
||||
#endif /* !defined OSSL_CRYPTO_HTTP_LOCAL_H */
|
@ -1,4 +1,4 @@
|
||||
LIBS=../../libcrypto
|
||||
SOURCE[../../libcrypto]=\
|
||||
ocsp_asn.c ocsp_ext.c ocsp_ht.c ocsp_lib.c ocsp_cl.c \
|
||||
ocsp_asn.c ocsp_ext.c ocsp_http.c ocsp_lib.c ocsp_cl.c \
|
||||
ocsp_srv.c ocsp_prn.c ocsp_vfy.c ocsp_err.c v3_ocsp.c
|
||||
|
@ -1,502 +0,0 @@
|
||||
/*
|
||||
* Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "e_os.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "crypto/ctype.h"
|
||||
#include <string.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/ocsp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/buffer.h>
|
||||
|
||||
/* Stateful OCSP request code, supporting non-blocking I/O */
|
||||
|
||||
/* Opaque OCSP request status structure */
|
||||
|
||||
struct ocsp_req_ctx_st {
|
||||
int state; /* Current I/O state */
|
||||
unsigned char *iobuf; /* Line buffer */
|
||||
int iobuflen; /* Line buffer length */
|
||||
BIO *io; /* BIO to perform I/O with */
|
||||
BIO *mem; /* Memory BIO response is built into */
|
||||
unsigned long asn1_len; /* ASN1 length of response */
|
||||
unsigned long max_resp_len; /* Maximum length of response */
|
||||
};
|
||||
|
||||
#define OCSP_MAX_RESP_LENGTH (100 * 1024)
|
||||
#define OCSP_MAX_LINE_LEN 4096;
|
||||
|
||||
/* OCSP states */
|
||||
|
||||
/* If set no reading should be performed */
|
||||
#define OHS_NOREAD 0x1000
|
||||
/* Error condition */
|
||||
#define OHS_ERROR (0 | OHS_NOREAD)
|
||||
/* First line being read */
|
||||
#define OHS_FIRSTLINE 1
|
||||
/* MIME headers being read */
|
||||
#define OHS_HEADERS 2
|
||||
/* OCSP initial header (tag + length) being read */
|
||||
#define OHS_ASN1_HEADER 3
|
||||
/* OCSP content octets being read */
|
||||
#define OHS_ASN1_CONTENT 4
|
||||
/* First call: ready to start I/O */
|
||||
#define OHS_ASN1_WRITE_INIT (5 | OHS_NOREAD)
|
||||
/* Request being sent */
|
||||
#define OHS_ASN1_WRITE (6 | OHS_NOREAD)
|
||||
/* Request being flushed */
|
||||
#define OHS_ASN1_FLUSH (7 | OHS_NOREAD)
|
||||
/* Completed */
|
||||
#define OHS_DONE (8 | OHS_NOREAD)
|
||||
/* Headers set, no final \r\n included */
|
||||
#define OHS_HTTP_HEADER (9 | OHS_NOREAD)
|
||||
|
||||
static int parse_http_line1(char *line);
|
||||
|
||||
OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline)
|
||||
{
|
||||
OCSP_REQ_CTX *rctx = OPENSSL_zalloc(sizeof(*rctx));
|
||||
|
||||
if (rctx == NULL)
|
||||
return NULL;
|
||||
rctx->state = OHS_ERROR;
|
||||
rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
|
||||
rctx->mem = BIO_new(BIO_s_mem());
|
||||
rctx->io = io;
|
||||
if (maxline > 0)
|
||||
rctx->iobuflen = maxline;
|
||||
else
|
||||
rctx->iobuflen = OCSP_MAX_LINE_LEN;
|
||||
rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
|
||||
if (rctx->iobuf == NULL || rctx->mem == NULL) {
|
||||
OCSP_REQ_CTX_free(rctx);
|
||||
return NULL;
|
||||
}
|
||||
return rctx;
|
||||
}
|
||||
|
||||
void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx)
|
||||
{
|
||||
if (!rctx)
|
||||
return;
|
||||
BIO_free(rctx->mem);
|
||||
OPENSSL_free(rctx->iobuf);
|
||||
OPENSSL_free(rctx);
|
||||
}
|
||||
|
||||
BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx)
|
||||
{
|
||||
return rctx->mem;
|
||||
}
|
||||
|
||||
void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len)
|
||||
{
|
||||
if (len == 0)
|
||||
rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
|
||||
else
|
||||
rctx->max_resp_len = len;
|
||||
}
|
||||
|
||||
int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val)
|
||||
{
|
||||
static const char req_hdr[] =
|
||||
"Content-Type: application/ocsp-request\r\n"
|
||||
"Content-Length: %d\r\n\r\n";
|
||||
int reqlen = ASN1_item_i2d(val, NULL, it);
|
||||
if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0)
|
||||
return 0;
|
||||
if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0)
|
||||
return 0;
|
||||
rctx->state = OHS_ASN1_WRITE_INIT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx,
|
||||
ASN1_VALUE **pval, const ASN1_ITEM *it)
|
||||
{
|
||||
int rv, len;
|
||||
const unsigned char *p;
|
||||
|
||||
rv = OCSP_REQ_CTX_nbio(rctx);
|
||||
if (rv != 1)
|
||||
return rv;
|
||||
|
||||
len = BIO_get_mem_data(rctx->mem, &p);
|
||||
*pval = ASN1_item_d2i(NULL, &p, len, it);
|
||||
if (*pval == NULL) {
|
||||
rctx->state = OHS_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path)
|
||||
{
|
||||
static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
|
||||
|
||||
if (path == NULL)
|
||||
path = "/";
|
||||
|
||||
if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0)
|
||||
return 0;
|
||||
rctx->state = OHS_HTTP_HEADER;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req)
|
||||
{
|
||||
return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
|
||||
(ASN1_VALUE *)req);
|
||||
}
|
||||
|
||||
int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
if (!name)
|
||||
return 0;
|
||||
if (BIO_puts(rctx->mem, name) <= 0)
|
||||
return 0;
|
||||
if (value) {
|
||||
if (BIO_write(rctx->mem, ": ", 2) != 2)
|
||||
return 0;
|
||||
if (BIO_puts(rctx->mem, value) <= 0)
|
||||
return 0;
|
||||
}
|
||||
if (BIO_write(rctx->mem, "\r\n", 2) != 2)
|
||||
return 0;
|
||||
rctx->state = OHS_HTTP_HEADER;
|
||||
return 1;
|
||||
}
|
||||
|
||||
OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
|
||||
int maxline)
|
||||
{
|
||||
|
||||
OCSP_REQ_CTX *rctx = NULL;
|
||||
rctx = OCSP_REQ_CTX_new(io, maxline);
|
||||
if (rctx == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!OCSP_REQ_CTX_http(rctx, "POST", path))
|
||||
goto err;
|
||||
|
||||
if (req && !OCSP_REQ_CTX_set1_req(rctx, req))
|
||||
goto err;
|
||||
|
||||
return rctx;
|
||||
|
||||
err:
|
||||
OCSP_REQ_CTX_free(rctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We
|
||||
* need to obtain the numeric code and (optional) informational message.
|
||||
*/
|
||||
|
||||
static int parse_http_line1(char *line)
|
||||
{
|
||||
int retcode;
|
||||
char *p, *q, *r;
|
||||
/* Skip to first white space (passed protocol info) */
|
||||
|
||||
for (p = line; *p && !ossl_isspace(*p); p++)
|
||||
continue;
|
||||
if (*p == '\0') {
|
||||
OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Skip past white space to start of response code */
|
||||
while (*p && ossl_isspace(*p))
|
||||
p++;
|
||||
|
||||
if (*p == '\0') {
|
||||
OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find end of response code: first whitespace after start of code */
|
||||
for (q = p; *q && !ossl_isspace(*q); q++)
|
||||
continue;
|
||||
|
||||
if (*q == '\0') {
|
||||
OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set end of response code and start of message */
|
||||
*q++ = 0;
|
||||
|
||||
/* Attempt to parse numeric code */
|
||||
retcode = strtoul(p, &r, 10);
|
||||
|
||||
if (*r)
|
||||
return 0;
|
||||
|
||||
/* Skip over any leading white space in message */
|
||||
while (*q && ossl_isspace(*q))
|
||||
q++;
|
||||
|
||||
if (*q) {
|
||||
/*
|
||||
* Finally zap any trailing white space in message (include CRLF)
|
||||
*/
|
||||