All pastes #912605 Raw Edit

SEXY: Small package proxy for ge

public text v1 · immutable
#912605 ·published 2008-02-21 14:23 UTC
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));
}