aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2020-05-21 15:00:11 -0400
committerBen Burwell <ben@benburwell.com>2020-05-21 18:38:31 -0400
commitc365f2cb4ffcbef02d2e9a5e17aa9d7f1fe81df7 (patch)
tree6edbb7f1c12a8afbde06a56423a1c5092a9dbacd /lib
parent5d965b48987457eb0db01c2eb65712aec37e49f5 (diff)
Add gemini protocol support
The gemini protocol's "speculative specification" is documented at the following URLs: https://gemini.circumlunar.space/docs/spec-spec.txt gopher://gemini.circumlunar.space/1/docs/spec-spec.txt gemini://gemini.circumlunar.space/docs/spec-spec.txt This patch introduces preliminary support for version 0.11.0 of the specification, as of March 1st 2020.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.inc2
-rw-r--r--lib/curl_setup.h3
-rw-r--r--lib/gemini.c179
-rw-r--r--lib/gemini.h29
-rw-r--r--lib/url.c11
-rw-r--r--lib/urldata.h1
-rw-r--r--lib/version.c3
7 files changed, 227 insertions, 1 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 08b0f73a3..e4789b5b5 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -51,6 +51,7 @@ LIB_CFILES = altsvc.c amigaos.c asyn-ares.c asyn-thread.c base64.c \
curl_ntlm_core.c curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \
curl_sasl.c curl_sspi.c curl_threads.c dict.c dotdot.c easy.c escape.c \
file.c fileinfo.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \
+ gemini.c \
gopher.c hash.c hmac.c hostasyn.c hostcheck.c hostip.c hostip4.c hostip6.c \
hostsyn.c http.c http2.c http_chunks.c http_digest.c http_negotiate.c \
http_ntlm.c http_proxy.c idn_win32.c if2ip.c imap.c inet_ntop.c inet_pton.c \
@@ -70,6 +71,7 @@ LIB_HFILES = altsvc.h amigaos.h arpa_telnet.h asyn.h conncache.h connect.h \
curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h curl_sec.h curl_setup.h \
curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h \
dotdot.h easyif.h escape.h file.h fileinfo.h formdata.h ftp.h url.h \
+ gemini.h \
ftplistparser.h getinfo.h gopher.h hash.h hostcheck.h hostip.h http.h \
http2.h http_chunks.h http_digest.h http_negotiate.h http_ntlm.h \
http_proxy.h if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index a86a235b3..a102e0519 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -196,6 +196,9 @@
# ifndef CURL_DISABLE_GOPHER
# define CURL_DISABLE_GOPHER
# endif
+# ifndef CURL_DISABLE_GEMINI
+# define CURL_DISABLE_GEMINI
+# endif
# ifndef CURL_DISABLE_SMB
# define CURL_DISABLE_SMB
# endif
diff --git a/lib/gemini.c b/lib/gemini.c
new file mode 100644
index 000000000..bd1953d82
--- /dev/null
+++ b/lib/gemini.c
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_GEMINI
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "connect.h"
+#include "progress.h"
+#include "gemini.h"
+#include "select.h"
+#include "strdup.h"
+#include "url.h"
+#include "escape.h"
+#include "warnless.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "vtls/vtls.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode gemini_do(struct connectdata *conn, bool *done);
+static CURLcode gemini_connecting(struct connectdata *conn, bool *done);
+CURLcode Curl_gemini_connect(struct connectdata *conn, bool *done);
+
+/*
+ * Gemini protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_gemini = {
+ "GEMINI", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ gemini_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_gemini_connect, /* connect_it */
+ gemini_connecting, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ PORT_GEMINI, /* defport */
+ CURLPROTO_GEMINI, /* protocol */
+ PROTOPT_NONE /* flags */
+};
+
+static CURLcode gemini_do(struct connectdata *conn, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ timediff_t timeout_ms;
+ ssize_t amount, k;
+ size_t len;
+ int what;
+ char *req = NULL;
+ char *u = NULL;
+
+ *done = TRUE; /* unconditionally */
+
+ /* format the request URL */
+ if (curl_url_get(data->state.uh, CURLUPART_URL, &u, 0) != CURLUE_OK)
+ return CURLE_URL_MALFORMAT;
+
+ /* build the request line */
+ req = aprintf("%s\r\n", u);
+ if (!req)
+ return CURLE_OUT_OF_MEMORY;
+
+ len = strlen(req);
+
+ /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is
+ sent, which could be sizeable with long selectors. */
+ k = curlx_uztosz(len);
+
+ for(;;) {
+ result = Curl_write(conn, sockfd, req, k, &amount);
+ if(!result) { /* Which may not have written it all! */
+ result = Curl_client_write(conn, CLIENTWRITE_HEADER, req, amount);
+ if(result)
+ break;
+
+ k -= amount;
+ req += amount;
+ if(k < 1)
+ break; /* but it did write it all */
+ }
+ else
+ break;
+
+ timeout_ms = Curl_timeleft(conn->data, NULL, FALSE);
+ if(timeout_ms < 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ if(!timeout_ms)
+ timeout_ms = TIMEDIFF_T_MAX;
+
+ /* Don't busyloop. The entire loop thing is a work-around as it causes a
+ BLOCKING behavior which is a NO-NO. This function should rather be
+ split up in a do and a doing piece where the pieces that aren't
+ possible to send now will be sent in the doing function repeatedly
+ until the entire request is sent.
+ */
+ what = SOCKET_WRITABLE(sockfd, timeout_ms);
+ if(what < 0) {
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ else if(!what) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ }
+
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ return CURLE_OK;
+}
+
+static CURLcode gemini_connecting(struct connectdata *conn, bool *done)
+{
+ CURLcode result;
+ DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
+
+ /* perform SSL initialization for this socket */
+ result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
+ if(result)
+ connclose(conn, "Failed TLS connection");
+
+ return result;
+}
+
+/*
+ * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
+ * the generic Curl_connect().
+ */
+CURLcode Curl_gemini_connect(struct connectdata *conn, bool *done)
+{
+ CURLcode result;
+
+ /* perform SSL initialization */
+ result = gemini_connecting(conn, done);
+ if(result)
+ return result;
+
+ return CURLE_OK;
+}
+#endif /*CURL_DISABLE_GEMINI*/
diff --git a/lib/gemini.h b/lib/gemini.h
new file mode 100644
index 000000000..54cc6afd0
--- /dev/null
+++ b/lib/gemini.h
@@ -0,0 +1,29 @@
+#ifndef HEADER_CURL_GEMINI_H
+#define HEADER_CURL_GEMINI_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef CURL_DISABLE_GEMINI
+extern const struct Curl_handler Curl_handler_gemini;
+#endif
+
+#endif /* HEADER_CURL_GEMINI_H */
diff --git a/lib/url.c b/lib/url.c
index 0173dc88a..e2b63cf72 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -113,6 +113,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "inet_ntop.h"
#include "http_ntlm.h"
#include "curl_rtmp.h"
+#include "gemini.h"
#include "gopher.h"
#include "mqtt.h"
#include "http_proxy.h"
@@ -145,7 +146,7 @@ static unsigned int get_protocol_family(unsigned int protocol);
* Protocol table. Schemes (roughly) in 2019 popularity order:
*
* HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP,
- * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT
+ * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT, GEMINI
*/
static const struct Curl_handler * const protocols[] = {
@@ -249,6 +250,10 @@ static const struct Curl_handler * const protocols[] = {
&Curl_handler_dict,
#endif
+#ifndef CURL_DISABLE_GEMINI
+ &Curl_handler_gemini,
+#endif
+
(struct Curl_handler *) NULL
};
@@ -4093,6 +4098,10 @@ static unsigned int get_protocol_family(unsigned int protocol)
family = CURLPROTO_GOPHER;
break;
+ case CURLPROTO_GEMINI:
+ family = CURLPROTO_GEMINI;
+ break;
+
case CURLPROTO_SMB:
case CURLPROTO_SMBS:
family = CURLPROTO_SMB;
diff --git a/lib/urldata.h b/lib/urldata.h
index 9b4ce5f5b..46321c5fd 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -49,6 +49,7 @@
#define PORT_RTMPT PORT_HTTP
#define PORT_RTMPS PORT_HTTPS
#define PORT_GOPHER 70
+#define PORT_GEMINI 1965
#define PORT_MQTT 1883
#define DICT_MATCH "/MATCH:"
diff --git a/lib/version.c b/lib/version.c
index 14e509606..87c69a187 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -250,6 +250,9 @@ static const char * const protocols[] = {
#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
"ftps",
#endif
+#ifndef CURL_DISABLE_GEMINI
+ "gemini",
+#endif
#ifndef CURL_DISABLE_GOPHER
"gopher",
#endif