curl-curl/lib/protocol.c
Stefan Eissing eb14705280
protocol source, all about protocols and uri schemes
Add protocol.h and protocol.c containing all about libcurl's
known URI schemes and their protocol handlers (so they exist).

Moves the scheme definitions from the various sources files into
protocol.c. Schemes are known and used, even of the protocol
handler is not build or just not implemented at all.

Closes #20906
2026-03-16 08:39:02 +01:00

572 lines
17 KiB
C

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 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.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.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "protocol.h"
#include "strcase.h"
#include "dict.h"
#include "file.h"
#include "ftp.h"
#include "gopher.h"
#include "http.h"
#include "imap.h"
#include "curl_ldap.h"
#include "mqtt.h"
#include "pop3.h"
#include "curl_rtmp.h"
#include "rtsp.h"
#include "smb.h"
#include "smtp.h"
#include "telnet.h"
#include "tftp.h"
#include "ws.h"
#include "vssh/ssh.h"
/* All URI schemes known to libcurl, but not necessarily implemented
* by protocol handlers. */
const struct Curl_scheme Curl_scheme_dict = {
"dict", /* scheme */
#ifdef CURL_DISABLE_DICT
ZERO_NULL,
#else
&Curl_protocol_dict,
#endif
CURLPROTO_DICT, /* protocol */
CURLPROTO_DICT, /* family */
PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */
PORT_DICT, /* defport */
};
const struct Curl_scheme Curl_scheme_file = {
"file", /* scheme */
#ifdef CURL_DISABLE_FILE
ZERO_NULL,
#else
&Curl_protocol_file,
#endif
CURLPROTO_FILE, /* protocol */
CURLPROTO_FILE, /* family */
PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */
0 /* defport */
};
const struct Curl_scheme Curl_scheme_ftp = {
"ftp", /* scheme */
#ifdef CURL_DISABLE_FTP
ZERO_NULL,
#else
&Curl_protocol_ftp,
#endif
CURLPROTO_FTP, /* protocol */
CURLPROTO_FTP, /* family */
PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
PROTOPT_WILDCARD | PROTOPT_SSL_REUSE |
PROTOPT_CONN_REUSE, /* flags */
PORT_FTP, /* defport */
};
const struct Curl_scheme Curl_scheme_ftps = {
"ftps", /* scheme */
#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_ftp,
#endif
CURLPROTO_FTPS, /* protocol */
CURLPROTO_FTP, /* family */
PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD |
PROTOPT_CONN_REUSE, /* flags */
PORT_FTPS, /* defport */
};
const struct Curl_scheme Curl_scheme_gopher = {
"gopher", /* scheme */
#ifdef CURL_DISABLE_GOPHER
ZERO_NULL,
#else
&Curl_protocol_gopher,
#endif
CURLPROTO_GOPHER, /* protocol */
CURLPROTO_GOPHER, /* family */
PROTOPT_NONE, /* flags */
PORT_GOPHER, /* defport */
};
const struct Curl_scheme Curl_scheme_gophers = {
"gophers", /* scheme */
#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_gophers,
#endif
CURLPROTO_GOPHERS, /* protocol */
CURLPROTO_GOPHER, /* family */
PROTOPT_SSL, /* flags */
PORT_GOPHER, /* defport */
};
const struct Curl_scheme Curl_scheme_http = {
"http", /* scheme */
#ifdef CURL_DISABLE_HTTP
ZERO_NULL,
#else
&Curl_protocol_http,
#endif
CURLPROTO_HTTP, /* protocol */
CURLPROTO_HTTP, /* family */
PROTOPT_CREDSPERREQUEST | /* flags */
PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
PORT_HTTP, /* defport */
};
const struct Curl_scheme Curl_scheme_https = {
"https", /* scheme */
#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_http,
#endif
CURLPROTO_HTTPS, /* protocol */
CURLPROTO_HTTP, /* family */
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
PORT_HTTPS, /* defport */
};
const struct Curl_scheme Curl_scheme_imap = {
"imap", /* scheme */
#ifdef CURL_DISABLE_IMAP
ZERO_NULL,
#else
&Curl_protocol_imap,
#endif
CURLPROTO_IMAP, /* protocol */
CURLPROTO_IMAP, /* family */
PROTOPT_CLOSEACTION | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE |
PROTOPT_CONN_REUSE,
PORT_IMAP, /* defport */
};
const struct Curl_scheme Curl_scheme_imaps = {
"imaps", /* scheme */
#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_imap,
#endif
CURLPROTO_IMAPS, /* protocol */
CURLPROTO_IMAP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
PORT_IMAPS, /* defport */
};
const struct Curl_scheme Curl_scheme_ldap = {
"ldap", /* scheme */
#ifdef CURL_DISABLE_LDAP
ZERO_NULL,
#else
&Curl_protocol_ldap,
#endif
CURLPROTO_LDAP, /* protocol */
CURLPROTO_LDAP, /* family */
PROTOPT_SSL_REUSE, /* flags */
PORT_LDAP, /* defport */
};
const struct Curl_scheme Curl_scheme_ldaps = {
"ldaps", /* scheme */
#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL)
ZERO_NULL,
#else
&Curl_protocol_ldap,
#endif
CURLPROTO_LDAPS, /* protocol */
CURLPROTO_LDAP, /* family */
PROTOPT_SSL, /* flags */
PORT_LDAPS, /* defport */
};
const struct Curl_scheme Curl_scheme_mqtt = {
"mqtt", /* scheme */
#ifdef CURL_DISABLE_MQTT
ZERO_NULL,
#else
&Curl_protocol_mqtt,
#endif
CURLPROTO_MQTT, /* protocol */
CURLPROTO_MQTT, /* family */
PROTOPT_NONE, /* flags */
PORT_MQTT, /* defport */
};
const struct Curl_scheme Curl_scheme_mqtts = {
"mqtts", /* scheme */
#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_mqtts,
#endif
CURLPROTO_MQTTS, /* protocol */
CURLPROTO_MQTT, /* family */
PROTOPT_SSL, /* flags */
PORT_MQTTS, /* defport */
};
const struct Curl_scheme Curl_scheme_pop3 = {
"pop3", /* scheme */
#ifdef CURL_DISABLE_POP3
ZERO_NULL,
#else
&Curl_protocol_pop3,
#endif
CURLPROTO_POP3, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
PORT_POP3, /* defport */
};
const struct Curl_scheme Curl_scheme_pop3s = {
"pop3s", /* scheme */
#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_pop3,
#endif
CURLPROTO_POP3S, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
PORT_POP3S, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmp = {
"rtmp", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMP, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMP, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpt = {
"rtmpt", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPT, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPT, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpe = {
"rtmpe", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPE, /* protocol */
CURLPROTO_RTMPE, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMP, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpte = {
"rtmpte", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPTE, /* protocol */
CURLPROTO_RTMPTE, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPT, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmps = {
"rtmps", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPS, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPS, /* defport */
};
const struct Curl_scheme Curl_scheme_rtmpts = {
"rtmpts", /* scheme */
#ifndef USE_LIBRTMP
ZERO_NULL,
#else
&Curl_protocol_rtmp,
#endif
CURLPROTO_RTMPTS, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE, /* flags */
PORT_RTMPS, /* defport */
};
const struct Curl_scheme Curl_scheme_rtsp = {
"rtsp", /* scheme */
#ifdef CURL_DISABLE_RTSP
ZERO_NULL,
#else
&Curl_protocol_rtsp,
#endif
CURLPROTO_RTSP, /* protocol */
CURLPROTO_RTSP, /* family */
PROTOPT_CONN_REUSE, /* flags */
PORT_RTSP, /* defport */
};
const struct Curl_scheme Curl_scheme_sftp = {
"SFTP", /* scheme */
#ifndef USE_SSH
NULL,
#else
&Curl_protocol_sftp,
#endif
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
PORT_SSH /* defport */
};
const struct Curl_scheme Curl_scheme_scp = {
"SCP", /* scheme */
#ifndef USE_SSH
NULL,
#else
&Curl_protocol_scp,
#endif
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
PORT_SSH, /* defport */
};
const struct Curl_scheme Curl_scheme_smb = {
"smb", /* scheme */
#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE)
ZERO_NULL,
#else
&Curl_protocol_smb,
#endif
CURLPROTO_SMB, /* protocol */
CURLPROTO_SMB, /* family */
PROTOPT_CONN_REUSE, /* flags */
PORT_SMB, /* defport */
};
const struct Curl_scheme Curl_scheme_smbs = {
"smbs", /* scheme */
#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) || \
!defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_smb,
#endif
CURLPROTO_SMBS, /* protocol */
CURLPROTO_SMB, /* family */
PROTOPT_SSL | PROTOPT_CONN_REUSE, /* flags */
PORT_SMBS, /* defport */
};
const struct Curl_scheme Curl_scheme_smtp = {
"smtp", /* scheme */
#ifdef CURL_DISABLE_SMTP
ZERO_NULL,
#else
&Curl_protocol_smtp,
#endif
CURLPROTO_SMTP, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
PORT_SMTP, /* defport */
};
const struct Curl_scheme Curl_scheme_smtps = {
"smtps", /* scheme */
#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_smtp,
#endif
CURLPROTO_SMTPS, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
PORT_SMTPS, /* defport */
};
const struct Curl_scheme Curl_scheme_telnet = {
"telnet", /* scheme */
#ifdef CURL_DISABLE_TELNET
ZERO_NULL,
#else
&Curl_protocol_telnet,
#endif
CURLPROTO_TELNET, /* protocol */
CURLPROTO_TELNET, /* family */
PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */
PORT_TELNET, /* defport */
};
const struct Curl_scheme Curl_scheme_tftp = {
"tftp", /* scheme */
#ifdef CURL_DISABLE_TFTP
ZERO_NULL,
#else
&Curl_protocol_tftp,
#endif
CURLPROTO_TFTP, /* protocol */
CURLPROTO_TFTP, /* family */
PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */
PORT_TFTP, /* defport */
};
const struct Curl_scheme Curl_scheme_ws = {
"WS", /* scheme */
#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP)
ZERO_NULL,
#else
&Curl_protocol_ws,
#endif
CURLPROTO_WS, /* protocol */
CURLPROTO_HTTP, /* family */
PROTOPT_CREDSPERREQUEST | /* flags */
PROTOPT_USERPWDCTRL,
PORT_HTTP /* defport */
};
const struct Curl_scheme Curl_scheme_wss = {
"WSS", /* scheme */
#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) || \
!defined(USE_SSL)
ZERO_NULL,
#else
&Curl_protocol_ws,
#endif
CURLPROTO_WSS, /* protocol */
CURLPROTO_HTTP, /* family */
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
PROTOPT_USERPWDCTRL,
PORT_HTTPS /* defport */
};
/* Returns a struct scheme pointer if the name is a known scheme. Check the
->run struct field for non-NULL to figure out if an implementation is
present. */
const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len)
{
/* table generated by schemetable.c:
1. gcc schemetable.c && ./a.out
2. check how small the table gets
3. tweak the hash algorithm, then rerun from 1
4. when the table is good enough
5. copy the table into this source code
6. make sure this function uses the same hash function that worked for
schemetable.c
*/
static const struct Curl_scheme * const all_schemes[67] = {
&Curl_scheme_file,
&Curl_scheme_mqtts, NULL,
&Curl_scheme_gophers, NULL,
&Curl_scheme_rtmpe,
&Curl_scheme_smtp,
&Curl_scheme_sftp,
&Curl_scheme_smb,
&Curl_scheme_smtps,
&Curl_scheme_telnet,
&Curl_scheme_gopher,
&Curl_scheme_tftp, NULL, NULL, NULL,
&Curl_scheme_ftps,
&Curl_scheme_http,
&Curl_scheme_imap,
&Curl_scheme_rtmps,
&Curl_scheme_rtmpt, NULL, NULL, NULL,
&Curl_scheme_ldaps,
&Curl_scheme_wss,
&Curl_scheme_https, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&Curl_scheme_rtsp,
&Curl_scheme_smbs,
&Curl_scheme_scp, NULL, NULL, NULL,
&Curl_scheme_pop3, NULL, NULL,
&Curl_scheme_rtmp, NULL, NULL, NULL,
&Curl_scheme_rtmpte, NULL, NULL, NULL,
&Curl_scheme_dict, NULL, NULL, NULL,
&Curl_scheme_mqtt,
&Curl_scheme_pop3s,
&Curl_scheme_imaps, NULL,
&Curl_scheme_ws, NULL,
&Curl_scheme_rtmpts,
&Curl_scheme_ldap, NULL, NULL,
&Curl_scheme_ftp,
};
if(len && (len <= 7)) {
const char *s = scheme;
size_t l = len;
const struct Curl_scheme *h;
unsigned int c = 978;
while(l) {
c <<= 5;
c += (unsigned int)Curl_raw_tolower(*s);
s++;
l--;
}
h = all_schemes[c % 67];
if(h && curl_strnequal(scheme, h->name, len) && !h->name[len])
return h;
}
return NULL;
}
const struct Curl_scheme *Curl_get_scheme(const char *scheme)
{
return Curl_getn_scheme(scheme, strlen(scheme));
}