websocket: initial (not integrated)
[src/app-framework-binder.git] / src / afb-websock.c
1 /*
2  * Copyright 2016 IoT.bzh
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Inspired by the work of 
6  *
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
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 #define _GNU_SOURCE
21 #include <microhttpd.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <sys/uio.h>
25
26 #include <openssl/sha.h>
27
28 #include "websock.h"
29
30 #include "../include/local-def.h"
31
32
33 #include "afb-method.h"
34 #include "afb-hreq.h"
35
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";
42
43
44 struct afb_websock
45 {
46         int fd;
47         struct MHD_Connection *connection;
48         struct websock *ws;
49 };
50
51 static ssize_t afb_websock_writev(struct afb_websock *ws, const struct iovec *iov, int iovcnt)
52 {
53         ssize_t rc;
54         do {
55                 rc = writev(ws->fd, iov, iovcnt);
56         } while(rc == -1 && errno == EINTR);
57         return rc;
58 }
59
60 static ssize_t afb_websock_readv(struct afb_websock *ws, const struct iovec *iov, int iovcnt)
61 {
62         ssize_t rc;
63         do {
64                 rc = readv(ws->fd, iov, iovcnt);
65         } while(rc == -1 && errno == EINTR);
66         return rc;
67 }
68
69 static void afb_websock_disconnect(struct afb_websock *ws)
70 {
71 }
72
73 static void afb_websock_on_close(struct afb_websock *ws, uint16_t code, size_t size)
74 {
75 }
76
77 static void afb_websock_on_content(struct afb_websock *ws, int last, size_t size)
78 {
79 }
80
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,
85
86         .on_ping = NULL,
87         .on_pong = NULL,
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
92 };
93
94 static void enc64(unsigned char *in, char *out)
95 {
96         static const char tob64[] =
97                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
98                 "abcdefghijklmnopqrstuvwxyz"
99                 "0123456789+/";
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];
104 }
105
106 static void make_accept_value(const char *key, char result[29])
107 {
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);
115         md[20] = 0;
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]);
123         result[27] = '=';
124         result[28] = 0;
125 }
126
127 static int handshake(struct afb_hreq *hreq, struct afb_websock **ws)
128 {
129         const char *connection, *upgrade, *key, *version, *protocols;
130         char acceptval[29];
131         int vernum;
132         struct MHD_Response *response;
133
134         upgrade = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_UPGRADE);
135         if (upgrade == NULL || strcasecmp(upgrade, websocket_s))
136                 return 0;
137
138         connection = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONNECTION);
139         if (connection == NULL || strcasecmp (connection, MHD_HTTP_HEADER_UPGRADE))
140                 return 0;
141
142         if(hreq->method != afb_method_get || strcasecmp(hreq->version, MHD_HTTP_VERSION_1_1))
143                 return 0;
144
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)
148                 return 0;
149
150         vernum = atoi(version);
151         if (vernum != 13)
152                 return 0;
153
154         if (*ws == NULL)
155                 return 1;
156
157         protocols = afb_hreq_get_header(hreq, sec_websocket_protocol_s);
158
159         if (vernum != 13) {
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);
164                 return 2;
165         }
166
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);
174
175         return 1;
176 }
177
178 int afb_websock_is_handshake(struct afb_hreq *hreq)
179 {
180         return handshake(hreq, NULL);
181 }
182
183 int afb_websock_open_if(struct afb_hreq *hreq, struct afb_websock **ws)
184 {
185         assert(*ws != NULL);
186         *ws = NULL;
187         return handshake(hreq, ws);
188 }
189
190 int afb_websock_open(struct afb_hreq *hreq, struct afb_websock **ws)
191 {
192         int rc = afb_websock_open_if(hreq, ws);
193         return rc ? rc : -1;
194 }
195