rendered paste body#if 0#!/bin/env sh[ -e /etc/make.conf ] && source /etc/make.confecho gcc -Wall $CFLAGS -pthread -o sexy main.cgcc -Wall $CFLAGS -pthread -o sexy main.cexit#endif/** * sexy: Small package proxy for gentoo, without features. * Author: Nelnire * Contact: nelnire@gmail.com * Licence: GPL-2 * * Change: * Juanary 09 2008 - First change * December 07 2007 - First version */#include "conf.h" /* Edit conf.h */#include <arpa/inet.h>#include <fcntl.h>#include <netdb.h>#include <pthread.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/stat.h>#include <unistd.h>#define BUFSIZE 4096#define TIMEOUT 3int mksfd(int port){ int sfd, opt = 1; struct sockaddr_in saddr; sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sfd<0) return -1; saddr.sin_family = AF_INET; saddr.sin_port = htons(PORT); saddr.sin_addr.s_addr = INADDR_ANY; if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))<0) return -1; if(bind(sfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr))<0) return -1; if(listen(sfd, 5)<0) return -1; return sfd;}void connclose(int cfd, int code){ char buf[BUFSIZE]; snprintf(buf, BUFSIZE, "HTTP/1.1 %d\r\n" "Connection: close\r\n" "Content-type: text/html\r\n" "\r\n" "<html><head>\n" "<title>%d</title>\n" "</head><body>\n" "<h1>%d</h1>\n" "</body></html>\n", code, code, code); send(cfd, buf, strlen(buf), 0); close(cfd); pthread_exit(NULL);}int timeout(int cfd){ fd_set fdset; struct timeval timeout; FD_ZERO(&fdset); FD_SET(cfd, &fdset); timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; return select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);}int checkquery(int cfd, char *file, int max){ char r; char *tok, query[256]; int n=0; query[0] = '\0'; /* Get the query */ while(1) { if(timeout(cfd)==0) return -1; recv(cfd, &r, 1, 0); strncat(query, &r, 1); if(query[n] == '\n' && (n>1 && query[n-1] == '\r')) /* strtok segfault avec n>0 */ { query[n-1] = '\0'; break; } n++; if(n==256) connclose(cfd, 500); } /* Check the query */ tok = strtok(query, " "); if(!strcmp(tok, "\r\n")) connclose(cfd, 400); if(strcmp(tok, "GET")) connclose(cfd, 501); tok = strtok(NULL, " "); if(strncmp(tok, "/distfiles/", 11)) connclose(cfd, 403); /* ... */ if(strlen(tok) == 11) connclose(cfd, 403); if(tok[11] == '.') connclose(cfd, 403); strncpy(file, tok+11, max); return 0;}int findfile(char *file){ int fd = open(file, O_RDONLY); if(fd<0) return 0; close(fd); return 1;}int gf_checkresp(char *resp){ /* The next supose that "resp" is * the first buffer receved */ if(strncmp(resp+9, "200", 3)==0) return 0; else if(strncmp(resp+9, "404", 3)==0) return 1; else return -1;}int gf_getlen(char *noname){ int size; if(sscanf(noname, "Content-Length: %d", &size)) return size; return 0;}int getfile(char *file){ int fd = -1; socklen_t s; struct sockaddr_in saddr; struct hostent *h; int r; char buf[BUFSIZE+1]; int check; /* Check the serv resp */ int shift; /* Header shift */ if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0) return -1; if((h = gethostbyname(GENTOO_MIRROR))==NULL) return -1; saddr.sin_family = AF_INET; saddr.sin_port = htons(80); bcopy(h->h_addr, &(saddr.sin_addr), h->h_length); if(connect(s, (struct sockaddr *)&saddr, sizeof(saddr))<0) return -1; /* Send the header */ /* NOTE: "Connection: close" is precised in order to not wait * for server deconnection if the size isn't known. */ snprintf(buf, BUFSIZE, "GET /distfiles/%s HTTP/1.1\r\n" "host: %s\r\n" "Connection: Close\r\n" "\r\n" , file, GENTOO_MIRROR); send(s, buf, strlen(buf), 0); /* Get the file */ enum e_stage {GETRESP, GETHEAD, GETDATA}; enum e_stage stage = GETRESP; int size = 0, tsize = 0; while( (r = read(s, buf, BUFSIZE)) ) { buf[r] = '\0'; shift = 0; if(r<0) return -1; if(stage == GETRESP) { check = gf_checkresp(buf); if(check) return check; /* Open the file to save */ if(fd == -1) { fd = open(file, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(fd<0) return -1; } stage = GETHEAD; } if( stage == GETHEAD) { if(strstr(buf, "Content-Length:")) tsize = gf_getlen(strstr(buf, "Content-Length:")); /* NOTE: This may fail if the CRLF is cut on two 'buf' */ if(strstr(buf, "\r\n\r\n")) { shift += strlen(buf)-strlen(strstr(buf, "\r\n\r\n"))+4; stage = GETDATA; } } if(stage == GETDATA) { write(fd, buf+shift, r-shift); if((size += r-shift)==tsize) break; } } close(fd); return 0;}void sendfile(int cfd, char *file){ int fd, r, w; struct stat st; char buf[BUFSIZE]; fd = open(file, O_RDONLY); if(fd<0) connclose(cfd, 500); if(fstat(fd, &st)<0) connclose(cfd, 500); snprintf(buf, BUFSIZE, "HTTP/1.1 200\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n", (int)st.st_size); w = send(cfd, buf, strlen(buf), 0); if(w<0) return; while( (r = read(fd, buf, BUFSIZE)) ) { if(r<0) connclose(cfd, 500); w = send(cfd, buf, r, 0); if(w<0) return; }}void *handler(void *data){ int cfd = (int)data; char file[256]; int v; v = checkquery(cfd, file, 255); if(v==-1){ close(cfd); return NULL; } if(!findfile(file)) { v = getfile(file); if(v== 1) connclose(cfd, 404); if(v==-1) connclose(cfd, 500); } sendfile(cfd, file); sleep(1); /* */ close(cfd); return NULL;}int run(int sfd){ if(sfd<0) return 1; int cfd; pthread_t th; while(1) { cfd = accept(sfd, NULL, 0); if(cfd<0) return 1; if(pthread_create(&th, NULL, handler, (void*)cfd)<0) return 1; if(pthread_detach(th)<0) return 1; }}int main(void){ if(chdir(DISTDIR)<0) return 1; signal(SIGPIPE, SIG_IGN); pid_t pid = fork(); if(pid<0) return 1; else if(pid>0) exit(0); return run(mksfd(PORT));}