build: drop more forward function declarations

Most by moving functions around. Also delete unused ones.
Reducing their number from 83 to 33.

Remaining ones due to:
- circular dependencies.
- H3 code, that I did not attempt to update and likely the above applies.
- static declarations with attributes (`CURL_PRINTF`, `WARN_UNUSED_RESULT`).
- OS400 code.

Closes #20321
This commit is contained in:
Viktor Szakats
2026-01-14 17:11:55 +01:00
parent 6437bd79ae
commit 82e0d387a2
15 changed files with 1095 additions and 1250 deletions

View File

@@ -259,113 +259,6 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
return rc;
}
static ssize_t on_session_send(nghttp2_session *h2,
const uint8_t *buf, size_t blen,
int flags, void *userp);
static int proxy_h2_on_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame,
void *userp);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static int proxy_h2_on_frame_send(nghttp2_session *session,
const nghttp2_frame *frame,
void *userp);
#endif
static int proxy_h2_on_stream_close(nghttp2_session *session,
int32_t stream_id,
uint32_t error_code, void *userp);
static int proxy_h2_on_header(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
uint8_t flags,
void *userp);
static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *mem, size_t len, void *userp);
/*
* Initialize the cfilter context
*/
static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_proxy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OUT_OF_MEMORY;
nghttp2_session_callbacks *cbs = NULL;
int rc;
DEBUGASSERT(!ctx->h2);
memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
if(tunnel_stream_init(cf, &ctx->tunnel))
goto out;
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
failf(data, "Could not initialize nghttp2 callbacks");
goto out;
}
nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
nghttp2_session_callbacks_set_on_frame_recv_callback(
cbs, proxy_h2_on_frame_recv);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
proxy_h2_on_frame_send);
#endif
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
cbs, tunnel_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
cbs, proxy_h2_on_stream_close);
nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
/* The nghttp2 session is not yet setup, do it */
rc = proxy_h2_client_new(cf, cbs);
if(rc) {
failf(data, "Could not initialize nghttp2");
goto out;
}
{
nghttp2_settings_entry iv[3];
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = H2_TUNNEL_WINDOW_SIZE;
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[2].value = 0;
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3);
if(rc) {
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
}
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
PROXY_HTTP2_HUGE_WINDOW_SIZE);
if(rc) {
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
/* all set, traffic will be send on connect */
result = CURLE_OK;
out:
if(cbs)
nghttp2_session_callbacks_del(cbs);
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
return result;
}
static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx)
{
return !nghttp2_session_want_read(ctx->h2) &&
@@ -1051,6 +944,89 @@ out:
return result;
}
/*
* Initialize the cfilter context
*/
static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_proxy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OUT_OF_MEMORY;
nghttp2_session_callbacks *cbs = NULL;
int rc;
DEBUGASSERT(!ctx->h2);
memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
if(tunnel_stream_init(cf, &ctx->tunnel))
goto out;
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
failf(data, "Could not initialize nghttp2 callbacks");
goto out;
}
nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
nghttp2_session_callbacks_set_on_frame_recv_callback(
cbs, proxy_h2_on_frame_recv);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
proxy_h2_on_frame_send);
#endif
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
cbs, tunnel_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
cbs, proxy_h2_on_stream_close);
nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
/* The nghttp2 session is not yet setup, do it */
rc = proxy_h2_client_new(cf, cbs);
if(rc) {
failf(data, "Could not initialize nghttp2");
goto out;
}
{
nghttp2_settings_entry iv[3];
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = H2_TUNNEL_WINDOW_SIZE;
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[2].value = 0;
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3);
if(rc) {
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
}
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
PROXY_HTTP2_HUGE_WINDOW_SIZE);
if(rc) {
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
/* all set, traffic will be send on connect */
result = CURLE_OK;
out:
if(cbs)
nghttp2_session_callbacks_del(cbs);
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
return result;
}
static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)

View File

@@ -33,9 +33,6 @@
#include "select.h"
#include "curlx/strparse.h"
static void cf_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn);
#ifdef UNITTESTS
/* used by unit2600.c */
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -55,9 +52,6 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
return CURLE_OK;
}
static void conn_report_connect_stats(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
@@ -451,6 +445,51 @@ static CURLcode cf_verboseconnect(struct Curl_easy *data,
}
#endif
static CURLcode cf_cntrl_all(struct connectdata *conn,
struct Curl_easy *data,
bool ignore_result,
int event, int arg1, void *arg2)
{
CURLcode result = CURLE_OK;
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
event, arg1, arg2);
if(!ignore_result && result)
break;
}
return result;
}
static void cf_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
/**
* Update connection statistics
*/
static void conn_report_connect_stats(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf) {
struct curltime connected;
struct curltime appconnected;
memset(&connected, 0, sizeof(connected));
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
if(connected.tv_sec || connected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
memset(&appconnected, 0, sizeof(appconnected));
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
if(appconnected.tv_sec || appconnected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
}
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
int sockindex,
bool blocking,
@@ -922,23 +961,6 @@ Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex)
return cf ? cf_get_remote_addr(cf, data) : NULL;
}
static CURLcode cf_cntrl_all(struct connectdata *conn,
struct Curl_easy *data,
bool ignore_result,
int event, int arg1, void *arg2)
{
CURLcode result = CURLE_OK;
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
event, arg1, arg2);
if(!ignore_result && result)
break;
}
return result;
}
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
{
return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL);
@@ -976,34 +998,6 @@ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
CF_CTRL_DATA_PAUSE, do_pause, NULL);
}
static void cf_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
/**
* Update connection statistics
*/
static void conn_report_connect_stats(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf) {
struct curltime connected;
struct curltime appconnected;
memset(&connected, 0, sizeof(connected));
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
if(connected.tv_sec || connected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
memset(&appconnected, 0, sizeof(appconnected));
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
if(appconnected.tv_sec || appconnected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
}
}
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
bool *input_pending)
{

View File

@@ -69,11 +69,6 @@ struct cpool_bundle {
char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
};
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
static struct cpool_bundle *cpool_bundle_create(const char *dest)
{
struct cpool_bundle *bundle;
@@ -189,6 +184,53 @@ static void cpool_remove_conn(struct cpool *cpool,
}
}
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted)
{
bool done = FALSE;
DEBUGASSERT(data);
DEBUGASSERT(!data->conn);
DEBUGASSERT(cpool);
DEBUGASSERT(!conn->bits.in_cpool);
/*
* If this connection is not marked to force-close, leave it open if there
* are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
" still in use by %u transfers", conn->connection_id,
conn->attached_xfers);
return;
}
/* treat the connection as aborted in CONNECT_ONLY situations, we do
* not know what the APP did with it. */
if(conn->connect_only)
aborted = TRUE;
conn->bits.aborted = aborted;
/* We do not shutdown dead connections. The term 'dead' can be misleading
* here, as we also mark errored connections/transfers as 'dead'.
* If we do a shutdown for an aborted transfer, the server might think
* it was successful otherwise (for example an ftps: upload). This is
* not what we want. */
if(aborted)
done = TRUE;
if(!done) {
/* Attempt to shutdown the connection right away. */
Curl_cshutdn_run_once(cpool->idata, conn, &done);
}
if(done || !data->multi)
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
else
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
}
void Curl_cpool_destroy(struct cpool *cpool)
{
if(cpool && cpool->initialised && cpool->idata) {
@@ -592,53 +634,6 @@ bool Curl_cpool_find(struct Curl_easy *data,
return result;
}
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted)
{
bool done = FALSE;
DEBUGASSERT(data);
DEBUGASSERT(!data->conn);
DEBUGASSERT(cpool);
DEBUGASSERT(!conn->bits.in_cpool);
/*
* If this connection is not marked to force-close, leave it open if there
* are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
" still in use by %u transfers", conn->connection_id,
conn->attached_xfers);
return;
}
/* treat the connection as aborted in CONNECT_ONLY situations, we do
* not know what the APP did with it. */
if(conn->connect_only)
aborted = TRUE;
conn->bits.aborted = aborted;
/* We do not shutdown dead connections. The term 'dead' can be misleading
* here, as we also mark errored connections/transfers as 'dead'.
* If we do a shutdown for an aborted transfer, the server might think
* it was successful otherwise (for example an ftps: upload). This is
* not what we want. */
if(aborted)
done = TRUE;
if(!done) {
/* Attempt to shutdown the connection right away. */
Curl_cshutdn_run_once(cpool->idata, conn, &done);
}
if(done || !data->multi)
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
else
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
}
void Curl_conn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool aborted)

View File

@@ -101,22 +101,6 @@ struct cw_out_ctx {
BIT(errored);
};
static CURLcode cw_out_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes);
static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer);
static CURLcode cw_out_init(struct Curl_easy *data,
struct Curl_cwriter *writer);
const struct Curl_cwtype Curl_cwt_out = {
"cw-out",
NULL,
cw_out_init,
cw_out_write,
cw_out_close,
sizeof(struct cw_out_ctx)
};
static CURLcode cw_out_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
@@ -455,6 +439,15 @@ static CURLcode cw_out_write(struct Curl_easy *data,
return CURLE_OK;
}
const struct Curl_cwtype Curl_cwt_out = {
"cw-out",
NULL,
cw_out_init,
cw_out_write,
cw_out_close,
sizeof(struct cw_out_ctx)
};
bool Curl_cw_out_is_paused(struct Curl_easy *data)
{
struct Curl_cwriter *cw_out;

View File

@@ -70,23 +70,6 @@ struct cw_pause_ctx {
size_t buf_total;
};
static CURLcode cw_pause_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes);
static void cw_pause_close(struct Curl_easy *data,
struct Curl_cwriter *writer);
static CURLcode cw_pause_init(struct Curl_easy *data,
struct Curl_cwriter *writer);
const struct Curl_cwtype Curl_cwt_pause = {
"cw-pause",
NULL,
cw_pause_init,
cw_pause_write,
cw_pause_close,
sizeof(struct cw_pause_ctx)
};
static CURLcode cw_pause_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
@@ -220,6 +203,15 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
return result;
}
const struct Curl_cwtype Curl_cwt_pause = {
"cw-pause",
NULL,
cw_pause_init,
cw_pause_write,
cw_pause_close,
sizeof(struct cw_pause_ctx)
};
CURLcode Curl_cw_pause_flush(struct Curl_easy *data)
{
struct Curl_cwriter *cw_pause;

View File

@@ -124,7 +124,59 @@ struct cf_h2_ctx {
#undef CF_CTX_CALL_DATA
#define CF_CTX_CALL_DATA(cf) ((struct cf_h2_ctx *)(cf)->ctx)->call_data
static void h2_stream_hash_free(unsigned int id, void *stream);
/**
* All about the H2 internals of a stream
*/
struct h2_stream_ctx {
struct bufq sendbuf; /* request buffer */
struct h1_req_parser h1; /* parsing the request */
struct dynhds resp_trailers; /* response trailer fields */
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
curl_off_t nrcvd_data; /* number of DATA bytes received */
char **push_headers; /* allocated array */
size_t push_headers_used; /* number of entries filled in */
size_t push_headers_alloc; /* number of entries allocated */
int status_code; /* HTTP response status code */
uint32_t error; /* stream error code */
CURLcode xfer_result; /* Result of writing out response */
int32_t local_window_size; /* the local recv window size */
int32_t id; /* HTTP/2 protocol identifier for stream */
BIT(resp_hds_complete); /* we have a complete, final response */
BIT(closed); /* TRUE on stream close */
BIT(reset); /* TRUE on stream reset */
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
BIT(bodystarted);
BIT(body_eos); /* the complete body has been added to `sendbuf` and
* is being/has been processed from there. */
BIT(write_paused); /* stream write is paused */
};
static void free_push_headers(struct h2_stream_ctx *stream)
{
size_t i;
for(i = 0; i < stream->push_headers_used; i++)
curlx_free(stream->push_headers[i]);
Curl_safefree(stream->push_headers);
stream->push_headers_used = 0;
}
static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
{
Curl_bufq_free(&stream->sendbuf);
Curl_h1_req_parse_free(&stream->h1);
Curl_dynhds_free(&stream->resp_trailers);
free_push_headers(stream);
curlx_free(stream);
}
static void h2_stream_hash_free(unsigned int id, void *stream)
{
(void)id;
DEBUGASSERT(stream);
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
}
static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
{
@@ -212,41 +264,6 @@ static CURLcode cf_h2_update_settings(struct cf_h2_ctx *ctx,
return CURLE_OK;
}
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
struct Curl_easy *data);
static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
struct Curl_easy *data);
/**
* All about the H2 internals of a stream
*/
struct h2_stream_ctx {
struct bufq sendbuf; /* request buffer */
struct h1_req_parser h1; /* parsing the request */
struct dynhds resp_trailers; /* response trailer fields */
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
curl_off_t nrcvd_data; /* number of DATA bytes received */
char **push_headers; /* allocated array */
size_t push_headers_used; /* number of entries filled in */
size_t push_headers_alloc; /* number of entries allocated */
int status_code; /* HTTP response status code */
uint32_t error; /* stream error code */
CURLcode xfer_result; /* Result of writing out response */
int32_t local_window_size; /* the local recv window size */
int32_t id; /* HTTP/2 protocol identifier for stream */
BIT(resp_hds_complete); /* we have a complete, final response */
BIT(closed); /* TRUE on stream close */
BIT(reset); /* TRUE on stream reset */
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
BIT(bodystarted);
BIT(body_eos); /* the complete body has been added to `sendbuf` and
* is being/has been processed from there. */
BIT(write_paused); /* stream write is paused */
};
#define H2_STREAM_CTX(ctx, data) \
((struct h2_stream_ctx *)( \
data? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL))
@@ -275,31 +292,6 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
return stream;
}
static void free_push_headers(struct h2_stream_ctx *stream)
{
size_t i;
for(i = 0; i < stream->push_headers_used; i++)
curlx_free(stream->push_headers[i]);
Curl_safefree(stream->push_headers);
stream->push_headers_used = 0;
}
static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
{
Curl_bufq_free(&stream->sendbuf);
Curl_h1_req_parse_free(&stream->h1);
Curl_dynhds_free(&stream->resp_trailers);
free_push_headers(stream);
curlx_free(stream);
}
static void h2_stream_hash_free(unsigned int id, void *stream)
{
(void)id;
DEBUGASSERT(stream);
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
}
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
struct Curl_easy *data)
@@ -406,6 +398,29 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
return CURLE_OK;
}
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
size_t nwritten;
CURLcode result;
if(Curl_bufq_is_empty(&ctx->outbufq))
return CURLE_OK;
result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0,
&nwritten);
if(result) {
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
Curl_bufq_len(&ctx->outbufq));
ctx->nw_out_blocked = 1;
}
return result;
}
return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
}
static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
@@ -464,154 +479,6 @@ static int h2_client_new(struct Curl_cfilter *cf,
return rc;
}
static ssize_t send_callback(nghttp2_session *h2,
const uint8_t *mem, size_t length, int flags,
void *userp);
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
void *userp);
static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame,
int lib_error_code,
void *user_data);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
void *userp);
#endif
static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *mem, size_t len, void *userp);
static int on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *userp);
static int on_begin_headers(nghttp2_session *session,
const nghttp2_frame *frame, void *userp);
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
uint8_t flags,
void *userp);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static int error_callback(nghttp2_session *session, const char *msg,
size_t len, void *userp);
#endif
static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
CURLcode result = CURLE_OUT_OF_MEMORY;
int rc;
nghttp2_session_callbacks *cbs = NULL;
DEBUGASSERT(!ctx->h2);
DEBUGASSERT(ctx->initialized);
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
failf(data, "Could not initialize nghttp2 callbacks");
goto out;
}
nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
cf_h2_on_invalid_frame_recv);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
#endif
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
cbs, on_data_chunk_recv);
nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
nghttp2_session_callbacks_set_on_begin_headers_callback(
cbs, on_begin_headers);
nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
#endif
/* The nghttp2 session is not yet setup, do it */
rc = h2_client_new(cf, cbs);
if(rc) {
failf(data, "Could not initialize nghttp2");
goto out;
}
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
if(ctx->via_h1_upgrade) {
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
* in the H1 request and we upgrade from there. This stream
* is opened implicitly as #1. */
uint8_t binsettings[H2_BINSETTINGS_LEN];
ssize_t rclen;
size_t binlen; /* length of the binsettings data */
rclen = populate_binsettings(binsettings, data);
if(!curlx_sztouz(rclen, &binlen) || !binlen) {
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
result = CURLE_FAILED_INIT;
goto out;
}
result = http2_data_setup(cf, data, &stream);
if(result)
goto out;
DEBUGASSERT(stream);
stream->id = 1;
/* queue SETTINGS frame (again) */
rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
data->state.httpreq == HTTPREQ_HEAD,
NULL);
if(rc) {
failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
data);
if(rc) {
infof(data, "http/2: failed to set user_data for stream %u",
stream->id);
DEBUGASSERT(0);
}
CURL_TRC_CF(data, cf, "created session via Upgrade");
}
else {
nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
size_t ivlen;
ivlen = populate_settings(iv, data, ctx);
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
iv, ivlen);
if(rc) {
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
}
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
HTTP2_HUGE_WINDOW_SIZE);
if(rc) {
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
/* all set, traffic will be send on connect */
result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
out:
if(cbs)
nghttp2_session_callbacks_del(cbs);
return result;
}
/*
* Returns nonzero if current HTTP/2 session should be closed.
*/
@@ -738,29 +605,6 @@ void Curl_http2_ver(char *p, size_t len)
(void)curl_msnprintf(p, len, "nghttp2/%s", h2->version_str);
}
static CURLcode nw_out_flush(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
size_t nwritten;
CURLcode result;
if(Curl_bufq_is_empty(&ctx->outbufq))
return CURLE_OK;
result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0,
&nwritten);
if(result) {
if(result == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
Curl_bufq_len(&ctx->outbufq));
ctx->nw_out_blocked = 1;
}
return result;
}
return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
}
/*
* The implementation of nghttp2_send_callback type. Here we write |data| with
* size |length| to the network and return the number of bytes actually
@@ -2533,6 +2377,125 @@ static CURLcode cf_h2_adjust_pollset(struct Curl_cfilter *cf,
return result;
}
static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
CURLcode result = CURLE_OUT_OF_MEMORY;
int rc;
nghttp2_session_callbacks *cbs = NULL;
DEBUGASSERT(!ctx->h2);
DEBUGASSERT(ctx->initialized);
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
failf(data, "Could not initialize nghttp2 callbacks");
goto out;
}
nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
cf_h2_on_invalid_frame_recv);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
#endif
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
cbs, on_data_chunk_recv);
nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
nghttp2_session_callbacks_set_on_begin_headers_callback(
cbs, on_begin_headers);
nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
#endif
/* The nghttp2 session is not yet setup, do it */
rc = h2_client_new(cf, cbs);
if(rc) {
failf(data, "Could not initialize nghttp2");
goto out;
}
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
if(ctx->via_h1_upgrade) {
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
* in the H1 request and we upgrade from there. This stream
* is opened implicitly as #1. */
uint8_t binsettings[H2_BINSETTINGS_LEN];
ssize_t rclen;
size_t binlen; /* length of the binsettings data */
rclen = populate_binsettings(binsettings, data);
if(!curlx_sztouz(rclen, &binlen) || !binlen) {
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
result = CURLE_FAILED_INIT;
goto out;
}
result = http2_data_setup(cf, data, &stream);
if(result)
goto out;
DEBUGASSERT(stream);
stream->id = 1;
/* queue SETTINGS frame (again) */
rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
data->state.httpreq == HTTPREQ_HEAD,
NULL);
if(rc) {
failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
data);
if(rc) {
infof(data, "http/2: failed to set user_data for stream %u",
stream->id);
DEBUGASSERT(0);
}
CURL_TRC_CF(data, cf, "created session via Upgrade");
}
else {
nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
size_t ivlen;
ivlen = populate_settings(iv, data, ctx);
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
iv, ivlen);
if(rc) {
failf(data, "nghttp2_submit_settings() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
}
rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
HTTP2_HUGE_WINDOW_SIZE);
if(rc) {
failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
nghttp2_strerror(rc), rc);
result = CURLE_HTTP2;
goto out;
}
/* all set, traffic will be send on connect */
result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
out:
if(cbs)
nghttp2_session_callbacks_del(cbs);
return result;
}
static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)

