rendered paste body#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 <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 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"
"<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));
}