ws

ws.git
git clone git://git.lenczewski.org/ws.git
Log | Files | Refs | LICENSE

ws.h (6283B)


      1 #ifndef WS_H
      2 #define WS_H
      3 
      4 #include <openssl/evp.h>
      5 #include <openssl/sha.h>
      6 
      7 #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
      8 
      9 #define WS_KEY_LENGTH ((SHA_DIGEST_LENGTH * 4) / 3)
     10 
     11 static inline int
     12 ws_key_digest(unsigned char *buf, size_t len, unsigned char out[static WS_KEY_LENGTH])
     13 {
     14 	unsigned char concat[128], sha1[SHA_DIGEST_LENGTH];
     15 	size_t concat_len = snprintf((char *) concat, sizeof concat, "%.*s%s",
     16 			(int) len, buf, WS_GUID);
     17 	SHA1(concat, concat_len, sha1);
     18 	return EVP_EncodeBlock(out, sha1, sizeof sha1);
     19 }
     20 
     21 static inline uint16_t
     22 ws_hton16(uint16_t v)
     23 {
     24 	return htons(v);
     25 }
     26 
     27 static inline uint32_t
     28 ws_hton32(uint32_t v)
     29 {
     30 	return htonl(v);
     31 }
     32 
     33 static inline uint64_t
     34 ws_hton64(uint64_t v)
     35 {
     36 	if (1 == htons(1)) return v;
     37 
     38 	uint32_t lower = (v & 0xffffffff), upper = (v >> 32);
     39 	uint64_t res = (((uint64_t) htonl(lower)) << 32) | htonl(upper);
     40 	return res;
     41 }
     42 
     43 static inline uint16_t
     44 ws_ntoh16(uint16_t v)
     45 {
     46 	return ntohs(v);
     47 }
     48 
     49 static inline uint32_t
     50 ws_ntoh32(uint32_t v)
     51 {
     52 	return ntohl(v);
     53 }
     54 
     55 static inline uint64_t
     56 ws_ntoh64(uint64_t v)
     57 {
     58 	if (1 == htons(1)) return v;
     59 
     60 	uint32_t lower = (v & 0xffffffff), upper = (v >> 32);
     61 	uint64_t res = (((uint64_t) ntohl(lower)) << 32) | htonl(upper);
     62 	return res;
     63 }
     64 
     65 enum ws_error {
     66 	/* 0 - 999 unused */
     67 
     68 	WS_ERROR_OK			= 1000,
     69 	WS_ERROR_GOING_AWAY		= 1001,
     70 	WS_ERROR_PROTOCOL_ERROR		= 1002,
     71 	WS_ERROR_UNSUPPORTED_DATA	= 1003,
     72 
     73 	/* 1004 reserved */
     74 
     75 	WS_ERROR_NO_CODE_RECIEVED	= 1005,
     76 	WS_ERROR_CLOSED_ABNORMALLY	= 1006,
     77 
     78 	WS_ERROR_INVALID_PAYLOAD_DATA	= 1007,
     79 	WS_ERROR_POLICY_VIOLATED	= 1008,
     80 	WS_ERROR_MESSAGE_TOO_BIG	= 1009,
     81 	WS_ERROR_UNSUPPORTED_EXTENSION	= 1010,
     82 	WS_ERROR_INTERNAL_SERVER_ERROR	= 1011,
     83 
     84 	/* 1012 - 1014 reserved */
     85 
     86 	WS_ERROR_TLS_HANDSHAKE_FAILURE	= 1015,
     87 
     88 	/* 3000 - 3999 reserved */
     89 
     90 	/* 4000 - 4999 reserved for application */
     91 };
     92 
     93 enum ws_opcode {
     94 	WS_CONT		= 0x0,
     95 	WS_DATA_UTF8	= 0x1,
     96 	WS_DATA_BINARY	= 0x2,
     97 
     98 	/* 0x3 - 0x7 reserved */
     99 
    100 	WS_CLOSE	= 0x8,
    101 	WS_PING		= 0x9,
    102 	WS_PONG		= 0xa,
    103 
    104 	/* 0xb - 0xf reserved */
    105 };
    106 
    107 struct ws_frame {
    108 	enum ws_opcode opcode;
    109 	uint8_t fin;
    110 	uint8_t res;
    111 	uint64_t len;
    112 	uint32_t mask;
    113 };
    114 
    115 static inline int
    116 ws_frame_recv(int sock, struct ws_frame *out)
    117 {
    118 	int res;
    119 
    120 	uint8_t hdr[8];
    121 
    122 	if ((res = recv(sock, hdr, 2, 0)) < 0)
    123 		return -1;
    124 
    125 	out->fin = (hdr[0] >> 7) & 0x1;
    126 	out->res = (hdr[0] >> 4) & 0x7;
    127 	out->opcode = hdr[0] & 0xf;
    128 
    129 	int masked = (hdr[1] >> 7), len = hdr[1] & 0x7f;
    130 
    131 	if (len == 127) {
    132 		if ((res = recv(sock, hdr, 8, 0)) < 0)
    133 			return -1;
    134 
    135 		uint64_t raw = *((uint64_t *) hdr);
    136 		out->len = ws_ntoh64(raw);
    137 	} else if (len == 126) {
    138 		if ((res = recv(sock, hdr, 2, 0)) < 0)
    139 			return -1;
    140 
    141 		uint16_t raw = *((uint16_t *) hdr);
    142 		out->len = ws_ntoh16(raw);
    143 	} else /* len < 126 */ {
    144 		out->len = len;
    145 	}
    146 
    147 	if (masked) {
    148 		if ((res = recv(sock, hdr, 4, 0)) < 0)
    149 			return -1;
    150 
    151 		uint32_t raw = *((uint32_t *) hdr);
    152 		out->mask = ws_ntoh32(raw);
    153 	} else {
    154 		out->mask = 0;
    155 	}
    156 
    157 	return 0;
    158 }
    159 
    160 static inline int
    161 ws_frame_send(int sock, struct ws_frame const *frame)
    162 {
    163 	int res;
    164 
    165 	/* serialise frame header */
    166 	uint8_t len;
    167 	if (frame->len < 126) {
    168 		len = frame->len;
    169 	} else if (frame->len <= UINT16_MAX) {
    170 		len = 126;
    171 	} else {
    172 		len = 127;
    173 	}
    174 
    175 	uint8_t ptr = 0;
    176 	uint8_t hdr[14];
    177 	hdr[ptr++] = ((!!(frame->fin)) << 7) | ((frame->res & 0x7) << 4) | (frame->opcode & 0xf);
    178 	hdr[ptr++] = ((!!(frame->mask)) << 7) | len;
    179 
    180 	if (len == 126) {
    181 		uint16_t raw = ws_hton16(frame->len);
    182 		memcpy(hdr + ptr, &raw, 2);
    183 		ptr += 2;
    184 	}
    185 
    186 	if (len == 127) {
    187 		uint64_t raw = ws_hton64(frame->len);
    188 		memcpy(hdr + ptr, &raw, 8);
    189 		ptr += 8;
    190 	}
    191 
    192 	if (frame->mask) {
    193 		uint32_t raw = ws_hton32(frame->mask);
    194 		memcpy(hdr + ptr, &raw, 4);
    195 		ptr += 4;
    196 	}
    197 
    198 	/* send frame header */
    199 	if ((res = send(sock, hdr, ptr, 0)) < 0)
    200 		return -1;
    201 
    202 	return 0;
    203 }
    204 
    205 static inline int
    206 ws_data_recv(int sock, struct ws_frame const *frame, unsigned char *buf, size_t cap)
    207 {
    208 	assert(frame->len <= cap);
    209 
    210 	int res;
    211 
    212 	uint64_t remaining = frame->len;
    213 
    214 	unsigned char payload[1024];
    215 	unsigned char key[4] = {
    216 		(frame->mask & 0xff000000) >> 24,
    217 		(frame->mask & 0x00ff0000) >> 16,
    218 		(frame->mask & 0x0000ff00) >> 8,
    219 		(frame->mask & 0x000000ff),
    220 	};
    221 
    222 	do {
    223 		size_t masked = (sizeof payload < remaining) ? sizeof payload : remaining;
    224 
    225 		/* receive masked payload */
    226 		size_t masked_recv = 0;
    227 		do {
    228 			if ((res = recv(sock, payload + masked_recv, masked - masked_recv, 0)) < 0)
    229 				return -1;
    230 			masked_recv += res;
    231 		} while (masked_recv < masked);
    232 
    233 		/* unmask payload */
    234 		for (size_t i = 0; i < masked; i++)
    235 			*buf++ = payload[i] ^ key[i % 4];
    236 
    237 		remaining -= masked;
    238 	} while (remaining);
    239 
    240 	return 0;
    241 }
    242 
    243 static inline int
    244 ws_data_send(int sock, struct ws_frame const *frame, unsigned char const *buf)
    245 {
    246 	int res;
    247 
    248 	/* send frame payload */
    249 	uint64_t remaining = frame->len;
    250 
    251 	unsigned char payload[1024];
    252 	unsigned char key[4] = {
    253 		(frame->mask & 0xff000000) >> 24,
    254 		(frame->mask & 0x00ff0000) >> 16,
    255 		(frame->mask & 0x0000ff00) >> 8,
    256 		(frame->mask & 0x000000ff),
    257 	};
    258 
    259 	do {
    260 		size_t masked = (sizeof payload < remaining) ? sizeof payload : remaining;
    261 
    262 		/* mask payload */
    263 		for (size_t i = 0; i < masked; i++)
    264 			payload[i] = *buf++ ^ key[i % 4];
    265 
    266 		/* send masked payload */
    267 		size_t masked_send = 0;
    268 		do {
    269 			if ((res = send(sock, payload + masked_send, masked - masked_send, 0)) < 0) return -1;
    270 			masked_send += res;
    271 		} while (masked_send < masked);
    272 
    273 		remaining -= masked;
    274 	} while (remaining);
    275 
    276 	return 0;
    277 }
    278 
    279 static inline int
    280 ws_msg_send(int sock, enum ws_opcode opcode, unsigned char *buf, size_t len, uint32_t mask, int fragment)
    281 {
    282 	struct ws_frame frame;
    283 	frame.opcode = opcode;
    284 	frame.fin = !fragment;
    285 	frame.res = 0;
    286 	frame.len = len;
    287 	frame.mask = mask;
    288 
    289 	if (ws_frame_send(sock, &frame) < 0)
    290 		return -1;
    291 
    292 	return ws_data_send(sock, &frame, buf);
    293 }
    294 
    295 static inline int
    296 ws_close(int sock, enum ws_error err, unsigned char *msg, size_t len, uint32_t mask)
    297 {
    298 	assert(len <= 123);
    299 
    300 	struct ws_frame frame;
    301 	frame.opcode = WS_CLOSE;
    302 	frame.fin = 1;
    303 	frame.res = 0;
    304 	frame.len = len;
    305 	frame.mask = mask;
    306 
    307 	if (ws_frame_send(sock, &frame) < 0)
    308 		return -1;
    309 
    310 	/* send optional error code */
    311 	int res;
    312 	uint16_t raw = ws_hton16(err);
    313 	if ((res = send(sock, &raw, sizeof raw, 0)) < 0)
    314 		return -1;
    315 
    316 	return ws_data_send(sock, &frame, msg);
    317 }
    318 
    319 #endif /* WS_H */