View File

@@ -53,30 +53,8 @@ struct Curl_easy;
static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
void *instream, bool *hasread);
/* Encoders. */
static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
curl_mimepart *part);
static curl_off_t encoder_nop_size(curl_mimepart *part);
static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
curl_mimepart *part);
static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
curl_mimepart *part);
static curl_off_t encoder_base64_size(curl_mimepart *part);
static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
curl_mimepart *part);
static curl_off_t encoder_qp_size(curl_mimepart *part);
static curl_off_t mime_size(curl_mimepart *part);
static const struct mime_encoder encoders[] = {
{ "binary", encoder_nop_read, encoder_nop_size },
{ "8bit", encoder_nop_read, encoder_nop_size },
{ "7bit", encoder_7bit_read, encoder_nop_size },
{ "base64", encoder_base64_read, encoder_base64_size },
{ "quoted-printable", encoder_qp_read, encoder_qp_size },
{ ZERO_NULL, ZERO_NULL, ZERO_NULL }
};
/* Quoted-printable character class table.
*
* We cannot rely on ctype functions since quoted-printable input data
@@ -1435,6 +1413,15 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
return CURLE_OK;
}
static const struct mime_encoder encoders[] = {
{ "binary", encoder_nop_read, encoder_nop_size },
{ "8bit", encoder_nop_read, encoder_nop_size },
{ "7bit", encoder_7bit_read, encoder_nop_size },
{ "base64", encoder_base64_read, encoder_base64_size },
{ "quoted-printable", encoder_qp_read, encoder_qp_size },
{ ZERO_NULL, ZERO_NULL, ZERO_NULL }
};
/* Set mime data transfer encoder. */
CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
{

View File

@@ -73,31 +73,6 @@ struct RTSP {
#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
((unsigned int)((unsigned char)((p)[3]))))
/*
* Parse and write out an RTSP response.
* @param data the transfer
* @param conn the connection
* @param buf data read from connection
* @param blen amount of data in buf
* @param is_eos TRUE iff this is the last write
* @param readmore out, TRUE iff complete buf was consumed and more data
* is needed
*/
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
const char *buf,
size_t blen,
bool is_eos);
static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data,
const char *buf,
size_t blen,
bool is_eos);
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static uint32_t rtsp_conncheck(struct Curl_easy *data,
struct connectdata *check,
uint32_t checks_to_perform);
/* this returns the socket to wait for in the DO and DOING state for the multi
interface and then we are always _sending_ a request and thus we wait for
the single socket to become writable only */
@@ -108,11 +83,6 @@ static CURLcode rtsp_do_pollset(struct Curl_easy *data,
return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]);
}
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
size_t len);
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
const char *transport);
#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */
static void rtsp_easy_dtor(void *key, size_t klen, void *entry)
@@ -649,6 +619,48 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data,
return CURLE_OK;
}
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
size_t len)
{
size_t wrote;
curl_write_callback writeit;
void *user_ptr;
if(len == 0) {
failf(data, "Cannot write a 0 size RTP packet.");
return CURLE_WRITE_ERROR;
}
/* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
pointer to write out the RTP data. */
if(data->set.fwrite_rtp) {
writeit = data->set.fwrite_rtp;
user_ptr = data->set.rtp_out;
}
else {
writeit = data->set.fwrite_func;
user_ptr = data->set.out;
}
Curl_set_in_callback(data, TRUE);
wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
Curl_set_in_callback(data, FALSE);
if(CURL_WRITEFUNC_PAUSE == wrote) {
failf(data, "Cannot pause RTP");
return CURLE_WRITE_ERROR;
}
if(wrote != len) {
failf(data, "Failed writing RTP data");
return CURLE_WRITE_ERROR;
}
return CURLE_OK;
}
static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
struct rtsp_conn *rtspc,
const char *buf,
@@ -812,6 +824,16 @@ out:
return result;
}
/*
* Parse and write out an RTSP response.
* @param data the transfer
* @param conn the connection
* @param buf data read from connection
* @param blen amount of data in buf
* @param is_eos TRUE iff this is the last write
* @param readmore out, TRUE iff complete buf was consumed and more data
* is needed
*/
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
const char *buf,
size_t blen,
@@ -908,45 +930,46 @@ static CURLcode rtsp_rtp_write_resp_hd(struct Curl_easy *data,
return rtsp_rtp_write_resp(data, buf, blen, is_eos);
}
static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr,
size_t len)
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
const char *transport)
{
size_t wrote;
curl_write_callback writeit;
void *user_ptr;
if(len == 0) {
failf(data, "Cannot write a 0 size RTP packet.");
return CURLE_WRITE_ERROR;
/* If we receive multiple Transport response-headers, the interleaved
channels of each response header is recorded and used together for
subsequent data validity checks.*/
/* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
const char *start, *end;
start = transport;
while(start && *start) {
curlx_str_passblanks(&start);
end = strchr(start, ';');
if(checkprefix("interleaved=", start)) {
curl_off_t chan1, chan2, chan;
const char *p = start + 12;
if(!curlx_str_number(&p, &chan1, 255)) {
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
chan2 = chan1;
if(!curlx_str_single(&p, '-')) {
if(curlx_str_number(&p, &chan2, 255)) {
infof(data, "Unable to read the interleaved parameter from "
"Transport header: [%s]", transport);
chan2 = chan1;
}
}
for(chan = chan1; chan <= chan2; chan++) {
int idx = (int)chan / 8;
int off = (int)chan % 8;
rtp_channel_mask[idx] |= (unsigned char)(1 << off);
}
}
else {
infof(data, "Unable to read the interleaved parameter from "
"Transport header: [%s]", transport);
}
break;
}
/* skip to next parameter */
start = (!end) ? end : (end + 1);
}
/* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
pointer to write out the RTP data. */
if(data->set.fwrite_rtp) {
writeit = data->set.fwrite_rtp;
user_ptr = data->set.rtp_out;
}
else {
writeit = data->set.fwrite_func;
user_ptr = data->set.out;
}
Curl_set_in_callback(data, TRUE);
wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
Curl_set_in_callback(data, FALSE);
if(CURL_WRITEFUNC_PAUSE == wrote) {
failf(data, "Cannot pause RTP");
return CURLE_WRITE_ERROR;
}
if(wrote != len) {
failf(data, "Failed writing RTP data");
return CURLE_WRITE_ERROR;
}
return CURLE_OK;
}
@@ -1019,49 +1042,6 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
return CURLE_OK;
}
static CURLcode rtsp_parse_transport(struct Curl_easy *data,
const char *transport)
{
/* If we receive multiple Transport response-headers, the interleaved
channels of each response header is recorded and used together for
subsequent data validity checks.*/
/* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
const char *start, *end;
start = transport;
while(start && *start) {
curlx_str_passblanks(&start);
end = strchr(start, ';');
if(checkprefix("interleaved=", start)) {
curl_off_t chan1, chan2, chan;
const char *p = start + 12;
if(!curlx_str_number(&p, &chan1, 255)) {
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
chan2 = chan1;
if(!curlx_str_single(&p, '-')) {
if(curlx_str_number(&p, &chan2, 255)) {
infof(data, "Unable to read the interleaved parameter from "
"Transport header: [%s]", transport);
chan2 = chan1;
}
}
for(chan = chan1; chan <= chan2; chan++) {
int idx = (int)chan / 8;
int off = (int)chan % 8;
rtp_channel_mask[idx] |= (unsigned char)(1 << off);
}
}
else {
infof(data, "Unable to read the interleaved parameter from "
"Transport header: [%s]", transport);
}
break;
}
/* skip to next parameter */
start = (!end) ? end : (end + 1);
}
return CURLE_OK;
}
/*
* RTSP handler interface.
*/

