mdebug

cortex m series debugger
git clone http://frotz.net/git/mdebug.git
Log | Files | Refs | README | LICENSE

websocket.c (5181B)


      1 /* websocket.c
      2  *
      3  * Copyright 2015 Brian Swetland <swetland@frotz.net>
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <stdint.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 #include <fcntl.h>
     24 
     25 #include <sys/types.h>
     26 #include <sys/socket.h>
     27 #include <sys/uio.h>
     28 
     29 #include "sha1.h"
     30 #include "websocket.h"
     31 
     32 struct ws_server {
     33 	FILE *fp;
     34 	int fd;
     35 	ws_callback_t func;
     36 	void *cookie;
     37 };
     38 
     39 int base64_decode(uint8_t *data, uint32_t len, uint8_t *out);
     40 int base64_encode(uint8_t *data, uint32_t len, uint8_t *out);
     41 
     42 static const char *wsmagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
     43 
     44 #define F_FIN		0x80
     45 #define F_INVAL		0x70
     46 #define OP_NONE		0
     47 #define OP_TEXT		1
     48 #define OP_BINARY	2
     49 #define OP_CLOSE	8
     50 #define OP_PING		9
     51 #define OP_PONG		10
     52 
     53 #define F_MASKED	0x80
     54 #define LEN_16BIT	0x7E
     55 #define LEN_64BIT	0x7F
     56 
     57 static int ws_send(int fd, unsigned op, const void *data, size_t len);
     58 
     59 int ws_send_text(ws_server_t *ws, const void *data, size_t len) {
     60 	return ws_send(ws->fd, OP_TEXT, data, len);
     61 }
     62 
     63 int ws_send_binary(ws_server_t *ws, const void *data, size_t len) {
     64 	return ws_send(ws->fd, OP_BINARY, data, len);
     65 }
     66 
     67 static int _ws_handshake(ws_server_t *ws) {
     68 	FILE *fp = ws->fp;
     69 	unsigned check = 0;
     70 	char line[8192 + 128];
     71 	uint8_t digest[SHA_DIGEST_SIZE];
     72 	int n;
     73 
     74 	for (;;) {
     75 		if (fgets(line, sizeof(line), fp) == NULL) {
     76 			return -1;
     77 		}
     78 		if ((n = strlen(line)) == 0) {
     79 			return -1;
     80 		}
     81 		if (line[n-1] == '\n') {
     82 			line[n-1] = 0;
     83 			if ((n > 1) && (line[n-2] == '\r')) {
     84 				line[n-2] = 0;
     85 			}
     86 		}
     87 		if (line[0] == 0) {
     88 			//printf(">BREAK<\n");
     89 			break;
     90 		}
     91 		//printf(">HEADER: %s<\n", line);
     92 		if (!strcasecmp(line, "upgrade: websocket")) {
     93 			check |= 1;
     94 			continue;
     95 		}
     96 		if (!strcasecmp(line, "connection: upgrade")) {
     97 			// may be keep-alive, etc
     98 			check |= 2;
     99 			continue;
    100 		}
    101 		if (!strncasecmp(line, "sec-websocket-key: ", 19)) {
    102 			check |= 4;
    103 			strcat(line, wsmagic);
    104 			SHA(line + 19, strlen(line + 19), digest);
    105 		}
    106 		if (!strcasecmp(line, "sec-websocket-version: 13")) {
    107 			check |= 8;
    108 		}
    109 		// Host:
    110 		// Origin:
    111 		// Sec-WebSocket-Protocol:
    112 		// Sec-WebSocket-Extensions:
    113 	}
    114 	if ((check & 13) != 13) {
    115 		return -1;
    116 	}
    117 	n = base64_encode((void*) digest, SHA_DIGEST_SIZE, (void*) line);
    118 	line[n] = 0;
    119 	fprintf(fp,
    120 		"HTTP/1.1 101 Switching Protocols\r\n"
    121 		"Upgrade: websocket\r\n"
    122 		"Connection: Upgrade\r\n"
    123 		"Sec-Websocket-Accept: %s\r\n"
    124 		"\r\n", line
    125 	);
    126 	return 0;
    127 }
    128 
    129 int ws_process_messages(ws_server_t *ws) {
    130 	FILE *fp = ws->fp;
    131 	unsigned char hdr[2+2+4];
    132 	unsigned char msg[65536];
    133 	unsigned len;
    134 	for (;;) {
    135 		if (fread(hdr, 2, 1, fp) != 1) break;
    136 		// client must not set invalid flags
    137 		if (hdr[0] & F_INVAL) break;
    138 		// client must mask
    139 		if (!(hdr[1] & F_MASKED)) break;
    140 		// todo: fragments
    141 		if (!(hdr[0] & F_FIN)) break;
    142 		// todo: large packets
    143 		if ((hdr[1] & 0x7F) == LEN_64BIT) break;
    144 		if ((hdr[1] & 0x7F) == LEN_16BIT) {
    145 			if (fread(hdr + 2, 6, 1, fp) != 1) break;
    146 			len = (hdr[2] << 8) | hdr[3];
    147 		} else {
    148 			if (fread(hdr + 4, 4, 1, fp) != 1) break;
    149 			len = hdr[1] & 0x7F;
    150 		}
    151 #if 0
    152 		printf("<op %d len %d mask %02x%02x%02x%02x>\n",
    153 			hdr[0] & 15, len, hdr[4], hdr[5], hdr[6], hdr[7]);
    154 #endif
    155 		if (fread(msg, len, 1, fp) != 1) break;
    156 		// op?
    157 #if 0
    158 		unsigned i;
    159 		for (i = 0; i < len; i++) {
    160 			msg[i] ^= hdr[4 + (i & 3)];
    161 			printf("%02x ", msg[i]);
    162 		}
    163 		printf("\n");
    164 #endif
    165 		switch (hdr[0] & 15) {
    166 		case OP_PING:
    167 			break;
    168 		case OP_PONG:
    169 			break;
    170 		case OP_CLOSE:
    171 			// todo: reply
    172 			return 0;
    173 		case OP_TEXT:
    174 			ws->func(OP_TEXT, msg, len, ws->cookie);
    175 			break;
    176 		case OP_BINARY:
    177 			ws->func(OP_BINARY, msg, len, ws->cookie);
    178 			break;
    179 		default:
    180 			// invalid opcode
    181 			return 0;
    182 		}
    183 	}
    184 	return 0;
    185 }
    186 
    187 static int ws_send(int fd, unsigned op, const void *data, size_t len) {
    188 	unsigned char hdr[4];
    189 	struct iovec iov[2];
    190 	if (len > 65535) return -1;
    191 	hdr[0] = F_FIN | (op & 15);
    192 	if (len < 126) {
    193 		hdr[1] = len;
    194 		iov[0].iov_len = 2;
    195 	} else {
    196 		hdr[1] = LEN_16BIT;
    197 		hdr[2] = len >> 8;
    198 		hdr[3] = len;
    199 		iov[0].iov_len = 4;
    200 	}
    201 	iov[0].iov_base = hdr;
    202 	iov[1].iov_base = (void*) data;
    203 	iov[1].iov_len = len;
    204 	return writev(fd, iov, 2);
    205 }
    206 
    207 ws_server_t *ws_handshake(int fd, ws_callback_t func, void *cookie) {
    208 	ws_server_t *ws;
    209 
    210 	if ((ws = malloc(sizeof(ws_server_t))) == NULL) {
    211 		goto fail;
    212 	}
    213 	if ((ws->fp = fdopen(fd, "r+")) == NULL) {
    214 		goto fail;
    215 	}
    216 	if (_ws_handshake(ws)) {
    217 		goto fail;
    218 	}
    219 	ws->fd = fd;
    220 	ws->func = func;
    221 	ws->cookie = cookie;
    222 	return ws;
    223 fail:
    224 	if (ws != NULL) {
    225 		free(ws);
    226 		if (ws->fp) {
    227 			fclose(ws->fp);
    228 			return NULL;
    229 		}
    230 	}
    231 	close(fd);
    232 	return NULL;
    233 }
    234 
    235 void ws_close(ws_server_t *ws) {
    236 	fclose(ws->fp);
    237 	free(ws);
    238 }