curl 2020-06-30 (5a1fc8d3)

Code extracted from:

    https://github.com/curl/curl.git

at commit 5a1fc8d33808d7b22f57bdf9403cda7ff07b0670 (curl-7_71_1).
This commit is contained in:
Curl Upstream
2020-06-30 15:30:38 +02:00
committed by Brad King
parent 5717fdc114
commit 4446fda8e0
39 changed files with 250 additions and 218 deletions
+1 -1
View File
@@ -755,7 +755,7 @@ Curl_cookie_add(struct Curl_easy *data,
co->path = malloc(pathlen + 1); /* one extra for the zero byte */
if(co->path) {
memcpy(co->path, path, pathlen);
co->path[pathlen] = 0; /* zero terminate */
co->path[pathlen] = 0; /* null-terminate */
co->spath = sanitize_cookie_path(co->path);
if(!co->spath)
badcookie = TRUE; /* out of memory bad */
+2 -2
View File
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,7 +42,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn,
size_t working_path_len;
CURLcode result =
Curl_urldecode(data, data->state.up.path, 0, &working_path,
&working_path_len, FALSE);
&working_path_len, REJECT_ZERO);
if(result)
return result;
+2 -1
View File
@@ -99,7 +99,8 @@ static char *unescape_word(struct Curl_easy *data, const char *inputbuff)
char *dictp;
size_t len;
CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len, FALSE);
CURLcode result = Curl_urldecode(data, inputbuff, 0, &newp, &len,
REJECT_NADA);
if(!newp || result)
return NULL;
+5 -5
View File
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,7 +39,7 @@
* Curl_dedotdotify()
* @unittest: 1395
*
* This function gets a zero-terminated path with dot and dotdot sequences
* This function gets a null-terminated path with dot and dotdot sequences
* passed in and strips them off according to the rules in RFC 3986 section
* 5.2.4.
*
@@ -62,7 +62,7 @@ char *Curl_dedotdotify(const char *input)
if(!out)
return NULL; /* out of memory */
*out = 0; /* zero terminates, for inputs like "./" */
*out = 0; /* null-terminates, for inputs like "./" */
/* get a cloned copy of the input */
clone = strdup(input);
@@ -129,7 +129,7 @@ char *Curl_dedotdotify(const char *input)
if(*outptr == '/')
break;
}
*outptr = 0; /* zero-terminate where it stops */
*outptr = 0; /* null-terminate where it stops */
}
else if(!strcmp("/..", clone)) {
clone[2]='/';
@@ -141,7 +141,7 @@ char *Curl_dedotdotify(const char *input)
if(*outptr == '/')
break;
}
*outptr = 0; /* zero-terminate where it stops */
*outptr = 0; /* null-terminate where it stops */
}
/* D. if the input buffer consists only of "." or "..", then remove
+1 -1
View File
@@ -161,7 +161,7 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
}
/*
* Append a zero terminated string at the end.
* Append a null-terminated string at the end.
*/
CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
{
+1 -1
View File
@@ -23,7 +23,7 @@
***************************************************************************/
struct dynbuf {
char *bufr; /* point to a zero terminated allocated buffer */
char *bufr; /* point to a null-terminated allocated buffer */
size_t leng; /* number of bytes *EXCLUDING* the zero terminator */
size_t allc; /* size of the current allocation */
size_t toobig; /* size limit for the buffer */
+18 -6
View File
@@ -89,6 +89,9 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH);
length = (inlength?(size_t)inlength:strlen(string));
if(!length)
return strdup("");
while(length--) {
unsigned char in = *string; /* we need to treat the characters unsigned */
@@ -120,19 +123,26 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
/*
* Curl_urldecode() URL decodes the given string.
*
* Optionally detects control characters (byte codes lower than 32) in the
* data and rejects such data.
*
* Returns a pointer to a malloced string in *ostring with length given in
* *olen. If length == 0, the length is assumed to be strlen(string).
*
* 'data' can be set to NULL but then this function can't convert network
* data to host for non-ascii.
*
* ctrl options:
* - REJECT_NADA: accept everything
* - REJECT_CTRL: rejects control characters (byte codes lower than 32) in
* the data
* - REJECT_ZERO: rejects decoded zero bytes
*
* The values for the enum starts at 2, to make the assert detect legacy
* invokes that used TRUE/FALSE (0 and 1).
*/
CURLcode Curl_urldecode(struct Curl_easy *data,
const char *string, size_t length,
char **ostring, size_t *olen,
bool reject_ctrl)
enum urlreject ctrl)
{
size_t alloc;
char *ns;
@@ -141,6 +151,7 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
CURLcode result = CURLE_OK;
DEBUGASSERT(string);
DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
alloc = (length?length:strlen(string)) + 1;
ns = malloc(alloc);
@@ -176,7 +187,8 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
alloc -= 2;
}
if(reject_ctrl && (in < 0x20)) {
if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
((ctrl == REJECT_ZERO) && (in == 0))) {
free(ns);
return CURLE_URL_MALFORMAT;
}
@@ -210,7 +222,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string,
size_t inputlen = length;
size_t outputlen;
CURLcode res = Curl_urldecode(data, string, inputlen, &str, &outputlen,
FALSE);
REJECT_NADA);
if(res)
return NULL;
+9 -2
View File
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -25,9 +25,16 @@
* allocated string or NULL if an error occurred. */
bool Curl_isunreserved(unsigned char in);
enum urlreject {
REJECT_NADA = 2,
REJECT_CTRL,
REJECT_ZERO
};
CURLcode Curl_urldecode(struct Curl_easy *data,
const char *string, size_t length,
char **ostring, size_t *olen,
bool reject_crlf);
enum urlreject ctrl);
#endif /* HEADER_CURL_ESCAPE_H */
+1 -1
View File
@@ -144,7 +144,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done)
size_t real_path_len;
CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path,
&real_path_len, FALSE);
&real_path_len, REJECT_ZERO);
if(result)
return result;
+2 -2
View File
@@ -602,7 +602,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
/* Note that there's small risk that form->name is NULL here if the
app passed in a bad combo, so we better check for that first. */
if(form->name) {
/* copy name (without strdup; possibly not nul-terminated) */
/* copy name (without strdup; possibly not null-terminated) */
form->name = Curl_memdup(form->name, form->namelength?
form->namelength:
strlen(form->name) + 1);
@@ -771,7 +771,7 @@ void curl_formfree(struct curl_httppost *form)
}
/* Set mime part name, taking care of non nul-terminated name string. */
/* Set mime part name, taking care of non null-terminated name string. */
static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
{
char *zname;
+11 -7
View File
@@ -1043,6 +1043,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
} /* data->set.ftpport */
if(!host) {
const char *r;
/* not an interface and not a host name, get default by extracting
the IP from the control connection */
sslen = sizeof(ss);
@@ -1055,13 +1056,15 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
switch(sa->sa_family) {
#ifdef ENABLE_IPV6
case AF_INET6:
Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
break;
#endif
default:
Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
break;
}
if(!r)
return CURLE_FTP_PORT_FAILED;
host = hbuf; /* use this host name */
possibly_non_local = FALSE; /* we know it is local now */
}
@@ -1449,7 +1452,7 @@ static CURLcode ftp_state_list(struct connectdata *conn)
/* url-decode before evaluation: e.g. paths starting/ending with %2f */
const char *slashPos = NULL;
char *rawPath = NULL;
result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, TRUE);
result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
if(result)
return result;
@@ -2824,7 +2827,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
store++;
ptr++;
}
*store = '\0'; /* zero terminate */
*store = '\0'; /* null-terminate */
}
if(entry_extracted) {
/* If the path name does not look like an absolute path (i.e.: it
@@ -2888,7 +2891,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
ptr++;
for(store = os; *ptr && *ptr != ' ';)
*store++ = *ptr++;
*store = '\0'; /* zero terminate */
*store = '\0'; /* null-terminate */
/* Check for special servers here. */
@@ -3191,7 +3194,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(!result)
/* get the url-decoded "raw" path */
result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen,
REJECT_CTRL);
if(result) {
/* We can limp along anyway (and should try to since we may already be in
* the error path) */
@@ -4107,7 +4111,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
ftpc->cwdfail = FALSE;
/* url-decode ftp path before further evaluation */
result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
if(result)
return result;
+1 -1
View File
@@ -116,7 +116,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
newp += 2;
/* ... and finally unescape */
result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE);
result = Curl_urldecode(data, newp, 0, &sel, &len, REJECT_ZERO);
free(gopherpath);
if(result)
return result;
+6 -4
View File
@@ -125,7 +125,8 @@ const struct Curl_handler Curl_handler_http = {
ZERO_NULL, /* connection_check */
PORT_HTTP, /* defport */
CURLPROTO_HTTP, /* protocol */
PROTOPT_CREDSPERREQUEST /* flags */
PROTOPT_CREDSPERREQUEST | /* flags */
PROTOPT_USERPWDCTRL
};
#ifdef USE_SSL
@@ -150,7 +151,8 @@ const struct Curl_handler Curl_handler_https = {
ZERO_NULL, /* connection_check */
PORT_HTTPS, /* defport */
CURLPROTO_HTTPS, /* protocol */
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN /* flags */
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN | /* flags */
PROTOPT_USERPWDCTRL
};
#endif
@@ -268,7 +270,7 @@ char *Curl_copy_header_value(const char *header)
return NULL;
memcpy(value, start, len);
value[len] = 0; /* zero terminate */
value[len] = 0; /* null-terminate */
return value;
}
@@ -306,7 +308,7 @@ static CURLcode http_output_basic(struct connectdata *conn, bool proxy)
pwd = conn->passwd;
}
out = aprintf("%s:%s", user, pwd);
out = aprintf("%s:%s", user, pwd ? pwd : "");
if(!out)
return CURLE_OUT_OF_MEMORY;
+44
View File
@@ -469,6 +469,46 @@ static struct Curl_easy *duphandle(struct Curl_easy *data)
return second;
}
static int set_transfer_url(struct Curl_easy *data,
struct curl_pushheaders *hp)
{
const char *v;
CURLU *u = curl_url();
CURLUcode uc;
char *url;
v = curl_pushheader_byname(hp, ":scheme");
if(v) {
uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
if(uc)
return 1;
}
v = curl_pushheader_byname(hp, ":authority");
if(v) {
uc = curl_url_set(u, CURLUPART_HOST, v, 0);
if(uc)
return 2;
}
v = curl_pushheader_byname(hp, ":path");
if(v) {
uc = curl_url_set(u, CURLUPART_PATH, v, 0);
if(uc)
return 3;
}
uc = curl_url_get(u, CURLUPART_URL, &url, 0);
if(uc)
return 4;
curl_url_cleanup(u);
if(data->change.url_alloc)
free(data->change.url);
data->change.url_alloc = TRUE;
data->change.url = url;
return 0;
}
static int push_promise(struct Curl_easy *data,
struct connectdata *conn,
@@ -505,6 +545,10 @@ static int push_promise(struct Curl_easy *data,
goto fail;
}
rv = set_transfer_url(newhandle, &heads);
if(rv)
goto fail;
Curl_set_in_callback(data, true);
rv = data->multi->push_cb(data, newhandle,
stream->push_headers_used, &heads,
+8 -5
View File
@@ -118,7 +118,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
if(iface->ifa_addr->sa_family == af) {
if(strcasecompare(iface->ifa_name, interf)) {
void *addr;
char *ip;
const char *ip;
char scope[12] = "";
char ipstr[64];
#ifdef ENABLE_IPV6
@@ -153,15 +153,15 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
}
if(scopeid)
msnprintf(scope, sizeof(scope), "%%%u", scopeid);
msnprintf(scope, sizeof(scope), "%%%u", scopeid);
#endif
}
else
#endif
addr =
&((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
&((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
res = IF2IP_FOUND;
ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
msnprintf(buf, buf_size, "%s%s", ip, scope);
break;
}
@@ -190,6 +190,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
struct sockaddr_in *s;
curl_socket_t dummy;
size_t len;
const char *r;
(void)remote_scope;
(void)local_scope_id;
@@ -219,9 +220,11 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
s = (struct sockaddr_in *)(void *)&req.ifr_addr;
memcpy(&in, &s->sin_addr, sizeof(in));
Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
sclose(dummy);
if(!r)
return IF2IP_NOT_FOUND;
return IF2IP_FOUND;
}
+6 -4
View File
@@ -1957,7 +1957,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
end--;
result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
TRUE);
REJECT_CTRL);
if(result)
return result;
}
@@ -1979,7 +1979,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
return CURLE_URL_MALFORMAT;
/* Decode the name parameter */
result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
result = Curl_urldecode(data, begin, ptr - begin, &name, NULL,
REJECT_CTRL);
if(result)
return result;
@@ -1989,7 +1990,8 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
ptr++;
/* Decode the value parameter */
result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen,
REJECT_CTRL);
if(result) {
free(name);
return result;
@@ -2077,7 +2079,7 @@ static CURLcode imap_parse_custom_request(struct connectdata *conn)
if(custom) {
/* URL decode the custom request */
result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, REJECT_CTRL);
/* Extract the parameters if specified */
if(!result) {
+4 -3
View File
@@ -883,7 +883,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
LDAP_TRACE(("DN '%s'\n", dn));
/* Unescape the DN */
result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, FALSE);
result = Curl_urldecode(conn->data, dn, 0, &unescaped, NULL, REJECT_ZERO);
if(result) {
rc = LDAP_NO_MEMORY;
@@ -949,7 +949,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
/* Unescape the attribute */
result = Curl_urldecode(conn->data, attributes[i], 0, &unescaped, NULL,
FALSE);
REJECT_ZERO);
if(result) {
free(attributes);
@@ -1018,7 +1018,8 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
LDAP_TRACE(("filter '%s'\n", filter));
/* Unescape the filter */
result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL, FALSE);
result = Curl_urldecode(conn->data, filter, 0, &unescaped, NULL,
REJECT_ZERO);
if(result) {
rc = LDAP_NO_MEMORY;
+1 -1
View File
@@ -1419,7 +1419,7 @@ CURLcode curl_mime_data(curl_mimepart *part,
if(datasize)
memcpy(part->data, data, datasize);
part->data[datasize] = '\0'; /* Set a nul terminator as sentinel. */
part->data[datasize] = '\0'; /* Set a null terminator as sentinel. */
part->readfunc = mime_mem_read;
part->seekfunc = mime_mem_seek;
+2 -1
View File
@@ -211,7 +211,8 @@ static CURLcode mqtt_get_topic(struct connectdata *conn,
char *path = conn->data->state.up.path;
if(strlen(path) > 1) {
result = Curl_urldecode(conn->data, path + 1, 0, topic, topiclen, FALSE);
result = Curl_urldecode(conn->data, path + 1, 0, topic, topiclen,
REJECT_NADA);
}
else {
failf(conn->data, "Error: No topic specified.");
+4 -118
View File
@@ -374,11 +374,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
multi->max_concurrent_streams = 100;
multi->ipv6_works = Curl_ipv6works(NULL);
#ifdef USE_WINSOCK
multi->wsa_event = WSACreateEvent();
if(multi->wsa_event == WSA_INVALID_EVENT)
goto error;
#else
#ifdef ENABLE_WAKEUP
if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
@@ -391,7 +386,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
multi->wakeup_pair[0] = CURL_SOCKET_BAD;
multi->wakeup_pair[1] = CURL_SOCKET_BAD;
}
#endif
#endif
return multi;
@@ -1073,15 +1067,11 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
unsigned int i;
unsigned int nfds = 0;
unsigned int curlfds;
bool ufds_malloc = FALSE;
long timeout_internal;
int retcode = 0;
#ifndef USE_WINSOCK
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct pollfd *ufds = &a_few_on_stack[0];
bool ufds_malloc = FALSE;
#else
DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
#endif
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1127,16 +1117,11 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
nfds += extra_nfds; /* add the externally provided ones */
#ifdef ENABLE_WAKEUP
#ifdef USE_WINSOCK
if(use_wakeup) {
#else
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
#endif
++nfds;
}
#endif
#ifndef USE_WINSOCK
if(nfds > NUM_POLLS_ON_STACK) {
/* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
big, so at 2^29 sockets this value might wrap. When a process gets
@@ -1147,9 +1132,7 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
return CURLM_OUT_OF_MEMORY;
ufds_malloc = TRUE;
}
nfds = 0;
#endif
/* only do the second loop if we found descriptors in the first stage run
above */
@@ -1162,36 +1145,22 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
#ifdef USE_WINSOCK
long mask = 0;
#endif
if(bitmap & GETSOCK_READSOCK(i)) {
#ifdef USE_WINSOCK
mask |= FD_READ;
#else
ufds[nfds].fd = sockbunch[i];
ufds[nfds].events = POLLIN;
++nfds;
#endif
s = sockbunch[i];
}
if(bitmap & GETSOCK_WRITESOCK(i)) {
#ifdef USE_WINSOCK
mask |= FD_WRITE;
#else
ufds[nfds].fd = sockbunch[i];
ufds[nfds].events = POLLOUT;
++nfds;
#endif
s = sockbunch[i];
}
if(s == CURL_SOCKET_BAD) {
break;
}
#ifdef USE_WINSOCK
if(WSAEventSelect(s, multi->wsa_event, mask) != 0)
return CURLM_INTERNAL_ERROR;
#endif
}
data = data->next; /* check next handle */
@@ -1200,17 +1169,6 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
/* Add external file descriptions from poll-like struct curl_waitfd */
for(i = 0; i < extra_nfds; i++) {
#ifdef USE_WINSOCK
long events = 0;
if(extra_fds[i].events & CURL_WAIT_POLLIN)
events |= FD_READ;
if(extra_fds[i].events & CURL_WAIT_POLLPRI)
events |= FD_OOB;
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
events |= FD_WRITE;
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, events) != 0)
return CURLM_INTERNAL_ERROR;
#else
ufds[nfds].fd = extra_fds[i].fd;
ufds[nfds].events = 0;
if(extra_fds[i].events & CURL_WAIT_POLLIN)
@@ -1220,61 +1178,28 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
if(extra_fds[i].events & CURL_WAIT_POLLOUT)
ufds[nfds].events |= POLLOUT;
++nfds;
#endif
}
#ifdef ENABLE_WAKEUP
#ifndef USE_WINSOCK
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
ufds[nfds].fd = multi->wakeup_pair[0];
ufds[nfds].events = POLLIN;
++nfds;
}
#endif
#endif
if(nfds) {
int pollrc;
/* wait... */
#ifdef USE_WINSOCK
DWORD waitrc = WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE,
timeout_ms, FALSE);
/* WSA_WAIT_EVENT_0 is 0, so waitrc >= WSA_WAIT_EVENT_0 warns */
if(waitrc == WSA_WAIT_EVENT_0)
pollrc = 1;
else
pollrc = -1;
#else
pollrc = Curl_poll(ufds, nfds, timeout_ms);
#endif
if(pollrc > 0) {
#ifdef USE_WINSOCK
retcode = 0;
#else
retcode = pollrc;
#endif
/* copy revents results from the poll to the curl_multi_wait poll
struct, the bit values of the actual underlying poll() implementation
may not be the same as the ones in the public libcurl API! */
for(i = 0; i < extra_nfds; i++) {
unsigned short mask = 0;
#ifdef USE_WINSOCK
WSANETWORKEVENTS events = {0};
if(WSAEnumNetworkEvents(extra_fds[i].fd, multi->wsa_event,
&events) == 0) {
if(events.lNetworkEvents & FD_READ)
mask |= CURL_WAIT_POLLIN;
if(events.lNetworkEvents & FD_WRITE)
mask |= CURL_WAIT_POLLOUT;
if(events.lNetworkEvents & FD_OOB)
mask |= CURL_WAIT_POLLPRI;
if(events.lNetworkEvents != 0)
retcode++;
}
WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0);
#else
unsigned r = ufds[curlfds + i].revents;
if(r & POLLIN)
@@ -1283,39 +1208,10 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
mask |= CURL_WAIT_POLLOUT;
if(r & POLLPRI)
mask |= CURL_WAIT_POLLPRI;
#endif
extra_fds[i].revents = mask;
}
#ifdef USE_WINSOCK
/* Count up all our own sockets that had activity,
and remove them from the event. */
if(curlfds) {
data = multi->easyp;
while(data) {
bitmap = multi_getsock(data, sockbunch);
for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
WSANETWORKEVENTS events = {0};
if(WSAEnumNetworkEvents(sockbunch[i], multi->wsa_event,
&events) == 0) {
if(events.lNetworkEvents != 0)
retcode++;
}
WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
}
else
break;
}
data = data->next;
}
}
WSAResetEvent(multi->wsa_event);
#else
#ifdef ENABLE_WAKEUP
if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
if(ufds[curlfds + extra_nfds].revents & POLLIN) {
@@ -1328,8 +1224,10 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
when there is no more data, breaking the loop. */
nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
if(nread <= 0) {
#ifndef USE_WINSOCK
if(nread < 0 && EINTR == SOCKERRNO)
continue;
#endif
break;
}
}
@@ -1337,15 +1235,12 @@ static CURLMcode Curl_multi_wait(struct Curl_multi *multi,
retcode--;
}
}
#endif
#endif
}
}
#ifndef USE_WINSOCK
if(ufds_malloc)
free(ufds);
#endif
if(ret)
*ret = retcode;
if(!extrawait || nfds)
@@ -1400,10 +1295,6 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
return CURLM_BAD_HANDLE;
#ifdef ENABLE_WAKEUP
#ifdef USE_WINSOCK
if(WSASetEvent(multi->wsa_event))
return CURLM_OK;
#else
/* the wakeup_pair variable is only written during init and cleanup,
making it safe to access from another thread after the init part
and before cleanup */
@@ -1436,7 +1327,6 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
return CURLM_OK;
}
}
#endif
#endif
return CURLM_WAKEUP_FAILURE;
}
@@ -2596,13 +2486,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
#ifdef USE_WINSOCK
WSACloseEvent(multi->wsa_event);
#else
#ifdef ENABLE_WAKEUP
sclose(multi->wakeup_pair[0]);
sclose(multi->wakeup_pair[1]);
#endif
#endif
free(multi);
-4
View File
@@ -138,13 +138,9 @@ struct Curl_multi {
previous callback */
unsigned int max_concurrent_streams;
#ifdef USE_WINSOCK
WSAEVENT wsa_event; /* winsock event used for waits */
#else
#ifdef ENABLE_WAKEUP
curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
0 is used for read, 1 is used for write */
#endif
#endif
/* multiplexing wanted */
bool multiplexing;
+2 -2
View File
@@ -384,10 +384,10 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
/* This is the end of the last line, copy the last line to the
start of the buffer and zero terminate, for old times sake */
start of the buffer and null-terminate, for old times sake */
size_t n = ptr - pp->linestart_resp;
memmove(buf, pp->linestart_resp, n);
buf[n] = 0; /* zero terminate */
buf[n] = 0; /* null-terminate */
keepon = FALSE;
pp->linestart_resp = ptr + 1; /* advance pointer */
i++; /* skip this before getting out */
+2 -2
View File
@@ -1390,7 +1390,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn)
const char *path = &data->state.up.path[1]; /* skip leading path */
/* URL decode the path for the message ID */
return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
return Curl_urldecode(data, path, 0, &pop3->id, NULL, REJECT_CTRL);
}
/***********************************************************************
@@ -1408,7 +1408,7 @@ static CURLcode pop3_parse_custom_request(struct connectdata *conn)
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, REJECT_CTRL);
return result;
}
+1 -1
View File
@@ -593,7 +593,7 @@ static CURLcode chop_write(struct connectdata *conn,
return pausewrite(data, type, ptr, len);
}
if(wrote != chunklen) {
failf(data, "Failed writing body (%zu != %zu)", wrote, chunklen);
failf(data, "Failure writing output to destination");
return CURLE_WRITE_ERROR;
}
}
+2 -2
View File
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
* Copyright (C) 2016-2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2016-2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -964,7 +964,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
/* URL decode the path */
CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL,
TRUE);
REJECT_CTRL);
if(result)
return result;
+3 -2
View File
@@ -1689,7 +1689,8 @@ static CURLcode smtp_parse_url_path(struct connectdata *conn)
}
/* URL decode the path and use it as the domain in our EHLO */
return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL,
REJECT_CTRL);
}
/***********************************************************************
@@ -1707,7 +1708,7 @@ static CURLcode smtp_parse_custom_request(struct connectdata *conn)
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, REJECT_CTRL);
return result;
}
+1 -1
View File
@@ -795,7 +795,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
#endif /* end of not Windows */
buf[max] = '\0'; /* make sure the string is zero terminated */
buf[max] = '\0'; /* make sure the string is null-terminated */
/* strip trailing '\r\n' or '\n'. */
p = strrchr(buf, '\n');
+2 -2
View File
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -52,7 +52,7 @@ Curl_strtok_r(char *ptr, const char *sep, char **end)
if(**end) {
/* the end is not a null byte */
**end = '\0'; /* zero terminate it! */
**end = '\0'; /* null-terminate it! */
++*end; /* advance the last pointer to beyond the null byte */
}
+1 -1
View File
@@ -487,7 +487,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
file name so we skip the always-present first letter of the path
string. */
result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0,
&filename, NULL, FALSE);
&filename, NULL, REJECT_ZERO);
if(result)
return result;
+24 -13
View File
@@ -1894,23 +1894,32 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
if(result)
return result;
uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user,
CURLU_URLDECODE);
/* we don't use the URL API's URL decoder option here since it rejects
control codes and we want to allow them for some schemes in the user and
password fields */
uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0);
if(!uc) {
conn->user = strdup(data->state.up.user);
if(!conn->user)
return CURLE_OUT_OF_MEMORY;
char *decoded;
result = Curl_urldecode(NULL, data->state.up.user, 0, &decoded, NULL,
conn->handler->flags&PROTOPT_USERPWDCTRL ?
REJECT_ZERO : REJECT_CTRL);
if(result)
return result;
conn->user = decoded;
conn->bits.user_passwd = TRUE;
}
else if(uc != CURLUE_NO_USER)
return Curl_uc_to_curlcode(uc);
uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password,
CURLU_URLDECODE);
uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
if(!uc) {
conn->passwd = strdup(data->state.up.password);
if(!conn->passwd)
return CURLE_OUT_OF_MEMORY;
char *decoded;
result = Curl_urldecode(NULL, data->state.up.password, 0, &decoded, NULL,
conn->handler->flags&PROTOPT_USERPWDCTRL ?
REJECT_ZERO : REJECT_CTRL);
if(result)
return result;
conn->passwd = decoded;
conn->bits.user_passwd = TRUE;
}
else if(uc != CURLUE_NO_PASSWORD)
@@ -2373,10 +2382,10 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data,
if(proxyuser)
result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL,
FALSE);
REJECT_ZERO);
if(!result && proxypasswd)
result = Curl_urldecode(data, proxypasswd, 0, &conn->http_proxy.passwd,
NULL, FALSE);
NULL, REJECT_ZERO);
return result;
}
@@ -3600,6 +3609,7 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.str[STRING_SSL_CIPHER13_LIST_ORIG];
data->set.ssl.primary.pinned_key =
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_ORIG];
#ifndef CURL_DISABLE_PROXY
data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
@@ -3613,6 +3623,7 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
data->set.proxy_ssl.primary.pinned_key =
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
data->set.proxy_ssl.cert = data->set.str[STRING_CERT_PROXY];
@@ -3646,7 +3657,7 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG];
if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
&conn->ssl_config)) {
&conn->ssl_config)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
+8 -5
View File
@@ -225,7 +225,7 @@ static void strcpy_url(char *output, const char *url, bool relative)
break;
}
}
*optr = 0; /* zero terminate output buffer */
*optr = 0; /* null-terminate output buffer */
}
@@ -584,7 +584,7 @@ static CURLUcode junkscan(const char *part)
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x7f,
0x00 /* zero terminate */
0x00 /* null-terminate */
};
size_t n = strlen(part);
size_t nfine = strcspn(part, badbytes);
@@ -1185,7 +1185,10 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
if(urldecode) {
char *decoded;
size_t dlen;
CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, TRUE);
/* this unconditional rejection of control bytes is documented
API behavior */
CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen,
REJECT_CTRL);
free(*part);
if(res) {
*part = NULL;
@@ -1395,7 +1398,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
i = (const unsigned char *)part;
for(o = enc; *i; ++o, ++i)
*o = (*i == ' ') ? '+' : *i;
*o = 0; /* zero terminate */
*o = 0; /* null-terminate */
part = strdup(enc);
if(!part) {
free(enc);
@@ -1419,7 +1422,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
o += 3;
}
}
*o = 0; /* zero terminate */
*o = 0; /* null-terminate */
newp = enc;
if(free_part)
free((char *)part);
+4 -1
View File
@@ -229,6 +229,7 @@ struct ssl_primary_config {
char *cipher_list; /* list of ciphers to use */
char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
char *pinned_key;
struct curl_blob *cert_blob;
BIT(verifypeer); /* set TRUE if this is desired */
BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
BIT(verifystatus); /* set TRUE if certificate status must be checked */
@@ -766,6 +767,8 @@ struct Curl_handler {
HTTP proxy as HTTP proxies may know
this protocol and act as a gateway */
#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */
#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in
user name and password */
#define CONNCHECK_NONE 0 /* No checks */
#define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */
@@ -1562,7 +1565,7 @@ enum dupstring {
STRING_DNS_LOCAL_IP4,
STRING_DNS_LOCAL_IP6,
/* -- end of zero-terminated strings -- */
/* -- end of null-terminated strings -- */
STRING_LASTZEROTERMINATED,
+3 -1
View File
@@ -757,7 +757,9 @@ static ngtcp2_conn_callbacks ng_callbacks = {
cb_extend_max_stream_data,
NULL, /* dcid_status */
NULL, /* handshake_confirmed */
NULL /* recv_new_token */
NULL, /* recv_new_token */
ngtcp2_crypto_delete_crypto_aead_ctx_cb,
ngtcp2_crypto_delete_crypto_cipher_ctx_cb
};
/*
+10
View File
@@ -246,9 +246,14 @@ mbed_connect_step1(struct connectdata *conn,
const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
char * const ssl_cert = SSL_SET_OPTION(cert);
const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
#ifndef CURL_DISABLE_PROXY
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name;
const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
#else
const char * const hostname = conn->host.name;
const long int port = conn->remote_port;
#endif
int ret = -1;
char errorbuf[128];
errorbuf[0] = 0;
@@ -538,9 +543,14 @@ mbed_connect_step2(struct connectdata *conn,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
const mbedtls_x509_crt *peercert;
#ifndef CURL_DISABLE_PROXY
const char * const pinnedpubkey = SSL_IS_PROXY() ?
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
#else
const char * const pinnedpubkey =
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
#endif
conn->recv[sockindex] = mbed_recv;
conn->send[sockindex] = mbed_send;
+6 -5
View File
@@ -31,6 +31,11 @@
#include <limits.h>
/* Wincrypt must be included before anything that could include OpenSSL. */
#if defined(USE_WIN32_CRYPTO)
#include <wincrypt.h>
#endif
#include "urldata.h"
#include "sendf.h"
#include "formdata.h" /* for the boundary function */
@@ -48,10 +53,6 @@
#include "strerror.h"
#include "curl_printf.h"
#if defined(USE_WIN32_CRYPTO)
#include <wincrypt.h>
#endif
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
@@ -1635,7 +1636,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
type itself: for example for an IA5String the data will be ASCII"
It has been however verified that in 0.9.6 and 0.9.7, IA5String
is always zero-terminated.
is always null-terminated.
*/
if((altlen == strlen(altptr)) &&
/* if this isn't true, there was an embedded zero in the name
+45 -3
View File
@@ -63,6 +63,7 @@
#include "warnless.h"
#include "curl_base64.h"
#include "curl_printf.h"
#include "strdup.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -82,6 +83,44 @@
else \
dest->var = NULL;
#define CLONE_BLOB(var) \
if(blobdup(&dest->var, source->var)) \
return FALSE;
static CURLcode blobdup(struct curl_blob **dest,
struct curl_blob *src)
{
DEBUGASSERT(dest);
DEBUGASSERT(!*dest);
if(src) {
/* only if there's data to dupe! */
struct curl_blob *d;
d = malloc(sizeof(struct curl_blob) + src->len);
if(!d)
return CURLE_OUT_OF_MEMORY;
d->len = src->len;
/* Always duplicate because the connection may survive longer than the
handle that passed in the blob. */
d->flags = CURL_BLOB_COPY;
d->data = (void *)((char *)d + sizeof(struct curl_blob));
memcpy(d->data, src->data, src->len);
*dest = d;
}
return CURLE_OK;
}
/* returns TRUE if the blobs are identical */
static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
{
if(!first && !second) /* both are NULL */
return TRUE;
if(!first || !second) /* one is NULL */
return FALSE;
if(first->len != second->len) /* different sizes */
return FALSE;
return !memcmp(first->data, second->data, first->len); /* same data */
}
bool
Curl_ssl_config_matches(struct ssl_primary_config *data,
struct ssl_primary_config *needle)
@@ -91,6 +130,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
(data->verifypeer == needle->verifypeer) &&
(data->verifyhost == needle->verifyhost) &&
(data->verifystatus == needle->verifystatus) &&
blobcmp(data->cert_blob, needle->cert_blob) &&
Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
@@ -115,6 +155,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
dest->verifystatus = source->verifystatus;
dest->sessionid = source->sessionid;
CLONE_BLOB(cert_blob);
CLONE_STRING(CApath);
CLONE_STRING(CAfile);
CLONE_STRING(clientcert);
@@ -137,6 +178,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
Curl_safefree(sslc->cipher_list);
Curl_safefree(sslc->cipher_list13);
Curl_safefree(sslc->pinned_key);
Curl_safefree(sslc->cert_blob);
}
#ifdef USE_SSL
@@ -706,7 +748,7 @@ CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num)
}
/*
* 'value' is NOT a zero terminated string
* 'value' is NOT a null-terminated string
*/
CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
int certnum,
@@ -728,10 +770,10 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
/* sprintf the label and colon */
msnprintf(output, outlen, "%s:", label);
/* memcpy the value (it might not be zero terminated) */
/* memcpy the value (it might not be null-terminated) */
memcpy(&output[labellen + 1], value, valuelen);
/* zero terminate the output */
/* null-terminate the output */
output[labellen + 1 + valuelen] = 0;
nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);