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" \
48basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51coap_base64_encode_buffer(
const uint8_t *
string,
size_t len,
char *encoded,
52 const size_t max_encoded_len) {
56 if ((((len + 2) / 3 * 4) + 1) > max_encoded_len) {
62 for (i = 0; i < len - 2; i += 3) {
63 *p++ = basis_64[(
string[i] >> 2) & 0x3F];
64 *p++ = basis_64[((
string[i] & 0x3) << 4) |
65 ((int)(
string[i + 1] & 0xF0) >> 4)];
66 *p++ = basis_64[((
string[i + 1] & 0xF) << 2) |
67 ((int)(
string[i + 2] & 0xC0) >> 6)];
68 *p++ = basis_64[
string[i + 2] & 0x3F];
71 *p++ = basis_64[(
string[i] >> 2) & 0x3F];
73 *p++ = basis_64[((
string[i] & 0x3) << 4)];
76 *p++ = basis_64[((
string[i] & 0x3) << 4) |
77 ((int)(
string[i + 1] & 0xF0) >> 4)];
78 *p++ = basis_64[((
string[i + 1] & 0xF) << 2)];
88coap_base64_decode_buffer(
const char *bufcoded,
size_t *len, uint8_t *bufplain,
89 const size_t max_decoded_len) {
94 static const uint8_t pr2six[256] = {
96 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
97 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
98 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
99 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
100 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
101 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
102 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
103 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
104 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
105 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
106 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
107 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
108 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 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
114 bufin = (
const uint8_t *)bufcoded;
115 while (pr2six[*(bufin++)] <= 63);
116 nprbytes = (bufin - (
const unsigned char *) bufcoded) - 1;
117 nbytesdecoded = ((nprbytes + 3) / 4) * 3;
118 if ((nbytesdecoded - ((4 - nprbytes) & 3)) > max_decoded_len)
122 bufin = (
const uint8_t *)bufcoded;
124 while (nprbytes > 4) {
126 (uint8_t)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
128 (uint8_t)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
130 (uint8_t)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
137 *(bufout++) = (uint8_t)(pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
140 *(bufout++) = (uint8_t)(pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
143 *(bufout++) = (uint8_t)(pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
147 *len = nbytesdecoded - ((4 - nprbytes) & 3);
152coap_ws_log_header(
const coap_session_t *session,
const uint8_t *header) {
153#if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG
160 int extra_hdr_len = 2;
163 if (bytes_size == 127) {
165 }
else if (bytes_size == 126) {
171 for (i = 0; i < extra_hdr_len; i++) {
172 snprintf(&buf[i*3], 4,
" %02x", header[i]);
185 for (i = 0; i <
sizeof(session->ws->key); i++) {
186 snprintf(&buf[i*3], 4,
" %02x", session->ws->key[i]);
192coap_ws_mask_data(
coap_session_t *session, uint8_t *data,
size_t data_len) {
196 for (i = 0; i < data_len; i++) {
218 if (!session->ws->up) {
222 if (session->ws->sent_close)
226 if (datalen <= 125) {
228 }
else if (datalen <= 0xffff) {
230 ws_header[2] = (datalen >> 8) & 0xff;
231 ws_header[3] = datalen & 0xff;
235 ws_header[2] = ((uint64_t)datalen >> 56) & 0xff;
236 ws_header[3] = ((uint64_t)datalen >> 48) & 0xff;
237 ws_header[4] = ((uint64_t)datalen >> 40) & 0xff;
238 ws_header[5] = ((uint64_t)datalen >> 32) & 0xff;
239 ws_header[6] = (datalen >> 24) & 0xff;
240 ws_header[7] = (datalen >> 16) & 0xff;
241 ws_header[8] = (datalen >> 8) & 0xff;
242 ws_header[9] = datalen & 0xff;
250 memcpy(session->ws->mask_key, &ws_header[hdr_len], 4);
253 coap_ws_log_header(session, ws_header);
259 memcpy(wdata, ws_header, hdr_len);
260 memcpy(&wdata[hdr_len], data, datalen);
263 coap_ws_mask_data(session, &wdata[hdr_len], datalen);
272 if (ret == (ssize_t)(datalen + hdr_len))
283 char *cp = strchr((
char *)session->ws->http_hdr,
' ');
286 cp = strchr((
char *)session->ws->http_hdr,
'\t');
304 if (strcasecmp((
char *)ws->
http_hdr,
305 "GET /.well-known/coap HTTP/1.1") != 0) {
313 value = coap_ws_split_rd_header(session);
317 if (strcasecmp((
char *)ws->
http_hdr,
"Host:") == 0) {
323 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Upgrade:") == 0) {
328 if (strcasecmp(value,
"websocket") != 0) {
333 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Connection:") == 0) {
338 if (strcasecmp(value,
"Upgrade") != 0 &&
339 strcasecmp(value,
"keep-alive, Upgrade") != 0) {
344 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Key:") == 0) {
351 if (!coap_base64_decode_buffer(value, &len, ws->
key,
353 len !=
sizeof(ws->
key)) {
357 coap_ws_log_key(session);
359 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
364 if (strcasecmp(value,
"coap") != 0) {
369 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Version:") == 0) {
374 if (strcasecmp(value,
"13") != 0) {
383#define COAP_WS_KEY_EXT "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
386coap_ws_build_key_hash(
coap_session_t *session,
char *hash,
size_t max_hash_len) {
387 char buf[28 +
sizeof(COAP_WS_KEY_EXT)];
391 if (max_hash_len < 29)
393 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
396 if (strlen(buf) >= 28)
398 strcat(buf, COAP_WS_KEY_EXT);
399 info.s = (uint8_t *)buf;
400 info.length = strlen(buf);
404 if (!coap_base64_encode_buffer(hashed->
s, hashed->
length,
405 hash, max_hash_len)) {
419 value = coap_ws_split_rd_header(session);
421 if (strcmp((
char *)ws->
http_hdr,
"HTTP/1.1") != 0 ||
422 atoi(value) != 101) {
430 value = coap_ws_split_rd_header(session);
434 if (strcasecmp((
char *)ws->
http_hdr,
"Upgrade:") == 0) {
439 if (strcasecmp(value,
"websocket") != 0) {
444 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Connection:") == 0) {
449 if (strcasecmp(value,
"Upgrade") != 0) {
454 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Accept:") == 0) {
461 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
464 if (strcmp(hash, value) != 0) {
468 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
473 if (strcasecmp(value,
"coap") != 0) {
515 cp = strchr((
char *)ws->
http_hdr,
'\n');
527 if (!coap_ws_rd_http_header_server(session)) {
531 if (!coap_ws_rd_http_header_client(session)) {
575 ssize_t bytes_size = 0;
576 ssize_t extra_hdr_len = 0;
589 if (!session->ws->up) {
592 if (!coap_ws_rd_http_header(session)) {
593 snprintf(buf,
sizeof(buf),
"HTTP/1.1 400 Invalid request\r\n\r\n");
603 if (!session->ws->up)
609 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
612 snprintf(buf,
sizeof(buf), COAP_WS_RESPONSE, hash);
625 if (session->ws->hdr_ofs == 0)
630 if (!session->ws->all_hdr_in) {
632 &session->ws->rd_header[session->ws->hdr_ofs],
633 sizeof(session->ws->rd_header) - session->ws->hdr_ofs);
636 session->ws->hdr_ofs += (int)ret;
638 if (session->ws->hdr_ofs < 2)
644 session->ws->close_reason = 1002;
650 if (bytes_size == 127) {
652 }
else if (bytes_size == 126) {
656 memcpy(session->ws->mask_key, &session->ws->rd_header[2 + extra_hdr_len], 4);
659 if (session->ws->hdr_ofs < 2 + extra_hdr_len)
663 coap_ws_log_header(session, session->ws->rd_header);
668 session->ws->close_reason = 1003;
674 session->ws->recv_close = 1;
679 session->ws->all_hdr_in = 1;
682 if (bytes_size == 127) {
683 bytes_size = ((uint64_t)session->ws->rd_header[2] << 56) +
684 ((uint64_t)session->ws->rd_header[3] << 48) +
685 ((uint64_t)session->ws->rd_header[4] << 40) +
686 ((uint64_t)session->ws->rd_header[5] << 32) +
687 ((uint64_t)session->ws->rd_header[6] << 24) +
688 ((uint64_t)session->ws->rd_header[7] << 16) +
689 ((uint64_t)session->ws->rd_header[8] << 8) +
690 session->ws->rd_header[9];
691 }
else if (bytes_size == 126) {
692 bytes_size = ((uint16_t)session->ws->rd_header[2] << 8) +
693 session->ws->rd_header[3];
695 session->ws->data_size = bytes_size;
696 if ((
size_t)bytes_size > datalen) {
697 coap_log_err(
"coap_ws_read: packet size bigger than provided data space"
698 " (%zu > %zu)\n", bytes_size, datalen);
700 session->ws->close_reason = 1009;
708 ret = session->ws->hdr_ofs - 2 - extra_hdr_len;
710 assert(2 + extra_hdr_len < (ssize_t)
sizeof(session->ws->rd_header));
712 if (ret <= bytes_size) {
714 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], ret);
715 session->ws->data_ofs = ret;
716 if (ret == bytes_size) {
719 coap_ws_mask_data(session, data, bytes_size);
721 session->ws->all_hdr_in = 0;
722 session->ws->hdr_ofs = 0;
725 session->ws->close_reason = (data[0] << 8) + data[1];
728 session->ws->close_reason);
729 session->ws->recv_close = 1;
730 if (!session->ws->sent_close)
738 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], bytes_size);
739 session->ws->data_ofs = bytes_size;
742 coap_ws_mask_data(session, data, bytes_size);
745 memmove(session->ws->rd_header,
746 &session->ws->rd_header[2 + extra_hdr_len + bytes_size],
748 session->ws->all_hdr_in = 0;
749 session->ws->hdr_ofs = (int)(ret - bytes_size);
753 session->ws->data_ofs = 0;
759 &data[session->ws->data_ofs],
760 session->ws->data_size - session->ws->data_ofs);
763 session->ws->data_ofs += ret;
764 if (session->ws->data_ofs == session->ws->data_size) {
767 coap_ws_mask_data(session, data, session->ws->data_size);
769 session->ws->all_hdr_in = 0;
770 session->ws->hdr_ofs = 0;
771 session->ws->data_ofs = 0;
774 return session->ws->data_size;
778 session->ws->data_size, session->ws->data_ofs);
782#define COAP_WS_REQUEST \
783 "GET /.well-known/coap HTTP/1.1\r\n" \
785 "Upgrade: websocket\r\n" \
786 "Connection: Upgrade\r\n" \
787 "Sec-WebSocket-Key: %s\r\n" \
788 "Sec-WebSocket-Protocol: coap\r\n" \
789 "Sec-WebSocket-Version: 13\r\n" \
809 if (!session->ws_host) {
815 coap_ws_log_key(session);
816 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
817 base64,
sizeof(base64)))
826 if (strchr((
const char *)session->ws_host->s,
':')) {
828 snprintf(host,
sizeof(host),
"[%s]:%d", session->ws_host->s, port);
830 snprintf(host,
sizeof(host),
"[%s]", 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);
839 snprintf(buf,
sizeof(buf), COAP_WS_REQUEST, host, base64);
855 if (session->ws && session->ws->up) {
856#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
860 if (!session->ws->sent_close) {
871 memcpy(session->ws->mask_key, &ws_header[hdr_len], 4);
874 coap_ws_log_header(session, ws_header);
875 if (session->ws->close_reason == 0)
876 session->ws->close_reason = 1000;
878 ws_header[hdr_len] = session->ws->close_reason >> 8;
879 ws_header[hdr_len+1] = session->ws->close_reason & 0xff;
881 coap_ws_mask_data(session, &ws_header[hdr_len], 2);
883 session->ws->sent_close = 1;
886 session->ws->close_reason);
888 if (ret != hdr_len+2) {
892#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
901 FD_SET(session->
sock.
fd, &readfds);
904 result = select((
int)(session->
sock.
fd+1), &readfds, NULL, NULL, &tv);
908 }
else if (result > 0) {
921 if (!session | !ws_host)
925 if (!session->ws_host)
uint16_t coap_address_get_port(const coap_address_t *addr)
Returns the port from addr in host byte order.
@ COAP_NACK_WS_LAYER_FAILED
Library specific build wrapper for coap_internal.h.
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_prng_lkd(void *buf, size_t len)
Fills buf with len random bytes using the default pseudo random number generator.
int coap_handle_event_lkd(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.
@ 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_lkd(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...
int coap_tcp_is_supported(void)
Check whether TCP is available.
int coap_tls_is_supported(void)
Check whether TLS is available.
int coap_ws_is_supported(void)
Check whether WebSockets is available.
int coap_wss_is_supported(void)
Check whether Secure WebSockets is available.
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_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.
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 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 http_hdr[160]
(Partial) 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.