Skip to content

Instantly share code, notes, and snippets.

@aquantus-ru
Created June 16, 2025 15:15
Show Gist options
  • Save aquantus-ru/17429f741a2fe4c685954beaa90c4ab8 to your computer and use it in GitHub Desktop.
Save aquantus-ru/17429f741a2fe4c685954beaa90c4ab8 to your computer and use it in GitHub Desktop.
gcc -o http_share http_share.c
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <time.h>
#define BUFFER_SIZE 8192
static volatile int keep_running = 1;
void int_handler(int dummy) {
keep_running = 0;
}
static char guid[33]; // 32 hex + null
// Generate a simple random hex GUID
void generate_guid(char *buf, size_t len) {
const char *hex = "0123456789abcdef";
for (size_t i = 0; i < len - 1; i++) {
buf[i] = hex[rand() % 16];
}
buf[len - 1] = '\0';
}
void log_message(FILE *logf, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(logf, fmt, args);
fflush(logf);
va_end(args);
}
// Get MIME type based on file extension
const char *get_mime_type(const char *path) {
const char *ext = strrchr(path, '.');
if (!ext) return "application/octet-stream";
if (strcmp(ext, ".html") == 0) return "text/html";
if (strcmp(ext, ".css") == 0) return "text/css";
if (strcmp(ext, ".js") == 0) return "application/javascript";
if (strcmp(ext, ".png") == 0) return "image/png";
if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg";
if (strcmp(ext, ".gif") == 0) return "image/gif";
if (strcmp(ext, ".mp4") == 0) return "video/mp4";
return "application/octet-stream";
}
// Write HTTP 404 response (empty, no message per requirement)
void send_404(int client_sock) {
const char *msg = "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n";
write(client_sock, msg, strlen(msg));
}
// Send 200 OK + Content-Type header
void send_response_header(int client_sock, const char *mime, size_t content_length) {
char header[256];
int len = snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %zu\r\n"
"Accept-Ranges: bytes\r\n"
"Connection: close\r\n\r\n",
mime, content_length);
write(client_sock, header, len);
}
// Serve the file in chunks
void serve_file(int client_sock, const char *filepath, FILE *logf, const char *client_ip) {
int fd = open(filepath, O_RDONLY);
if (fd == -1) {
send_404(client_sock);
return;
}
struct stat st;
if (fstat(fd, &st) == -1) {
close(fd);
send_404(client_sock);
return;
}
const char *mime = get_mime_type(filepath);
send_response_header(client_sock, mime, st.st_size);
char buffer[BUFFER_SIZE];
ssize_t r;
while ((r = read(fd, buffer, sizeof(buffer))) > 0) {
if (write(client_sock, buffer, r) != r) break;
}
close(fd);
log_message(logf, "[INFO] GET /shared?file=%s from %s\n", guid, client_ip);
}
// Parse IP from sockaddr for logging
void sockaddr_to_ip(struct sockaddr_in *addr, char *ip_str, size_t size) {
inet_ntop(AF_INET, &(addr->sin_addr), ip_str, size);
}
// Fork & exec ssh tunnel, capture output line with tunnel url, print clean URL
pid_t start_ssh_tunnel(int port, char *tunnel_url, size_t url_size) {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
return -1;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid == 0) {
// Child: redirect stdout to pipe write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO); // redirect stderr too to silence unwanted messages
close(pipefd[1]);
// No tty, quiet, strict host key checking off
char port_arg[32];
snprintf(port_arg, sizeof(port_arg), "%d", port);
execlp("ssh", "ssh", "-T",
"-oLogLevel=ERROR",
"-oStrictHostKeyChecking=no",
"-R", "80:localhost", port_arg,
"serveo.net", NULL);
perror("execlp ssh");
exit(1);
}
// Parent: close write end, read tunnel url line
close(pipefd[1]);
FILE *fp = fdopen(pipefd[0], "r");
if (!fp) {
perror("fdopen");
return -1;
}
// Wait for a line containing serveo.net URL or timeout after ~10 sec
char line[512];
time_t start = time(NULL);
while (time(NULL) - start < 10) {
if (fgets(line, sizeof(line), fp) == NULL) {
usleep(100000); // wait 0.1s before retry
continue;
}
// Example line contains:
// "Forwarding HTTP traffic from https://8ebdf2fd66251c3d9ef059a37002f2e5.serveo.net"
if (strstr(line, "Forwarding HTTP traffic from ")) {
char *url_start = strstr(line, "https://");
if (url_start) {
size_t len = strcspn(url_start, "\r\n");
if (len >= url_size) len = url_size - 1;
strncpy(tunnel_url, url_start, len);
tunnel_url[len] = '\0';
break;
}
}
}
fclose(fp);
return pid;
}
void usage(const char *progname) {
printf(
"Usage: %s -f <file> [-p <port>] [-l <stdout|file>] [-d] [-t]\n"
" -f <file> File to share (required)\n"
" -p <port> Port to listen on (default 8080)\n"
" -l <logging> Logging: stdout or filename (default stdout)\n"
" -d Run as daemon\n"
" -t Use serveo.net SSH tunnel\n"
" -h Show this help\n",
progname);
}
int main(int argc, char *argv[]) {
int opt;
char *filepath = NULL;
int port = 8080;
FILE *logf = stdout;
char logfile_path[256] = {0};
int daemon_mode = 0;
int use_tunnel = 0;
pid_t ssh_pid = -1;
char tunnel_url[256] = {0};
srand(time(NULL));
while ((opt = getopt(argc, argv, "f:p:l:dth")) != -1) {
switch (opt) {
case 'f':
filepath = optarg;
break;
case 'p':
port = atoi(optarg);
if (port <= 0 || port > 65535) {
fprintf(stderr, "Invalid port number.\n");
return 1;
}
break;
case 'l':
if (strcmp(optarg, "stdout") == 0) {
logf = stdout;
} else {
strncpy(logfile_path, optarg, sizeof(logfile_path) - 1);
}
break;
case 'd':
daemon_mode = 1;
break;
case 't':
use_tunnel = 1;
break;
case 'h':
default:
usage(argv[0]);
return 0;
}
}
if (!filepath) {
fprintf(stderr, "File to share is required (-f).\n");
usage(argv[0]);
return 1;
}
// Open logfile if specified
if (logfile_path[0]) {
logf = fopen(logfile_path, "a");
if (!logf) {
perror("Failed to open log file");
return 1;
}
}
// Daemonize if requested
if (daemon_mode) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
}
if (pid > 0) {
// parent exits
return 0;
}
// child continues
if (setsid() < 0) {
perror("setsid");
return 1;
}
fclose(stdin);
fclose(stdout);
fclose(stderr);
// Reopen stdout/stderr to logfile or /dev/null
if (logf != stdout) {
dup2(fileno(logf), STDOUT_FILENO);
dup2(fileno(logf), STDERR_FILENO);
} else {
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
}
}
generate_guid(guid, sizeof(guid));
// Validate file exists
struct stat st;
if (stat(filepath, &st) != 0 || !S_ISREG(st.st_mode)) {
fprintf(stderr, "File not found or not a regular file: %s\n", filepath);
return 1;
}
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0) {
perror("socket");
return 1;
}
int optval = 1;
setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
struct sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
return 1;
}
if (listen(server_sock, 10) < 0) {
perror("listen");
return 1;
}
// Setup signal handler for clean exit
signal(SIGINT, int_handler);
signal(SIGTERM, int_handler);
// Start tunnel if requested
if (use_tunnel) {
log_message(logf, "[INFO] Starting tunnel via serveo.net\n");
ssh_pid = start_ssh_tunnel(port, tunnel_url, sizeof(tunnel_url));
if (ssh_pid < 0) {
log_message(logf, "[ERROR] Failed to start SSH tunnel\n");
if (logf != stdout) fclose(logf);
return 1;
}
if (tunnel_url[0]) {
log_message(logf, "[TUNNEL] Forwarding HTTP traffic from %s\n", tunnel_url);
log_message(logf, "[INFO] Tunnel ready.\n");
// Print clean URL
printf("Link: %s/shared?file=%s\n", tunnel_url, guid);
fflush(stdout);
} else {
log_message(logf, "[ERROR] Tunnel URL not found.\n");
}
} else {
// No tunnel: get local IP address (use 127.0.0.1 fallback)
char ipbuf[INET_ADDRSTRLEN] = "127.0.0.1";
int sock_tmp = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_tmp >= 0) {
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS IP
serv.sin_port = htons(53);
connect(sock_tmp, (struct sockaddr *)&serv, sizeof(serv));
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
if (getsockname(sock_tmp, (struct sockaddr *)&name, &namelen) == 0) {
inet_ntop(AF_INET, &name.sin_addr, ipbuf, sizeof(ipbuf));
}
close(sock_tmp);
}
printf("Link: http://%s:%d/shared?file=%s\n", ipbuf, port, guid);
fflush(stdout);
}
log_message(logf, "[INFO] Serving file '%s' on port %d with GUID %s\n", filepath, port, guid);
// Main accept loop
while (keep_running) {
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
int client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &addrlen);
if (client_sock < 0) {
if (errno == EINTR) continue;
perror("accept");
break;
}
char client_ip[INET_ADDRSTRLEN];
sockaddr_to_ip(&client_addr, client_ip, sizeof(client_ip));
pid_t pid = fork();
if (pid < 0) {
perror("fork");
close(client_sock);
continue;
}
if (pid == 0) {
// Child: handle request and exit
close(server_sock);
// Read HTTP request header (only the first line)
char req[BUFFER_SIZE] = {0};
ssize_t r = read(client_sock, req, sizeof(req) - 1);
if (r <= 0) {
close(client_sock);
exit(0);
}
req[r] = 0;
// Parse GET line
char method[16], uri[1024];
if (sscanf(req, "%15s %1023s", method, uri) != 2) {
close(client_sock);
exit(0);
}
// Check if URI matches /shared?file=<guid>
if (strcmp(method, "GET") != 0) {
send_404(client_sock);
close(client_sock);
exit(0);
}
// Check prefix "/shared?file="
const char *prefix = "/shared?file=";
if (strncmp(uri, prefix, strlen(prefix)) != 0) {
// No error, just close connection silently
close(client_sock);
exit(0);
}
const char *requested_guid = uri + strlen(prefix);
if (strcmp(requested_guid, guid) != 0) {
// No error, close silently
close(client_sock);
exit(0);
}
serve_file(client_sock, filepath, logf, client_ip);
close(client_sock);
exit(0);
}
close(client_sock);
// Reap zombies
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
// Cleanup on exit
if (ssh_pid > 0) {
kill(ssh_pid, SIGTERM);
waitpid(ssh_pid, NULL, 0);
}
close(server_sock);
if (logf != stdout) fclose(logf);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment