nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: struct client { nuclear@0: int s; nuclear@0: char *rcvbuf; nuclear@0: int bufsz; nuclear@0: struct client *next; nuclear@0: }; nuclear@0: nuclear@0: int start_server(void); nuclear@0: int accept_conn(int lis); nuclear@0: int handle_client(struct client *c); nuclear@0: nuclear@0: static int lis; nuclear@0: static int port = 8080; nuclear@0: static struct client *clist; nuclear@0: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@0: if((lis = start_server()) == -1) { nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: for(;;) { nuclear@0: struct client *c, dummy; nuclear@0: int maxfd = lis; nuclear@0: fd_set rdset; nuclear@0: nuclear@0: FD_ZERO(&rdset); nuclear@0: FD_SET(lis, &rdset); nuclear@0: nuclear@0: c = clist; nuclear@0: while(c) { nuclear@0: if(c->s > maxfd) { nuclear@0: maxfd = c->s; nuclear@0: } nuclear@0: FD_SET(c->s, &rdset); nuclear@0: c = c->next; nuclear@0: } nuclear@0: nuclear@0: while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR); nuclear@0: nuclear@0: c = clist; nuclear@0: while(c) { nuclear@0: if(FD_ISSET(c->s, &rdset)) { nuclear@0: handle_client(c); nuclear@0: } nuclear@0: c = c->next; nuclear@0: } nuclear@0: nuclear@0: if(FD_ISSET(lis, &rdset)) { nuclear@0: accept_conn(lis); nuclear@0: } nuclear@0: nuclear@0: dummy.next = clist; nuclear@0: c = &dummy; nuclear@0: nuclear@0: while(c->next) { nuclear@0: struct client *n = c->next; nuclear@0: nuclear@0: if(n->s == -1) { nuclear@0: /* marked for removal */ nuclear@0: c->next = n->next; nuclear@0: free(n); nuclear@0: } else { nuclear@0: c = c->next; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int start_server(void) nuclear@0: { nuclear@0: int s; nuclear@0: struct sockaddr_in sa; nuclear@0: nuclear@0: if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { nuclear@0: perror("failed to create listening socket"); nuclear@0: return -1; nuclear@0: } nuclear@0: fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); nuclear@0: nuclear@0: memset(&sa, 0, sizeof sa); nuclear@0: sa.sin_family = AF_INET; nuclear@0: sa.sin_addr.s_addr = INADDR_ANY; nuclear@0: sa.sin_port = htons(port); nuclear@0: nuclear@0: if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) { nuclear@0: fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno)); nuclear@0: return -1; nuclear@0: } nuclear@0: listen(s, 16); nuclear@0: nuclear@0: return s; nuclear@0: } nuclear@0: nuclear@0: int accept_conn(int lis) nuclear@0: { nuclear@0: int s; nuclear@0: struct client *c; nuclear@0: struct sockaddr_in addr; nuclear@0: socklen_t addr_sz = sizeof addr; nuclear@0: nuclear@0: if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) { nuclear@0: perror("failed to accept incoming connection"); nuclear@0: return -1; nuclear@0: } nuclear@0: fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); nuclear@0: nuclear@0: if(!(c = malloc(sizeof *c))) { nuclear@0: perror("failed to allocate memory while accepting connection"); nuclear@0: return -1; nuclear@0: } nuclear@0: c->s = s; nuclear@0: c->rcvbuf = 0; nuclear@0: c->bufsz = 0; nuclear@0: c->next = clist; nuclear@0: clist = c; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int handle_client(struct client *c) nuclear@0: { nuclear@0: static char buf[2048]; nuclear@0: int rdsz; nuclear@0: nuclear@0: while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { nuclear@0: if(c->rcvbuf) { nuclear@0: int newsz = c->bufsz + rdsz; nuclear@0: char *newbuf = realloc(buf, newsz); nuclear@0: if(!newbuf) { nuclear@0: fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); nuclear@0: /* TODO http error */ nuclear@0: goto drop; nuclear@0: } nuclear@0: nuclear@0: memcpy(newbuf + c->bufsz, buf, rdsz); nuclear@0: nuclear@0: c->rcvbuf = newbuf; nuclear@0: c->bufsz = newsz; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /* TODO: parse header, drop on invalid, determine if the request nuclear@0: * is complete and process nuclear@0: */ nuclear@0: nuclear@0: if(rdsz == -1 && errno != EAGAIN) { nuclear@0: drop: nuclear@0: close(c->s); nuclear@0: c->s = -1; nuclear@0: free(c->rcvbuf); nuclear@0: } nuclear@0: return 0; nuclear@0: }