Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
625 lines (551 sloc) 11.9 KB
;Copyright (C) 1999-2002 Indrek Mandre <indrek@mare.ee>
; Konstantin Boldyshev <konst@linuxassembly.org>
; Rudolf Marek <marekr2@fel.cvut.cz>
;
;$Id: httpd.asm,v 1.23 2006/02/06 06:03:39 konst Exp $
;
;hackers' sub-1K httpd
;
;syntax: httpd document-root port [logfile [err404file] | err404file]
;
;example: httpd /htdocs/ 8888
; lynx http://localhost:8888/
;
; httpd /htdocs/ 8888 /htdocs/httpd.log /htdocs/404.html
;
; - when / is the last symbol in request, appends index.html
; - in case of error just closes connection
; - takes 16kb + 16kb for every request in memory, forks on every request,
; - good enough to serve basic documentation.
;
;I tried to make it as secure as possible, there should be no buffer overflows.
;I at least tried not to make any. It ignores requests with included '..'.
;Here I did a bit testing and it served about 214.8688524590 pages per second.
;Perhaps I am wrong though, but this was the statistics :)
;
;Note that starting from version 0.02 IM no longer maintains httpd.
;Actually it was heavily rewritten since 0.04 and is now maintained by KB;
;however you can still find original IM code and notes throughout the source.
;
;0.01: 17-Jun-1999 initial release (IM)
;0.02: 04-Jul-1999 fixed bug with 2.0 kernel, minor changes (KB)
;0.03: 29-Jul-1999 size improvements (KB)
;0.04: 09-Feb-2000 portability fixes (KB)
;0.05: 25-Feb-2000 heavy rewrite of network code, size improvements,
; portability fixes (KB)
;0.06: 05-Apr-2000 finally works on FreeBSD! (KB)
;0.07: 30-Jun-2000 added support for custom 404 error message,
; enabled by %define ERR404 (KB)
; thanks to Mooneer Salem <mooneer@earthlink.net>
;0.08: 10-Sep-2000 squeezed few more bytes (KB)
;0.09: 16-Jan-2001 added support for "Content-Type: text/plain"
; for .text, .txt, .log and no-extension files,
; enabled by %define SENDHEADER (KB)
;0.10 10-Jan-2002 added logging (IP||HEADER),
; added err404file command line argument,
; more content types (RM),
; added extension-content type table,
; fixed infinite loop if err404file is missing,
; size improvements (KB)
;0.11 14-Mar-2002 added initial cgi support (SL),
; '%' support in filenames (RM),
; send default mimetype for unknown extensions (KB)
;0.12 30-Aug-2002 no longer runs as root if UID defined (JH)
; fixed cgi build problem, sendfile support (saved 21 bytes) (RM)
%include "system.inc"
;Most useful option is SENDHEADER. It could be implemented using external file
;(like usual http servers do), but static implementation has advantages too.
;
;when both LOG and ERR404 are enabled:
; logfile is 3rd command-line argument and err404file is 4th
;when only one of {LOG,ERR404} is enabled:
; corresponding filename is 3rd command-line argument
;So, you must know compile-time configuration, but eventually
;this will be rewritten in a more suitable manner.
;There is a %define variable "CGI" to enable httpd to execve CGI scripts.
;There are some assumptions. Currently, it assumes the file is named ".cgi"
;and in the HTML document root with the other HTML files.
;Probably there should be a separate cgi-bin directory.
;The stdin/stdout of the script will be the socket fd.
;I put an empty environment list. There is no POST method yet;
;in case it is developed, then the data from POST body is passed
;to environment variables perhaps. There are several other environment
;variables for CGI as well, such as HTTP_REFERER, etc..
;Another assumption so far is the server assumes the output is HTML.
;I let the header be sent by the server, but usually the CGI script
;sends its own headers. I think this is easy enough to fix.
;%define SENDHEADER
;%define LOG
;%define ERR404
;%define CGI
;%define PROC_HANDLE ;%
;%define UID 99
%ifdef LOG
%define LOG_HEADER
;%define LOG_DEBUG
%endif
%ifdef __LINUX__
%if __KERNEL__ >=22
%define USE_SENDFILE
%endif
%endif
CODESEG
index_file db "index.html" ;must not exceed 10 bytes!
setsockoptvals dd 1
START:
pop ebp
cmp ebp,byte 3 ;at least 2 arguments must be there
%ifdef ERR404
jb near false_exit
%else
jb false_exit
%endif
pop esi ;our own name
pop dword [root] ;document root
pop esi ;port number
xor eax,eax
xor ebx,ebx
.n1:
lodsb
sub al,'0'
jb .n2
cmp al,9
ja .n2
imul ebx,byte 10
add ebx,eax
jmps .n1
.n2:
xchg bh,bl ;now save port number into bindsock struct
shl ebx,16
mov bl,AF_INET ;if (AF_INET > 0xff) mov bx,AF_INET
mov [bindsockstruct],ebx
%ifdef LOG
sub ebp,byte 3
jz .begin
%elifdef ERR404
sub ebp,byte 3
jz .begin
%endif
%ifdef LOG
pop eax
sys_open eax,O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR
test eax,eax
js .l0
mov [logfd],eax
.l0:
%ifdef ERR404
dec ebp
jz .l4
%endif
%endif
%ifdef ERR404
pop esi
or esi,esi
jz .l4
mov [err404],esi
mov ecx,esi
.l2:
lodsb
or al,al
jnz .l2
sub esi,ecx
dec esi
jz .l4
inc esi
mov [err404len],esi
.l4:
%endif
.begin:
sys_socket PF_INET,SOCK_STREAM,IPPROTO_TCP
test eax,eax
js false_exit
xchg ebp,eax ;socket descriptor
sys_setsockopt ebp,SOL_SOCKET,SO_REUSEADDR,setsockoptvals,4
or eax,eax
jz do_bind
false_exit:
_mov ebx,1
real_exit:
sys_exit
do_bind:
sys_bind ebp,bindsockstruct,16 ;bind(s, struct sockaddr *bindsockstruct, 16)
or eax,eax
jnz false_exit
sys_listen ebp,5 ;listen(s, 5)
or eax,eax
jnz false_exit
%ifdef UID
sys_setgid UID
sys_setuid
%endif
sys_fork ;fork after everything is done and exit main process
or eax,eax
jz acceptloop
true_exit:
_mov ebx,0
jmps real_exit
acceptloop:
mov [arg2],byte 16 ;sizeof(struct sockaddr_in)
sys_accept ebp,arg1,arg2 ;accept(s, struct sockaddr *arg1, int *arg2)
test eax,eax
js acceptloop
xchg edi,eax ;our descriptor
;wait4(pid, status, options, rusage)
;there must be 2 wait4 calls! Without them zombies can stay on the system
sys_wait4 0xffffffff,NULL,WNOHANG,NULL
sys_wait4
%ifdef LOG
; mov edx,arg3
; mov byte [edx],0x10
; sys_getpeername edi,filebuf,arg3
mov eax,[arg1+4]
push edi
mov edi,filebuf+020
mov esi,edi
xchg ah,al
ror eax,16
xchg ah,al
call i2ip
sub esi,edi
inc edi
mov ebx,eax
sys_write [logfd],edi,esi
pop edi
%endif
sys_fork ;we now fork, child goes his own way, daddy goes back to accept
or eax,eax
jz .forward
sys_close edi
_jmp acceptloop
.forward:
sys_read edi,filebuf,0xfff
cmp eax,byte 7 ;there must be at least 7 symbols in request
jb near endrequest
.endrequestnot3:
push eax
%ifdef LOG_HEADER
sys_write [logfd],filebuf,eax
%endif
mov ebx,finalpath
mov ecx,[root]
mov edx,ecx
.back:
;first, copy the document root
mov al,[ecx]
mov byte [ebx],al
inc ebx
inc ecx
cmp byte [ecx],0
jne .back
sub ecx,edx
pop eax
add ecx,eax
cmp ecx,0xfff
ja near endrequest
.endrequestnot2:
;now append the demanded
mov ecx,filebuf+4 ;past "GET "
.loopme:
cmp word [ebx-2],".."
jz near endrequest ;security error, can't have '..' in request
.endrequestnot:
mov dl,[ecx]
;
;"With PROC_HANDLE defined, paths with %20's in will cause erroneous 404 messages"
cmp dl,' '
jz .loopout ; (rhs) Check if end of f'req.
; We HAVE to check for a space HERE, because if we do so in the old place,
; paths with %20's in'em will cause a bad 404 with PROC_HANDLE on.
%ifdef PROC_HANDLE
cmp dl,'%'
jnz .not_proc
call convert_into_dl
.not_proc:
%endif
mov [ebx],dl
or dl,dl
je .loopout
cmp dl,' '
%ifdef CGI
jb toentrequest
%else
jb endrequest
%endif
jz .loopout
cmp dl,'?'
jz .loopout
cmp dl,0xd
jz .loopout
cmp dl,0xa
jz .loopout
inc ebx
inc ecx
jmps .loopme
.loopout:
mov byte [ebx],0
cmp byte [ebx-1],'/'
jne index
mov eax,index_file ;move 10 bytes through FPU stack
fld tword [eax]
fstp tword [ebx]
; mov dword [ebx],"inde" ;append index.html :)
; mov dword [ebx+4],"x.ht"
; mov word [ebx+8],"ml"
index:
%ifdef LOG_DEBUG
mov ecx,finalpath
sub ebx,ecx
sys_write [logfd],EMPTY,ebx
%endif
sys_open finalpath,O_RDONLY
test eax,eax
%ifdef ERR404
js error404
%else
js endrequest
%endif
%ifdef SENDHEADER
call sendheader
%endif
%ifdef CGI
;; If filename ends ".cgi", sys_execve it
push edi
push eax
_mov edi,finalpath
_mov ecx,0xfff ;0xfff is max filename size
_mov eax,0
repne scasb ;find '\0' after URI
sub edi,byte 5 ;".cgi\0"
mov ebx,edi
pop eax
pop edi
cmp dword [ebx],".cgi"
jnz sendnoncgi
call execcgi
toentrequest:
jmp endrequest ;should log error instead
sendnoncgi:
%endif
mov ebx,eax
mov esi,eax
mov ecx,filebuf
%ifdef USE_SENDFILE
;sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
push byte 0 ;we can leave this on stack
sys_sendfile edi,eax,esp,0xffffffff ;Linux 2.2+ support this
sys_close ecx
%else
.writeloop:
sys_read EMPTY,EMPTY,0xfff
test eax,eax
js .endread
sys_write edi,EMPTY,eax
mov ebx,esi
test eax,eax
jz .endread
jns .writeloop
.endread:
sys_close
%endif
;due the stupidity of netscape we need to send another packet newline \n,
;so it can handle one line data but I'm afraid it might break something,
;so watch this code carefully in the future
; sys_write edi,nl,1
endrequest:
; sys_read ebp,filebuf,0xff
; sys_shutdown ebp,1 ;shutdown(sock, how)
; sys_close ebp
jmp true_exit
;nl db 0xa
%ifdef ERR404
error404:
pusha
mov ecx,[err404len]
or ecx,ecx
jz .end
mov esi,[err404]
mov edi,finalpath
push ecx ;save values
push esi
push edi
rep cmpsb ;check if we can't open ourself
jz .end0
pop edi
pop esi
pop ecx
rep movsb ;copy to finalpath
popa
jmp index
.end0:
add esp,byte 4*3
.end:
popa
jmp endrequest
%endif
%ifdef LOG
i2ip:
std
mov byte [edi],__n
dec edi
.next:
mov ebx,eax
call .conv
xchg eax,ebx
mov al,'.'
stosb
shr eax,8
jnz .next
cld
inc edi
mov byte [edi],' '
ret
.conv:
mov cl,010
.divide:
xor ah,ah
div cl ;ah=reminder
xchg ah,al
add al,'0'
stosb
xchg ah,al
or al,al
jnz .divide
ret
%endif
%ifdef SENDHEADER
h1 db "HTTP/1.1 200 OK",__n
db "Server: asmutils httpd",__n
db "Content-Type: "
_lenh1 equ $ - h1
%assign lenh1 _lenh1
c_plain db "text/plain",EOL
c_html db "text/html",EOL
c_jpeg db "image/jpeg",EOL
c_png db "image/png",EOL
c_gif db "image/gif",EOL
c_def db "application/octet-stream",EOL
ending db __n,__n
extension_tab:
dd "text", c_plain
dd "txt", c_plain
dd "log", c_plain
dd "html", c_html
dd "htm", c_html
dd "jpeg", c_jpeg
dd "jpg", c_jpeg
dd "png", c_png
dd "gif", c_gif
%ifdef CGI
dd "cgi", c_html
%endif
dd 0, c_def
sendheader:
pusha
mov esi,finalpath
mov ebx,esi
.cc1:
lodsb
or al,al
jnz .cc1
.cc2:
dec esi
cmp esi,ebx
jz .return0
cmp byte [esi],'.'
jnz .cc2
.return0:
mov eax,[esi + 1]
mov edx,extension_tab - 8
.cc3:
add edx,byte 8
mov ecx,[edx]
or ecx,ecx
jz .write_content
cmp eax,ecx
jnz .cc3
.write_content:
push edx
sys_write edi,h1,lenh1 ;header
pop edx
mov ecx,[edx + 4]
mov edx,ecx
dec edx
.cc5:
inc edx
cmp [edx],byte EOL
jnz .cc5
sub edx,ecx
sys_write ;write content type
sys_write EMPTY,ending,2
.return:
popa
ret
%endif
%ifdef CGI
execcgi:
pusha
;; Make the socket stdin/stdout for the CGI program
sys_dup2 edi,STDIN
sys_dup2 EMPTY,STDOUT
sys_close
;; Execute the CGI program.
;; argv[0] is finalpath and env is NULL. (?)
mov eax,finalpath
mov ecx,execve_argv
mov [ecx],eax
sys_execve [ecx],ecx,0
;; Hopefully we don't get here
popa
ret
%endif
%ifdef PROC_HANDLE
;ecx - source % in our case
convert_into_dl:
push eax
inc ecx
xor eax,eax
xor edx,edx
mov al,[ecx]
call .check_b
mov dl,al
shl dl,4
inc ecx
mov al,[ecx]
call .check_b
add edx,eax
pop eax
ret
.check_b:
sub al,'0'
jb endrequest_jmp
cmp al,010
jb .ok
and al,0dfh ;force upper case
cmp al,010h
jz endrequest_jmp
cmp al,'F'-'0'
ja endrequest_jmp
add al,9 ;ok, add 9 for strip
.ok:
and al,0fh ;strip high 4 bits
ret
endrequest_jmp: jmp endrequest
%endif
UDATASEG
%ifdef ERR404
err404len resd 1 ;filename length
err404 resd 1 ;pointer
%endif
%ifdef LOG
logfd resd 1
%endif
%ifdef CGI
execve_argv resd 2 ;two ptrs, argv[0] and NULL
%endif
arg1 resb 0xff
arg2 resb 0xff
root resd 1
bindsockstruct resd 4
finalpath resb 0x1010 ;10 is for safety
filebuf resb 0x1010
END
You can’t perform that action at this time.