← Commit list

6c31df453b

Daniel Stenberg
Daniel Stenberg
2026-01-17
SUCCESS Duration: 412s

Subject

mqtt: initial support for MQTTS

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

CMakeLists.txt+1 -0
@@ -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)
configure.ac+3 -0
@@ -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"
docs/FAQ.md+1 -1
@@ -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,
docs/cmdline-opts/_DESCRIPTION.md+2 -2
@@ -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.
docs/internals/MQTT.md+4 -0
@@ -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:
docs/libcurl/opts/CURLOPT_PROTOCOLS.md+2 -0
@@ -51,6 +51,8 @@ CURLPROTO_IMAP
CURLPROTO_IMAPS
CURLPROTO_LDAP
CURLPROTO_LDAPS
+CURLPROTO_MQTT
+CURLPROTO_MQTTS
CURLPROTO_POP3
CURLPROTO_POP3S
CURLPROTO_RTMP
docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md+2 -2
@@ -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
docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md+2 -0
@@ -58,6 +58,8 @@ CURLPROTO_IMAP
CURLPROTO_IMAPS
CURLPROTO_LDAP
CURLPROTO_LDAPS
+CURLPROTO_MQTT
+CURLPROTO_MQTTS
CURLPROTO_POP3
CURLPROTO_POP3S
CURLPROTO_RTMP
docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md+2 -2
@@ -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
docs/libcurl/symbols-in-versions+1 -0
@@ -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
docs/tests/FILEFORMAT.md+1 -0
@@ -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
include/curl/curl.h+1 -0
@@ -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
lib/mqtt.c+46 -0
@@ -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.
*/
lib/mqtt.h+3 -0
@@ -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 */
lib/url.c+6 -1
@@ -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
lib/urldata.h+1 -0
@@ -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;
lib/version.c+3 -0
@@ -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
scripts/schemetable.c+1 -0
@@ -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" },
tests/data/Makefile.am+2 -0
@@ -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 \
tests/data/test1640+54 -0
@@ -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>
tests/serverhelp.pm+1 -1
@@ -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 &&
tests/servers.pm+37 -3
@@ -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',