1*6236dae4SAndroid Build Coastguard Worker /***************************************************************************
2*6236dae4SAndroid Build Coastguard Worker * _ _ ____ _
3*6236dae4SAndroid Build Coastguard Worker * Project ___| | | | _ \| |
4*6236dae4SAndroid Build Coastguard Worker * / __| | | | |_) | |
5*6236dae4SAndroid Build Coastguard Worker * | (__| |_| | _ <| |___
6*6236dae4SAndroid Build Coastguard Worker * \___|\___/|_| \_\_____|
7*6236dae4SAndroid Build Coastguard Worker *
8*6236dae4SAndroid Build Coastguard Worker * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9*6236dae4SAndroid Build Coastguard Worker * Copyright (C) Björn Stenberg, <[email protected]>
10*6236dae4SAndroid Build Coastguard Worker *
11*6236dae4SAndroid Build Coastguard Worker * This software is licensed as described in the file COPYING, which
12*6236dae4SAndroid Build Coastguard Worker * you should have received as part of this distribution. The terms
13*6236dae4SAndroid Build Coastguard Worker * are also available at https://curl.se/docs/copyright.html.
14*6236dae4SAndroid Build Coastguard Worker *
15*6236dae4SAndroid Build Coastguard Worker * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16*6236dae4SAndroid Build Coastguard Worker * copies of the Software, and permit persons to whom the Software is
17*6236dae4SAndroid Build Coastguard Worker * furnished to do so, under the terms of the COPYING file.
18*6236dae4SAndroid Build Coastguard Worker *
19*6236dae4SAndroid Build Coastguard Worker * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20*6236dae4SAndroid Build Coastguard Worker * KIND, either express or implied.
21*6236dae4SAndroid Build Coastguard Worker *
22*6236dae4SAndroid Build Coastguard Worker * SPDX-License-Identifier: curl
23*6236dae4SAndroid Build Coastguard Worker *
24*6236dae4SAndroid Build Coastguard Worker ***************************************************************************/
25*6236dae4SAndroid Build Coastguard Worker
26*6236dae4SAndroid Build Coastguard Worker #include "curl_setup.h"
27*6236dae4SAndroid Build Coastguard Worker
28*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_MQTT
29*6236dae4SAndroid Build Coastguard Worker
30*6236dae4SAndroid Build Coastguard Worker #include "urldata.h"
31*6236dae4SAndroid Build Coastguard Worker #include <curl/curl.h>
32*6236dae4SAndroid Build Coastguard Worker #include "transfer.h"
33*6236dae4SAndroid Build Coastguard Worker #include "sendf.h"
34*6236dae4SAndroid Build Coastguard Worker #include "progress.h"
35*6236dae4SAndroid Build Coastguard Worker #include "mqtt.h"
36*6236dae4SAndroid Build Coastguard Worker #include "select.h"
37*6236dae4SAndroid Build Coastguard Worker #include "strdup.h"
38*6236dae4SAndroid Build Coastguard Worker #include "url.h"
39*6236dae4SAndroid Build Coastguard Worker #include "escape.h"
40*6236dae4SAndroid Build Coastguard Worker #include "warnless.h"
41*6236dae4SAndroid Build Coastguard Worker #include "curl_printf.h"
42*6236dae4SAndroid Build Coastguard Worker #include "curl_memory.h"
43*6236dae4SAndroid Build Coastguard Worker #include "multiif.h"
44*6236dae4SAndroid Build Coastguard Worker #include "rand.h"
45*6236dae4SAndroid Build Coastguard Worker
46*6236dae4SAndroid Build Coastguard Worker /* The last #include file should be: */
47*6236dae4SAndroid Build Coastguard Worker #include "memdebug.h"
48*6236dae4SAndroid Build Coastguard Worker
49*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_CONNECT 0x10
50*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_CONNACK 0x20
51*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_PUBLISH 0x30
52*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_SUBSCRIBE 0x82
53*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_SUBACK 0x90
54*6236dae4SAndroid Build Coastguard Worker #define MQTT_MSG_DISCONNECT 0xe0
55*6236dae4SAndroid Build Coastguard Worker
56*6236dae4SAndroid Build Coastguard Worker #define MQTT_CONNACK_LEN 2
57*6236dae4SAndroid Build Coastguard Worker #define MQTT_SUBACK_LEN 3
58*6236dae4SAndroid Build Coastguard Worker #define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */
59*6236dae4SAndroid Build Coastguard Worker
60*6236dae4SAndroid Build Coastguard Worker /*
61*6236dae4SAndroid Build Coastguard Worker * Forward declarations.
62*6236dae4SAndroid Build Coastguard Worker */
63*6236dae4SAndroid Build Coastguard Worker
64*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_do(struct Curl_easy *data, bool *done);
65*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_done(struct Curl_easy *data,
66*6236dae4SAndroid Build Coastguard Worker CURLcode status, bool premature);
67*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_doing(struct Curl_easy *data, bool *done);
68*6236dae4SAndroid Build Coastguard Worker static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn,
69*6236dae4SAndroid Build Coastguard Worker curl_socket_t *sock);
70*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_setup_conn(struct Curl_easy *data,
71*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn);
72*6236dae4SAndroid Build Coastguard Worker
73*6236dae4SAndroid Build Coastguard Worker /*
74*6236dae4SAndroid Build Coastguard Worker * MQTT protocol handler.
75*6236dae4SAndroid Build Coastguard Worker */
76*6236dae4SAndroid Build Coastguard Worker
77*6236dae4SAndroid Build Coastguard Worker const struct Curl_handler Curl_handler_mqtt = {
78*6236dae4SAndroid Build Coastguard Worker "mqtt", /* scheme */
79*6236dae4SAndroid Build Coastguard Worker mqtt_setup_conn, /* setup_connection */
80*6236dae4SAndroid Build Coastguard Worker mqtt_do, /* do_it */
81*6236dae4SAndroid Build Coastguard Worker mqtt_done, /* done */
82*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* do_more */
83*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* connect_it */
84*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* connecting */
85*6236dae4SAndroid Build Coastguard Worker mqtt_doing, /* doing */
86*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* proto_getsock */
87*6236dae4SAndroid Build Coastguard Worker mqtt_getsock, /* doing_getsock */
88*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* domore_getsock */
89*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* perform_getsock */
90*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* disconnect */
91*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* write_resp */
92*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* write_resp_hd */
93*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* connection_check */
94*6236dae4SAndroid Build Coastguard Worker ZERO_NULL, /* attach connection */
95*6236dae4SAndroid Build Coastguard Worker PORT_MQTT, /* defport */
96*6236dae4SAndroid Build Coastguard Worker CURLPROTO_MQTT, /* protocol */
97*6236dae4SAndroid Build Coastguard Worker CURLPROTO_MQTT, /* family */
98*6236dae4SAndroid Build Coastguard Worker PROTOPT_NONE /* flags */
99*6236dae4SAndroid Build Coastguard Worker };
100*6236dae4SAndroid Build Coastguard Worker
mqtt_setup_conn(struct Curl_easy * data,struct connectdata * conn)101*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_setup_conn(struct Curl_easy *data,
102*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn)
103*6236dae4SAndroid Build Coastguard Worker {
104*6236dae4SAndroid Build Coastguard Worker /* allocate the HTTP-specific struct for the Curl_easy, only to survive
105*6236dae4SAndroid Build Coastguard Worker during this request */
106*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq;
107*6236dae4SAndroid Build Coastguard Worker (void)conn;
108*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(data->req.p.mqtt == NULL);
109*6236dae4SAndroid Build Coastguard Worker
110*6236dae4SAndroid Build Coastguard Worker mq = calloc(1, sizeof(struct MQTT));
111*6236dae4SAndroid Build Coastguard Worker if(!mq)
112*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
113*6236dae4SAndroid Build Coastguard Worker Curl_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
114*6236dae4SAndroid Build Coastguard Worker data->req.p.mqtt = mq;
115*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
116*6236dae4SAndroid Build Coastguard Worker }
117*6236dae4SAndroid Build Coastguard Worker
mqtt_send(struct Curl_easy * data,char * buf,size_t len)118*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_send(struct Curl_easy *data,
119*6236dae4SAndroid Build Coastguard Worker char *buf, size_t len)
120*6236dae4SAndroid Build Coastguard Worker {
121*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
122*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
123*6236dae4SAndroid Build Coastguard Worker size_t n;
124*6236dae4SAndroid Build Coastguard Worker result = Curl_xfer_send(data, buf, len, FALSE, &n);
125*6236dae4SAndroid Build Coastguard Worker if(result)
126*6236dae4SAndroid Build Coastguard Worker return result;
127*6236dae4SAndroid Build Coastguard Worker Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
128*6236dae4SAndroid Build Coastguard Worker if(len != n) {
129*6236dae4SAndroid Build Coastguard Worker size_t nsend = len - n;
130*6236dae4SAndroid Build Coastguard Worker char *sendleftovers = Curl_memdup(&buf[n], nsend);
131*6236dae4SAndroid Build Coastguard Worker if(!sendleftovers)
132*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
133*6236dae4SAndroid Build Coastguard Worker mq->sendleftovers = sendleftovers;
134*6236dae4SAndroid Build Coastguard Worker mq->nsend = nsend;
135*6236dae4SAndroid Build Coastguard Worker }
136*6236dae4SAndroid Build Coastguard Worker else {
137*6236dae4SAndroid Build Coastguard Worker mq->sendleftovers = NULL;
138*6236dae4SAndroid Build Coastguard Worker mq->nsend = 0;
139*6236dae4SAndroid Build Coastguard Worker }
140*6236dae4SAndroid Build Coastguard Worker return result;
141*6236dae4SAndroid Build Coastguard Worker }
142*6236dae4SAndroid Build Coastguard Worker
143*6236dae4SAndroid Build Coastguard Worker /* Generic function called by the multi interface to figure out what socket(s)
144*6236dae4SAndroid Build Coastguard Worker to wait for and for what actions during the DOING and PROTOCONNECT
145*6236dae4SAndroid Build Coastguard Worker states */
mqtt_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * sock)146*6236dae4SAndroid Build Coastguard Worker static int mqtt_getsock(struct Curl_easy *data,
147*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn,
148*6236dae4SAndroid Build Coastguard Worker curl_socket_t *sock)
149*6236dae4SAndroid Build Coastguard Worker {
150*6236dae4SAndroid Build Coastguard Worker (void)data;
151*6236dae4SAndroid Build Coastguard Worker sock[0] = conn->sock[FIRSTSOCKET];
152*6236dae4SAndroid Build Coastguard Worker return GETSOCK_READSOCK(FIRSTSOCKET);
153*6236dae4SAndroid Build Coastguard Worker }
154*6236dae4SAndroid Build Coastguard Worker
mqtt_encode_len(char * buf,size_t len)155*6236dae4SAndroid Build Coastguard Worker static int mqtt_encode_len(char *buf, size_t len)
156*6236dae4SAndroid Build Coastguard Worker {
157*6236dae4SAndroid Build Coastguard Worker int i;
158*6236dae4SAndroid Build Coastguard Worker
159*6236dae4SAndroid Build Coastguard Worker for(i = 0; (len > 0) && (i < 4); i++) {
160*6236dae4SAndroid Build Coastguard Worker unsigned char encoded;
161*6236dae4SAndroid Build Coastguard Worker encoded = len % 0x80;
162*6236dae4SAndroid Build Coastguard Worker len /= 0x80;
163*6236dae4SAndroid Build Coastguard Worker if(len)
164*6236dae4SAndroid Build Coastguard Worker encoded |= 0x80;
165*6236dae4SAndroid Build Coastguard Worker buf[i] = (char)encoded;
166*6236dae4SAndroid Build Coastguard Worker }
167*6236dae4SAndroid Build Coastguard Worker
168*6236dae4SAndroid Build Coastguard Worker return i;
169*6236dae4SAndroid Build Coastguard Worker }
170*6236dae4SAndroid Build Coastguard Worker
171*6236dae4SAndroid Build Coastguard Worker /* add the passwd to the CONNECT packet */
add_passwd(const char * passwd,const size_t plen,char * pkt,const size_t start,int remain_pos)172*6236dae4SAndroid Build Coastguard Worker static int add_passwd(const char *passwd, const size_t plen,
173*6236dae4SAndroid Build Coastguard Worker char *pkt, const size_t start, int remain_pos)
174*6236dae4SAndroid Build Coastguard Worker {
175*6236dae4SAndroid Build Coastguard Worker /* magic number that need to be set properly */
176*6236dae4SAndroid Build Coastguard Worker const size_t conn_flags_pos = remain_pos + 8;
177*6236dae4SAndroid Build Coastguard Worker if(plen > 0xffff)
178*6236dae4SAndroid Build Coastguard Worker return 1;
179*6236dae4SAndroid Build Coastguard Worker
180*6236dae4SAndroid Build Coastguard Worker /* set password flag */
181*6236dae4SAndroid Build Coastguard Worker pkt[conn_flags_pos] |= 0x40;
182*6236dae4SAndroid Build Coastguard Worker
183*6236dae4SAndroid Build Coastguard Worker /* length of password provided */
184*6236dae4SAndroid Build Coastguard Worker pkt[start] = (char)((plen >> 8) & 0xFF);
185*6236dae4SAndroid Build Coastguard Worker pkt[start + 1] = (char)(plen & 0xFF);
186*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[start + 2], passwd, plen);
187*6236dae4SAndroid Build Coastguard Worker return 0;
188*6236dae4SAndroid Build Coastguard Worker }
189*6236dae4SAndroid Build Coastguard Worker
190*6236dae4SAndroid Build Coastguard Worker /* add user to the CONNECT packet */
add_user(const char * username,const size_t ulen,unsigned char * pkt,const size_t start,int remain_pos)191*6236dae4SAndroid Build Coastguard Worker static int add_user(const char *username, const size_t ulen,
192*6236dae4SAndroid Build Coastguard Worker unsigned char *pkt, const size_t start, int remain_pos)
193*6236dae4SAndroid Build Coastguard Worker {
194*6236dae4SAndroid Build Coastguard Worker /* magic number that need to be set properly */
195*6236dae4SAndroid Build Coastguard Worker const size_t conn_flags_pos = remain_pos + 8;
196*6236dae4SAndroid Build Coastguard Worker if(ulen > 0xffff)
197*6236dae4SAndroid Build Coastguard Worker return 1;
198*6236dae4SAndroid Build Coastguard Worker
199*6236dae4SAndroid Build Coastguard Worker /* set username flag */
200*6236dae4SAndroid Build Coastguard Worker pkt[conn_flags_pos] |= 0x80;
201*6236dae4SAndroid Build Coastguard Worker /* length of username provided */
202*6236dae4SAndroid Build Coastguard Worker pkt[start] = (unsigned char)((ulen >> 8) & 0xFF);
203*6236dae4SAndroid Build Coastguard Worker pkt[start + 1] = (unsigned char)(ulen & 0xFF);
204*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[start + 2], username, ulen);
205*6236dae4SAndroid Build Coastguard Worker return 0;
206*6236dae4SAndroid Build Coastguard Worker }
207*6236dae4SAndroid Build Coastguard Worker
208*6236dae4SAndroid Build Coastguard Worker /* add client ID to the CONNECT packet */
add_client_id(const char * client_id,const size_t client_id_len,char * pkt,const size_t start)209*6236dae4SAndroid Build Coastguard Worker static int add_client_id(const char *client_id, const size_t client_id_len,
210*6236dae4SAndroid Build Coastguard Worker char *pkt, const size_t start)
211*6236dae4SAndroid Build Coastguard Worker {
212*6236dae4SAndroid Build Coastguard Worker if(client_id_len != MQTT_CLIENTID_LEN)
213*6236dae4SAndroid Build Coastguard Worker return 1;
214*6236dae4SAndroid Build Coastguard Worker pkt[start] = 0x00;
215*6236dae4SAndroid Build Coastguard Worker pkt[start + 1] = MQTT_CLIENTID_LEN;
216*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN);
217*6236dae4SAndroid Build Coastguard Worker return 0;
218*6236dae4SAndroid Build Coastguard Worker }
219*6236dae4SAndroid Build Coastguard Worker
220*6236dae4SAndroid Build Coastguard Worker /* Set initial values of CONNECT packet */
init_connpack(char * packet,char * remain,int remain_pos)221*6236dae4SAndroid Build Coastguard Worker static int init_connpack(char *packet, char *remain, int remain_pos)
222*6236dae4SAndroid Build Coastguard Worker {
223*6236dae4SAndroid Build Coastguard Worker /* Fixed header starts */
224*6236dae4SAndroid Build Coastguard Worker /* packet type */
225*6236dae4SAndroid Build Coastguard Worker packet[0] = MQTT_MSG_CONNECT;
226*6236dae4SAndroid Build Coastguard Worker /* remaining length field */
227*6236dae4SAndroid Build Coastguard Worker memcpy(&packet[1], remain, remain_pos);
228*6236dae4SAndroid Build Coastguard Worker /* Fixed header ends */
229*6236dae4SAndroid Build Coastguard Worker
230*6236dae4SAndroid Build Coastguard Worker /* Variable header starts */
231*6236dae4SAndroid Build Coastguard Worker /* protocol length */
232*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 1] = 0x00;
233*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 2] = 0x04;
234*6236dae4SAndroid Build Coastguard Worker /* protocol name */
235*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 3] = 'M';
236*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 4] = 'Q';
237*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 5] = 'T';
238*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 6] = 'T';
239*6236dae4SAndroid Build Coastguard Worker /* protocol level */
240*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 7] = 0x04;
241*6236dae4SAndroid Build Coastguard Worker /* CONNECT flag: CleanSession */
242*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 8] = 0x02;
243*6236dae4SAndroid Build Coastguard Worker /* keep-alive 0 = disabled */
244*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 9] = 0x00;
245*6236dae4SAndroid Build Coastguard Worker packet[remain_pos + 10] = 0x3c;
246*6236dae4SAndroid Build Coastguard Worker /* end of variable header */
247*6236dae4SAndroid Build Coastguard Worker return remain_pos + 10;
248*6236dae4SAndroid Build Coastguard Worker }
249*6236dae4SAndroid Build Coastguard Worker
mqtt_connect(struct Curl_easy * data)250*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_connect(struct Curl_easy *data)
251*6236dae4SAndroid Build Coastguard Worker {
252*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
253*6236dae4SAndroid Build Coastguard Worker int pos = 0;
254*6236dae4SAndroid Build Coastguard Worker int rc = 0;
255*6236dae4SAndroid Build Coastguard Worker /* remain length */
256*6236dae4SAndroid Build Coastguard Worker int remain_pos = 0;
257*6236dae4SAndroid Build Coastguard Worker char remain[4] = {0};
258*6236dae4SAndroid Build Coastguard Worker size_t packetlen = 0;
259*6236dae4SAndroid Build Coastguard Worker size_t payloadlen = 0;
260*6236dae4SAndroid Build Coastguard Worker size_t start_user = 0;
261*6236dae4SAndroid Build Coastguard Worker size_t start_pwd = 0;
262*6236dae4SAndroid Build Coastguard Worker char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
263*6236dae4SAndroid Build Coastguard Worker const size_t clen = strlen("curl");
264*6236dae4SAndroid Build Coastguard Worker char *packet = NULL;
265*6236dae4SAndroid Build Coastguard Worker
266*6236dae4SAndroid Build Coastguard Worker /* extracting username from request */
267*6236dae4SAndroid Build Coastguard Worker const char *username = data->state.aptr.user ?
268*6236dae4SAndroid Build Coastguard Worker data->state.aptr.user : "";
269*6236dae4SAndroid Build Coastguard Worker const size_t ulen = strlen(username);
270*6236dae4SAndroid Build Coastguard Worker /* extracting password from request */
271*6236dae4SAndroid Build Coastguard Worker const char *passwd = data->state.aptr.passwd ?
272*6236dae4SAndroid Build Coastguard Worker data->state.aptr.passwd : "";
273*6236dae4SAndroid Build Coastguard Worker const size_t plen = strlen(passwd);
274*6236dae4SAndroid Build Coastguard Worker
275*6236dae4SAndroid Build Coastguard Worker payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
276*6236dae4SAndroid Build Coastguard Worker /* The plus 2 are for the MSB and LSB describing the length of the string to
277*6236dae4SAndroid Build Coastguard Worker * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
278*6236dae4SAndroid Build Coastguard Worker if(ulen)
279*6236dae4SAndroid Build Coastguard Worker payloadlen += 2;
280*6236dae4SAndroid Build Coastguard Worker if(plen)
281*6236dae4SAndroid Build Coastguard Worker payloadlen += 2;
282*6236dae4SAndroid Build Coastguard Worker
283*6236dae4SAndroid Build Coastguard Worker /* getting how much occupy the remain length */
284*6236dae4SAndroid Build Coastguard Worker remain_pos = mqtt_encode_len(remain, payloadlen + 10);
285*6236dae4SAndroid Build Coastguard Worker
286*6236dae4SAndroid Build Coastguard Worker /* 10 length of variable header and 1 the first byte of the fixed header */
287*6236dae4SAndroid Build Coastguard Worker packetlen = payloadlen + 10 + remain_pos + 1;
288*6236dae4SAndroid Build Coastguard Worker
289*6236dae4SAndroid Build Coastguard Worker /* allocating packet */
290*6236dae4SAndroid Build Coastguard Worker if(packetlen > 268435455)
291*6236dae4SAndroid Build Coastguard Worker return CURLE_WEIRD_SERVER_REPLY;
292*6236dae4SAndroid Build Coastguard Worker packet = malloc(packetlen);
293*6236dae4SAndroid Build Coastguard Worker if(!packet)
294*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
295*6236dae4SAndroid Build Coastguard Worker memset(packet, 0, packetlen);
296*6236dae4SAndroid Build Coastguard Worker
297*6236dae4SAndroid Build Coastguard Worker /* set initial values for the CONNECT packet */
298*6236dae4SAndroid Build Coastguard Worker pos = init_connpack(packet, remain, remain_pos);
299*6236dae4SAndroid Build Coastguard Worker
300*6236dae4SAndroid Build Coastguard Worker result = Curl_rand_alnum(data, (unsigned char *)&client_id[clen],
301*6236dae4SAndroid Build Coastguard Worker MQTT_CLIENTID_LEN - clen + 1);
302*6236dae4SAndroid Build Coastguard Worker /* add client id */
303*6236dae4SAndroid Build Coastguard Worker rc = add_client_id(client_id, strlen(client_id), packet, pos + 1);
304*6236dae4SAndroid Build Coastguard Worker if(rc) {
305*6236dae4SAndroid Build Coastguard Worker failf(data, "Client ID length mismatched: [%zu]", strlen(client_id));
306*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
307*6236dae4SAndroid Build Coastguard Worker goto end;
308*6236dae4SAndroid Build Coastguard Worker }
309*6236dae4SAndroid Build Coastguard Worker infof(data, "Using client id '%s'", client_id);
310*6236dae4SAndroid Build Coastguard Worker
311*6236dae4SAndroid Build Coastguard Worker /* position where starts the user payload */
312*6236dae4SAndroid Build Coastguard Worker start_user = pos + 3 + MQTT_CLIENTID_LEN;
313*6236dae4SAndroid Build Coastguard Worker /* position where starts the password payload */
314*6236dae4SAndroid Build Coastguard Worker start_pwd = start_user + ulen;
315*6236dae4SAndroid Build Coastguard Worker /* if username was provided, add it to the packet */
316*6236dae4SAndroid Build Coastguard Worker if(ulen) {
317*6236dae4SAndroid Build Coastguard Worker start_pwd += 2;
318*6236dae4SAndroid Build Coastguard Worker
319*6236dae4SAndroid Build Coastguard Worker rc = add_user(username, ulen,
320*6236dae4SAndroid Build Coastguard Worker (unsigned char *)packet, start_user, remain_pos);
321*6236dae4SAndroid Build Coastguard Worker if(rc) {
322*6236dae4SAndroid Build Coastguard Worker failf(data, "Username is too large: [%zu]", ulen);
323*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
324*6236dae4SAndroid Build Coastguard Worker goto end;
325*6236dae4SAndroid Build Coastguard Worker }
326*6236dae4SAndroid Build Coastguard Worker }
327*6236dae4SAndroid Build Coastguard Worker
328*6236dae4SAndroid Build Coastguard Worker /* if passwd was provided, add it to the packet */
329*6236dae4SAndroid Build Coastguard Worker if(plen) {
330*6236dae4SAndroid Build Coastguard Worker rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
331*6236dae4SAndroid Build Coastguard Worker if(rc) {
332*6236dae4SAndroid Build Coastguard Worker failf(data, "Password is too large: [%zu]", plen);
333*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
334*6236dae4SAndroid Build Coastguard Worker goto end;
335*6236dae4SAndroid Build Coastguard Worker }
336*6236dae4SAndroid Build Coastguard Worker }
337*6236dae4SAndroid Build Coastguard Worker
338*6236dae4SAndroid Build Coastguard Worker if(!result)
339*6236dae4SAndroid Build Coastguard Worker result = mqtt_send(data, packet, packetlen);
340*6236dae4SAndroid Build Coastguard Worker
341*6236dae4SAndroid Build Coastguard Worker end:
342*6236dae4SAndroid Build Coastguard Worker if(packet)
343*6236dae4SAndroid Build Coastguard Worker free(packet);
344*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.user);
345*6236dae4SAndroid Build Coastguard Worker Curl_safefree(data->state.aptr.passwd);
346*6236dae4SAndroid Build Coastguard Worker return result;
347*6236dae4SAndroid Build Coastguard Worker }
348*6236dae4SAndroid Build Coastguard Worker
mqtt_disconnect(struct Curl_easy * data)349*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_disconnect(struct Curl_easy *data)
350*6236dae4SAndroid Build Coastguard Worker {
351*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
352*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
353*6236dae4SAndroid Build Coastguard Worker result = mqtt_send(data, (char *)"\xe0\x00", 2);
354*6236dae4SAndroid Build Coastguard Worker Curl_safefree(mq->sendleftovers);
355*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&mq->recvbuf);
356*6236dae4SAndroid Build Coastguard Worker return result;
357*6236dae4SAndroid Build Coastguard Worker }
358*6236dae4SAndroid Build Coastguard Worker
mqtt_recv_atleast(struct Curl_easy * data,size_t nbytes)359*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
360*6236dae4SAndroid Build Coastguard Worker {
361*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
362*6236dae4SAndroid Build Coastguard Worker size_t rlen = Curl_dyn_len(&mq->recvbuf);
363*6236dae4SAndroid Build Coastguard Worker CURLcode result;
364*6236dae4SAndroid Build Coastguard Worker
365*6236dae4SAndroid Build Coastguard Worker if(rlen < nbytes) {
366*6236dae4SAndroid Build Coastguard Worker unsigned char readbuf[1024];
367*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
368*6236dae4SAndroid Build Coastguard Worker
369*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(nbytes - rlen < sizeof(readbuf));
370*6236dae4SAndroid Build Coastguard Worker result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread);
371*6236dae4SAndroid Build Coastguard Worker if(result)
372*6236dae4SAndroid Build Coastguard Worker return result;
373*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(nread >= 0);
374*6236dae4SAndroid Build Coastguard Worker if(Curl_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
375*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
376*6236dae4SAndroid Build Coastguard Worker rlen = Curl_dyn_len(&mq->recvbuf);
377*6236dae4SAndroid Build Coastguard Worker }
378*6236dae4SAndroid Build Coastguard Worker return (rlen >= nbytes) ? CURLE_OK : CURLE_AGAIN;
379*6236dae4SAndroid Build Coastguard Worker }
380*6236dae4SAndroid Build Coastguard Worker
mqtt_recv_consume(struct Curl_easy * data,size_t nbytes)381*6236dae4SAndroid Build Coastguard Worker static void mqtt_recv_consume(struct Curl_easy *data, size_t nbytes)
382*6236dae4SAndroid Build Coastguard Worker {
383*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
384*6236dae4SAndroid Build Coastguard Worker size_t rlen = Curl_dyn_len(&mq->recvbuf);
385*6236dae4SAndroid Build Coastguard Worker if(rlen <= nbytes)
386*6236dae4SAndroid Build Coastguard Worker Curl_dyn_reset(&mq->recvbuf);
387*6236dae4SAndroid Build Coastguard Worker else
388*6236dae4SAndroid Build Coastguard Worker Curl_dyn_tail(&mq->recvbuf, rlen - nbytes);
389*6236dae4SAndroid Build Coastguard Worker }
390*6236dae4SAndroid Build Coastguard Worker
mqtt_verify_connack(struct Curl_easy * data)391*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_verify_connack(struct Curl_easy *data)
392*6236dae4SAndroid Build Coastguard Worker {
393*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
394*6236dae4SAndroid Build Coastguard Worker CURLcode result;
395*6236dae4SAndroid Build Coastguard Worker char *ptr;
396*6236dae4SAndroid Build Coastguard Worker
397*6236dae4SAndroid Build Coastguard Worker result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN);
398*6236dae4SAndroid Build Coastguard Worker if(result)
399*6236dae4SAndroid Build Coastguard Worker goto fail;
400*6236dae4SAndroid Build Coastguard Worker
401*6236dae4SAndroid Build Coastguard Worker /* verify CONNACK */
402*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
403*6236dae4SAndroid Build Coastguard Worker ptr = Curl_dyn_ptr(&mq->recvbuf);
404*6236dae4SAndroid Build Coastguard Worker Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_CONNACK_LEN);
405*6236dae4SAndroid Build Coastguard Worker
406*6236dae4SAndroid Build Coastguard Worker if(ptr[0] != 0x00 || ptr[1] != 0x00) {
407*6236dae4SAndroid Build Coastguard Worker failf(data, "Expected %02x%02x but got %02x%02x",
408*6236dae4SAndroid Build Coastguard Worker 0x00, 0x00, ptr[0], ptr[1]);
409*6236dae4SAndroid Build Coastguard Worker Curl_dyn_reset(&mq->recvbuf);
410*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
411*6236dae4SAndroid Build Coastguard Worker goto fail;
412*6236dae4SAndroid Build Coastguard Worker }
413*6236dae4SAndroid Build Coastguard Worker mqtt_recv_consume(data, MQTT_CONNACK_LEN);
414*6236dae4SAndroid Build Coastguard Worker fail:
415*6236dae4SAndroid Build Coastguard Worker return result;
416*6236dae4SAndroid Build Coastguard Worker }
417*6236dae4SAndroid Build Coastguard Worker
mqtt_get_topic(struct Curl_easy * data,char ** topic,size_t * topiclen)418*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_get_topic(struct Curl_easy *data,
419*6236dae4SAndroid Build Coastguard Worker char **topic, size_t *topiclen)
420*6236dae4SAndroid Build Coastguard Worker {
421*6236dae4SAndroid Build Coastguard Worker char *path = data->state.up.path;
422*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_URL_MALFORMAT;
423*6236dae4SAndroid Build Coastguard Worker if(strlen(path) > 1) {
424*6236dae4SAndroid Build Coastguard Worker result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA);
425*6236dae4SAndroid Build Coastguard Worker if(!result && (*topiclen > 0xffff)) {
426*6236dae4SAndroid Build Coastguard Worker failf(data, "Too long MQTT topic");
427*6236dae4SAndroid Build Coastguard Worker result = CURLE_URL_MALFORMAT;
428*6236dae4SAndroid Build Coastguard Worker }
429*6236dae4SAndroid Build Coastguard Worker }
430*6236dae4SAndroid Build Coastguard Worker else
431*6236dae4SAndroid Build Coastguard Worker failf(data, "No MQTT topic found. Forgot to URL encode it?");
432*6236dae4SAndroid Build Coastguard Worker
433*6236dae4SAndroid Build Coastguard Worker return result;
434*6236dae4SAndroid Build Coastguard Worker }
435*6236dae4SAndroid Build Coastguard Worker
mqtt_subscribe(struct Curl_easy * data)436*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_subscribe(struct Curl_easy *data)
437*6236dae4SAndroid Build Coastguard Worker {
438*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
439*6236dae4SAndroid Build Coastguard Worker char *topic = NULL;
440*6236dae4SAndroid Build Coastguard Worker size_t topiclen;
441*6236dae4SAndroid Build Coastguard Worker unsigned char *packet = NULL;
442*6236dae4SAndroid Build Coastguard Worker size_t packetlen;
443*6236dae4SAndroid Build Coastguard Worker char encodedsize[4];
444*6236dae4SAndroid Build Coastguard Worker size_t n;
445*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
446*6236dae4SAndroid Build Coastguard Worker
447*6236dae4SAndroid Build Coastguard Worker result = mqtt_get_topic(data, &topic, &topiclen);
448*6236dae4SAndroid Build Coastguard Worker if(result)
449*6236dae4SAndroid Build Coastguard Worker goto fail;
450*6236dae4SAndroid Build Coastguard Worker
451*6236dae4SAndroid Build Coastguard Worker conn->proto.mqtt.packetid++;
452*6236dae4SAndroid Build Coastguard Worker
453*6236dae4SAndroid Build Coastguard Worker packetlen = topiclen + 5; /* packetid + topic (has a two byte length field)
454*6236dae4SAndroid Build Coastguard Worker + 2 bytes topic length + QoS byte */
455*6236dae4SAndroid Build Coastguard Worker n = mqtt_encode_len((char *)encodedsize, packetlen);
456*6236dae4SAndroid Build Coastguard Worker packetlen += n + 1; /* add one for the control packet type byte */
457*6236dae4SAndroid Build Coastguard Worker
458*6236dae4SAndroid Build Coastguard Worker packet = malloc(packetlen);
459*6236dae4SAndroid Build Coastguard Worker if(!packet) {
460*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
461*6236dae4SAndroid Build Coastguard Worker goto fail;
462*6236dae4SAndroid Build Coastguard Worker }
463*6236dae4SAndroid Build Coastguard Worker
464*6236dae4SAndroid Build Coastguard Worker packet[0] = MQTT_MSG_SUBSCRIBE;
465*6236dae4SAndroid Build Coastguard Worker memcpy(&packet[1], encodedsize, n);
466*6236dae4SAndroid Build Coastguard Worker packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff;
467*6236dae4SAndroid Build Coastguard Worker packet[2 + n] = conn->proto.mqtt.packetid & 0xff;
468*6236dae4SAndroid Build Coastguard Worker packet[3 + n] = (topiclen >> 8) & 0xff;
469*6236dae4SAndroid Build Coastguard Worker packet[4 + n ] = topiclen & 0xff;
470*6236dae4SAndroid Build Coastguard Worker memcpy(&packet[5 + n], topic, topiclen);
471*6236dae4SAndroid Build Coastguard Worker packet[5 + n + topiclen] = 0; /* QoS zero */
472*6236dae4SAndroid Build Coastguard Worker
473*6236dae4SAndroid Build Coastguard Worker result = mqtt_send(data, (char *)packet, packetlen);
474*6236dae4SAndroid Build Coastguard Worker
475*6236dae4SAndroid Build Coastguard Worker fail:
476*6236dae4SAndroid Build Coastguard Worker free(topic);
477*6236dae4SAndroid Build Coastguard Worker free(packet);
478*6236dae4SAndroid Build Coastguard Worker return result;
479*6236dae4SAndroid Build Coastguard Worker }
480*6236dae4SAndroid Build Coastguard Worker
481*6236dae4SAndroid Build Coastguard Worker /*
482*6236dae4SAndroid Build Coastguard Worker * Called when the first byte was already read.
483*6236dae4SAndroid Build Coastguard Worker */
mqtt_verify_suback(struct Curl_easy * data)484*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_verify_suback(struct Curl_easy *data)
485*6236dae4SAndroid Build Coastguard Worker {
486*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
487*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
488*6236dae4SAndroid Build Coastguard Worker struct mqtt_conn *mqtt = &conn->proto.mqtt;
489*6236dae4SAndroid Build Coastguard Worker CURLcode result;
490*6236dae4SAndroid Build Coastguard Worker char *ptr;
491*6236dae4SAndroid Build Coastguard Worker
492*6236dae4SAndroid Build Coastguard Worker result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN);
493*6236dae4SAndroid Build Coastguard Worker if(result)
494*6236dae4SAndroid Build Coastguard Worker goto fail;
495*6236dae4SAndroid Build Coastguard Worker
496*6236dae4SAndroid Build Coastguard Worker /* verify SUBACK */
497*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
498*6236dae4SAndroid Build Coastguard Worker ptr = Curl_dyn_ptr(&mq->recvbuf);
499*6236dae4SAndroid Build Coastguard Worker Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_SUBACK_LEN);
500*6236dae4SAndroid Build Coastguard Worker
501*6236dae4SAndroid Build Coastguard Worker if(((unsigned char)ptr[0]) != ((mqtt->packetid >> 8) & 0xff) ||
502*6236dae4SAndroid Build Coastguard Worker ((unsigned char)ptr[1]) != (mqtt->packetid & 0xff) ||
503*6236dae4SAndroid Build Coastguard Worker ptr[2] != 0x00) {
504*6236dae4SAndroid Build Coastguard Worker Curl_dyn_reset(&mq->recvbuf);
505*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
506*6236dae4SAndroid Build Coastguard Worker goto fail;
507*6236dae4SAndroid Build Coastguard Worker }
508*6236dae4SAndroid Build Coastguard Worker mqtt_recv_consume(data, MQTT_SUBACK_LEN);
509*6236dae4SAndroid Build Coastguard Worker fail:
510*6236dae4SAndroid Build Coastguard Worker return result;
511*6236dae4SAndroid Build Coastguard Worker }
512*6236dae4SAndroid Build Coastguard Worker
mqtt_publish(struct Curl_easy * data)513*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_publish(struct Curl_easy *data)
514*6236dae4SAndroid Build Coastguard Worker {
515*6236dae4SAndroid Build Coastguard Worker CURLcode result;
516*6236dae4SAndroid Build Coastguard Worker char *payload = data->set.postfields;
517*6236dae4SAndroid Build Coastguard Worker size_t payloadlen;
518*6236dae4SAndroid Build Coastguard Worker char *topic = NULL;
519*6236dae4SAndroid Build Coastguard Worker size_t topiclen;
520*6236dae4SAndroid Build Coastguard Worker unsigned char *pkt = NULL;
521*6236dae4SAndroid Build Coastguard Worker size_t i = 0;
522*6236dae4SAndroid Build Coastguard Worker size_t remaininglength;
523*6236dae4SAndroid Build Coastguard Worker size_t encodelen;
524*6236dae4SAndroid Build Coastguard Worker char encodedbytes[4];
525*6236dae4SAndroid Build Coastguard Worker curl_off_t postfieldsize = data->set.postfieldsize;
526*6236dae4SAndroid Build Coastguard Worker
527*6236dae4SAndroid Build Coastguard Worker if(!payload) {
528*6236dae4SAndroid Build Coastguard Worker DEBUGF(infof(data, "mqtt_publish without payload, return bad arg"));
529*6236dae4SAndroid Build Coastguard Worker return CURLE_BAD_FUNCTION_ARGUMENT;
530*6236dae4SAndroid Build Coastguard Worker }
531*6236dae4SAndroid Build Coastguard Worker if(postfieldsize < 0)
532*6236dae4SAndroid Build Coastguard Worker payloadlen = strlen(payload);
533*6236dae4SAndroid Build Coastguard Worker else
534*6236dae4SAndroid Build Coastguard Worker payloadlen = (size_t)postfieldsize;
535*6236dae4SAndroid Build Coastguard Worker
536*6236dae4SAndroid Build Coastguard Worker result = mqtt_get_topic(data, &topic, &topiclen);
537*6236dae4SAndroid Build Coastguard Worker if(result)
538*6236dae4SAndroid Build Coastguard Worker goto fail;
539*6236dae4SAndroid Build Coastguard Worker
540*6236dae4SAndroid Build Coastguard Worker remaininglength = payloadlen + 2 + topiclen;
541*6236dae4SAndroid Build Coastguard Worker encodelen = mqtt_encode_len(encodedbytes, remaininglength);
542*6236dae4SAndroid Build Coastguard Worker
543*6236dae4SAndroid Build Coastguard Worker /* add the control byte and the encoded remaining length */
544*6236dae4SAndroid Build Coastguard Worker pkt = malloc(remaininglength + 1 + encodelen);
545*6236dae4SAndroid Build Coastguard Worker if(!pkt) {
546*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
547*6236dae4SAndroid Build Coastguard Worker goto fail;
548*6236dae4SAndroid Build Coastguard Worker }
549*6236dae4SAndroid Build Coastguard Worker
550*6236dae4SAndroid Build Coastguard Worker /* assemble packet */
551*6236dae4SAndroid Build Coastguard Worker pkt[i++] = MQTT_MSG_PUBLISH;
552*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[i], encodedbytes, encodelen);
553*6236dae4SAndroid Build Coastguard Worker i += encodelen;
554*6236dae4SAndroid Build Coastguard Worker pkt[i++] = (topiclen >> 8) & 0xff;
555*6236dae4SAndroid Build Coastguard Worker pkt[i++] = (topiclen & 0xff);
556*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[i], topic, topiclen);
557*6236dae4SAndroid Build Coastguard Worker i += topiclen;
558*6236dae4SAndroid Build Coastguard Worker memcpy(&pkt[i], payload, payloadlen);
559*6236dae4SAndroid Build Coastguard Worker i += payloadlen;
560*6236dae4SAndroid Build Coastguard Worker result = mqtt_send(data, (char *)pkt, i);
561*6236dae4SAndroid Build Coastguard Worker
562*6236dae4SAndroid Build Coastguard Worker fail:
563*6236dae4SAndroid Build Coastguard Worker free(pkt);
564*6236dae4SAndroid Build Coastguard Worker free(topic);
565*6236dae4SAndroid Build Coastguard Worker return result;
566*6236dae4SAndroid Build Coastguard Worker }
567*6236dae4SAndroid Build Coastguard Worker
mqtt_decode_len(unsigned char * buf,size_t buflen,size_t * lenbytes)568*6236dae4SAndroid Build Coastguard Worker static size_t mqtt_decode_len(unsigned char *buf,
569*6236dae4SAndroid Build Coastguard Worker size_t buflen, size_t *lenbytes)
570*6236dae4SAndroid Build Coastguard Worker {
571*6236dae4SAndroid Build Coastguard Worker size_t len = 0;
572*6236dae4SAndroid Build Coastguard Worker size_t mult = 1;
573*6236dae4SAndroid Build Coastguard Worker size_t i;
574*6236dae4SAndroid Build Coastguard Worker unsigned char encoded = 128;
575*6236dae4SAndroid Build Coastguard Worker
576*6236dae4SAndroid Build Coastguard Worker for(i = 0; (i < buflen) && (encoded & 128); i++) {
577*6236dae4SAndroid Build Coastguard Worker encoded = buf[i];
578*6236dae4SAndroid Build Coastguard Worker len += (encoded & 127) * mult;
579*6236dae4SAndroid Build Coastguard Worker mult *= 128;
580*6236dae4SAndroid Build Coastguard Worker }
581*6236dae4SAndroid Build Coastguard Worker
582*6236dae4SAndroid Build Coastguard Worker if(lenbytes)
583*6236dae4SAndroid Build Coastguard Worker *lenbytes = i;
584*6236dae4SAndroid Build Coastguard Worker
585*6236dae4SAndroid Build Coastguard Worker return len;
586*6236dae4SAndroid Build Coastguard Worker }
587*6236dae4SAndroid Build Coastguard Worker
588*6236dae4SAndroid Build Coastguard Worker #ifdef DEBUGBUILD
589*6236dae4SAndroid Build Coastguard Worker static const char *statenames[]={
590*6236dae4SAndroid Build Coastguard Worker "MQTT_FIRST",
591*6236dae4SAndroid Build Coastguard Worker "MQTT_REMAINING_LENGTH",
592*6236dae4SAndroid Build Coastguard Worker "MQTT_CONNACK",
593*6236dae4SAndroid Build Coastguard Worker "MQTT_SUBACK",
594*6236dae4SAndroid Build Coastguard Worker "MQTT_SUBACK_COMING",
595*6236dae4SAndroid Build Coastguard Worker "MQTT_PUBWAIT",
596*6236dae4SAndroid Build Coastguard Worker "MQTT_PUB_REMAIN",
597*6236dae4SAndroid Build Coastguard Worker
598*6236dae4SAndroid Build Coastguard Worker "NOT A STATE"
599*6236dae4SAndroid Build Coastguard Worker };
600*6236dae4SAndroid Build Coastguard Worker #endif
601*6236dae4SAndroid Build Coastguard Worker
602*6236dae4SAndroid Build Coastguard Worker /* The only way to change state */
mqstate(struct Curl_easy * data,enum mqttstate state,enum mqttstate nextstate)603*6236dae4SAndroid Build Coastguard Worker static void mqstate(struct Curl_easy *data,
604*6236dae4SAndroid Build Coastguard Worker enum mqttstate state,
605*6236dae4SAndroid Build Coastguard Worker enum mqttstate nextstate) /* used if state == FIRST */
606*6236dae4SAndroid Build Coastguard Worker {
607*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
608*6236dae4SAndroid Build Coastguard Worker struct mqtt_conn *mqtt = &conn->proto.mqtt;
609*6236dae4SAndroid Build Coastguard Worker #ifdef DEBUGBUILD
610*6236dae4SAndroid Build Coastguard Worker infof(data, "%s (from %s) (next is %s)",
611*6236dae4SAndroid Build Coastguard Worker statenames[state],
612*6236dae4SAndroid Build Coastguard Worker statenames[mqtt->state],
613*6236dae4SAndroid Build Coastguard Worker (state == MQTT_FIRST) ? statenames[nextstate] : "");
614*6236dae4SAndroid Build Coastguard Worker #endif
615*6236dae4SAndroid Build Coastguard Worker mqtt->state = state;
616*6236dae4SAndroid Build Coastguard Worker if(state == MQTT_FIRST)
617*6236dae4SAndroid Build Coastguard Worker mqtt->nextstate = nextstate;
618*6236dae4SAndroid Build Coastguard Worker }
619*6236dae4SAndroid Build Coastguard Worker
620*6236dae4SAndroid Build Coastguard Worker
mqtt_read_publish(struct Curl_easy * data,bool * done)621*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
622*6236dae4SAndroid Build Coastguard Worker {
623*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
624*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
625*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
626*6236dae4SAndroid Build Coastguard Worker size_t remlen;
627*6236dae4SAndroid Build Coastguard Worker struct mqtt_conn *mqtt = &conn->proto.mqtt;
628*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
629*6236dae4SAndroid Build Coastguard Worker unsigned char packet;
630*6236dae4SAndroid Build Coastguard Worker
631*6236dae4SAndroid Build Coastguard Worker switch(mqtt->state) {
632*6236dae4SAndroid Build Coastguard Worker MQTT_SUBACK_COMING:
633*6236dae4SAndroid Build Coastguard Worker case MQTT_SUBACK_COMING:
634*6236dae4SAndroid Build Coastguard Worker result = mqtt_verify_suback(data);
635*6236dae4SAndroid Build Coastguard Worker if(result)
636*6236dae4SAndroid Build Coastguard Worker break;
637*6236dae4SAndroid Build Coastguard Worker
638*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
639*6236dae4SAndroid Build Coastguard Worker break;
640*6236dae4SAndroid Build Coastguard Worker
641*6236dae4SAndroid Build Coastguard Worker case MQTT_SUBACK:
642*6236dae4SAndroid Build Coastguard Worker case MQTT_PUBWAIT:
643*6236dae4SAndroid Build Coastguard Worker /* we are expecting PUBLISH or SUBACK */
644*6236dae4SAndroid Build Coastguard Worker packet = mq->firstbyte & 0xf0;
645*6236dae4SAndroid Build Coastguard Worker if(packet == MQTT_MSG_PUBLISH)
646*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_PUB_REMAIN, MQTT_NOSTATE);
647*6236dae4SAndroid Build Coastguard Worker else if(packet == MQTT_MSG_SUBACK) {
648*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_SUBACK_COMING, MQTT_NOSTATE);
649*6236dae4SAndroid Build Coastguard Worker goto MQTT_SUBACK_COMING;
650*6236dae4SAndroid Build Coastguard Worker }
651*6236dae4SAndroid Build Coastguard Worker else if(packet == MQTT_MSG_DISCONNECT) {
652*6236dae4SAndroid Build Coastguard Worker infof(data, "Got DISCONNECT");
653*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
654*6236dae4SAndroid Build Coastguard Worker goto end;
655*6236dae4SAndroid Build Coastguard Worker }
656*6236dae4SAndroid Build Coastguard Worker else {
657*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
658*6236dae4SAndroid Build Coastguard Worker goto end;
659*6236dae4SAndroid Build Coastguard Worker }
660*6236dae4SAndroid Build Coastguard Worker
661*6236dae4SAndroid Build Coastguard Worker /* -- switched state -- */
662*6236dae4SAndroid Build Coastguard Worker remlen = mq->remaining_length;
663*6236dae4SAndroid Build Coastguard Worker infof(data, "Remaining length: %zu bytes", remlen);
664*6236dae4SAndroid Build Coastguard Worker if(data->set.max_filesize &&
665*6236dae4SAndroid Build Coastguard Worker (curl_off_t)remlen > data->set.max_filesize) {
666*6236dae4SAndroid Build Coastguard Worker failf(data, "Maximum file size exceeded");
667*6236dae4SAndroid Build Coastguard Worker result = CURLE_FILESIZE_EXCEEDED;
668*6236dae4SAndroid Build Coastguard Worker goto end;
669*6236dae4SAndroid Build Coastguard Worker }
670*6236dae4SAndroid Build Coastguard Worker Curl_pgrsSetDownloadSize(data, remlen);
671*6236dae4SAndroid Build Coastguard Worker data->req.bytecount = 0;
672*6236dae4SAndroid Build Coastguard Worker data->req.size = remlen;
673*6236dae4SAndroid Build Coastguard Worker mq->npacket = remlen; /* get this many bytes */
674*6236dae4SAndroid Build Coastguard Worker FALLTHROUGH();
675*6236dae4SAndroid Build Coastguard Worker case MQTT_PUB_REMAIN: {
676*6236dae4SAndroid Build Coastguard Worker /* read rest of packet, but no more. Cap to buffer size */
677*6236dae4SAndroid Build Coastguard Worker char buffer[4*1024];
678*6236dae4SAndroid Build Coastguard Worker size_t rest = mq->npacket;
679*6236dae4SAndroid Build Coastguard Worker if(rest > sizeof(buffer))
680*6236dae4SAndroid Build Coastguard Worker rest = sizeof(buffer);
681*6236dae4SAndroid Build Coastguard Worker result = Curl_xfer_recv(data, buffer, rest, &nread);
682*6236dae4SAndroid Build Coastguard Worker if(result) {
683*6236dae4SAndroid Build Coastguard Worker if(CURLE_AGAIN == result) {
684*6236dae4SAndroid Build Coastguard Worker infof(data, "EEEE AAAAGAIN");
685*6236dae4SAndroid Build Coastguard Worker }
686*6236dae4SAndroid Build Coastguard Worker goto end;
687*6236dae4SAndroid Build Coastguard Worker }
688*6236dae4SAndroid Build Coastguard Worker if(!nread) {
689*6236dae4SAndroid Build Coastguard Worker infof(data, "server disconnected");
690*6236dae4SAndroid Build Coastguard Worker result = CURLE_PARTIAL_FILE;
691*6236dae4SAndroid Build Coastguard Worker goto end;
692*6236dae4SAndroid Build Coastguard Worker }
693*6236dae4SAndroid Build Coastguard Worker
694*6236dae4SAndroid Build Coastguard Worker /* if QoS is set, message contains packet id */
695*6236dae4SAndroid Build Coastguard Worker result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
696*6236dae4SAndroid Build Coastguard Worker if(result)
697*6236dae4SAndroid Build Coastguard Worker goto end;
698*6236dae4SAndroid Build Coastguard Worker
699*6236dae4SAndroid Build Coastguard Worker mq->npacket -= nread;
700*6236dae4SAndroid Build Coastguard Worker if(!mq->npacket)
701*6236dae4SAndroid Build Coastguard Worker /* no more PUBLISH payload, back to subscribe wait state */
702*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
703*6236dae4SAndroid Build Coastguard Worker break;
704*6236dae4SAndroid Build Coastguard Worker }
705*6236dae4SAndroid Build Coastguard Worker default:
706*6236dae4SAndroid Build Coastguard Worker DEBUGASSERT(NULL); /* illegal state */
707*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
708*6236dae4SAndroid Build Coastguard Worker goto end;
709*6236dae4SAndroid Build Coastguard Worker }
710*6236dae4SAndroid Build Coastguard Worker end:
711*6236dae4SAndroid Build Coastguard Worker return result;
712*6236dae4SAndroid Build Coastguard Worker }
713*6236dae4SAndroid Build Coastguard Worker
mqtt_do(struct Curl_easy * data,bool * done)714*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
715*6236dae4SAndroid Build Coastguard Worker {
716*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
717*6236dae4SAndroid Build Coastguard Worker *done = FALSE; /* unconditionally */
718*6236dae4SAndroid Build Coastguard Worker
719*6236dae4SAndroid Build Coastguard Worker result = mqtt_connect(data);
720*6236dae4SAndroid Build Coastguard Worker if(result) {
721*6236dae4SAndroid Build Coastguard Worker failf(data, "Error %d sending MQTT CONNECT request", result);
722*6236dae4SAndroid Build Coastguard Worker return result;
723*6236dae4SAndroid Build Coastguard Worker }
724*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_FIRST, MQTT_CONNACK);
725*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
726*6236dae4SAndroid Build Coastguard Worker }
727*6236dae4SAndroid Build Coastguard Worker
mqtt_done(struct Curl_easy * data,CURLcode status,bool premature)728*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_done(struct Curl_easy *data,
729*6236dae4SAndroid Build Coastguard Worker CURLcode status, bool premature)
730*6236dae4SAndroid Build Coastguard Worker {
731*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
732*6236dae4SAndroid Build Coastguard Worker (void)status;
733*6236dae4SAndroid Build Coastguard Worker (void)premature;
734*6236dae4SAndroid Build Coastguard Worker Curl_safefree(mq->sendleftovers);
735*6236dae4SAndroid Build Coastguard Worker Curl_dyn_free(&mq->recvbuf);
736*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
737*6236dae4SAndroid Build Coastguard Worker }
738*6236dae4SAndroid Build Coastguard Worker
mqtt_doing(struct Curl_easy * data,bool * done)739*6236dae4SAndroid Build Coastguard Worker static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
740*6236dae4SAndroid Build Coastguard Worker {
741*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_OK;
742*6236dae4SAndroid Build Coastguard Worker struct connectdata *conn = data->conn;
743*6236dae4SAndroid Build Coastguard Worker struct mqtt_conn *mqtt = &conn->proto.mqtt;
744*6236dae4SAndroid Build Coastguard Worker struct MQTT *mq = data->req.p.mqtt;
745*6236dae4SAndroid Build Coastguard Worker ssize_t nread;
746*6236dae4SAndroid Build Coastguard Worker unsigned char recvbyte;
747*6236dae4SAndroid Build Coastguard Worker
748*6236dae4SAndroid Build Coastguard Worker *done = FALSE;
749*6236dae4SAndroid Build Coastguard Worker
750*6236dae4SAndroid Build Coastguard Worker if(mq->nsend) {
751*6236dae4SAndroid Build Coastguard Worker /* send the remainder of an outgoing packet */
752*6236dae4SAndroid Build Coastguard Worker char *ptr = mq->sendleftovers;
753*6236dae4SAndroid Build Coastguard Worker result = mqtt_send(data, mq->sendleftovers, mq->nsend);
754*6236dae4SAndroid Build Coastguard Worker free(ptr);
755*6236dae4SAndroid Build Coastguard Worker if(result)
756*6236dae4SAndroid Build Coastguard Worker return result;
757*6236dae4SAndroid Build Coastguard Worker }
758*6236dae4SAndroid Build Coastguard Worker
759*6236dae4SAndroid Build Coastguard Worker infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
760*6236dae4SAndroid Build Coastguard Worker switch(mqtt->state) {
761*6236dae4SAndroid Build Coastguard Worker case MQTT_FIRST:
762*6236dae4SAndroid Build Coastguard Worker /* Read the initial byte only */
763*6236dae4SAndroid Build Coastguard Worker result = Curl_xfer_recv(data, (char *)&mq->firstbyte, 1, &nread);
764*6236dae4SAndroid Build Coastguard Worker if(result)
765*6236dae4SAndroid Build Coastguard Worker break;
766*6236dae4SAndroid Build Coastguard Worker else if(!nread) {
767*6236dae4SAndroid Build Coastguard Worker failf(data, "Connection disconnected");
768*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
769*6236dae4SAndroid Build Coastguard Worker result = CURLE_RECV_ERROR;
770*6236dae4SAndroid Build Coastguard Worker break;
771*6236dae4SAndroid Build Coastguard Worker }
772*6236dae4SAndroid Build Coastguard Worker Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1);
773*6236dae4SAndroid Build Coastguard Worker /* remember the first byte */
774*6236dae4SAndroid Build Coastguard Worker mq->npacket = 0;
775*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
776*6236dae4SAndroid Build Coastguard Worker FALLTHROUGH();
777*6236dae4SAndroid Build Coastguard Worker case MQTT_REMAINING_LENGTH:
778*6236dae4SAndroid Build Coastguard Worker do {
779*6236dae4SAndroid Build Coastguard Worker result = Curl_xfer_recv(data, (char *)&recvbyte, 1, &nread);
780*6236dae4SAndroid Build Coastguard Worker if(result || !nread)
781*6236dae4SAndroid Build Coastguard Worker break;
782*6236dae4SAndroid Build Coastguard Worker Curl_debug(data, CURLINFO_HEADER_IN, (char *)&recvbyte, 1);
783*6236dae4SAndroid Build Coastguard Worker mq->pkt_hd[mq->npacket++] = recvbyte;
784*6236dae4SAndroid Build Coastguard Worker } while((recvbyte & 0x80) && (mq->npacket < 4));
785*6236dae4SAndroid Build Coastguard Worker if(!result && nread && (recvbyte & 0x80))
786*6236dae4SAndroid Build Coastguard Worker /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
787*6236dae4SAndroid Build Coastguard Worker 127 * 128^3 bytes. server tried to send more */
788*6236dae4SAndroid Build Coastguard Worker result = CURLE_WEIRD_SERVER_REPLY;
789*6236dae4SAndroid Build Coastguard Worker if(result)
790*6236dae4SAndroid Build Coastguard Worker break;
791*6236dae4SAndroid Build Coastguard Worker mq->remaining_length = mqtt_decode_len(mq->pkt_hd, mq->npacket, NULL);
792*6236dae4SAndroid Build Coastguard Worker mq->npacket = 0;
793*6236dae4SAndroid Build Coastguard Worker if(mq->remaining_length) {
794*6236dae4SAndroid Build Coastguard Worker mqstate(data, mqtt->nextstate, MQTT_NOSTATE);
795*6236dae4SAndroid Build Coastguard Worker break;
796*6236dae4SAndroid Build Coastguard Worker }
797*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_FIRST, MQTT_FIRST);
798*6236dae4SAndroid Build Coastguard Worker
799*6236dae4SAndroid Build Coastguard Worker if(mq->firstbyte == MQTT_MSG_DISCONNECT) {
800*6236dae4SAndroid Build Coastguard Worker infof(data, "Got DISCONNECT");
801*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
802*6236dae4SAndroid Build Coastguard Worker }
803*6236dae4SAndroid Build Coastguard Worker break;
804*6236dae4SAndroid Build Coastguard Worker case MQTT_CONNACK:
805*6236dae4SAndroid Build Coastguard Worker result = mqtt_verify_connack(data);
806*6236dae4SAndroid Build Coastguard Worker if(result)
807*6236dae4SAndroid Build Coastguard Worker break;
808*6236dae4SAndroid Build Coastguard Worker
809*6236dae4SAndroid Build Coastguard Worker if(data->state.httpreq == HTTPREQ_POST) {
810*6236dae4SAndroid Build Coastguard Worker result = mqtt_publish(data);
811*6236dae4SAndroid Build Coastguard Worker if(!result) {
812*6236dae4SAndroid Build Coastguard Worker result = mqtt_disconnect(data);
813*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
814*6236dae4SAndroid Build Coastguard Worker }
815*6236dae4SAndroid Build Coastguard Worker mqtt->nextstate = MQTT_FIRST;
816*6236dae4SAndroid Build Coastguard Worker }
817*6236dae4SAndroid Build Coastguard Worker else {
818*6236dae4SAndroid Build Coastguard Worker result = mqtt_subscribe(data);
819*6236dae4SAndroid Build Coastguard Worker if(!result) {
820*6236dae4SAndroid Build Coastguard Worker mqstate(data, MQTT_FIRST, MQTT_SUBACK);
821*6236dae4SAndroid Build Coastguard Worker }
822*6236dae4SAndroid Build Coastguard Worker }
823*6236dae4SAndroid Build Coastguard Worker break;
824*6236dae4SAndroid Build Coastguard Worker
825*6236dae4SAndroid Build Coastguard Worker case MQTT_SUBACK:
826*6236dae4SAndroid Build Coastguard Worker case MQTT_PUBWAIT:
827*6236dae4SAndroid Build Coastguard Worker case MQTT_PUB_REMAIN:
828*6236dae4SAndroid Build Coastguard Worker result = mqtt_read_publish(data, done);
829*6236dae4SAndroid Build Coastguard Worker break;
830*6236dae4SAndroid Build Coastguard Worker
831*6236dae4SAndroid Build Coastguard Worker default:
832*6236dae4SAndroid Build Coastguard Worker failf(data, "State not handled yet");
833*6236dae4SAndroid Build Coastguard Worker *done = TRUE;
834*6236dae4SAndroid Build Coastguard Worker break;
835*6236dae4SAndroid Build Coastguard Worker }
836*6236dae4SAndroid Build Coastguard Worker
837*6236dae4SAndroid Build Coastguard Worker if(result == CURLE_AGAIN)
838*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
839*6236dae4SAndroid Build Coastguard Worker return result;
840*6236dae4SAndroid Build Coastguard Worker }
841*6236dae4SAndroid Build Coastguard Worker
842*6236dae4SAndroid Build Coastguard Worker #endif /* CURL_DISABLE_MQTT */
843