/* * Copyright (C) 2026 Verdant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROGRAM_NAME "shsd" #define VERSION "0.0.1" #define COPYRIGHT_YEAR "2026" #define AUTHOR "Verdant " typedef enum { GET = 0, POST = 1, UNKNOWN = -1 } HTTP_METHODS; static void usage(const char *progname) { fprintf(stderr, "Usage: %s \n", progname); exit(EXIT_FAILURE); } static void version() { printf("%s %s\n", PROGRAM_NAME, VERSION); printf("Copyright (C) %s %s\n", COPYRIGHT_YEAR, AUTHOR); printf("License GPLv3+: GNU GPL version 3 or later .\n"); printf("This is free software: you are free to change and redistribute it.\n"); printf("There is NO WARRANTY, to the extent permitted by law.\n"); } static int shsd_sendfile(int in_fd, FILE *ou_fd) { struct stat s; int stat_result = fstat(fileno(ou_fd), &s); if (stat_result != 0) { perror("fstat"); return -1; } sendfile(in_fd, fileno(ou_fd), NULL, s.st_size); return 0; } static HTTP_METHODS get_require_method(char *method) { HTTP_METHODS m; if (strcmp(method, "GET") == 0) { return m = GET; } if (strcmp(method, "POST") == 0) { return m = POST; } else { return m = UNKNOWN; } } static bool sdhd_isdir(const char *path, int fd) { struct stat st; stat(path, &st); if (S_ISDIR(st.st_mode)) { return true; } return false; } static int handle_client(int client_fd, char *buf) { char method[16], path[256]; if (sscanf(buf, "%15s %255s", method, path) < 2) { close(client_fd); return -1; } if (strcmp(path, "/") == 0) { strcpy(path, "/index.html"); } char local_path[1024]; snprintf(local_path, sizeof(local_path), "./www%s", path); printf("Path = %s\n", local_path); if (sdhd_isdir(local_path, client_fd)) { strcat(local_path, "/index.html"); } printf("Path = %s\n", local_path); FILE *fp = fopen(local_path, "r"); if (!fp) { perror("fopen"); fp = fopen("./www/404.html", "r"); shsd_sendfile(client_fd, fp); return -1; } shsd_sendfile(client_fd, fp); return 0; } int main(int argc, char **argv) { if (argc < 2) { usage(argv[0]); exit(1); } else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { version(); exit(0); } struct addrinfo hints, *res; struct sockaddr_storage their_addr; socklen_t addr_size; int sockfd, new_fd; int yes = 1; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; // use IPv4 or IPv6, whichever hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // fill in my IP for me getaddrinfo(NULL, argv[1], &hints, &res); sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) { perror("bind"); close(sockfd); exit(1); } if (listen(sockfd, 10) == -1) { perror("listen"); close(sockfd); exit(1); } addr_size = sizeof(their_addr); char buf[1024]; memset(buf, 0, 1024); int recv_result; while (1) { new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size); recv_result = recv(new_fd, (void *)buf, 1024, 0); if (recv_result == -1) { perror("recv"); return -1; } for (int i = 0; i < sizeof(buf); i++) { printf("%c", buf[i]); } if (strncmp(buf, "GET", 3) == 0) { char *http_status = "HTTP/1.1 200 OK\r\n"; char *http_content_type = "text/html\r\n\r\n"; printf("Received GET request.\n"); send(new_fd, http_status, strlen(http_status), 0); send(new_fd, http_content_type, strlen(http_content_type), 0); handle_client(new_fd, buf); } memset(buf, 0, 1024); close(new_fd); } close(sockfd); close(new_fd); return 0; }