266 lines
5.3 KiB
C
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;
|
|
}
|