2 * Copyright 2016 IoT.bzh
3 * Author: José Bollo <jose.bollo@iot.bzh>
5 * Inspired by the work of
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
21 #include <microhttpd.h>
26 #include <openssl/sha.h>
30 #include "../include/local-def.h"
33 #include "afb-method.h"
36 static const char websocket_s[] = "websocket";
37 static const char sec_websocket_key_s[] = "Sec-WebSocket-Key";
38 static const char sec_websocket_version_s[] = "Sec-WebSocket-Version";
39 static const char sec_websocket_accept_s[] = "Sec-WebSocket-Accept";
40 static const char sec_websocket_protocol_s[] = "Sec-WebSocket-Protocol";
41 static const char websocket_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
47 struct MHD_Connection *connection;
51 static ssize_t afb_websock_writev(struct afb_websock *ws, const struct iovec *iov, int iovcnt)
55 rc = writev(ws->fd, iov, iovcnt);
56 } while(rc == -1 && errno == EINTR);
60 static ssize_t afb_websock_readv(struct afb_websock *ws, const struct iovec *iov, int iovcnt)
64 rc = readv(ws->fd, iov, iovcnt);
65 } while(rc == -1 && errno == EINTR);
69 static void afb_websock_disconnect(struct afb_websock *ws)
73 static void afb_websock_on_close(struct afb_websock *ws, uint16_t code, size_t size)
77 static void afb_websock_on_content(struct afb_websock *ws, int last, size_t size)
81 static struct websock_itf afb_websock_itf = {
82 .writev = (void*)afb_websock_writev,
83 .readv = (void*)afb_websock_readv,
84 .disconnect = (void*)afb_websock_disconnect,
88 .on_close = (void*)afb_websock_on_close,
89 .on_text = (void*)afb_websock_on_content,
90 .on_binary = (void*)afb_websock_on_content,
91 .on_continue = (void*)afb_websock_on_content
94 static void enc64(unsigned char *in, char *out)
96 static const char tob64[] =
97 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
98 "abcdefghijklmnopqrstuvwxyz"
100 out[0] = tob64[in[0] >> 2];
101 out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
102 out[2] = tob64[((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)];
103 out[3] = tob64[in[2] & 0x3f];
106 static void make_accept_value(const char *key, char result[29])
108 unsigned char md[SHA_DIGEST_LENGTH+1];
109 size_t len = strlen(key);
110 char *buffer = alloca(len + sizeof websocket_uuid - 1);
111 memcpy(buffer, key, len);
112 memcpy(buffer + len, websocket_uuid, sizeof websocket_uuid - 1);
113 SHA1((const unsigned char *)buffer, (unsigned long)(len + sizeof websocket_uuid - 1), md);
114 assert(SHA_DIGEST_LENGTH == 20);
116 enc64(&md[0], &result[0]);
117 enc64(&md[3], &result[4]);
118 enc64(&md[6], &result[8]);
119 enc64(&md[9], &result[12]);
120 enc64(&md[12], &result[16]);
121 enc64(&md[15], &result[20]);
122 enc64(&md[18], &result[24]);
127 static int handshake(struct afb_hreq *hreq, struct afb_websock **ws)
129 const char *connection, *upgrade, *key, *version, *protocols;
132 struct MHD_Response *response;
134 upgrade = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_UPGRADE);
135 if (upgrade == NULL || strcasecmp(upgrade, websocket_s))
138 connection = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONNECTION);
139 if (connection == NULL || strcasecmp (connection, MHD_HTTP_HEADER_UPGRADE))
142 if(hreq->method != afb_method_get || strcasecmp(hreq->version, MHD_HTTP_VERSION_1_1))
145 key = afb_hreq_get_header(hreq, sec_websocket_key_s);
146 version = afb_hreq_get_header(hreq, sec_websocket_version_s);
147 if (key == NULL || version == NULL)
150 vernum = atoi(version);
157 protocols = afb_hreq_get_header(hreq, sec_websocket_protocol_s);
160 response = MHD_create_response_from_data(0,NULL,0,0);
161 MHD_add_response_header (response, sec_websocket_version_s, "13");
162 MHD_queue_response (hreq->connection, MHD_HTTP_BAD_REQUEST, response);
163 MHD_destroy_response (response);
167 make_accept_value(key, acceptval);
168 response = MHD_create_response_from_data(0,NULL,0,0);
169 MHD_add_response_header (response, sec_websocket_accept_s, acceptval);
170 MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION, MHD_HTTP_HEADER_UPGRADE);
171 MHD_add_response_header (response, MHD_HTTP_HEADER_UPGRADE, websocket_s);
172 MHD_queue_response (hreq->connection, MHD_HTTP_SWITCHING_PROTOCOLS, response);
173 MHD_destroy_response (response);
178 int afb_websock_is_handshake(struct afb_hreq *hreq)
180 return handshake(hreq, NULL);
183 int afb_websock_open_if(struct afb_hreq *hreq, struct afb_websock **ws)
187 return handshake(hreq, ws);
190 int afb_websock_open(struct afb_hreq *hreq, struct afb_websock **ws)
192 int rc = afb_websock_open_if(hreq, ws);