#if 0 #!/bin/env sh [ -e /etc/make.conf ] && source /etc/make.conf echo gcc -Wall $CFLAGS -pthread -o sexy main.c gcc -Wall $CFLAGS -pthread -o sexy main.c exit #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 #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 4096 #define TIMEOUT 3 int 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" "\n" "%d\n" "\n" "

%d

\n" "\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)); }