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 }