#include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "gopher.h" #ifndef OPEN_MAX #define OPEN_MAX 1024 #endif #define PORT "3000" #define BUFSIZE 1024 #define HOSTMAX 1025 #define SERVMAX 256 static int create_server(const char *port) { int fd, yes = 1; struct addrinfo *res, *p, hints; char host[HOSTMAX], serv[SERVMAX]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(NULL, port, &hints, &res) != 0) { perror("getaddrinfo"); return -1; } for (p = res; p != NULL; p = p->ai_next) { if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("socket"); continue; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); close(fd); return -1; } if (ioctl(fd, FIONBIO, &yes) == -1) { perror("ioctl"); close(fd); return -1; } if (bind(fd, p->ai_addr, p->ai_addrlen) == -1) { perror("bind"); close(fd); continue; } break; } if (p == NULL) { freeaddrinfo(res); fprintf(stderr, "failed to bind\n"); return -1; } if (listen(fd, 32) == -1) { freeaddrinfo(res); perror("listen"); return -1; } if (getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST) != 0) { freeaddrinfo(res); perror("getaddrinfo"); return -1; } freeaddrinfo(res); logmsg(LOG_INFO, "listening on %s:%s", host, serv); return fd; } static int accept_connection(int s_fd) { struct sockaddr_storage sa; socklen_t salen; char host[HOSTMAX]; int cfd; if ((cfd = accept(s_fd, (struct sockaddr *) & sa, &salen)) == -1) { if (errno != EWOULDBLOCK || errno != EAGAIN) { perror("accept"); exit(1); } return -1; } if (getnameinfo((struct sockaddr *) & sa, salen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { perror("gethostname"); return -1; } logmsg(LOG_INFO, "connection accepted from %s", host); return cfd; } static void handle_client(int cfd) { int n; char buf[BUFSIZE]; char *lf; n = recv(cfd, &buf, BUFSIZE - 1, 0); switch (n) { case -1: if (errno != EWOULDBLOCK || errno != EAGAIN) { perror("recv"); } break; case 0: logmsg(LOG_INFO, "connection closed by client"); break; default: lf = strchr(buf, '\r'); if (lf == NULL || *lf != buf[n-2]) { logmsg(LOG_INFO, "invalid request"); break; } *lf = '\0'; logmsg(LOG_INFO, "path: %s", buf); handle_path(cfd, buf); } close(cfd); } static void main_loop(int sfd) { int i, rc, end_server = 0; int cfd, nfds = 1, curr_size = 0; struct pollfd pfds[OPEN_MAX]; memset(pfds, 0, sizeof(pfds)); pfds[0].fd = sfd; pfds[0].events = POLLIN; do { rc = poll(pfds, nfds, -1); if (rc == -1) { perror("poll"); break; } if (rc == 0) { fprintf(stderr, "poll timeout\n"); break; } curr_size = nfds; for (i = 0; i < curr_size; i++) { if (pfds[i].revents == 0) continue; if (pfds[i].revents != POLLIN) { logmsg(LOG_INFO, "revents not POLLIN\n"); end_server = 1; break; } if (pfds[i].fd == sfd) { do { cfd = accept_connection(sfd); if (cfd != -1) { pfds[nfds].fd = cfd; pfds[nfds].events = POLLIN; nfds++; } } while (cfd != -1); } else { handle_client(pfds[i].fd); pfds[i].fd = -1; } } } while (end_server == 0); } int main() { int fd; fd = create_server(PORT); if (fd == -1) { fprintf(stderr, "failed to create server\n"); exit(0); } main_loop(fd); return 0; }