#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; }