sfb/sfb.c
2024-11-29 12:03:08 +00:00

266 lines
5.3 KiB
C

#include <arpa/inet.h>
#include <fcntl.h>
#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
/* For slug generation */
const char *symbols = "abcdefghijklmnopqrstuvwxyz0123456789";
/* Permission for directories */
const int permission = S_IRWXU | S_IRGRP | S_IROTH | S_IXOTH | S_IXGRP;
typedef struct {
int socket;
struct sockaddr_in address;
} connection;
void
die(char *err)
{
fprintf(stderr, "%s\n", err);
exit(1);
}
static void
error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
printf("[ERROR] ");
vprintf(fmt, args);
printf("\n");
va_end(args);
}
static void
status(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
printf("[LOG] ");
vprintf(fmt, args);
printf("\n");
va_end(args);
}
/*
* Create string with current date and time
*/
static void
get_date(char *buf)
{
time_t cur_time = time(NULL);
/* Save data to provided buffer */
if (ctime_r(&cur_time, buf) == NULL) {
/* Couldn't get date, set first byte to 0 so it won't be displayed */
buf[0] = 0;
return;
}
/* Remove additional new line */
buf[strlen(buf) - 1] = 0;
}
static void
save_entry(const char *ip, const char *hostname, const char *id)
{
FILE *f = fopen(log_file_path, "a");
if (!f) {
error("Error opening log file");
return;
}
char date[26];
get_date(date);
/* Write request to file */
fprintf(f, "[%s] [%s] [%s] [%s]\n", date, ip, hostname, id);
fclose(f);
}
static void *
handle_connection(void *args)
{
connection *c = (connection *) args;
/* Get client's IP */
const char *ip = inet_ntoa(c->address.sin_addr);
/* Get client's hostname */
char hostname[512];
if (getnameinfo((struct sockaddr *)&c->address, sizeof(c->address),
hostname, sizeof(hostname), NULL, 0, 0) != 0 ) {
/* Couldn't resolve a hostname */
strcpy(hostname, "N/A");
}
char date[26];
get_date(date);
status("[%s] [%s] [%s]: New connection", date, ip, hostname);
uint8_t *buffer = malloc(max_file_size);
if (!buffer) {
error("Error allocating memory");
goto quit;
}
memset(buffer, 0, max_file_size);
const int r = recv(c->socket, buffer, max_file_size, 0);
if (r <= 0) {
/* TODO: log unsuccessful and rejected connections */
error("No data received from the client");
goto quit;
} else {
buffer[r] = 0;
/* TODO: Check if requested was performed with a known protocol */
/* Generate id */
char id[id_len + 1];
for (int i = 0; i < id_len; i++) {
int n = rand() % strlen(symbols);
id[i] = symbols[n];
}
id[id_len] = 0;
/* 1 for slash and 1 for null */
size_t len = strlen(output_dir) + id_len + 2;
/* Create path */
char path[len];
snprintf(path, len, "%s/%s", output_dir, id);
/* Try create directory for paste */
if (mkdir(path, permission) != 0) {
error("Error creating directory for paste");
goto quit;
}
/* Save to file */
const char *file_name = "index.txt";
len += strlen(file_name) + 1;
/* Construct path */
char paste_path[len];
snprintf(paste_path, len, "%s/%s", path, file_name);
/* Save file */
FILE *f = fopen(paste_path, "w");
if (!f) {
goto quit;
}
if (fprintf(f, "%s", buffer) < 0) {
fclose(f);
goto quit;
}
fclose(f);
/* Write a response to the user */
const size_t url_len = strlen(url) + id_len + 3;
char full_url[url_len];
snprintf(full_url, url_len, "%s/%s\n", url, id);
/* Send the response */
write(c->socket, full_url, url_len);
status("[%s] [%s] [%s]: Received %d bytes, saved to %s", date, ip, hostname, r, id);
save_entry(ip, hostname, id);
goto quit;
}
quit:
free(buffer);
close(c->socket);
free(c);
pthread_exit(NULL);
return NULL;
}
int
main(void)
{
srand(time(NULL));
mkdir(output_dir, permission);
if (access(output_dir, W_OK) != 0) {
error("Output directory is not writable");
return -1;
}
FILE *f = fopen(log_file_path, "a+");
if (!f) {
die("Cannot open log file");
}
fclose(f);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return -1;
}
/* Reuse address */
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1} , sizeof(int)) < 0) {
perror("setsockopt");
return -1;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(listen_addr);
address.sin_port = htons(port);
if (bind(fd, (struct sockaddr *) &address, sizeof(address)) < 0) {
perror("bind");
return -1;
}
if (listen(fd, 128) != 0) {
perror("listen");
return -1;
}
status("spb listening on %s:%d", listen_addr, port);
while (1) {
/* Create a thread for each connection */
struct sockaddr_in address;
socklen_t addlen = sizeof(address);
/* Get client fd */
int s = accept(fd, (struct sockaddr *) &address, &addlen);
if (s < 0) {
error("Error on accepting connection!");
continue;
}
connection *c = malloc(sizeof(connection));
if (!c) {
die("Error allocating memory");
}
c->socket = s;
c->address = address;
pthread_t id;
if (pthread_create(&id, NULL, &handle_connection, c) != 0) {
error("Error spawning thread");
continue;
}
pthread_detach(id);
}
return 0;
}