mqtt: initial support for MQTTS

Closes #19418
This commit is contained in:
Daniel Stenberg
2026-01-17 17:23:44 +01:00
parent 1730407b74
commit 6c31df453b
22 changed files with 176 additions and 12 deletions

View File

@@ -1953,6 +1953,7 @@ curl_add_if("IPNS" NOT CURL_DISABLE_IPFS)
curl_add_if("RTSP" NOT CURL_DISABLE_RTSP)
curl_add_if("RTMP" USE_LIBRTMP)
curl_add_if("MQTT" NOT CURL_DISABLE_MQTT)
curl_add_if("MQTTS" NOT CURL_DISABLE_MQTT AND _ssl_enabled)
curl_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS)
curl_add_if("WSS" NOT CURL_DISABLE_WEBSOCKETS AND _ssl_enabled)
if(_items)

View File

@@ -5424,6 +5424,9 @@ if test "$CURL_DISABLE_GOPHER" != "1"; then
fi
if test "$CURL_DISABLE_MQTT" != "1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS MQTT"
if test "$SSL_ENABLED" = "1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS MQTTS"
fi
fi
if test "$CURL_DISABLE_POP3" != "1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS POP3"

View File

@@ -21,7 +21,7 @@ The curl project produces two products:
### libcurl
A client-side URL transfer library, supporting DICT, FILE, FTP, FTPS, GOPHER,
GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP,
GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTMP,
RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS.
libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading,

View File

@@ -4,8 +4,8 @@
**curl** is a tool for transferring data from or to a server using URLs. It
supports these protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS,
IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP,
SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS.
IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTMP, RTMPS, RTSP, SCP,
SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS.
curl is powered by libcurl for all transfer-related features. See
*libcurl(3)* for details.

View File

@@ -12,6 +12,10 @@ A plain "GET" subscribes to the topic and prints all published messages.
Doing a "POST" publishes the post data to the topic and exits.
## TLS protection
Use `mqtts://` to do MQTT over TLS: MQTTS.
### Subscribing
Command usage:

View File

@@ -51,6 +51,8 @@ CURLPROTO_IMAP
CURLPROTO_IMAPS
CURLPROTO_LDAP
CURLPROTO_LDAPS
CURLPROTO_MQTT
CURLPROTO_MQTTS
CURLPROTO_POP3
CURLPROTO_POP3S
CURLPROTO_RTMP

View File

@@ -42,8 +42,8 @@ set, it returns error.
These are the available protocols:
DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS,
MQTT, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP, SCP, SFTP,
SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS
MQTT, MQTTS, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP,
SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS
You can set "ALL" as a short-cut to enable all protocols. Note that by setting
all, you may enable protocols that were not supported the day you write this

View File

@@ -58,6 +58,8 @@ CURLPROTO_IMAP
CURLPROTO_IMAPS
CURLPROTO_LDAP
CURLPROTO_LDAPS
CURLPROTO_MQTT
CURLPROTO_MQTTS
CURLPROTO_POP3
CURLPROTO_POP3S
CURLPROTO_RTMP

View File

