/* * (C) 2009 A. V. Leo. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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[] = "Not Found

Not Found

"; 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( "\na.leo's 100lines webserver: %s\n\n\n" "\n

a.leo's 100lines webserver: %s\n

\n", arg, arg); printf( "
"
            ""
            ""
            ""
            ""
            ""
            "\n");
    while( (e = readdir(d)) ) {
        if(!stat(e->d_name, &st) )
            printf( ""
                    ""
                    "\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( "
Mode:User:Group:Modification Date:File Size:Filename:
%04o%i%i%s%'lu%s%s
" ); 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; }