#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include struct pollfd *listens = NULL; size_t num_listens = 0; void log_error(const char * restrict format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } void setup_listen(void) { struct addrinfo hints; struct addrinfo *getaddrinfo_result; // AF_UNSPEC: either IPv4 or IPv6 // SOCK_STREAM: TCP // AI_PASSIVE: fill out my IP for me // AI_ADDRCONFIG: only return addresses I have a configured interface for memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; int status = getaddrinfo(NULL, "79", &hints, &getaddrinfo_result); if(status != 0) { log_error("getaddrinfo failed: %s\n", gai_strerror(status)); exit(1); } for(struct addrinfo *res = getaddrinfo_result; res != NULL; res = res->ai_next) { int yes = 1; // Add corresponding interface to table of sockets // Create socket int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if(sock == -1) { perror("socket"); exit(1); } // Disable the IPv4 over IPv6, as that results in IPv4 and IPv6 sockets conflicting and one of them not being able to be set up if(res->ai_family == AF_INET6) { if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) { perror("setsockopt"); exit(1); } } // Set reuseaddr if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { perror("setsockopt"); exit(1); } // Bind onto given address if(bind(sock, res->ai_addr, res->ai_addrlen) == -1) { perror("bind"); exit(1); } // Listen for incoming connections if(listen(sock, 1) == -1) { perror("listen"); exit(1); } // Grow the table of sockets size_t index = num_listens++; if(SIZE_MAX / sizeof(struct pollfd) < num_listens){ log_error("Too many interfaces to listen on\n"); exit(1); } listens = realloc(listens, num_listens * sizeof(struct pollfd)); if(listens == NULL) { perror("realloc"); exit(1); } // Add socket to the table struct pollfd new_pollobj = {.fd = sock, .events = POLLIN}; listens[index] = new_pollobj; } freeaddrinfo(getaddrinfo_result); } void drop_privileges(void) { uid_t uid = getuid(); gid_t gid = getgid(); if(setresgid(gid, gid, gid) != 0) { perror("setresgid"); exit(1); } if(setresuid(uid, uid, uid) != 0) { perror("setresuid"); exit(1); } } ssize_t writeall(int fd, const char *buf, size_t amount) { size_t written = 0; while(written < amount) { ssize_t r = write(fd, buf + written, amount - written); if(r < 0) { return r; } written += r; } return written; } void handle_connection(int sock) { size_t request_size = 0; char *request = NULL; for(;;) { ssize_t amount_read; char iobuf[1024]; amount_read = read(sock, &iobuf, sizeof(iobuf)); if(amount_read == 0) { log_error("Client hung up\n"); shutdown(sock, SHUT_RDWR); close(sock); return; } else if(amount_read < 0) { perror("read"); shutdown(sock, SHUT_RDWR); close(sock); return; } size_t index = request_size; request_size += amount_read; if((request = realloc(request, request_size)) == NULL) { perror("realloc"); exit(1); } memmove(request + index, iobuf, amount_read); if(request_size >= 2 && request[request_size-2] == '\r' && request[request_size-1] == '\n') { // Request read break; } } request[request_size-2] = '\0'; if(!strcmp(request, "")) { const char *response = "Who do you want to finger?\r\n"; if(writeall(sock, response, strlen(response)) < 0) { perror("write"); } } else { const char *response = "Lewd.\r\n"; if(writeall(sock, response, strlen(response)) < 0) { perror("write"); } } shutdown(sock, SHUT_RDWR); close(sock); } int main(void) { drop_privileges(); setup_listen(); for(;;) { int amount_ready = poll(listens, num_listens, -1); if(amount_ready < 0) { perror("poll"); exit(1); } for(size_t i = 0; i < num_listens && amount_ready > 0; i++) { if(listens[i].revents & POLLIN) { struct sockaddr_storage client_addr; socklen_t addr_size = sizeof(client_addr); int sock = accept(listens[i].fd, (struct sockaddr *)&client_addr, &addr_size); handle_connection(sock); amount_ready--; } } } }