All pastes #1617723 Raw Edit

Someone

public c v1 · immutable
#1617723 ·published 2009-10-13 13:12 UTC
rendered paste body
/* * (C) 2009 A. V. Leo. */#include <stdio.h>#include <stdlib.h>#include <time.h>#include <fcntl.h>#include <netdb.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <sys/wait.h>#include <sys/stat.h>#include <sys/sendfile.h>#include <sys/socket.h>#include <dirent.h>#include <locale.h>static void sig_chld(int sig){    int status;    (void)sig;    waitpid(-1, &status, WNOHANG);    fprintf(stderr, "child status: %i\n", status);}static unsigned char_to_num(char s){    int r = 0;    if( s >= '0' && s <= '9' )        r = s - '0';    else if (s >= 'A' && s <= 'F')        r = s - 'A' + 10;    else if (s >= 'a' && s <= 'f' )        r = s - 'a' + 10;    else fprintf(stderr, "char_to_num(): error: unable to decode %c\n", s);    return r;}static unsigned char dtochar(unsigned char* s){    return char_to_num(s[1]) + char_to_num(s[0])*16;}static unsigned char* dequote_r(unsigned char* in, unsigned char* out, int out_size){    unsigned char* o = out;    unsigned char* i = in;    int in_size = strlen((char*)in);    --out_size;    while(*i && (o - out) < out_size && (i - in < in_size)) {        switch(*i) {            case '+':                *o++ = ' ';                break;            case '%':                {                    if( (i - in < in_size - 2 ) ) {                        unsigned char s[2];                        s[0] = *++i; s[1] = *++i;                        *o++ = dtochar(s);                    }                    else {                        *o++ = *i;                        fprintf(stderr, "unable to decode '%s'/premature eof\n", in);                    }                }                break;            default:                *o++ = *i;        }	++i;    }    *o = 0;    return out;}static char* quote_r(const char* in, char* out, int out_size){    char* o = out;    const unsigned char* i = (unsigned char*)in;    if(!in || !out || out_size < 2)    {        fprintf(stderr, "quote(): invalid arguments passed!\n");        return "";    }    while( *i && (o - out) < (out_size - 2))    {        switch(*i)        {            case 'a' ... 'z':            case 'A' ... 'Z':            case '0' ... '9':            case '_':            case '-':	    case '.':	    case ',':	    case '/':                *o++ = *i++;                break;            default:                {                    const char* fmt = "0123456789ABCDEF";                    if( (o - out) >= (out_size - 4) )                    {                        fprintf(stderr, "error: no space for quoting (out_size: %i, pos: %i, encoded: %s remainder: %s)\n",					out_size, (o - out), out, i);                        goto quote_out;                    }                    *o++ = '%';                    *o++ = fmt[0xf & (*i >> 4)];                    *o++ = fmt[0xf & *i++];                }        }    }quote_out:    *o = '\0';    return out;}static const char* quote(const char* in){    static char res[4096];    return (const char*)quote_r(in, res, sizeof(res) - 1);}static const char* dequote(const char* in){    static char res[4096];    if(in)        return dequote_r(in, res, sizeof(res)-1);    return "";}static void do_404(char* ver){    unsigned char msg[] = "<html><head><title>Not Found</title><body><h1>Not Found</h1></body></html>";    printf("%s 404 NOT FOUND\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %lu\r\n\r\n%s",            ver, sizeof(msg) - 1, msg);    exit(0);}static void process_send_file(char* ver, unsigned char* arg, struct stat* st){    int fd = open((char*)arg, O_RDONLY);    if( fd == -1 ) do_404(ver);    printf("%s 200 OK\r\nConnection: close\r\nContent-Length: %i\r\n\r\n", ver, (int)st->st_size); fflush(stdout);    sendfile(1, fd, 0, st->st_size);    close(fd);    exit(0);}static int is_file(char* path){    struct stat st;    return ( !access((char*)path, F_OK) && !stat((char*)path, &st) && S_ISREG(st.st_mode));}static void process_list_directory(unsigned char* ver, unsigned char* arg){    struct dirent* e;    struct stat st;    DIR* d = opendir((char*)arg);    if( !d || chdir((char*)arg) ) do_404(ver);    printf( "%s 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n", ver);    fflush( stdout);    printf( "<html><head>\n<title>a.leo's 100lines webserver: %s\n</title>\n</head>\n"            "<body>\n<h1>a.leo's 100lines webserver: %s\n</h1>\n", arg, arg);    printf( "<pre><table>"            "<tr><td>Mode:</td>"            "<td>User:</td>"            "<td>Group:</td>"            "<td>Modification Date:</td>"            "<td>File Size:</td>"            "<td>Filename:</td></tr>\n");    while( (e = readdir(d)) ) {        if(!stat(e->d_name, &st) )            printf( "<tr><td align=\"right\">%04o</td><td alight=\"right\">%i</td>"                    "<td align=\"right\">%i</td><td align=\"right\">%s</td><td align=\"right\">%'lu</td>"                    "<td><a href=\"%s%s%s\">%s%s</a></td></tr>\n",                    0777 & st.st_mode, st.st_uid, st.st_gid, ctime(&st.st_mtime), (long)st.st_size,                    /*arg +(*arg == '/')*/"",                    quote(e->d_name), is_file(e->d_name)?"":"/", e->d_name, is_file(e->d_name)?"":"/");    }    printf( "</table></pre></body> </html>" );    fflush( stdout );    closedir(d);    exit(0);}static void process_get(unsigned char* ver, unsigned char* arg){    struct stat st;    fprintf(stderr, "arg: %s\n", arg);    arg = dequote(arg);    fprintf(stderr, "dequoted: %s\n", arg);    if( 0 == access((char*)arg, R_OK) && stat((char*)arg, &st) == 0) {        if( S_ISREG(st.st_mode) )            process_send_file(ver, arg, &st);        else if (S_ISDIR(st.st_mode) )            process_list_directory(ver, arg);        else            do_404(ver);    }    do_404(ver);}static int process_post(char* ver, char* arg, char* str, int str_len){    do_404(ver);}static void process_connect(){    unsigned char rq[201], ver[201], arg[2001], str[1024];    int r;    if( (r = read(1, str, sizeof(str)-1)) > 0 )  str[r] = 0; else do_404("HTTP/1.0");    fprintf(stderr, "GOT: %s\n", str);    if( strchr((char*)str, '\r')) *strchr((char*)str, '\r') = 0; else do_404("HTTP/1.0"); /* XXX */    /* XXX */    if( 3 != sscanf( str, "%200s %2000s %200s\r\n", rq, arg, ver) ) do_404("HTTP/1.0");    if( !strcmp(rq, "GET"))        process_get(ver, arg);    if( !strcmp(rq, "POST"))        process_post(ver, arg, str, r);       do_404(ver);}void config_stub(void){    const char* web_chroot = getenv("L_CHROOT");    const char* web_user = getenv("L_USER");    int uid;    if(geteuid() == 0)        setuid(0);    if(web_user == NULL)        web_user = "nobody";    if(web_chroot)    {        fprintf(stderr, "chrooting to %s\n", web_chroot);        if( chroot(web_chroot) != 0 )            perror("chroot");    }    if(web_user)    {        int uid = atoi(web_user);    }//    else         uid = 99;        fprintf(stderr, "dropping privilegies, uid=%i\n", uid);        if(seteuid(uid))            perror("seteuid");        if(setuid(uid))            perror("setuid");}int main(int n, char* a[]){    int r, s, fd;    struct addrinfo hints, *res;    const char* default_listen_addr = "localhost";    const char* default_port = "8888";    memset(&hints, 0, sizeof(hints));    hints.ai_socktype = SOCK_STREAM;    setlocale(LC_ALL, "");    config_stub();    if( (r = getaddrinfo( (n>2)?a[2]:default_listen_addr, (n>1)?a[1]:default_port, &hints, &res))) {        printf( "gai: %s\n", gai_strerror(r)); exit(5);    }    if( -1 == (s = socket(PF_INET, SOCK_STREAM, 0)) )   { perror("socket");  exit(1); }    r =  1;   setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r));    if( -1 == bind(s, res->ai_addr, res->ai_addrlen))   { perror("bind"); close(s); exit(2); }    if( -1 == listen(s, 5) )                            { perror("listen"); close(s); exit(3); }    freeaddrinfo(res);    signal(SIGCHLD, sig_chld);    while( (fd = accept(s, NULL, NULL)) != -1 ) {        int p = fork();        if(-1 == p) { close(fd); usleep(1000*50); continue; }        if(!p) {            dup2(fd, 0); dup2(fd, 1); /*dup2(fd, 2);*/            process_connect();        } else close(fd);        waitpid(-1, &p, WNOHANG);    }    close(s);    return 0;}