View File

@@ -45,42 +45,6 @@
#include "strerror.h"
#include "progress.h"
static CURLcode do_init_writer_stack(struct Curl_easy *data);
/* Curl_client_write() sends data to the write callback(s)
The bit pattern defines to what "streams" to write to. Body and/or header.
The defines are in sendf.h of course.
*/
CURLcode Curl_client_write(struct Curl_easy *data,
int type, const char *buf, size_t blen)
{
CURLcode result;
/* it is one of those, at least */
DEBUGASSERT(type &
(CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
/* BODY is only BODY (with optional EOS) */
DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
/* INFO is only INFO (with optional EOS) */
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
if(!data->req.writer_stack) {
result = do_init_writer_stack(data);
if(result)
return result;
DEBUGASSERT(data->req.writer_stack);
}
result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
type, blen, result);
return result;
}
static void cl_reset_writer(struct Curl_easy *data)
{
struct Curl_cwriter *writer = data->req.writer_stack;
@@ -206,6 +170,7 @@ struct cw_download_ctx {
struct Curl_cwriter super;
BIT(started_response);
};
/* Download client writer in phase CURL_CW_PROTOCOL that
* sees the "real" download body data. */
static CURLcode cw_download_write(struct Curl_easy *data,
@@ -351,6 +316,84 @@ static const struct Curl_cwtype cw_raw = {
sizeof(struct Curl_cwriter)
};
static CURLcode do_init_writer_stack(struct Curl_easy *data)
{
struct Curl_cwriter *writer;
CURLcode result;
DEBUGASSERT(!data->req.writer_stack);
result = Curl_cwriter_create(&data->req.writer_stack,
data, &Curl_cwt_out, CURL_CW_CLIENT);
if(result)
return result;
/* This places the "pause" writer behind the "download" writer that
* is added below. Meaning the "download" can do checks on content length
* and other things *before* write outs are buffered for paused transfers. */
result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
CURL_CW_PROTOCOL);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
return result;
}
/* Curl_client_write() sends data to the write callback(s)
The bit pattern defines to what "streams" to write to. Body and/or header.
The defines are in sendf.h of course.
*/
CURLcode Curl_client_write(struct Curl_easy *data,
int type, const char *buf, size_t blen)
{
CURLcode result;
/* it is one of those, at least */
DEBUGASSERT(type &
(CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
/* BODY is only BODY (with optional EOS) */
DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
/* INFO is only INFO (with optional EOS) */
DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
if(!data->req.writer_stack) {
result = do_init_writer_stack(data);
if(result)
return result;
DEBUGASSERT(data->req.writer_stack);
}
result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
type, blen, result);
return result;
}
/* Create an unencoding writer stage using the given handler. */
CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
struct Curl_easy *data,
@@ -400,51 +443,6 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
return n;
}
static CURLcode do_init_writer_stack(struct Curl_easy *data)
{
struct Curl_cwriter *writer;
CURLcode result;
DEBUGASSERT(!data->req.writer_stack);
result = Curl_cwriter_create(&data->req.writer_stack,
data, &Curl_cwt_out, CURL_CW_CLIENT);
if(result)
return result;
/* This places the "pause" writer behind the "download" writer that
* is added below. Meaning the "download" can do checks on content length
* and other things *before* write outs are buffered for paused transfers. */
result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
CURL_CW_PROTOCOL);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
if(!result) {
result = Curl_cwriter_add(data, writer);
if(result)
Curl_cwriter_free(data, writer);
}
if(result)
return result;
return result;
}
CURLcode Curl_cwriter_add(struct Curl_easy *data,
struct Curl_cwriter *writer)
{

View File

@@ -83,9 +83,6 @@ struct Curl_URL {
#define DEFAULT_SCHEME "https"
static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
unsigned int flags);
static void free_urlhandle(struct Curl_URL *u)
{
curlx_free(u->scheme);
@@ -222,81 +219,6 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
return 0;
}
/*
* Concatenate a relative URL onto a base URL making it absolute.
*/
static CURLUcode redirect_url(const char *base, const char *relurl,
CURLU *u, unsigned int flags)
{
struct dynbuf urlbuf;
bool host_changed = FALSE;
const char *useurl = relurl;
const char *cutoff = NULL;
size_t prelen;
CURLUcode uc;
/* protsep points to the start of the hostname, after [scheme]:// */
const char *protsep = base + strlen(u->scheme) + 3;
DEBUGASSERT(base && relurl && u); /* all set here */
if(!base)
return CURLUE_MALFORMED_INPUT; /* should never happen */
/* handle different relative URL types */
switch(relurl[0]) {
case '/':
if(relurl[1] == '/') {
/* protocol-relative URL: //example.com/path */
cutoff = protsep;
useurl = &relurl[2];
host_changed = TRUE;
}
else
/* absolute /path */
cutoff = strchr(protsep, '/');
break;
case '#':
/* fragment-only change */
if(u->fragment)
cutoff = strchr(protsep, '#');
break;
default:
/* path or query-only change */
if(u->query && u->query[0])
/* remove existing query */
cutoff = strchr(protsep, '?');
else if(u->fragment && u->fragment[0])
/* Remove existing fragment */
cutoff = strchr(protsep, '#');
if(relurl[0] != '?') {
/* append a relative path after the last slash */
cutoff = memrchr(protsep, '/',
cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
if(cutoff)
cutoff++; /* truncate after last slash */
}
break;
}
prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
/* build new URL */
curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
!urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
flags & ~CURLU_PATH_AS_IS);
}
else
uc = CURLUE_OUT_OF_MEMORY;
curlx_dyn_free(&urlbuf);
return uc;
}
/* scan for byte values <= 31, 127 and sometimes space */
CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace)
{
@@ -1286,6 +1208,81 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
return result;
}
/*
* Concatenate a relative URL onto a base URL making it absolute.
*/
static CURLUcode redirect_url(const char *base, const char *relurl,
CURLU *u, unsigned int flags)
{
struct dynbuf urlbuf;
bool host_changed = FALSE;
const char *useurl = relurl;
const char *cutoff = NULL;
size_t prelen;
CURLUcode uc;
/* protsep points to the start of the hostname, after [scheme]:// */
const char *protsep = base + strlen(u->scheme) + 3;
DEBUGASSERT(base && relurl && u); /* all set here */
if(!base)
return CURLUE_MALFORMED_INPUT; /* should never happen */
/* handle different relative URL types */
switch(relurl[0]) {
case '/':
if(relurl[1] == '/') {
/* protocol-relative URL: //example.com/path */
cutoff = protsep;
useurl = &relurl[2];
host_changed = TRUE;
}
else
/* absolute /path */
cutoff = strchr(protsep, '/');
break;
case '#':
/* fragment-only change */
if(u->fragment)
cutoff = strchr(protsep, '#');
break;
default:
/* path or query-only change */
if(u->query && u->query[0])
/* remove existing query */
cutoff = strchr(protsep, '?');
else if(u->fragment && u->fragment[0])
/* Remove existing fragment */
cutoff = strchr(protsep, '#');
if(relurl[0] != '?') {
/* append a relative path after the last slash */
cutoff = memrchr(protsep, '/',
cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
if(cutoff)
cutoff++; /* truncate after last slash */
}
break;
}
prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
/* build new URL */
curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
!urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
flags & ~CURLU_PATH_AS_IS);
}
else
uc = CURLUE_OUT_OF_MEMORY;
curlx_dyn_free(&urlbuf);
return uc;
}
/*
*/
CURLU *curl_url(void)

