Created
February 20, 2025 21:05
-
-
Save aneury1/5e0feea132145894cd5dc58dfa1a2a1d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <openssl/sha.h> // For SHA-1 (link with -lcrypto) | |
#include <openssl/bio.h> // For Base64 encoding | |
#include <openssl/evp.h> | |
#include <openssl/buffer.h> | |
#define PORT 8080 | |
#define BUFFER_SIZE 1024 | |
#define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | |
// Compute Sec-WebSocket-Accept header value (unchanged from previous example) | |
char* compute_accept_key(const char* client_key) { | |
char combined[100]; | |
snprintf(combined, sizeof(combined), "%s%s", client_key, MAGIC_STRING); | |
unsigned char sha1[SHA_DIGEST_LENGTH]; | |
SHA1((unsigned char*)combined, strlen(combined), sha1); | |
BIO* b64 = BIO_new(BIO_f_base64()); | |
BIO* mem = BIO_new(BIO_s_mem()); | |
b64 = BIO_push(b64, mem); | |
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); | |
BIO_write(b64, sha1, SHA_DIGEST_LENGTH); | |
BIO_flush(b64); | |
BUF_MEM* buffer; | |
BIO_get_mem_ptr(b64, &buffer); | |
char* accept_key = (char *)malloc(buffer->length + 1); | |
memcpy(accept_key, buffer->data, buffer->length); | |
accept_key[buffer->length] = '\0'; | |
BIO_free_all(b64); | |
return accept_key; | |
} | |
// Send HTTP handshake response (unchanged) | |
void send_handshake(int client_fd, const char* client_key) { | |
char* accept_key = compute_accept_key(client_key); | |
char response[BUFFER_SIZE]; | |
snprintf(response, sizeof(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); | |
write(client_fd, response, strlen(response)); | |
free(accept_key); | |
} | |
// Extract Sec-WebSocket-Key from HTTP request (unchanged) | |
char* extract_key(const char* request) { | |
const char* key_start = strstr(request, "Sec-WebSocket-Key: "); | |
if (!key_start) return NULL; | |
key_start += 19; | |
char* key_end = (char *)strstr(key_start, "\r\n"); | |
if (!key_end) return NULL; | |
int key_len = key_end - key_start; | |
char* key = (char *)malloc(key_len + 1); | |
strncpy(key, key_start, key_len); | |
key[key_len] = '\0'; | |
return key; | |
} | |
// Decode a masked payload | |
void decode_payload(char* payload, unsigned char* mask_key, int payload_len) { | |
for (int i = 0; i < payload_len; i++) { | |
payload[i] = payload[i] ^ mask_key[i % 4]; | |
} | |
} | |
int main() { | |
int server_fd, client_fd; | |
struct sockaddr_in address; | |
int addrlen = sizeof(address); | |
char buffer[BUFFER_SIZE] = {0}; | |
// Socket setup (unchanged) | |
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { | |
perror("Socket failed"); | |
exit(EXIT_FAILURE); | |
} | |
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 failed"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Server listening on port %d\n", PORT); | |
// Accept client | |
if ((client_fd = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { | |
perror("Accept failed"); | |
exit(EXIT_FAILURE); | |
} | |
// Handle handshake | |
read(client_fd, buffer, BUFFER_SIZE); | |
char* client_key = extract_key(buffer); | |
if (client_key) { | |
send_handshake(client_fd, client_key); | |
free(client_key); | |
} else { | |
printf("Invalid WebSocket request\n"); | |
close(client_fd); | |
close(server_fd); | |
return 1; | |
} | |
// WebSocket frame handling loop | |
while (1) { | |
memset(buffer, 0, BUFFER_SIZE); | |
int bytes_read = read(client_fd, buffer, BUFFER_SIZE); | |
if (bytes_read <= 0) break; | |
// Parse frame | |
unsigned char first_byte = buffer[0]; | |
unsigned char second_byte = buffer[1]; | |
int fin = (first_byte & 0x80) >> 7; // FIN bit | |
int opcode = first_byte & 0x0F; // Opcode (e.g., 0x1 for text) | |
int mask = (second_byte & 0x80) >> 7; // Mask bit | |
int payload_len = second_byte & 0x7F; // Payload length (basic case) | |
if (opcode == 0x1 && fin == 1) { // Text frame, FIN set | |
char* payload; | |
unsigned char* mask_key; | |
if (mask == 1) { | |
// Masked frame: mask key follows second byte | |
mask_key = (unsigned char*)buffer + 2; | |
payload = buffer + 6; // Payload starts after mask key | |
decode_payload(payload, mask_key, payload_len); | |
} else { | |
// Unmasked frame (shouldn’t happen from client, but handle for robustness) | |
payload = buffer + 2; | |
} | |
printf("Received: %.*s\n", payload_len, payload); | |
// Echo back (unmasked, FIN=1, opcode=0x1) | |
char frame[BUFFER_SIZE] = {(char)0x81, (char)payload_len}; | |
memcpy(frame + 2, payload, payload_len); | |
write(client_fd, frame, 2 + payload_len); | |
} else { | |
printf("Unsupported or fragmented frame\n"); | |
} | |
} | |
close(client_fd); | |
close(server_fd); | |
return 0; | |
} | |
#endif | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <sys/epoll.h> | |
#include <openssl/sha.h> | |
#include <openssl/bio.h> | |
#include <openssl/evp.h> | |
#include <openssl/buffer.h> | |
#define PORT 8080 | |
#define BUFFER_SIZE 1024 | |
#define MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" | |
#define MAX_EVENTS 10 | |
#define MAX_CLIENTS 100 | |
// Client state | |
typedef struct { | |
int fd; // Client socket FD | |
char buffer[BUFFER_SIZE]; // Incoming data buffer | |
int buf_len; // Bytes in buffer | |
int handshaked; // 0 = not handshaked, 1 = handshaked | |
} Client; | |
// Compute Sec-WebSocket-Accept | |
char* compute_accept_key(const char* client_key) { | |
char combined[100]; | |
snprintf(combined, sizeof(combined), "%s%s", client_key, MAGIC_STRING); | |
unsigned char sha1[SHA_DIGEST_LENGTH]; | |
SHA1((unsigned char*)combined, strlen(combined), sha1); | |
BIO* b64 = BIO_new(BIO_f_base64()); | |
BIO* mem = BIO_new(BIO_s_mem()); | |
b64 = BIO_push(b64, mem); | |
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); | |
BIO_write(b64, sha1, SHA_DIGEST_LENGTH); | |
BIO_flush(b64); | |
BUF_MEM* buffer; | |
BIO_get_mem_ptr(b64, &buffer); | |
char* accept_key =(char *)malloc(buffer->length + 1); | |
memcpy(accept_key, buffer->data, buffer->length); | |
accept_key[buffer->length] = '\0'; | |
BIO_free_all(b64); | |
return accept_key; | |
} | |
// Send handshake response | |
void send_handshake(int client_fd, const char* client_key) { | |
char* accept_key = compute_accept_key(client_key); | |
char response[BUFFER_SIZE]; | |
snprintf(response, sizeof(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); | |
write(client_fd, response, strlen(response)); | |
free(accept_key); | |
} | |
// Extract Sec-WebSocket-Key | |
char* extract_key(const char* request) { | |
const char* key_start = strstr(request, "Sec-WebSocket-Key: "); | |
if (!key_start) return NULL; | |
key_start += 19; | |
char* key_end = (char *)strstr(key_start, "\r\n"); | |
if (!key_end) return NULL; | |
int key_len = key_end - key_start; | |
char* key = (char *)malloc(key_len + 1); | |
strncpy(key, key_start, key_len); | |
key[key_len] = '\0'; | |
return key; | |
} | |
// Decode masked payload | |
void decode_payload(char* payload, unsigned char* mask_key, int payload_len) { | |
for (int i = 0; i < payload_len; i++) { | |
payload[i] ^= mask_key[i % 4]; | |
} | |
} | |
// Set socket to non-blocking | |
void set_nonblocking(int fd) { | |
int flags = fcntl(fd, F_GETFL, 0); | |
fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
} | |
int main() { | |
int server_fd, epoll_fd; | |
struct sockaddr_in address; | |
int addrlen = sizeof(address); | |
Client clients[MAX_CLIENTS] = {0}; // Array to track clients | |
// Create server socket | |
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { | |
perror("Socket failed"); | |
exit(EXIT_FAILURE); | |
} | |
set_nonblocking(server_fd); | |
// Set up address | |
address.sin_family = AF_INET; | |
address.sin_addr.s_addr = INADDR_ANY; | |
address.sin_port = htons(PORT); | |
// Bind and listen | |
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { | |
perror("Bind failed"); | |
exit(EXIT_FAILURE); | |
} | |
if (listen(server_fd, SOMAXCONN) < 0) { | |
perror("Listen failed"); | |
exit(EXIT_FAILURE); | |
} | |
printf("Server listening on port %d\n", PORT); | |
// Create epoll instance | |
epoll_fd = epoll_create1(0); | |
if (epoll_fd == -1) { | |
perror("Epoll create failed"); | |
exit(EXIT_FAILURE); | |
} | |
// Add server socket to epoll | |
struct epoll_event ev, events[MAX_EVENTS]; | |
ev.events = EPOLLIN; | |
ev.data.fd = server_fd; | |
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) { | |
perror("Epoll add server failed"); | |
exit(EXIT_FAILURE); | |
} | |
// Main event loop | |
while (1) { | |
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); | |
if (nfds == -1) { | |
perror("Epoll wait failed"); | |
continue; | |
} | |
for (int i = 0; i < nfds; i++) { | |
int fd = events[i].data.fd; | |
if (fd == server_fd) { | |
// Accept new connection | |
int client_fd = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen); | |
if (client_fd == -1) { | |
perror("Accept failed"); | |
continue; | |
} | |
set_nonblocking(client_fd); | |
// Add client to epoll | |
ev.events = EPOLLIN | EPOLLET; // Edge-triggered | |
ev.data.fd = client_fd; | |
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) { | |
perror("Epoll add client failed"); | |
close(client_fd); | |
continue; | |
} | |
// Store client | |
for (int j = 0; j < MAX_CLIENTS; j++) { | |
if (clients[j].fd == 0) { | |
clients[j].fd = client_fd; | |
clients[j].handshaked = 0; | |
clients[j].buf_len = 0; | |
break; | |
} | |
} | |
printf("New client connected: %d\n", client_fd); | |
} else { | |
// Find client | |
Client* client = NULL; | |
for (int j = 0; j < MAX_CLIENTS; j++) { | |
if (clients[j].fd == fd) { | |
client = &clients[j]; | |
break; | |
} | |
} | |
if (!client) continue; | |
// Read data | |
int bytes_read = read(fd, client->buffer + client->buf_len, BUFFER_SIZE - client->buf_len); | |
if (bytes_read <= 0) { | |
// Client disconnected or error | |
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); | |
close(fd); | |
memset(client, 0, sizeof(Client)); | |
printf("Client disconnected: %d\n", fd); | |
continue; | |
} | |
client->buf_len += bytes_read; | |
if (!client->handshaked) { | |
// Process handshake | |
char* client_key = extract_key(client->buffer); | |
if (client_key) { | |
send_handshake(fd, client_key); | |
client->handshaked = 1; | |
client->buf_len = 0; // Clear buffer after handshake | |
free(client_key); | |
} | |
} else { | |
// Process WebSocket frame | |
if (client->buf_len >= 2) { // Minimum frame size | |
unsigned char* buf = (unsigned char*)client->buffer; | |
int opcode = buf[0] & 0x0F; | |
int mask = (buf[1] & 0x80) >> 7; | |
int payload_len = buf[1] & 0x7F; | |
if (opcode == 0x1 && client->buf_len >= (2 + (mask ? 4 : 0) + payload_len)) { | |
char* payload = mask ? client->buffer + 6 : client->buffer + 2; | |
if (mask) { | |
unsigned char* mask_key = (unsigned char*)client->buffer + 2; | |
decode_payload(payload, mask_key, payload_len); | |
} | |
printf("Received from %d: %.*s\n", fd, payload_len, payload); | |
// Echo back | |
char frame[BUFFER_SIZE] = {(char)0x81, (char)payload_len}; | |
memcpy(frame + 2, payload, payload_len); | |
write(fd, frame, 2 + payload_len); | |
// Shift remaining data | |
int frame_len = 2 + (mask ? 4 : 0) + payload_len; | |
memmove(client->buffer, client->buffer + frame_len, client->buf_len - frame_len); | |
client->buf_len -= frame_len; | |
} | |
} | |
} | |
} | |
} | |
} | |
close(server_fd); | |
close(epoll_fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment