diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b3e81b..731f503 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,4 +24,10 @@ add_executable( src/smol-http-pthreads.c ) -install(TARGETS smol smol-pthreads smol-http smol-http-pthreads DESTINATION bin) \ No newline at end of file +add_executable( + ws + src/ws.c +) +target_link_libraries(ws ssl crypto) + +install(TARGETS smol smol-pthreads smol-http smol-http-pthreads ws DESTINATION bin) \ No newline at end of file diff --git a/Makefile b/Makefile index 58b6efa..db4d0c5 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ default: all all: clean build -build: smol smol-pthreads smol-http smol-http-pthreads +build: smol smol-pthreads smol-http smol-http-pthreads ws smol: $(CC) -o $(BIN)/smol $(SRC)/smol.c -lcrypto -lssl $(CFLAGS) @@ -23,6 +23,9 @@ smol-http: smol-http-pthreads: $(CC) -o $(BIN)/smol-http-pthreads $(SRC)/smol-http-pthreads.c $(CFLAGS) +ws: + $(CC) -o $(BIN)/ws $(SRC)/ws.c $(CFLAGS) -lcrypto -lssl + clean: if [ -d $(BIN) ]; then rm -rv $(BIN);mkdir -pv $(BIN);else mkdir -pv $(BIN);fi @@ -32,6 +35,7 @@ install: $(STRIP) $(PREFIX)/bin/smol-pthreads $(STRIP) $(PREFIX)/bin/smol-http $(STRIP) $(PREFIX)/bin/smol-http-pthreads + $(STRIP) $(PREFIX)/bin/ws uninstall: remove @@ -39,4 +43,5 @@ remove: if [ -f $(PREFIX)/bin/smol ];then rm -v $(PREFIX)/bin/smol;fi if [ -f $(PREFIX)/bin/smol-pthreads ];then rm -v $(PREFIX)/bin/smol-pthreads;fi if [ -f $(PREFIX)/bin/smol-http ];then rm -v $(PREFIX)/bin/smol-http;fi - if [ -f $(PREFIX)/bin/smol-http-pthreads ];then rm -v $(PREFIX)/bin/smol-http-pthreads;fi \ No newline at end of file + if [ -f $(PREFIX)/bin/smol-http-pthreads ];then rm -v $(PREFIX)/bin/smol-http-pthreads;fi + if [ -f $(PREFIX)/bin/ws ];then rm -v $(PREFIX)/bin/ws;fi \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..519e41f --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Document + + +

+ + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..29599bc --- /dev/null +++ b/index.js @@ -0,0 +1,19 @@ +const uptime = document.querySelector('#time') +const ws = new WebSocket('ws://172.16.17.21:8080'); +ws.onopen = () => { + ws.send('Hello Server!'); +} + +ws.onmessage = (e) => { + console.log('Received:', e.data); + var jsondata = JSON.parse(e.data); + if (jsondata.uptime.hour === '0' && jsondata.uptime.minute === '0') { + uptime.innerHTML = jsondata.uptime.second + "s " + return + } + if (jsondata.uptime.hour === '0') { + uptime.innerHTML = jsondata.uptime.minute + "m " + jsondata.uptime.second + "s " + return + } + uptime.innerHTML = jsondata.uptime.hour + "h " + jsondata.uptime.minute + "m " + jsondata.uptime.second + "s " +} diff --git a/meson.build b/meson.build index e757653..c8a1fe4 100644 --- a/meson.build +++ b/meson.build @@ -24,4 +24,11 @@ executable( 'smol-http-pthreads', 'src/smol-http-pthreads.c', install: true, +) + +executable( + 'ws', + 'src/ws.c', + dependencies: deps, + install: true, ) \ No newline at end of file diff --git a/src/ws.c b/src/ws.c new file mode 100644 index 0000000..6da747c --- /dev/null +++ b/src/ws.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 8080 +#define BUFFER_SIZE 1024 + +char* base64_encode(const unsigned char* input, int length) { + BIO *bio, *b64; + BUF_MEM *bufferPtr; + + b64 = BIO_new(BIO_f_base64()); + bio = BIO_new(BIO_s_mem()); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + BIO_write(bio, input, length); + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bufferPtr); + + // Создаем копию данных, так как буфер BIO будет очищен + char *output = (char *)malloc(bufferPtr->length + 1); + memcpy(output, bufferPtr->data, bufferPtr->length); + output[bufferPtr->length] = '\0'; + + BIO_free_all(bio); + return output; +} + +char* compute_accept_key(const char* client_key) { + char combined[256]; + sprintf(combined, "%s%s", client_key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + unsigned char sha_result[SHA_DIGEST_LENGTH]; + SHA1((unsigned char*)combined, strlen(combined), sha_result); + return base64_encode(sha_result, SHA_DIGEST_LENGTH); +} + +int read_fully(int socket, unsigned char* buffer, int length) { + int total = 0; + while(total < length) { + int readed = read(socket, buffer + total, length - total); + if(readed <= 0) return readed; + total += readed; + } + return total; +} + +int main() { + int server_fd, client_socket; + struct sockaddr_in address; + int addrlen = sizeof(address); + + if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("socket failed"); + exit(EXIT_FAILURE); + } + + int opt = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + signal(SIGPIPE, SIG_IGN); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(PORT); + + if(bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + if(listen(server_fd, 3) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + printf("Server listening on port %d\n", PORT); + + while(1) { + if((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { + perror("accept"); + continue; + } + + printf("Client connected\n"); + char buffer[BUFFER_SIZE] = {0}; + int valread = read(client_socket, buffer, BUFFER_SIZE); + + if(valread < 0) { + perror("read"); + close(client_socket); + continue; + } + + if(strstr(buffer, "Upgrade: websocket") && strstr(buffer, "Connection: Upgrade")) { + char* key_start = strstr(buffer, "Sec-WebSocket-Key: "); + if(key_start) { + key_start += 19; + char* key_end = strstr(key_start, "\r\n"); + if(key_end) { + char client_key[256]; + int key_len = key_end - key_start; + strncpy(client_key, key_start, key_len); + client_key[key_len] = '\0'; + + char* accept_key = compute_accept_key(client_key); + + char response[512]; + sprintf(response, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n\r\n", + accept_key); + + if (send(client_socket, response, strlen(response), 0) <= 0) { + printf("send failed\n"); + free(accept_key); + continue; + } + free(accept_key); + + while(1) { + unsigned char header[2]; + if(read_fully(client_socket, header, 2) <= 0) break; + + int opcode = header[0] & 0x0F; + int mask = (header[1] >> 7) & 1; + int length = header[1] & 0x7F; + + if(length == 126) { + unsigned char ext_len[2]; + read_fully(client_socket, ext_len, 2); + length = (ext_len[0] << 8) | ext_len[1]; + } else if(length == 127) { + printf("Large frames not supported\n"); + break; + } + + unsigned char masking_key[4]; + if(mask) { + read_fully(client_socket, masking_key, 4); + } + + unsigned char* data = malloc(length + 1); + read_fully(client_socket, data, length); + + if(mask) { + for(int i = 0; i < length; i++) { + data[i] ^= masking_key[i % 4]; + } + } + data[length] = '\0'; + + if(opcode == 0x08) break; // Close frame + + printf("Received: %s\n", data); + + // Prepare response + while (1) { + char timebuffer[BUFSIZ]; + memset(&timebuffer, '\0', BUFSIZ); + struct sysinfo x; + if (sysinfo(&x) == 0) { + sprintf(timebuffer, + "{\"uptime\":{\"hour\":\"%llu\",\"minute\":\"%llu\",\"second\":\"%llu\"}}", + (unsigned long long)x.uptime / 3600ULL, (unsigned long long)x.uptime / 60ULL % 60ULL, (unsigned long long)x.uptime % 60ULL); + } + printf("%s\n", timebuffer); + unsigned char response_header[2] = {0x81, strlen(timebuffer)}; + if (send(client_socket, response_header, 2, 0) <= 0) { + printf("send failed\n"); + break; + } + if (send(client_socket, timebuffer, strlen(timebuffer), 0) <= 0) { + printf("send failed\n"); + break; + } + sleep(1); + } + free(data); + } + } + } + } + printf("Client disconnected\n"); + close(client_socket); + } + close(server_fd); + return 0; +} \ No newline at end of file