View File

@@ -134,10 +134,6 @@
static bool s_win_has_alpn;
#endif
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *pinnedpubkey);
static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
void *BufDataPtr, unsigned long BufByteSize)
{
@@ -1115,6 +1111,74 @@ static CURLcode schannel_error(struct Curl_easy *data,
}
}
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *pinnedpubkey)
{
struct ssl_connect_data *connssl = cf->ctx;
struct schannel_ssl_backend_data *backend =
(struct schannel_ssl_backend_data *)connssl->backend;
CERT_CONTEXT *pCertContextServer = NULL;
/* Result is returned to caller */
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
DEBUGASSERT(backend);
/* if a path was not specified, do not pin */
if(!pinnedpubkey)
return CURLE_OK;
do {
SECURITY_STATUS sspi_status;
const char *x509_der;
DWORD x509_der_len;
struct Curl_X509certificate x509_parsed;
struct Curl_asn1Element *pubkey;
sspi_status =
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
char buffer[STRERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
break; /* failed */
}
if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
(pCertContextServer->cbCertEncoded > 0)))
break;
x509_der = (const char *)pCertContextServer->pbCertEncoded;
x509_der_len = pCertContextServer->cbCertEncoded;
memset(&x509_parsed, 0, sizeof(x509_parsed));
if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
break;
pubkey = &x509_parsed.subjectPublicKeyInfo;
if(!pubkey->header || pubkey->end <= pubkey->header) {
failf(data, "SSL: failed retrieving public key from server certificate");
break;
}
result = Curl_pin_peer_pubkey(data,
pinnedpubkey,
(const unsigned char *)pubkey->header,
(size_t)(pubkey->end - pubkey->header));
if(result) {
failf(data, "SSL: public key does not match pinned public key");
}
} while(0);
if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
return result;
}
static CURLcode schannel_connect_step2(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -2580,74 +2644,6 @@ static CURLcode schannel_random(struct Curl_easy *data,
return Curl_win32_random(entropy, length);
}
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *pinnedpubkey)
{
struct ssl_connect_data *connssl = cf->ctx;
struct schannel_ssl_backend_data *backend =
(struct schannel_ssl_backend_data *)connssl->backend;
CERT_CONTEXT *pCertContextServer = NULL;
/* Result is returned to caller */
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
DEBUGASSERT(backend);
/* if a path was not specified, do not pin */
if(!pinnedpubkey)
return CURLE_OK;
do {
SECURITY_STATUS sspi_status;
const char *x509_der;
DWORD x509_der_len;
struct Curl_X509certificate x509_parsed;
struct Curl_asn1Element *pubkey;
sspi_status =
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
char buffer[STRERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
break; /* failed */
}
if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
(pCertContextServer->cbCertEncoded > 0)))
break;
x509_der = (const char *)pCertContextServer->pbCertEncoded;
x509_der_len = pCertContextServer->cbCertEncoded;
memset(&x509_parsed, 0, sizeof(x509_parsed));
if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
break;
pubkey = &x509_parsed.subjectPublicKeyInfo;
if(!pubkey->header || pubkey->end <= pubkey->header) {
failf(data, "SSL: failed retrieving public key from server certificate");
break;
}
result = Curl_pin_peer_pubkey(data,
pinnedpubkey,
(const unsigned char *)pubkey->header,
(size_t)(pubkey->end - pubkey->header));
if(result) {
failf(data, "SSL: public key does not match pinned public key");
}
} while(0);
if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
return result;
}
static void schannel_checksum(const unsigned char *input,
size_t inputlen,
unsigned char *checksum,

View File

@@ -46,8 +46,6 @@
#include "../rand.h"
static bool cf_ssl_peer_key_is_global(const char *peer_key);
/* a peer+tls-config we cache sessions for */
struct Curl_ssl_scache_peer {
char *ssl_peer_key; /* id for peer + relevant TLS configuration */
@@ -69,6 +67,235 @@ struct Curl_ssl_scache_peer {
#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
const char *name,
const char *path,
bool *is_local)
{
if(path && path[0]) {
/* We try to add absolute paths, so that the session key can stay
* valid when used in another process with different CWD. However,
* when a path does not exist, this does not work. Then, we add
* the path as is. */
#ifdef _WIN32
char abspath[_MAX_PATH];
if(_fullpath(abspath, path, _MAX_PATH))
return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
*is_local = TRUE;
#elif defined(HAVE_REALPATH)
if(path[0] != '/') {
char *abspath = realpath(path, NULL);
if(abspath) {
CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
/* !checksrc! disable BANNEDFUNC 1 */
free(abspath); /* allocated by libc, free without memdebug */
return r;
}
*is_local = TRUE;
}
#endif
return curlx_dyn_addf(buf, ":%s-%s", name, path);
}
return CURLE_OK;
}
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
const char *name,
struct curl_blob *blob)
{
CURLcode r = CURLE_OK;
if(blob && blob->len) {
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
size_t i;
r = curlx_dyn_addf(buf, ":%s-", name);
if(r)
goto out;
r = Curl_sha256it(hash, blob->data, blob->len);
if(r)
goto out;
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
r = curlx_dyn_addf(buf, "%02x", hash[i]);
if(r)
goto out;
}
}
out:
return r;
}
#define CURL_SSLS_LOCAL_SUFFIX ":L"
#define CURL_SSLS_GLOBAL_SUFFIX ":G"
static bool cf_ssl_peer_key_is_global(const char *peer_key)
{
size_t len = peer_key ? strlen(peer_key) : 0;
return (len > 2) &&
(peer_key[len - 1] == 'G') &&
(peer_key[len - 2] == ':');
}
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
const char *tls_id,
char **ppeer_key)
{
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
struct dynbuf buf;
size_t key_len;
bool is_local = FALSE;
CURLcode r;
*ppeer_key = NULL;
curlx_dyn_init(&buf, 10 * 1024);
r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
if(r)
goto out;
switch(peer->transport) {
case TRNSPRT_TCP:
break;
case TRNSPRT_UDP:
r = curlx_dyn_add(&buf, ":UDP");
break;
case TRNSPRT_QUIC:
r = curlx_dyn_add(&buf, ":QUIC");
break;
case TRNSPRT_UNIX:
r = curlx_dyn_add(&buf, ":UNIX");
break;
default:
r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
break;
}
if(r)
goto out;
if(!ssl->verifypeer) {
r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
if(r)
goto out;
}
if(!ssl->verifyhost) {
r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
if(r)
goto out;
}
if(ssl->verifystatus) {
r = curlx_dyn_add(&buf, ":VRFY-STATUS");
if(r)
goto out;
}
if(!ssl->verifypeer || !ssl->verifyhost) {
if(cf->conn->bits.conn_to_host) {
r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
if(r)
goto out;
}
if(cf->conn->bits.conn_to_port) {
r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
if(r)
goto out;
}
}
if(ssl->version || ssl->version_max) {
r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
(ssl->version_max >> 16));
if(r)
goto out;
}
if(ssl->ssl_options) {
r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
if(r)
goto out;
}
if(ssl->cipher_list) {
r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
if(r)
goto out;
}
if(ssl->cipher_list13) {
r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
if(r)
goto out;
}
if(ssl->curves) {
r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
if(r)
goto out;
}
if(ssl->verifypeer) {
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
if(r)
goto out;
if(ssl->cert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
if(r)
goto out;
}
if(ssl->ca_info_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
if(r)
goto out;
}
if(ssl->issuercert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
if(r)
goto out;
}
}
if(ssl->pinned_key && ssl->pinned_key[0]) {
r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
if(r)
goto out;
}
if(ssl->clientcert && ssl->clientcert[0]) {
r = curlx_dyn_add(&buf, ":CCERT");
if(r)
goto out;
}
#ifdef USE_TLS_SRP
if(ssl->username || ssl->password) {
r = curlx_dyn_add(&buf, ":SRP-AUTH");
if(r)
goto out;
}
#endif
if(!tls_id || !tls_id[0]) {
r = CURLE_FAILED_INIT;
goto out;
}
r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
if(r)
goto out;
r = curlx_dyn_addf(&buf, is_local ?
CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
if(r)
goto out;
*ppeer_key = curlx_dyn_take(&buf, &key_len);
/* we just added printable char, and dynbuf always null-terminates, no need
* to track length */
out:
curlx_dyn_free(&buf);
return r;
}
struct Curl_ssl_scache {
unsigned int magic;
struct Curl_ssl_scache_peer *peers;
@@ -368,235 +595,6 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data)
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
}
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
const char *name,
const char *path,
bool *is_local)
{
if(path && path[0]) {
/* We try to add absolute paths, so that the session key can stay
* valid when used in another process with different CWD. However,
* when a path does not exist, this does not work. Then, we add
* the path as is. */
#ifdef _WIN32
char abspath[_MAX_PATH];
if(_fullpath(abspath, path, _MAX_PATH))
return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
*is_local = TRUE;
#elif defined(HAVE_REALPATH)
if(path[0] != '/') {
char *abspath = realpath(path, NULL);
if(abspath) {
CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
/* !checksrc! disable BANNEDFUNC 1 */
free(abspath); /* allocated by libc, free without memdebug */
return r;
}
*is_local = TRUE;
}
#endif
return curlx_dyn_addf(buf, ":%s-%s", name, path);
}
return CURLE_OK;
}
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
const char *name,
struct curl_blob *blob)
{
CURLcode r = CURLE_OK;
if(blob && blob->len) {
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
size_t i;
r = curlx_dyn_addf(buf, ":%s-", name);
if(r)
goto out;
r = Curl_sha256it(hash, blob->data, blob->len);
if(r)
goto out;
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
r = curlx_dyn_addf(buf, "%02x", hash[i]);
if(r)
goto out;
}
}
out:
return r;
}
#define CURL_SSLS_LOCAL_SUFFIX ":L"
#define CURL_SSLS_GLOBAL_SUFFIX ":G"
static bool cf_ssl_peer_key_is_global(const char *peer_key)
{
size_t len = peer_key ? strlen(peer_key) : 0;
return (len > 2) &&
(peer_key[len - 1] == 'G') &&
(peer_key[len - 2] == ':');
}
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
const char *tls_id,
char **ppeer_key)
{
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
struct dynbuf buf;
size_t key_len;
bool is_local = FALSE;
CURLcode r;
*ppeer_key = NULL;
curlx_dyn_init(&buf, 10 * 1024);
r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
if(r)
goto out;
switch(peer->transport) {
case TRNSPRT_TCP:
break;
case TRNSPRT_UDP:
r = curlx_dyn_add(&buf, ":UDP");
break;
case TRNSPRT_QUIC:
r = curlx_dyn_add(&buf, ":QUIC");
break;
case TRNSPRT_UNIX:
r = curlx_dyn_add(&buf, ":UNIX");
break;
default:
r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
break;
}
if(r)
goto out;
if(!ssl->verifypeer) {
r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
if(r)
goto out;
}
if(!ssl->verifyhost) {
r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
if(r)
goto out;
}
if(ssl->verifystatus) {
r = curlx_dyn_add(&buf, ":VRFY-STATUS");
if(r)
goto out;
}
if(!ssl->verifypeer || !ssl->verifyhost) {
if(cf->conn->bits.conn_to_host) {
r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
if(r)
goto out;
}
if(cf->conn->bits.conn_to_port) {
r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
if(r)
goto out;
}
}
if(ssl->version || ssl->version_max) {
r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
(ssl->version_max >> 16));
if(r)
goto out;
}
if(ssl->ssl_options) {
r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
if(r)
goto out;
}
if(ssl->cipher_list) {
r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
if(r)
goto out;
}
if(ssl->cipher_list13) {
r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
if(r)
goto out;
}
if(ssl->curves) {
r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
if(r)
goto out;
}
if(ssl->verifypeer) {
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
if(r)
goto out;
if(ssl->cert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
if(r)
goto out;
}
if(ssl->ca_info_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
if(r)
goto out;
}
if(ssl->issuercert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
if(r)
goto out;
}
}
if(ssl->pinned_key && ssl->pinned_key[0]) {
r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
if(r)
goto out;
}
if(ssl->clientcert && ssl->clientcert[0]) {
r = curlx_dyn_add(&buf, ":CCERT");
if(r)
goto out;
}
#ifdef USE_TLS_SRP
if(ssl->username || ssl->password) {
r = curlx_dyn_add(&buf, ":SRP-AUTH");
if(r)
goto out;
}
#endif
if(!tls_id || !tls_id[0]) {
r = CURLE_FAILED_INIT;
goto out;
}
r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
if(r)
goto out;
r = curlx_dyn_addf(&buf, is_local ?
CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
if(r)
goto out;
*ppeer_key = curlx_dyn_take(&buf, &key_len);
/* we just added printable char, and dynbuf always null-terminates, no need
* to track length */
out:
curlx_dyn_free(&buf);
return r;
}
static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
struct ssl_primary_config *conn_config)
{

View File

@@ -100,10 +100,6 @@
#undef USE_BIO_CHAIN
#endif
static CURLcode wssl_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done);
#ifdef OPENSSL_EXTRA
/*
* Availability note:

View File

@@ -29,10 +29,6 @@
#include "tool_util.h"
#include "toolx/tool_time.h"
static void dump(const char *timebuf, const char *idsbuf, const char *text,
FILE *stream, const unsigned char *ptr, size_t size,
trace tracetype, curl_infotype infotype);
/*
* Return the formatted HH:MM:SS for the tv_sec given.
*/
@@ -69,6 +65,60 @@ static void log_line_start(FILE *log, const char *timebuf,
fputs(s_infotype[type], log);
}
static void dump(const char *timebuf, const char *idsbuf, const char *text,
FILE *stream, const unsigned char *ptr, size_t size,
trace tracetype, curl_infotype infotype)
{
size_t i;
size_t c;
unsigned int width = 0x10;
if(tracetype == TRACE_ASCII)
/* without the hex output, we can fit more on screen */
width = 0x40;
curl_mfprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf,
text, size, size);
for(i = 0; i < size; i += width) {
curl_mfprintf(stream, "%04zx: ", i);
if(tracetype == TRACE_BIN) {
/* hex not disabled, show it */
for(c = 0; c < width; c++)
if(i + c < size)
curl_mfprintf(stream, "%02x ", ptr[i + c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i + c < size); c++) {
/* check for 0D0A; if found, skip past and start a new line of output */
if((tracetype == TRACE_ASCII) &&
(i + c + 1 < size) && (ptr[i + c] == 0x0D) &&
(ptr[i + c + 1] == 0x0A)) {
i += (c + 2 - width);
break;
}
(void)infotype;
curl_mfprintf(stream, "%c",
((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ?
ptr[i + c] : UNPRINTABLE_CHAR);
/* check again for 0D0A, to avoid an extra \n if it is at width */
if((tracetype == TRACE_ASCII) &&
(i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) &&
(ptr[i + c + 2] == 0x0A)) {
i += (c + 3 - width);
break;
}
}
fputc('\n', stream); /* newline */
}
fflush(stream);
}
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
CURL_FORMAT_CURL_OFF_T "] "
@@ -230,57 +280,3 @@ int tool_debug_cb(CURL *handle, curl_infotype type,
global->tracetype, type);
return 0;
}
static void dump(const char *timebuf, const char *idsbuf, const char *text,
FILE *stream, const unsigned char *ptr, size_t size,
trace tracetype, curl_infotype infotype)
{
size_t i;
size_t c;
unsigned int width = 0x10;
if(tracetype == TRACE_ASCII)
/* without the hex output, we can fit more on screen */
width = 0x40;
curl_mfprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf,
text, size, size);
for(i = 0; i < size; i += width) {
curl_mfprintf(stream, "%04zx: ", i);
if(tracetype == TRACE_BIN) {
/* hex not disabled, show it */
for(c = 0; c < width; c++)
if(i + c < size)
curl_mfprintf(stream, "%02x ", ptr[i + c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i + c < size); c++) {
/* check for 0D0A; if found, skip past and start a new line of output */
if((tracetype == TRACE_ASCII) &&
(i + c + 1 < size) && (ptr[i + c] == 0x0D) &&
(ptr[i + c + 1] == 0x0A)) {
i += (c + 2 - width);
break;
}
(void)infotype;
curl_mfprintf(stream, "%c",
((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ?
ptr[i + c] : UNPRINTABLE_CHAR);
/* check again for 0D0A, to avoid an extra \n if it is at width */
if((tracetype == TRACE_ASCII) &&
(i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) &&
(ptr[i + c + 2] == 0x0A)) {
i += (c + 3 - width);
break;
}
}
fputc('\n', stream); /* newline */
}
fflush(stream);
}

View File

@@ -27,22 +27,6 @@
#include "tool_writeout.h"
#include "tool_writeout_json.h"
static int writeTime(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json);
static int writeString(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json);
static int writeLong(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json);
static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json);
struct httpmap {
const char *str;
int num;
@@ -57,103 +41,6 @@ static const struct httpmap http_version[] = {
{ NULL, 0 } /* end of list */
};
/* The designated write function should be the same as the CURLINFO return type
with exceptions special cased in the respective function. For example,
http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
however it is output as a string and therefore is handled in writeString.
Yes: "http_version": "1.1"
No: "http_version": 1.1
Variable names MUST be in alphabetical order.
*/
static const struct writeoutvar variables[] = {
{ "certs", VAR_CERT, CURLINFO_NONE, writeString },
{ "conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset },
{ "content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString },
{ "errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString },
{ "exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong },
{ "filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString },
{ "ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH,
writeString },
{ "header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL },
{ "http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
{ "http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE,
writeLong },
{ "http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString },
{ "json", VAR_JSON, CURLINFO_NONE, NULL },
{ "local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString },
{ "local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong },
{ "method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString },
{ "num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong },
{ "num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong },
{ "num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong },
{ "num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong },
{ "num_retries", VAR_NUM_RETRY, CURLINFO_NONE, writeLong },
{ "onerror", VAR_ONERROR, CURLINFO_NONE, NULL },
{ "proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong },
{ "proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong },
{ "redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString },
{ "referer", VAR_REFERER, CURLINFO_REFERER, writeString },
{ "remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString },
{ "remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong },
{ "response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
{ "scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString },
{ "size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T,
writeOffset },
{ "size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong },
{ "size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong },
{ "size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset },
{ "speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
writeOffset },
{ "speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset },
{ "ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
writeLong },
{ "stderr", VAR_STDERR, CURLINFO_NONE, NULL },
{ "stdout", VAR_STDOUT, CURLINFO_NONE, NULL },
{ "time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
writeTime },
{ "time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime },
{ "time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
writeTime },
{ "time_posttransfer", VAR_POSTTRANSFER_TIME, CURLINFO_POSTTRANSFER_TIME_T,
writeTime },
{ "time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
writeTime },
{ "time_queue", VAR_QUEUE_TIME, CURLINFO_QUEUE_TIME_T, writeTime },
{ "time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime },
{ "time_starttransfer", VAR_STARTTRANSFER_TIME,
CURLINFO_STARTTRANSFER_TIME_T, writeTime },
{ "time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime },
{ "tls_earlydata", VAR_TLS_EARLYDATA_SENT, CURLINFO_EARLYDATA_SENT_T,
writeOffset },
{ "url", VAR_INPUT_URL, CURLINFO_NONE, writeString },
{ "url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString },
{ "url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString },
{ "url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString },
{ "url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString },
{ "url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString },
{ "url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString },
{ "url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString },
{ "url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString },
{ "url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString },
{ "url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString },
{ "url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString },
{ "urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString },
{ "urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString },
{ "urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString },
{ "urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString },
{ "urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString },
{ "urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString },
{ "urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString },
{ "urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString },
{ "urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString },
{ "urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString },
{ "urlnum", VAR_URLNUM, CURLINFO_NONE, writeOffset },
{ "xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset }
};
static int writeTime(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json)
@@ -530,13 +417,102 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
return 1; /* return 1 if anything was written */
}
static int matchvar(const void *m1, const void *m2)
{
const struct writeoutvar *v1 = m1;
const struct writeoutvar *v2 = m2;
/* The designated write function should be the same as the CURLINFO return type
with exceptions special cased in the respective function. For example,
http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
however it is output as a string and therefore is handled in writeString.
return strcmp(v1->name, v2->name);
}
Yes: "http_version": "1.1"
No: "http_version": 1.1
Variable names MUST be in alphabetical order.
*/
static const struct writeoutvar variables[] = {
{ "certs", VAR_CERT, CURLINFO_NONE, writeString },
{ "conn_id", VAR_CONN_ID, CURLINFO_CONN_ID, writeOffset },
{ "content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString },
{ "errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString },
{ "exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong },
{ "filename_effective", VAR_EFFECTIVE_FILENAME, CURLINFO_NONE, writeString },
{ "ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH,
writeString },
{ "header_json", VAR_HEADER_JSON, CURLINFO_NONE, NULL },
{ "http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
{ "http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE,
writeLong },
{ "http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString },
{ "json", VAR_JSON, CURLINFO_NONE, NULL },
{ "local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString },
{ "local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong },
{ "method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString },
{ "num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong },
{ "num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong },
{ "num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong },
{ "num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong },
{ "num_retries", VAR_NUM_RETRY, CURLINFO_NONE, writeLong },
{ "onerror", VAR_ONERROR, CURLINFO_NONE, NULL },
{ "proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong },
{ "proxy_used", VAR_PROXY_USED, CURLINFO_USED_PROXY, writeLong },
{ "redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString },
{ "referer", VAR_REFERER, CURLINFO_REFERER, writeString },
{ "remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString },
{ "remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong },
{ "response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong },
{ "scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString },
{ "size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T,
writeOffset },
{ "size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong },
{ "size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong },
{ "size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset },
{ "speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
writeOffset },
{ "speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset },
{ "ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
writeLong },
{ "stderr", VAR_STDERR, CURLINFO_NONE, NULL },
{ "stdout", VAR_STDOUT, CURLINFO_NONE, NULL },
{ "time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
writeTime },
{ "time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime },
{ "time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
writeTime },
{ "time_posttransfer", VAR_POSTTRANSFER_TIME, CURLINFO_POSTTRANSFER_TIME_T,
writeTime },
{ "time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
writeTime },
{ "time_queue", VAR_QUEUE_TIME, CURLINFO_QUEUE_TIME_T, writeTime },
{ "time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime },
{ "time_starttransfer", VAR_STARTTRANSFER_TIME,
CURLINFO_STARTTRANSFER_TIME_T, writeTime },
{ "time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime },
{ "tls_earlydata", VAR_TLS_EARLYDATA_SENT, CURLINFO_EARLYDATA_SENT_T,
writeOffset },
{ "url", VAR_INPUT_URL, CURLINFO_NONE, writeString },
{ "url.fragment", VAR_INPUT_URLFRAGMENT, CURLINFO_NONE, writeString },
{ "url.host", VAR_INPUT_URLHOST, CURLINFO_NONE, writeString },
{ "url.options", VAR_INPUT_URLOPTIONS, CURLINFO_NONE, writeString },
{ "url.password", VAR_INPUT_URLPASSWORD, CURLINFO_NONE, writeString },
{ "url.path", VAR_INPUT_URLPATH, CURLINFO_NONE, writeString },
{ "url.port", VAR_INPUT_URLPORT, CURLINFO_NONE, writeString },
{ "url.query", VAR_INPUT_URLQUERY, CURLINFO_NONE, writeString },
{ "url.scheme", VAR_INPUT_URLSCHEME, CURLINFO_NONE, writeString },
{ "url.user", VAR_INPUT_URLUSER, CURLINFO_NONE, writeString },
{ "url.zoneid", VAR_INPUT_URLZONEID, CURLINFO_NONE, writeString },
{ "url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString },
{ "urle.fragment", VAR_INPUT_URLEFRAGMENT, CURLINFO_NONE, writeString },
{ "urle.host", VAR_INPUT_URLEHOST, CURLINFO_NONE, writeString },
{ "urle.options", VAR_INPUT_URLEOPTIONS, CURLINFO_NONE, writeString },
{ "urle.password", VAR_INPUT_URLEPASSWORD, CURLINFO_NONE, writeString },
{ "urle.path", VAR_INPUT_URLEPATH, CURLINFO_NONE, writeString },
{ "urle.port", VAR_INPUT_URLEPORT, CURLINFO_NONE, writeString },
{ "urle.query", VAR_INPUT_URLEQUERY, CURLINFO_NONE, writeString },
{ "urle.scheme", VAR_INPUT_URLESCHEME, CURLINFO_NONE, writeString },
{ "urle.user", VAR_INPUT_URLEUSER, CURLINFO_NONE, writeString },
{ "urle.zoneid", VAR_INPUT_URLEZONEID, CURLINFO_NONE, writeString },
{ "urlnum", VAR_URLNUM, CURLINFO_NONE, writeOffset },
{ "xfer_id", VAR_EASY_ID, CURLINFO_XFER_ID, writeOffset }
};
#define MAX_WRITEOUT_NAME_LENGTH 24
@@ -720,6 +696,14 @@ static void output_header(struct per_transfer *per,
*pptr = ptr;
}
static int matchvar(const void *m1, const void *m2)
{
const struct writeoutvar *v1 = m1;
const struct writeoutvar *v2 = m2;
return strcmp(v1->name, v2->name);
}
void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
CURLcode per_result)
{