/* Copyright (c) 2021 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN #include #include #include #include "mosquitto_internal.h" #include "base64_mosq.h" #include "http_client.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" #include "picohttpparser.h" static int create_request_key(char **encoded) { uint8_t bytes[16]; util__random_bytes(bytes, sizeof(bytes)); return base64__encode(bytes, sizeof(bytes), encoded); } int http_c__context_init(struct mosquitto *context) { struct mosquitto__packet *packet; char *key; const char *path; context->transport = mosq_t_http; context->http_request = mosquitto__calloc(1, (size_t)context->wsd.http_header_size + 1); if(context->http_request == NULL){ return MOSQ_ERR_NOMEM; } if(create_request_key(&key)){ return MOSQ_ERR_UNKNOWN; } if(ws__create_accept_key(key, strlen(key), &context->wsd.accept_key)){ return MOSQ_ERR_UNKNOWN; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet) + 1024 + WS_PACKET_OFFSET); if(!packet) return MOSQ_ERR_NOMEM; path = context->wsd.http_path?context->wsd.http_path:"/mqtt"; packet->packet_length = (uint32_t )snprintf((char *)&packet->payload[WS_PACKET_OFFSET], 1024, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Protocol: mqtt\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n", path, context->host, key); SAFE_FREE(key); packet->packet_length += WS_PACKET_OFFSET; packet->to_process = packet->packet_length; context->http_request[0] = '\0'; return packet__queue(context, packet); } int http_c__context_cleanup(struct mosquitto *context) { SAFE_FREE(context->wsd.accept_key); mosquitto__FREE(context->http_request); return MOSQ_ERR_SUCCESS; } int http_c__read(struct mosquitto *mosq) { ssize_t read_length; enum mosquitto_client_state state; size_t hlen; int http_status; const char *http_msg; size_t http_msg_len; int http_minor_version; size_t http_header_count = 100; struct phr_header http_headers[100]; const char *client_key = NULL; size_t client_key_len = 0; size_t i; bool header_have_upgrade; bool header_have_connection; bool header_have_subprotocol; int rc = MOSQ_ERR_SUCCESS; if(!mosq){ return MOSQ_ERR_INVAL; } if(mosq->sock == INVALID_SOCKET){ return MOSQ_ERR_NO_CONN; } state = mosquitto__get_state(mosq); if(state == mosq_cs_connect_pending){ return MOSQ_ERR_SUCCESS; } hlen = strlen(mosq->http_request); read_length = net__read(mosq, &mosq->http_request[hlen], (size_t)mosq->wsd.http_header_size-hlen); if(read_length <= 0){ if(read_length == 0){ return MOSQ_ERR_CONN_LOST; /* EOF */ } #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } } } hlen += (size_t)read_length; mosq->http_request[hlen] = '\0'; read_length = phr_parse_response(mosq->http_request, hlen, &http_minor_version, &http_status, &http_msg, &http_msg_len, http_headers, &http_header_count, 0); if(read_length == -2){ // Partial read return MOSQ_ERR_SUCCESS; }else if(read_length == -1){ // Error return MOSQ_ERR_UNKNOWN; } if(http_status != 101){ mosquitto__FREE(mosq->http_request); /* FIXME Not supported - send 501 response */ return MOSQ_ERR_UNKNOWN; } header_have_upgrade = false; header_have_connection = false; header_have_subprotocol = false; for(i=0; iwsd.accept_key, client_key, client_key_len)){ // FIXME - 50x return MOSQ_ERR_UNKNOWN; } http_c__context_cleanup(mosq); ws__context_init(mosq); //* FIXME outgoing properites rc = send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL); if(rc){ packet__cleanup_all(mosq); net__socket_close(mosq); mosquitto__set_state(mosq, mosq_cs_new); } return rc; } #endif