diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 96fc7cd8f8..f46a074afa 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -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) diff --git a/lib/cfilters.c b/lib/cfilters.c index 8a6a163067..76c24e7a68 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -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) { diff --git a/lib/conncache.c b/lib/conncache.c index f19d061ca3..983224722a 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -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) diff --git a/lib/cw-out.c b/lib/cw-out.c index 69fb344fde..786d7509cd 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -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; diff --git a/lib/cw-pause.c b/lib/cw-pause.c index 481b9fb27d..2489a330a0 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -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; diff --git a/lib/http2.c b/lib/http2.c index f1c9fb59d6..3520b544a4 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -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) diff --git a/lib/mime.c b/lib/mime.c index 493924a567..f34e4f91d3 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -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) { diff --git a/lib/rtsp.c b/lib/rtsp.c index 09b01f176b..c3711caf04 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -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. */ diff --git a/lib/sendf.c b/lib/sendf.c index 680cd11356..402f9499f6 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -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) { diff --git a/lib/urlapi.c b/lib/urlapi.c index a67cf7a05f..c7c48ed798 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -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) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 891dfc4e73..64509fd4ce 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -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, diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index df04ec7e00..0149b51869 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -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) { diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 34dfd331ae..80272ea364 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -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: diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index 6a566c30b0..0c8568ff62 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -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); -} diff --git a/src/tool_writeout.c b/src/tool_writeout.c index 25c9622312..2ade080a18 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -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) {