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) {
343 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Key:") == 0) {
350 if (!coap_base64_decode_buffer(value, &len, ws->
key,
352 len !=
sizeof(ws->
key)) {
356 coap_ws_log_key(session);
358 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
363 if (strcasecmp(value,
"coap") != 0) {
368 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Version:") == 0) {
373 if (strcasecmp(value,
"13") != 0) {
382#define COAP_WS_KEY_EXT "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
385coap_ws_build_key_hash(
coap_session_t *session,
char *hash,
size_t max_hash_len) {
386 char buf[28 +
sizeof(COAP_WS_KEY_EXT)];
390 if (max_hash_len < 29)
392 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
395 if (strlen(buf) >= 28)
397 strcat(buf, COAP_WS_KEY_EXT);
398 info.s = (uint8_t *)buf;
399 info.length = strlen(buf);
403 if (!coap_base64_encode_buffer(hashed->
s, hashed->
length,
404 hash, max_hash_len)) {
418 value = coap_ws_split_rd_header(session);
420 if (strcmp((
char *)ws->
http_hdr,
"HTTP/1.1") != 0 ||
421 atoi(value) != 101) {
429 value = coap_ws_split_rd_header(session);
433 if (strcasecmp((
char *)ws->
http_hdr,
"Upgrade:") == 0) {
438 if (strcasecmp(value,
"websocket") != 0) {
443 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Connection:") == 0) {
448 if (strcasecmp(value,
"Upgrade") != 0) {
453 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Accept:") == 0) {
460 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
463 if (strcmp(hash, value) != 0) {
467 }
else if (strcasecmp((
char *)ws->
http_hdr,
"Sec-WebSocket-Protocol:") == 0) {
472 if (strcasecmp(value,
"coap") != 0) {
514 cp = strchr((
char *)ws->
http_hdr,
'\n');
526 if (!coap_ws_rd_http_header_server(session)) {
530 if (!coap_ws_rd_http_header_client(session)) {
574 ssize_t bytes_size = 0;
575 ssize_t extra_hdr_len = 0;
588 if (!session->ws->up) {
591 if (!coap_ws_rd_http_header(session)) {
592 snprintf(buf,
sizeof(buf),
"HTTP/1.1 400 Invalid request\r\n\r\n");
602 if (!session->ws->up)
608 if (!coap_ws_build_key_hash(session, hash,
sizeof(hash))) {
611 snprintf(buf,
sizeof(buf), COAP_WS_RESPONSE, hash);
624 if (session->ws->hdr_ofs == 0)
629 if (!session->ws->all_hdr_in) {
631 &session->ws->rd_header[session->ws->hdr_ofs],
632 sizeof(session->ws->rd_header) - session->ws->hdr_ofs);
635 session->ws->hdr_ofs += (int)ret;
637 if (session->ws->hdr_ofs < 2)
643 session->ws->close_reason = 1002;
649 if (bytes_size == 127) {
651 }
else if (bytes_size == 126) {
655 memcpy(session->ws->mask_key, &session->ws->rd_header[2 + extra_hdr_len], 4);
658 if (session->ws->hdr_ofs < 2 + extra_hdr_len)
662 coap_ws_log_header(session, session->ws->rd_header);
667 session->ws->close_reason = 1003;
673 session->ws->recv_close = 1;
678 session->ws->all_hdr_in = 1;
681 if (bytes_size == 127) {
682 bytes_size = ((uint64_t)session->ws->rd_header[2] << 56) +
683 ((uint64_t)session->ws->rd_header[3] << 48) +
684 ((uint64_t)session->ws->rd_header[4] << 40) +
685 ((uint64_t)session->ws->rd_header[5] << 32) +
686 ((uint64_t)session->ws->rd_header[6] << 24) +
687 ((uint64_t)session->ws->rd_header[7] << 16) +
688 ((uint64_t)session->ws->rd_header[8] << 8) +
689 session->ws->rd_header[9];
690 }
else if (bytes_size == 126) {
691 bytes_size = ((uint16_t)session->ws->rd_header[2] << 8) +
692 session->ws->rd_header[3];
694 session->ws->data_size = bytes_size;
695 if ((
size_t)bytes_size > datalen) {
696 coap_log_err(
"coap_ws_read: packet size bigger than provided data space"
697 " (%zu > %zu)\n", bytes_size, datalen);
699 session->ws->close_reason = 1009;
707 ret = session->ws->hdr_ofs - 2 - extra_hdr_len;
709 assert(2 + extra_hdr_len < (ssize_t)
sizeof(session->ws->rd_header));
711 if (ret <= bytes_size) {
713 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], ret);
714 session->ws->data_ofs = ret;
715 if (ret == bytes_size) {
718 coap_ws_mask_data(session, data, bytes_size);
720 session->ws->all_hdr_in = 0;
721 session->ws->hdr_ofs = 0;
724 session->ws->close_reason = (data[0] << 8) + data[1];
727 session->ws->close_reason);
728 session->ws->recv_close = 1;
729 if (!session->ws->sent_close)
737 memcpy(data, &session->ws->rd_header[2 + extra_hdr_len], bytes_size);
738 session->ws->data_ofs = bytes_size;
741 coap_ws_mask_data(session, data, bytes_size);
744 memmove(session->ws->rd_header,
745 &session->ws->rd_header[2 + extra_hdr_len + bytes_size],
747 session->ws->all_hdr_in = 0;
748 session->ws->hdr_ofs = (int)(ret - bytes_size);
752 session->ws->data_ofs = 0;
758 &data[session->ws->data_ofs],
759 session->ws->data_size - session->ws->data_ofs);
762 session->ws->data_ofs += ret;
763 if (session->ws->data_ofs == session->ws->data_size) {
766 coap_ws_mask_data(session, data, session->ws->data_size);
768 session->ws->all_hdr_in = 0;
769 session->ws->hdr_ofs = 0;
770 session->ws->data_ofs = 0;
773 return session->ws->data_size;
777 session->ws->data_size, session->ws->data_ofs);
781#define COAP_WS_REQUEST \
782 "GET /.well-known/coap HTTP/1.1\r\n" \
784 "Upgrade: websocket\r\n" \
785 "Connection: Upgrade\r\n" \
786 "Sec-WebSocket-Key: %s\r\n" \
787 "Sec-WebSocket-Protocol: coap\r\n" \
788 "Sec-WebSocket-Version: 13\r\n" \
808 if (!session->ws_host) {
813 coap_prng(session->ws->key,
sizeof(session->ws->key));
814 coap_ws_log_key(session);
815 if (!coap_base64_encode_buffer(session->ws->key,
sizeof(session->ws->key),
816 base64,
sizeof(base64)))
825 if (strchr((
const char *)session->ws_host->s,
':')) {
827 snprintf(host,
sizeof(host),
"[%s]:%d", session->ws_host->s, port);
829 snprintf(host,
sizeof(host),
"[%s]", session->ws_host->s);
833 snprintf(host,
sizeof(host),
"%s:%d", session->ws_host->s, port);
835 snprintf(host,
sizeof(host),
"%s", session->ws_host->s);
838 snprintf(buf,
sizeof(buf), COAP_WS_REQUEST, host, base64);
854 if (session->ws && session->ws->up) {
855#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
859 if (!session->ws->sent_close) {
870 memcpy(session->ws->mask_key, &ws_header[hdr_len], 4);
873 coap_ws_log_header(session, ws_header);
874 if (session->ws->close_reason == 0)
875 session->ws->close_reason = 1000;
877 ws_header[hdr_len] = session->ws->close_reason >> 8;
878 ws_header[hdr_len+1] = session->ws->close_reason & 0xff;
880 coap_ws_mask_data(session, &ws_header[hdr_len], 2);
882 session->ws->sent_close = 1;
885 session->ws->close_reason);
887 if (ret != hdr_len+2) {
891#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
900 FD_SET(session->
sock.
fd, &readfds);
903 result = select((
int)(session->
sock.
fd+1), &readfds, NULL, NULL, &tv);
907 }
else if (result > 0) {
920 if (!session | !ws_host)
924 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_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_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.
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_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...
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.