@@ -46,8 +46,8 @@ By default libcurl allows HTTP, HTTPS, FTP and FTPS on redirects (since
These are the available protocols:
DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS,
MQTT, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP, SCP, SFTP,
SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS
MQTT, MQTTS, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP,
SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS
You can set "ALL" as a short-cut to enable all protocols. Note that by setting
all, you may enable protocols that were not supported the day you write this

View File

@@ -968,6 +968,7 @@ CURLPROTO_IMAPS 7.20.0
CURLPROTO_LDAP 7.19.4
CURLPROTO_LDAPS 7.19.4
CURLPROTO_MQTT 7.71.0
CURLPROTO_MQTTS 8.19.0
CURLPROTO_POP3 7.20.0
CURLPROTO_POP3S 7.20.0
CURLPROTO_RTMP 7.21.0

View File

@@ -184,6 +184,7 @@ Available substitute variables include:
- `%IMAPPORT` - Port number of the IMAP server
- `%LOGDIR` - Log directory relative to %PWD
- `%MQTTPORT` - Port number of the MQTT server
- `%MQTTSPORT` - Port number of the MQTTS server
- `%NOLISTENPORT` - Port number where no service is listening
- `%POP36PORT` - IPv6 port number of the POP3 server
- `%POP3PORT` - Port number of the POP3 server

View File

@@ -1100,6 +1100,7 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy,
#define CURLPROTO_SMBS (1L << 27)
#define CURLPROTO_MQTT (1L << 28)
#define CURLPROTO_GOPHERS (1L << 29)
#define CURLPROTO_MQTTS (1L << 30)
#define CURLPROTO_ALL (~0L) /* enable everything */
/* long may be 32 or 64 bits, but we should never depend on anything else

View File

@@ -36,6 +36,8 @@
#include "url.h"
#include "escape.h"
#include "rand.h"
#include "cfilters.h"
#include "connect.h"
/* first byte is command.
second byte is for flags. */
@@ -934,6 +936,50 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
return result;
}
#ifdef USE_SSL
static CURLcode mqtts_connecting(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
CURLcode result;
result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
if(result)
connclose(conn, "Failed TLS connection");
return result;
}
/*
* MQTTS protocol handler.
*/
const struct Curl_handler Curl_handler_mqtts = {
"mqtts", /* scheme */
mqtt_setup_conn, /* setup_connection */
mqtt_do, /* do_it */
mqtt_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* connect_it */
mqtts_connecting, /* connecting */
mqtt_doing, /* doing */
ZERO_NULL, /* proto_pollset */
mqtt_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_MQTTS, /* defport */
CURLPROTO_MQTTS, /* protocol */
CURLPROTO_MQTT, /* family */
PROTOPT_SSL /* flags */
};
#endif
/*
* MQTT protocol handler.
*/

View File

@@ -25,6 +25,9 @@
***************************************************************************/
#ifndef CURL_DISABLE_MQTT
extern const struct Curl_handler Curl_handler_mqtt;
#ifdef USE_SSL
extern const struct Curl_handler Curl_handler_mqtts;
#endif
#endif
#endif /* HEADER_CURL_MQTT_H */

View File

@@ -1423,7 +1423,12 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
#else
NULL,
#endif
NULL, NULL,
#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT)
&Curl_handler_mqtts,
#else
NULL,
#endif
NULL,
#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER)
&Curl_handler_gophers,
#else

View File

@@ -50,6 +50,7 @@
#define PORT_RTMPS PORT_HTTPS
#define PORT_GOPHER 70
#define PORT_MQTT 1883
#define PORT_MQTTS 8883
struct curl_trc_featt;

View File

@@ -341,6 +341,9 @@ static const char * const supported_protocols[] = {
#ifndef CURL_DISABLE_MQTT
"mqtt",
#endif
#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT)
"mqtts",
#endif
#ifndef CURL_DISABLE_POP3
"pop3",
#endif

View File

@@ -51,6 +51,7 @@ static const struct detail scheme[] = {
" ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \\\n"
" (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))" },
{ "mqtt", "#ifndef CURL_DISABLE_MQTT" },
{ "mqtts", "#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT)" },
{ "pop3", "#ifndef CURL_DISABLE_POP3" },
{ "pop3s", "#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)" },
{ "rtmp", "#ifdef USE_LIBRTMP" },

View File

@@ -218,6 +218,8 @@ test1620 test1621 test1622 \
\
test1630 test1631 test1632 test1633 test1634 test1635 test1636 \
\
test1640 \
\
test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \
test1658 \
test1660 test1661 test1662 test1663 test1664 test1665 \

54
tests/data/test1640 Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
MQTT
MQTT SUBSCRIBE
MQTTS
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
hello
</data>
<datacheck hex="yes">
00 04 31 31 39 30 68 65 6c 6c 6f 5b 4c 46 5d 0a
</datacheck>
</reply>
# Client-side
<client>
<features>
mqtts
</features>
<server>
mqtts
</server>
<name>
MQTTS SUBSCRIBE
</name>
<command option="binary-trace">
mqtts://%HOSTIP:%MQTTSPORT/topic --insecure
</command>
</client>
# Verify data after the test has been "shot"
<verify>
# These are hexadecimal protocol dumps from the client
# Strip out the random part of the client id from the CONNECT message
# before comparison
<strippart>
s/^(.* 00044d5154540402003c000c6375726c).*/$1/
</strippart>
<protocol>
client CONNECT 18 00044d5154540402003c000c6375726c
server CONNACK 2 20020000
client SUBSCRIBE a 00010005746f70696300
server SUBACK 3 9003000100
server PUBLISH d 300d0005746f70696368656c6c6f0a
server DISCONNECT 0 e000
</protocol>
</verify>
</testcase>

View File

@@ -125,7 +125,7 @@ sub servername_str {
$proto = uc($proto) if($proto);
die "unsupported protocol: '$proto'" unless($proto &&
($proto =~ /^(((DNS|FTP|HTTP|HTTP\/2|HTTP\/3|IMAP|POP3|GOPHER|SMTP|HTTPS-MTLS)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|HTTPTLS|DICT|SMB|SMBS|TELNET|MQTT))$/));
($proto =~ /^(((DNS|FTP|HTTP|HTTP\/2|HTTP\/3|IMAP|POP3|GOPHER|SMTP|HTTPS-MTLS)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|HTTPTLS|DICT|SMB|SMBS|TELNET|MQTT|MQTTS))$/));
$ipver = (not $ipver) ? 'ipv4' : lc($ipver);
die "unsupported IP version: '$ipver'" unless($ipver &&

View File

@@ -237,8 +237,8 @@ sub init_serverpidfile_hash {
}
}
for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'httptls',
'dict', 'smb', 'smbs', 'telnet', 'mqtt', 'https-mtls',
'dns')) {
'dict', 'smb', 'smbs', 'telnet', 'mqtt', 'mqtts',
'https-mtls', 'dns')) {
for my $ipvnum ((4, 6)) {
for my $idnum ((1, 2)) {
my $serv = servername_id($proto, $ipvnum, $idnum);
@@ -1030,6 +1030,7 @@ my %protofunc = ('http' => \&verifyhttp,
'pop3s' => \&verifyftp,
'imaps' => \&verifyftp,
'mqtt' => \&verifypid,
'mqtts' => \&verifypid,
'smtps' => \&verifyftp,
'tftp' => \&verifyftp,
'ssh' => \&verifyssh,
@@ -1329,6 +1330,9 @@ sub runhttpsserver {
if($proto eq "gophers") {
$flags .= "--connect " . protoport("gopher");
}
elsif($proto eq "mqtts") {
$flags .= "--connect " . protoport("mqtt");
}
elsif(!$proxy) {
$flags .= "--connect " . protoport("http");
}
@@ -2955,6 +2959,36 @@ sub startservers {
$run{'mqtt'}="$pid $pid2";
}
}
elsif($what eq "mqtts" ) {
if(!$stunnel) {
# we cannot run mqtts tests without stunnel
return ("no stunnel", 4);
}
if($run{'mqtt'} &&
!responsive_mqtt_server("mqtt", "", $verbose)) {
if(stopserver('mqtt')) {
return ("failed stopping unresponsive MQTT server", 3);
}
}
if(!$run{'mqtt'}) {
($serr, $pid, $pid2, $PORT{"mqtt"}) = runmqttserver("", $verbose);
if($pid <= 0) {
return ("failed starting mqtt server", $serr);
}
logmsg sprintf("* pid mqtt => %d %d\n", $pid, $pid2) if($verbose);
$run{'mqtt'}="$pid $pid2";
}
if(!$run{$what}) {
($serr, $pid, $pid2, $PORT{$what}) =
runhttpsserver($verbose, $what, "", $certfile);
if($pid <= 0) {
return ("failed starting MQTTS server (stunnel)", $serr);
}
logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2)
if($verbose);
$run{$what}="$pid $pid2";
}
}
elsif($what eq "http-unix") {
if($run{'http-unix'} &&
!responsive_http_server("http", $verbose, "unix", $HTTPUNIXPATH)) {
@@ -3094,7 +3128,7 @@ sub subvariables {
'HTTP2', 'HTTP2TLS',
'HTTP3',
'IMAP', 'IMAP6', 'IMAPS',
'MQTT',
'MQTT', 'MQTTS',
'NOLISTEN',
'POP3', 'POP36', 'POP3S',
'RTSP', 'RTSP6',