113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#ifndef CURL_DISABLE_GOPHER 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include "urldata.h" 3013498266Sopenharmony_ci#include <curl/curl.h> 3113498266Sopenharmony_ci#include "transfer.h" 3213498266Sopenharmony_ci#include "sendf.h" 3313498266Sopenharmony_ci#include "cfilters.h" 3413498266Sopenharmony_ci#include "connect.h" 3513498266Sopenharmony_ci#include "progress.h" 3613498266Sopenharmony_ci#include "gopher.h" 3713498266Sopenharmony_ci#include "select.h" 3813498266Sopenharmony_ci#include "strdup.h" 3913498266Sopenharmony_ci#include "vtls/vtls.h" 4013498266Sopenharmony_ci#include "url.h" 4113498266Sopenharmony_ci#include "escape.h" 4213498266Sopenharmony_ci#include "warnless.h" 4313498266Sopenharmony_ci#include "curl_printf.h" 4413498266Sopenharmony_ci#include "curl_memory.h" 4513498266Sopenharmony_ci/* The last #include file should be: */ 4613498266Sopenharmony_ci#include "memdebug.h" 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci/* 4913498266Sopenharmony_ci * Forward declarations. 5013498266Sopenharmony_ci */ 5113498266Sopenharmony_ci 5213498266Sopenharmony_cistatic CURLcode gopher_do(struct Curl_easy *data, bool *done); 5313498266Sopenharmony_ci#ifdef USE_SSL 5413498266Sopenharmony_cistatic CURLcode gopher_connect(struct Curl_easy *data, bool *done); 5513498266Sopenharmony_cistatic CURLcode gopher_connecting(struct Curl_easy *data, bool *done); 5613498266Sopenharmony_ci#endif 5713498266Sopenharmony_ci 5813498266Sopenharmony_ci/* 5913498266Sopenharmony_ci * Gopher protocol handler. 6013498266Sopenharmony_ci * This is also a nice simple template to build off for simple 6113498266Sopenharmony_ci * connect-command-download protocols. 6213498266Sopenharmony_ci */ 6313498266Sopenharmony_ci 6413498266Sopenharmony_ciconst struct Curl_handler Curl_handler_gopher = { 6513498266Sopenharmony_ci "GOPHER", /* scheme */ 6613498266Sopenharmony_ci ZERO_NULL, /* setup_connection */ 6713498266Sopenharmony_ci gopher_do, /* do_it */ 6813498266Sopenharmony_ci ZERO_NULL, /* done */ 6913498266Sopenharmony_ci ZERO_NULL, /* do_more */ 7013498266Sopenharmony_ci ZERO_NULL, /* connect_it */ 7113498266Sopenharmony_ci ZERO_NULL, /* connecting */ 7213498266Sopenharmony_ci ZERO_NULL, /* doing */ 7313498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 7413498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 7513498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 7613498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 7713498266Sopenharmony_ci ZERO_NULL, /* disconnect */ 7813498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 7913498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 8013498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 8113498266Sopenharmony_ci PORT_GOPHER, /* defport */ 8213498266Sopenharmony_ci CURLPROTO_GOPHER, /* protocol */ 8313498266Sopenharmony_ci CURLPROTO_GOPHER, /* family */ 8413498266Sopenharmony_ci PROTOPT_NONE /* flags */ 8513498266Sopenharmony_ci}; 8613498266Sopenharmony_ci 8713498266Sopenharmony_ci#ifdef USE_SSL 8813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_gophers = { 8913498266Sopenharmony_ci "GOPHERS", /* scheme */ 9013498266Sopenharmony_ci ZERO_NULL, /* setup_connection */ 9113498266Sopenharmony_ci gopher_do, /* do_it */ 9213498266Sopenharmony_ci ZERO_NULL, /* done */ 9313498266Sopenharmony_ci ZERO_NULL, /* do_more */ 9413498266Sopenharmony_ci gopher_connect, /* connect_it */ 9513498266Sopenharmony_ci gopher_connecting, /* connecting */ 9613498266Sopenharmony_ci ZERO_NULL, /* doing */ 9713498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 9813498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 9913498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 10013498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 10113498266Sopenharmony_ci ZERO_NULL, /* disconnect */ 10213498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 10313498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 10413498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 10513498266Sopenharmony_ci PORT_GOPHER, /* defport */ 10613498266Sopenharmony_ci CURLPROTO_GOPHERS, /* protocol */ 10713498266Sopenharmony_ci CURLPROTO_GOPHER, /* family */ 10813498266Sopenharmony_ci PROTOPT_SSL /* flags */ 10913498266Sopenharmony_ci}; 11013498266Sopenharmony_ci 11113498266Sopenharmony_cistatic CURLcode gopher_connect(struct Curl_easy *data, bool *done) 11213498266Sopenharmony_ci{ 11313498266Sopenharmony_ci (void)data; 11413498266Sopenharmony_ci (void)done; 11513498266Sopenharmony_ci return CURLE_OK; 11613498266Sopenharmony_ci} 11713498266Sopenharmony_ci 11813498266Sopenharmony_cistatic CURLcode gopher_connecting(struct Curl_easy *data, bool *done) 11913498266Sopenharmony_ci{ 12013498266Sopenharmony_ci struct connectdata *conn = data->conn; 12113498266Sopenharmony_ci CURLcode result; 12213498266Sopenharmony_ci 12313498266Sopenharmony_ci result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); 12413498266Sopenharmony_ci if(result) 12513498266Sopenharmony_ci connclose(conn, "Failed TLS connection"); 12613498266Sopenharmony_ci *done = TRUE; 12713498266Sopenharmony_ci return result; 12813498266Sopenharmony_ci} 12913498266Sopenharmony_ci#endif 13013498266Sopenharmony_ci 13113498266Sopenharmony_cistatic CURLcode gopher_do(struct Curl_easy *data, bool *done) 13213498266Sopenharmony_ci{ 13313498266Sopenharmony_ci CURLcode result = CURLE_OK; 13413498266Sopenharmony_ci struct connectdata *conn = data->conn; 13513498266Sopenharmony_ci curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; 13613498266Sopenharmony_ci char *gopherpath; 13713498266Sopenharmony_ci char *path = data->state.up.path; 13813498266Sopenharmony_ci char *query = data->state.up.query; 13913498266Sopenharmony_ci char *sel = NULL; 14013498266Sopenharmony_ci char *sel_org = NULL; 14113498266Sopenharmony_ci timediff_t timeout_ms; 14213498266Sopenharmony_ci ssize_t amount, k; 14313498266Sopenharmony_ci size_t len; 14413498266Sopenharmony_ci int what; 14513498266Sopenharmony_ci 14613498266Sopenharmony_ci *done = TRUE; /* unconditionally */ 14713498266Sopenharmony_ci 14813498266Sopenharmony_ci /* path is guaranteed non-NULL */ 14913498266Sopenharmony_ci DEBUGASSERT(path); 15013498266Sopenharmony_ci 15113498266Sopenharmony_ci if(query) 15213498266Sopenharmony_ci gopherpath = aprintf("%s?%s", path, query); 15313498266Sopenharmony_ci else 15413498266Sopenharmony_ci gopherpath = strdup(path); 15513498266Sopenharmony_ci 15613498266Sopenharmony_ci if(!gopherpath) 15713498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci /* Create selector. Degenerate cases: / and /1 => convert to "" */ 16013498266Sopenharmony_ci if(strlen(gopherpath) <= 2) { 16113498266Sopenharmony_ci sel = (char *)""; 16213498266Sopenharmony_ci len = strlen(sel); 16313498266Sopenharmony_ci free(gopherpath); 16413498266Sopenharmony_ci } 16513498266Sopenharmony_ci else { 16613498266Sopenharmony_ci char *newp; 16713498266Sopenharmony_ci 16813498266Sopenharmony_ci /* Otherwise, drop / and the first character (i.e., item type) ... */ 16913498266Sopenharmony_ci newp = gopherpath; 17013498266Sopenharmony_ci newp += 2; 17113498266Sopenharmony_ci 17213498266Sopenharmony_ci /* ... and finally unescape */ 17313498266Sopenharmony_ci result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO); 17413498266Sopenharmony_ci free(gopherpath); 17513498266Sopenharmony_ci if(result) 17613498266Sopenharmony_ci return result; 17713498266Sopenharmony_ci sel_org = sel; 17813498266Sopenharmony_ci } 17913498266Sopenharmony_ci 18013498266Sopenharmony_ci k = curlx_uztosz(len); 18113498266Sopenharmony_ci 18213498266Sopenharmony_ci for(;;) { 18313498266Sopenharmony_ci /* Break out of the loop if the selector is empty because OpenSSL and/or 18413498266Sopenharmony_ci LibreSSL fail with errno 0 if this is the case. */ 18513498266Sopenharmony_ci if(strlen(sel) < 1) 18613498266Sopenharmony_ci break; 18713498266Sopenharmony_ci 18813498266Sopenharmony_ci result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount); 18913498266Sopenharmony_ci if(!result) { /* Which may not have written it all! */ 19013498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); 19113498266Sopenharmony_ci if(result) 19213498266Sopenharmony_ci break; 19313498266Sopenharmony_ci 19413498266Sopenharmony_ci k -= amount; 19513498266Sopenharmony_ci sel += amount; 19613498266Sopenharmony_ci if(k < 1) 19713498266Sopenharmony_ci break; /* but it did write it all */ 19813498266Sopenharmony_ci } 19913498266Sopenharmony_ci else 20013498266Sopenharmony_ci break; 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci timeout_ms = Curl_timeleft(data, NULL, FALSE); 20313498266Sopenharmony_ci if(timeout_ms < 0) { 20413498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 20513498266Sopenharmony_ci break; 20613498266Sopenharmony_ci } 20713498266Sopenharmony_ci if(!timeout_ms) 20813498266Sopenharmony_ci timeout_ms = TIMEDIFF_T_MAX; 20913498266Sopenharmony_ci 21013498266Sopenharmony_ci /* Don't busyloop. The entire loop thing is a work-around as it causes a 21113498266Sopenharmony_ci BLOCKING behavior which is a NO-NO. This function should rather be 21213498266Sopenharmony_ci split up in a do and a doing piece where the pieces that aren't 21313498266Sopenharmony_ci possible to send now will be sent in the doing function repeatedly 21413498266Sopenharmony_ci until the entire request is sent. 21513498266Sopenharmony_ci */ 21613498266Sopenharmony_ci what = SOCKET_WRITABLE(sockfd, timeout_ms); 21713498266Sopenharmony_ci if(what < 0) { 21813498266Sopenharmony_ci result = CURLE_SEND_ERROR; 21913498266Sopenharmony_ci break; 22013498266Sopenharmony_ci } 22113498266Sopenharmony_ci else if(!what) { 22213498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 22313498266Sopenharmony_ci break; 22413498266Sopenharmony_ci } 22513498266Sopenharmony_ci } 22613498266Sopenharmony_ci 22713498266Sopenharmony_ci free(sel_org); 22813498266Sopenharmony_ci 22913498266Sopenharmony_ci if(!result) 23013498266Sopenharmony_ci result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount); 23113498266Sopenharmony_ci if(result) { 23213498266Sopenharmony_ci failf(data, "Failed sending Gopher request"); 23313498266Sopenharmony_ci return result; 23413498266Sopenharmony_ci } 23513498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2); 23613498266Sopenharmony_ci if(result) 23713498266Sopenharmony_ci return result; 23813498266Sopenharmony_ci 23913498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); 24013498266Sopenharmony_ci return CURLE_OK; 24113498266Sopenharmony_ci} 24213498266Sopenharmony_ci#endif /* CURL_DISABLE_GOPHER */ 243