25#define strcasecmp _stricmp
26#define strncasecmp _strnicmp
29#define COAP_WS_RESPONSE \
30 "HTTP/1.1 101 Switching Protocols\r\n" \
31 "Upgrade: websocket\r\n" \
32 "Connection: Upgrade\r\n" \
33 "Sec-WebSocket-Accept: %s\r\n" \
34 "Sec-WebSocket-Protocol: coap\r\n" \
39#if defined(COAP_WITH_LIBOPENSSL) || defined(COAP_WITH_LIBGNUTLS) || defined(COAP_WITH_LIBMBEDTLS)
53basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
56coap_base64_encode_buffer(
const uint8_t *
string,
size_t len,
char *encoded,
57 const size_t max_encoded_len) {
61 if ((((len + 2) / 3 * 4) + 1) > max_encoded_len) {
67 for (i = 0; i < len - 2; i += 3) {
68 *p++ = basis_64[(
string[i] >> 2) & 0x3F];
69 *p++ = basis_64[((
string[i] & 0x3) << 4) |
70 ((int)(
string[i + 1] & 0xF0) >> 4)];
71 *p++ = basis_64[((
string[i + 1] & 0xF) << 2) |
72 ((int)(
string[i + 2] & 0xC0) >> 6)];
73 *p++ = basis_64[
string[i + 2] & 0x3F];
76 *p++ = basis_64[(
string[i] >> 2) & 0x3F];
78 *p++ = basis_64[((
string[i] & 0x3) << 4)];
81 *p++ = basis_64[((
string[i] & 0x3) << 4) |
82 ((int)(
string[i + 1] & 0xF0) >> 4)];
83 *p++ = basis_64[((
string[i + 1] & 0xF) << 2)];
93coap_base64_decode_buffer(
const char *bufcoded,
size_t *len, uint8_t *bufplain,
94 const size_t max_decoded_len) {
99 static const uint8_t pr2six[256] = {
101 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
102 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
103 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
104 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
105 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
106 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
107 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
108 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
109 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
110 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
111 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
112 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
113 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
114 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
115 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
116 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
119 bufin = (
const uint8_t *)bufcoded;
120 while (pr2six[*(bufin++)] <= 63);
121 nprbytes = (bufin - (
const unsigned char *) bufcoded) - 1;
122 nbytesdecoded = ((nprbytes + 3) / 4) * 3;
123 if ((nbytesdecoded - ((4 - nprbytes) & 3)) > max_decoded_len)
127 bufin = (
const uint8_t *)bufcoded;
129 while (nprbytes > 4) {
131 (uint8_t)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
133 (uint8_t)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
135 (uint8_t)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
142 *(bufout++) = (uint8_t)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
145 *(bufout++) = (uint8_t)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
148 *(bufout++) = (uint8_t)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
152 *len = nbytesdecoded - ((4 - nprbytes) & 3);
157coap_ws_log_header(
const coap_session_t *session,
const uint8_t *header) {
158#if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG
165 int extra_hdr_len = 2;
168 if (bytes_size == 127) {
170 }
else if (bytes_size == 126) {
176 for (i = 0; i < extra_hdr_len; i++) {
177 snprintf(&buf[i*3], 4,
" %02x", header[i]);
188 for (i = 0; i <
sizeof(session->ws->key); i++) {
189 snprintf(&buf[i*3], 4,
" %02x", session->ws->key[i]);
195coap_ws_mask_data(
coap_session_t *session, uint8_t *data,
size_t data_len) {
199 for (i = 0; i < data_len; i++) {
220 if (!session->ws->up) {
224 if (session->ws->sent_close)
228 if (datalen <= 125) {
230 }
else if (datalen <= 0xffff) {
232 ws_header[2] = (datalen >> 8) & 0xff;
233 ws_header[3] = datalen & 0xff;
237 ws_header[2] = ((uint64_t)datalen >> 56) & 0xff;
238 ws_header[3] = ((uint64_t)datalen >> 48) & 0xff;
239 ws_header[4] = ((uint64_t)datalen >> 40) & 0xff;
240 ws_header[5] = ((uint64_t)datalen >> 32) & 0xff;
241 ws_header[6] = (datalen >> 24) & 0xff;
242 ws_header[7] = (datalen >> 16) & 0xff;
243 ws_header[8] = (datalen >> 8) & 0xff;
244 ws_header[9] = datalen & 0xff;
252 memcpy(session->ws->mask_key, &ws_header[hdr_len], 4);
255 coap_ws_log_header(session, ws_header);
257 if (ret != hdr_len) {
268 session->ws->data_size = datalen;
269 memcpy(wdata, data, datalen);
270 coap_ws_mask_data(session, wdata, datalen);
279 if (ret == (ssize_t)datalen)
290 char *cp = strchr((
char *)session->ws->http_hdr,
' ');
293 cp = strchr((
char *)session->ws->http_hdr,
'\t');
311 if (strcasecmp((
char *)ws->
http_hdr,
312 "GET /.well-known/coap HTTP/1.1") != 0) {
320 value = coap_ws_split_rd_header(session);
324 if (strcasecmp((
char *)ws->
http_hdr,
"Host:") == 0) {
330 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Upgrade:") == 0) {
335 if (strcasecmp(value,
"websocket") != 0) {
340 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Connection:") == 0) {
345 if (strcasecmp(value,
"Upgrade") != 0) {
350 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Key:") == 0) {
357 if (!coap_base64_decode_buffer(value, &len, ws->
key,
359 len !=
sizeof(ws->
key)) {
363 coap_ws_log_key(session);
365 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
370 if (strcasecmp(value,
"coap") != 0) {
375 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Version:") == 0) {
380 if (strcasecmp(value,
"13") != 0) {
389#define COAP_WS_KEY_EXT "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
392coap_ws_build_key_hash(
coap_session_t *session,
char *hash,
size_t max_hash_len) {
393 char buf[28 +
sizeof(COAP_WS_KEY_EXT)];
397 if (max_hash_len < 29)
399 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
402 if (strlen(buf) >= 28)
404 strcat(buf, COAP_WS_KEY_EXT);
405 info.s = (uint8_t *)buf;
406 info.length = strlen(buf);
410 if (!coap_base64_encode_buffer(hashed->
s, hashed->
length,
411 hash, max_hash_len)) {
425 value = coap_ws_split_rd_header(session);
427 if (strcmp((
char *)ws->
http_hdr,
"HTTP/1.1") != 0 ||
428 atoi(value) != 101) {
436 value = coap_ws_split_rd_header(session);
440 if (strcasecmp((
char *)ws->
http_hdr,
"Upgrade:") == 0) {
445 if (strcasecmp(value,
"websocket") != 0) {
450 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Connection:") == 0) {
455 if (strcasecmp(value,
"Upgrade") != 0) {
460 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Accept:") == 0) {
467 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
470 if (strcmp(hash, value) != 0) {
474 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
479 if (strcasecmp(value,
"coap") != 0) {
521 cp = strchr((
char *)ws->
http_hdr,
'\n');
533 if (!coap_ws_rd_http_header_server(session)) {
537 if (!coap_ws_rd_http_header_client(session)) {
581 ssize_t bytes_size = 0;
582 ssize_t extra_hdr_len = 0;
595 if (!session->ws->up) {
598 if (!coap_ws_rd_http_header(session)) {
599 snprintf(buf,
sizeof(buf),
"HTTP/1.1 400 Invalid request\r\n\r\n");
609 if (!session->ws->up)
615 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
618 snprintf(buf,
sizeof(buf), COAP_WS_RESPONSE, hash);
631 if (session->ws->hdr_ofs == 0)
636 if (!session->ws->all_hdr_in) {
638 &session->ws->rd_header[session->ws->hdr_ofs],
639 sizeof(session->ws->rd_header) - session->ws->hdr_ofs);
642 session->ws->hdr_ofs += (int)ret;
644 if (session->ws->hdr_ofs < 2)
650 session->ws->close_reason = 1002;
656 if (bytes_size == 127) {
658 }
else if (bytes_size == 126) {
662 memcpy(session->ws->mask_key, &session->ws->rd_header[2 + extra_hdr_len], 4);
665 if (session->ws->hdr_ofs < 2 + extra_hdr_len)
669 coap_ws_log_header(session, session->ws->rd_header);
674 session->ws->close_reason = 1003;
680 session->ws->recv_close = 1;
685 session->ws->all_hdr_in = 1;
688 if (bytes_size == 127) {
689 bytes_size = ((uint64_t)session->ws->rd_header[2] << 56) +
690 ((uint64_t)session->ws->rd_header[3] << 48) +
691 ((uint64_t)session->ws->rd_header[4] << 40) +
692 ((uint64_t)session->ws->rd_header[5] << 32) +
693 ((uint64_t)session->ws->rd_header[6] << 24) +
694 ((uint64_t)session->ws->rd_header[7] << 16) +
695 ((uint64_t)session->ws->rd_header[8] << 8) +
696 session->ws->rd_header[9];
697 }
else if (bytes_size == 126) {
698 bytes_size = ((uint16_t)session->ws->rd_header[2] << 8) +
699 session->ws->rd_header[3];
701 session->ws->data_size = bytes_size;
702 if ((
size_t)bytes_size > datalen) {
703 coap_log_err(
"coap_ws_read: packet size bigger than provided data space"
704 " (%zu > %zu)\n", bytes_size, datalen);
706 session->ws->close_reason = 1009;
714 ret = session->ws->hdr_ofs - 2 - extra_hdr_len;
716 assert(2 + extra_hdr_len < (ssize_t)
sizeof(session->ws->rd_header));
718 if (ret <= bytes_size) {
720 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], ret);
721 session->ws->data_ofs = ret;
722 if (ret == bytes_size) {
725 coap_ws_mask_data(session, data, bytes_size);
727 session->ws->all_hdr_in = 0;
728 session->ws->hdr_ofs = 0;
731 session->ws->close_reason = (data[0] << 8) + data[1];
734 session->ws->close_reason);
735 session->ws->recv_close = 1;
736 if (!session->ws->sent_close)
744 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], bytes_size);
745 session->ws->data_ofs = bytes_size;
748 coap_ws_mask_data(session, data, bytes_size);
751 memmove(session->ws->rd_header,
752 &session->ws->rd_header[2 + extra_hdr_len + bytes_size],
754 session->ws->all_hdr_in = 0;
755 session->ws->hdr_ofs = (int)(ret - bytes_size);
759 session->ws->data_ofs = 0;
765 &data[session->ws->data_ofs],
766 session->ws->data_size - session->ws->data_ofs);
769 session->ws->data_ofs += ret;
770 if (session->ws->data_ofs == session->ws->data_size) {
773 coap_ws_mask_data(session, data, session->ws->data_size);
775 session->ws->all_hdr_in = 0;
776 session->ws->hdr_ofs = 0;
777 session->ws->data_ofs = 0;
780 return session->ws->data_size;
784 session->ws->data_size, session->ws->data_ofs);
788#define COAP_WS_REQUEST \
789 "GET /.well-known/coap HTTP/1.1\r\n" \
791 "Upgrade: websocket\r\n" \
792 "Connection: Upgrade\r\n" \
793 "Sec-WebSocket-Key: %s\r\n" \
794 "Sec-WebSocket-Protocol: coap\r\n" \
795 "Sec-WebSocket-Version: 13\r\n" \
815 if (!session->ws_host) {
820 coap_prng(session->ws->key,
sizeof(session->ws->key));
821 coap_ws_log_key(session);
822 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
823 base64,
sizeof(base64)))
832 if (strchr((
const char *)session->ws_host->s,
':')) {
834 snprintf(host,
sizeof(host),
"[%s]:%d", session->ws_host->s, port);
836 snprintf(host,
sizeof(host),
"[%s]", session->ws_host->s);
840 snprintf(host,
sizeof(host),
"%s:%d", session->ws_host->s, port);
842 snprintf(host,
sizeof(host),
"%s", session->ws_host->s);
845 snprintf(buf,
sizeof(buf), COAP_WS_REQUEST, host, base64);
861 if (session->ws && session->ws->up) {
864 if (!session->ws->sent_close) {
875 memcpy(session->ws->mask_key, &ws_header[hdr_len], 4);
878 coap_ws_log_header(session, ws_header);
879 if (session->ws->close_reason == 0)
880 session->ws->close_reason = 1000;
882 ws_header[hdr_len] = session->ws->close_reason >> 8;
883 ws_header[hdr_len+1] = session->ws->close_reason & 0xff;
885 coap_ws_mask_data(session, &ws_header[hdr_len], 2);
887 session->ws->sent_close = 1;
890 session->ws->close_reason);
892 if (ret != hdr_len+2) {
904 FD_SET(session->
sock.
fd, &readfds);
907 result = select((
int)(session->
sock.
fd+1), &readfds, NULL, NULL, &tv);
911 }
else if (result > 0) {
923 if (!session | !ws_host)
927 if (!session->ws_host)
uint16_t coap_address_get_port(const coap_address_t *addr)
Returns the port from addr in host byte order.
Pulls together all the internal only header files.
@ COAP_NACK_WS_LAYER_FAILED
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
int coap_tcp_is_supported(void)
Check whether TCP is available.
int coap_prng(void *buf, size_t len)
Fills buf with len random bytes using the default pseudo random number generator.
int coap_handle_event(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
int coap_crypto_hash(cose_alg_t alg, const coap_bin_const_t *data, coap_bin_const_t **hash)
Create a hash of the provided data.
int coap_tls_is_supported(void)
Check whether TLS is available.
@ COAP_EVENT_WS_CONNECTED
Triggered when the WebSockets layer is up.
@ COAP_EVENT_WS_CLOSED
Triggered when the WebSockets layer is closed.
@ COAP_EVENT_WS_PACKET_SIZE
Triggered when there is an oversize WebSockets packet.
#define coap_log_debug(...)
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log_info(...)
#define coap_log_err(...)
int coap_netif_available(coap_session_t *session)
Function interface to check whether netif for session is still available.
void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason)
Notify session that it has failed.
@ COAP_SESSION_TYPE_SERVER
server-side
@ COAP_SESSION_TYPE_CLIENT
client-side
@ COAP_SESSION_STATE_NONE
void coap_delete_bin_const(coap_bin_const_t *s)
Deletes the given const binary data and releases any memory allocated.
coap_str_const_t * coap_new_str_const(const uint8_t *data, size_t size)
Returns a new const string object with at least size+1 bytes storage allocated, and the provided data...
void coap_ws_establish(coap_session_t *session)
Layer function interface for layer below WebSockets accept/connect being established.
ssize_t coap_ws_write(coap_session_t *session, const uint8_t *data, size_t datalen)
Function interface for websockets data transmission.
void coap_ws_close(coap_session_t *session)
Layer function interface for WebSockets close for a session.
ssize_t coap_ws_read(coap_session_t *session, uint8_t *data, size_t datalen)
Function interface for websockets data receiving.
int coap_ws_is_supported(void)
Check whether WebSockets is available.
int coap_ws_set_host_request(coap_session_t *session, coap_str_const_t *ws_host)
Set the host for the HTTP Host: Header in the WebSockets Request.
int coap_wss_is_supported(void)
Check whether Secure WebSockets is available.
coap_address_t remote
remote address and port
CoAP binary data definition with const data.
size_t length
length of binary data
const uint8_t * s
read-only binary data
coap_layer_write_t l_write
coap_layer_establish_t l_establish
coap_layer_close_t l_close
Abstraction of virtual session that can be attached to coap_context_t (client) or coap_endpoint_t (se...
coap_socket_t sock
socket object for the session, if any
coap_session_state_t state
current state of relationship with peer
coap_addr_tuple_t addr_info
remote/local address info
coap_proto_t proto
protocol used
coap_session_type_t type
client or server side socket
coap_context_t * context
session's context
coap_layer_func_t lfunc[COAP_LAYER_LAST]
Layer functions to use.
CoAP string data definition with const data.
const uint8_t * s
read-only string data
size_t length
length of string
WebSockets session state.
uint8_t http_hdr[80]
(Partial) HTTP header
uint8_t up
WebSockets established.
uint8_t key[16]
Random, but agreed key value.
uint8_t seen_host
Seen Host: HTTP header (server)
uint8_t seen_ver
Seen version: HTTP header (server)
uint8_t seen_key
Seen Key: HTTP header.
uint8_t seen_conn
Seen Connection: HTTP header.
uint8_t seen_upg
Seen Upgrade: HTTP header.
uint32_t http_ofs
Current offset into http_hdr.
int hdr_ofs
Current offset into rd_header.
coap_session_type_t state
Client or Server.
uint8_t rd_header[COAP_MAX_FS]
(Partial) frame
uint8_t seen_proto
Seen Protocol: HTTP header.
uint8_t mask_key[4]
Masking key.
uint8_t seen_first
Seen first request/response HTTP header.