Спятил, да? А ещё если напишу, что планировал сделать многопоточность и нечто CGI-интерфейса? Вполне может быть, в рамках just for fun
Но остановился на том, чтоб отдавать статические страницы - время не дало допилить, да и энтузиазм уменьшился. Идея мне пришла из за курсовой работа по "Системному программированию" В СПбГУ ИТМО. Всех заставили писать резидент под DOS, на 8086 архитектуре. Не торт.
Основные фитчи, которые я успел реализовать:
- Форк, при необходимости
- Чтение конфига(ini-like)
- Создание слушающего сокета, установка параметров
- Парсер заголовков и запроса
- Обработчик запросов статического контента(не в /cgi-bin/)
- В некоторых местах совершенно корявую работу со стеком
Реализовано на NetwideASM, c частичным использованием функций glibc. Работать сервер будет только на 32-битном процессоре i686+, из за того, что дёргается прерывание ядра Linux - а номера функций отличаются в зависимости от процессора.
Алгоритм работы - довольно очевидный: пришло соединение, получили запрос, разобрали, прочитали запрашиваемый файл, выдали содержимое в сокет, закончили, в ожидании нового клиента. Сразу про строку запроса - на выход из DocumentRoot НЕ проверяется - это потенциальная дыра. [offtop]Я же говорил, энтузиазм вместе со временем закончились быстро.[/offtop]. Отсутствие файла так же не обрабатывается - клиенту выдаётся пустой ответ
Начиная(по иерархии) с парсера запроса память выделяется только из стека или из кучи - это прямой путь к многопоточности. И вроде даже корректно везде освобождается.
Файлы названы по смыслу, где то иногда мелькают переменные, со странным содержимым - это для отладки. А ещё в коде очень много комментариев, на английском есс-но. Плохом английском. Особенно мне надоело писать комменты к "add esp, 4 * n" - потому как очень много вызовов glibc, следовательно много раз нужно подчищать стек. Лучше чаще чем реже, но запутаться в один прекрасный момент, а потом искать в gdb где находится бага.
GDB заслуживает отдельных ругательств слов. Это не удобный, очень сложный и гибкий отладчик. К ему сложно, но вполне реально привыкнуть. Хbотя взгляд моего знакомого - профессионального дизассемблерщика(если можно так назвать), который всегда пользовался IDA и увидел gdb - не передать :) Спасибо, Тимофей(killer_of_mouse)!
Screenshot
Чтобы разбавить сухой текст описания - скриншот отданной страницы:
Source
Дальше - много много кода. Аккуратнее :)
aweb.asm
EXTERN kSysExit, kFork, loadConfig, startupMessage, sock
EXTERN cfgBindIp, cfgBindPort, isDoFork, strProcessForked
SECTION .code
GLOBAL _start ; program start point
_start:
mov eax, dword [isDoFork] ; should we do fork() ?
cmp eax, 0 ; really?
jz .afterFork ; no, just continue loading
.doFork:
call kFork ; do fork()
cmp eax, 0 ; 0 means we are forked
je .afterFork ; run the server
jmp .exitProgram ; parent process should exit
.afterFork:
call loadConfig ; load configuration from file
call startupMessage ; echo startup message to console
call sock ; create and listen network socket
.exitProgram:
call kSysExit ; stop the process
config.asm
; External functions
EXTERN kSysExit
; External C functions
EXTERN fgets, fopen, fclose, feof, printf
EXTERN strchr, strlen, strtok, strcmp, atoi, strcpy
EXTERN inet_addr
; External data
EXTERN strFileNotFound, strTerminating, fOpenRead, fileConfig
; Exported symbols
GLOBAL loadConfig:FUNCTION
GLOBAL cfgBindPort:DATA, cfgBindIp:DATA, cfgDocumentRoot:DATA, cfgRoot:DATA
SECTION .data
cfgBindIp: dd 127 << 0 | 0 << 8 | 0 << 16 | 1 << 24
cfgBindPort: dd 808
cfgRoot: db '/srv/www/local/'
times 120 db 0
SECTION .data
var:
.fHandle dd 0
.num db 'num = %d', 10, 0
.pair db '|%s = %s|', 10, 0
.hint db ' hint = "%s"', 10, 0
.hintd db ' hint = "%d"', 10, 0
.delimiter db '= ', 0
.key dd 0
.value dd 0
.kPort db 'port', 0
.kIp db 'ip', 0
.kRoot db 'root', 0
SECTION .bss
buffLen: equ 1024
buff: times buffLen db
SECTION .code
; procedure load and parse configuration data
loadConfig:
push dword fOpenRead ; mode - read
push dword fileConfig ; config location
CALL fopen ; do fopen()
add esp, 2 * 4 ; correct stack
mov [var.fHandle], eax ; var.fHandle = eax
cmp eax, 0 ; eax ?
jne .readFile ; eax != 0 - that's ok!
.cantOpen:
; Output error message
push dword fileConfig
push dword strFileNotFound
CALL printf
add esp, 2 * 4
push dword strTerminating
CALL printf
add esp, 4
CALL kSysExit ; quit...
jmp .endProc
.readFile:
.checkEof: ; Check for end of file
push dword [var.fHandle] ; push file handle
CALL feof ; feof()
add esp, 4 ; correct stack head
test eax, eax ; eax?
jnz .closeFile ; EOF?!
.getLine: ; get 1 line from file
push dword [var.fHandle] ; push handle again
push dword buffLen ; buffer length
push dword buff ; buffer pointer
CALL fgets ; call fgets()
add esp, 4 * 3 ; corrent stack
test eax, eax ; check for error
jz .readFile ; eax = 0 means error
.correctString: ; Correct end of string - where '#' located
push dword '#' ; symbol
push dword buff ; config string
CALL strchr ; call strchr()
add esp, 4 * 2 ; stack correct
cmp eax, 0 ; eax == 0 ?
je .checkLength ; so check length next
mov [eax], byte 0 ; set end of string
.checkLength: ; check for zero length
push dword buff ; string from file
CALL strlen ; strlen()
add esp, 4 ; esp+=4..
test eax, eax ; what about eax?
jz .readFile ; It's empty string!?
.removeChr10: ; remove end-of string symbol
push dword 10 ; symbol
push dword buff ; config string
CALL strchr ; call strchr()
add esp, 4 * 2 ; stack correct
test eax, eax
jz .isKeyValuePair
mov [eax], byte 0
.isKeyValuePair: ; Search for '=', which split name = value" pair
push dword '=' ; symbol
push dword buff ; config string
CALL strchr ; call strchr()
add esp, 4 * 2 ; stack correct
test eax, eax ; eax?!
jz .readFile ; if eax == 0, read next line
.split: ; Splits pair
push dword var.delimiter ; push addr to delimiter '='
push dword buff ; string from config file
CALL strtok ; strtok()
add esp, 4 * 2 ; esp += 8
mov [var.key], eax ; key string
push dword var.delimiter; string
push dword 0 ; push NULL to retrive next token
CALL strtok ; strtok(NULL, delimiter)
add esp, 4 * 2 ; esp += 8
mov [var.value], eax ; value string
.checkForPort: ; is key == port?
push dword [var.key] ; string 1
push dword var.kPort ; string 2
CALL strcmp ; do compare
add esp, 4 * 2 ; fix stack
test eax, eax ; what about eax?
jne .checkForIp ; not equals? next check
push dword [var.value] ; addr of string with number
CALL atoi ; do calculate integer value
add esp, 4 ; correct stack
mov [cfgBindPort], ax ; save value
jmp .checkEnd ; we are done
.checkForIp: ; is key == ip?
push dword [var.key] ; string 1
push dword var.kIp ; string 2
CALL strcmp ; do strcmp()
add esp, 4 * 2 ; correct stack
test eax, eax ; what about eax?
jne .checkForRoot ; not equals? next check
push dword [var.value] ; ip addr
CALL inet_addr ; do transform
add esp, 4 ; correct stack
mov [cfgBindIp], eax ; save value
.checkForRoot: ; is key == root?
push dword [var.key] ; string 1
push dword var.kRoot ; string 2
CALL strcmp ; do strcmp()
add esp, 4 * 2 ; correct stack
test eax, eax ; what about eax?
jne .checkEnd ; not equals? next check
push dword [var.value] ; copy from
push dword cfgRoot ; copy to
CALL strcpy ; copy config value item
add esp, 4 * 2 ; correct stack
.checkEnd:
jmp .readFile
push dword [var.value]
push dword [var.key]
push dword var.pair
call printf
add esp, 4 * 3
jmp .readFile ; read next line
.closeFile:
push dword [var.fHandle] ; file handle to close
CALL fclose ; make fclose()
add esp, 4 ; stack..
.endProc:
ret
consts.asm
SEGMENT .rodata
GLOBAL fOpenRead:DATA, fOpenWrite:DATA, fOpenAppend:DATA
fOpenRead: db 'r', 0 ; read only flag
fOpenWrite: db 'w', 0 ; write flag
fOpenAppend: db 'a', 0 ; append file
GLOBAL strFileNotFound:DATA, strTerminating:DATA, strDebugGeneral:DATA
GLOBAL strStartMsg:DATA, strProcessForked:DATA, strHandlerStatic:DATA
strFileNotFound: db 'File "%s" is not found!', 10, 0
strTerminating: db 'Terminating application..', 10, 0
strDebugGeneral: db '%s: %s', 10, 0
strStartMsg: db 10, 'Starting up server, build date: ', \
__DATE__, ' ',__TIME__, \
'. Bind to %s:%d', 10, 0
strProcessForked: db 'Process forked, pid: %d', 10, 0
strHandlerStatic: db 'handlerStatic: %s', 10, 0
GLOBAL fileDebug:DATA, fileConfig:DATA
fileDebug: db './debug.log',0 ; file for debug info
fileConfig: db './aweb.conf',0 ; config file
GLOBAL maxConnections:DATA
maxConnections: dd 1024
GLOBAL maxHeaders:DATA
maxHeaders: dd 1024
GLOBAL cgiBinFolder:DATA
cgiBinFolder: db '/cgi-bin', 0 ; prefix for cgi programs
GLOBAL headerHTTP200:DATA, headerStd:DATA, headerServer:DATA
headerHTTP200: db 'HTTP/1.0 200 OK', 10, 13, 0
headerServer: db "Server: ruX's simple web server written in NASM", 10, 13, 0
headerStd:
db 'Connection: close', 10, 13
db 'Cache-Control: no-cache,no-store,max-age=0,must-revalidate', 10, 13
db 10, 13
db 0
GLOBAL isDoFork:DATA
isDoFork: dd 1
sockets.asm
EXTERN cfgBindIp, cfgBindPort, maxConnections, headerServer
EXTERN htons, shutdown, fprintf, printf, malloc, free, realloc, strlen
EXTERN kSocketCall, kClose, processRequest
GLOBAL sendServerHeader:FUNCTION
; Constants used for arguments(from kernel source)
%define AF_INET 2
%define IPPROTO_TCP 6
%define SOCK_STREAM 1
%define PF_INET 2
%define MSG_PEEK 2
%define MSG_WAITALL 100h
%define SOL_SOCKET 1
%define SO_REUSEADDR 2
; Function numbers
%define SYS_SOCKET 1
%define SYS_BIND 2
%define SYS_LISTEN 4
%define SYS_ACCEPT 5
%define SYS_SEND 9
%define SYS_RECV 10
%define SYS_SETSOCKOPT 14
; Step increase memory for receive buffer
%define REALLOC_STEP 64
SECTION .data
connect db "We have a connection!", 10, 0 ; First string...
hello db "Hello... =]", 10, 0 ; Second string...
goodbye db "Goodbye... =[", 0 ; Third string...
socksbroke db "ERROR: socket() failed!", 10, 0 ; Fourth string...
bindsbroke db "ERROR: bind() failed!", 10, 0 ; Fifth string...
listensbroke db "ERROR: listen() failed!", 10, 0 ; Sixth string...
acceptsbroke db "ERROR: accept() failed!", 10, 0 ; Seventh string...
retval db "value buffer: %d, recv: %d", 10, 0
retstr db "recived: %s", 10 , 0
html db "HTTP 1.1", 10, 0, '$'
html_len equ $ - html
one dd 1
sa: ; sockaddr_in structure
.sin_family dw 0
.sin_port dw 0
.sin_addr dd 0
sock_args: ; socket() function argument
dd PF_INET, SOCK_STREAM, IPPROTO_TCP
bind_args: ; bind() function arguments
.fd dd 0 ; socket handle
.sockaddr dd sa ; pointer to sockaddr structure
.socklen_t dd 16 ; socklen_t addrlen
listen_args: ; listen() function arguments
.sock dd 0 ; sock handle
.backlog dd maxConnections ; max connections
accept_args: ; accept() arguments
.sockfd dd 0 ; socket handle
.addr dd 0 ; struct sockaddr *addr
.addrlen dd 0 ; socklen_t *addrlen
sockopts_args: ; setsockopt() and getsockopt() args
.sockfd dd 0 ; socket handle
.level dd SOL_SOCKET ; manipulation level
.optname dd SO_REUSEADDR ; option in selected level
.optval dd one ; option value
.optlen dd $ - one ; size of option value
socketfd: dd 0 ; socket handle
section .code ; Section Declaration
GLOBAL sock
sock:
; mov ebp, esp
;int socket(int domain, int type, int protocol);
.doSocket:
mov ebx, SYS_SOCKET ; socket() = int call 1
mov ecx, dword sock_args ; arguments to socket()
CALL kSocketCall
mov [socketfd], eax ; save socket handle
;
cmp eax, -1
je sockerr
;int setsockopt(int s, int level, int optname,
; const void *optval, socklen_t optlen);
.setSockOpt:
; ignoring port in state TIME_WAIT
mov ebx, SYS_SETSOCKOPT
mov eax, dword [socketfd]
mov [sockopts_args.sockfd], eax
mov ecx, dword sockopts_args
CALL kSocketCall
cmp eax, -1
je sockerr
;int bind(int socket, const struct sockaddr *address,
; socklen_t address_len);
.doBind:
; fillup sockaddr
mov [sa.sin_family], word AF_INET ; address family
mov al, byte [cfgBindPort+1] ; reverse bytes hi=>lo
mov ah, byte [cfgBindPort] ; reverse bytes lo=>hi
mov [sa.sin_port], ax ; set port
mov eax, dword [cfgBindIp] ; get ip from config
mov [sa.sin_addr], eax ; set ip to structure
; form arguments
mov eax, dword [socketfd] ; copy socket
mov [bind_args.fd], eax ; handle to struct
mov ebx, SYS_BIND ; require bind()
mov ecx, bind_args ; bind arguments
CALL kSocketCall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Do a little error-echecking here...
cmp eax, -1
je binderr
; int listen(int socket, int backlog);
.doListen:
mov eax, [socketfd] ; copy socket handle
mov [listen_args.sock], eax ; to structure listen_args
mov ebx, SYS_LISTEN ; require listen()
mov ecx, listen_args ; arguments
CALL kSocketCall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Do a little error-echecking here...
cmp eax, -1
je listenerr
;accept(fd, NULL, 0);
.doAccept:
mov eax, [socketfd] ; copy socket handle
mov [accept_args.sockfd], eax ; to structure
mov ebx, SYS_ACCEPT ; we need accept() function
mov ecx, accept_args ; ptr to arguments
CALL kSocketCall
; Do a little error-echecking here...
cmp eax, -1
je accepterr
.processConnection: ; here we have a connected socket peer in eax
push eax
CALL connectionHandler
jmp .doAccept
;
;exit_s(0)
exit_s:
; mov esp, ebp
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; These are just to print an error message to stdout should
; one of our socketcall()'s fail.
sockerr:
mov eax, 4
mov ebx, 1
mov ecx, socksbroke
mov edx, 24
int 0x80
jmp exit_s
binderr:
mov eax, 4
mov ebx, 1
mov ecx, bindsbroke
mov edx, 22
int 0x80
jmp exit_s
listenerr:
mov eax, 4
mov ebx, 1
mov ecx, listensbroke
mov edx, 24
int 0x80
jmp exit_s
accepterr:
mov eax, 4
mov ebx, 1
mov ecx, acceptsbroke
mov edx, 24
int 0x80
jmp exit_s
; connection handler function
; get 1 arg - socket handle
connectionHandler:
push ebp
mov ebp, esp ; preserve esp
%define sock dword [ebp+8] ; connected socket
jmp .initVars
.vars: ; declare variables (allocate memory for each)
; begin recv_args structure (argument for recv function)
%define recv_args 8 ; main offset from esp
%define recv_args_sock esp+recv_args
%define recv_args_buffer esp+4+recv_args
%define recv_args_length esp+8+recv_args
%define recv_args_flags esp+12+recv_args
sub esp, 16 ; allocate memory for recv_args
%define send_args 16
%define send_args_sock 16
.initVars:
mov eax, sock
mov [recv_args_sock], eax
mov [recv_args_flags], dword MSG_PEEK
mov [recv_args_length], dword REALLOC_STEP
.code:
.alloc:
push dword [recv_args_length]; count of bytes we need
CALL malloc ; allocate memory from heap
add esp, 4 ; correct stack
mov [recv_args_buffer], eax ; save pointer to buffer
.recvLength:
mov ebx, SYS_RECV ; we need recv()
mov ecx, esp
add ecx, recv_args ; arguments
CALL kSocketCall
cmp eax, dword -11 ; try again code?
je .recvLength ; recieve again
mov edx, eax
mov ebx, dword [recv_args_length]
push edx
push ebx
push retval
call printf
add esp, 8
pop eax
cmp eax, [recv_args_length]
jb .recv
.realoc:
add [recv_args_length], dword REALLOC_STEP
mov eax, dword [recv_args_buffer]
push dword [recv_args_length]
push eax
CALL realloc
add esp, 2 * 4
mov [recv_args_buffer], eax
jmp .recvLength
.recv:
mov ebx, SYS_RECV ; we need recv()
mov [recv_args_flags], dword 0 ; without flags!
mov ecx, esp ; point ecx ..
add ecx, recv_args ; .. to arguments
CALL kSocketCall ; do recv()
mov [recv_args_length], eax ; store received message length
cmp eax, 0 ; is there any error?
jbe .closeSocket ; then cancel request
push dword [recv_args_buffer]
push dword sock
CALL processRequest ; let's process request!
.closeSocket:
push sock
call kClose
.freeMem:
mov eax, dword [recv_args_buffer]
push eax
CALL free
add esp, 8
mov [recv_args_length], dword 0 ; mark block as free
.return:
cmp [recv_args_length], dword 0 ; we used malloc?
jne .freeMem ; yeah, so we need free memory
mov esp, ebp ; restore stack poiter
pop ebp
ret 4
;write(1, "We have a connection!", 22);
mov eax, 4 ; write() syscall
mov ebx, 1 ; stdout
mov ecx, connect ; our string
mov edx, 22 ; 22 characters in length
int 0x80 ; Call the kernel
;
;write(fd, "Hello... =]", 12);
mov eax, 4 ; write() syscall
mov ebx, edi ; sockfd
mov ecx, hello ; our string to send
mov edx, 12 ; 12 characters in length
int 0x80 ; Call the kernel
;push dword hello
;push dword edi
;call fprintf
;add esp, 8
;
;write(fd, "Goodbye... =[", 14);
mov eax, 4 ; write() syscall
mov ebx, edi ; sockfd
mov ecx, goodbye ; our string to send
mov edx, 14 ; 14 charactes in length
int 0x80 ; Call the kernel
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Now we clean up (Close the open file descriptor/s etc.) and exit_s
push dword edi
Call shutdown
pop eax
;close(fd)
push edi
CALL kClose
mov eax, 0
ret
; send to socket 'Server' header
; first argument in stack - sock handle
sendServerHeader:
push ebp
mov ebp, esp
push dword headerServer ; string to send
call strlen ; get string length
add esp, 4 ; correct stack
push dword 0 ; flags
push eax ; length
push dword headerServer ; header string
push dword [ebp+8] ; socket handle
mov ecx, esp ; ptr to struct in stack
mov ebx, 9 ; send() function number
call kSocketCall ; call it
add esp, 4 * 4 ; correct stack space
mov esp, ebp ; restore base stack pointer
pop ebp
ret 4
helpers.asm
; debugLine TYPE, INFO
%macro debugLine 2
EXTERN fileDebug, fopen, fclose, fprintf, fOpenAppend, strDebugGeneral
jmp %%code ; go to code area
%%type: db %1, 0 ; type of message
%%info: db %2, 0 ; message
%%code:
pusha ; save registers
push fOpenAppend ; just 'a\0' string
push fileDebug ; filename
CALL fopen ; call fopen()
add esp, 2 * 4 ; clean stack
push eax ; small trick - push arg for fclose
push %%type ; offset to first %s
push %%info ; second %s
push strDebugGeneral; format string
push eax ; file handle
CALL fprintf ; fprintf()
add esp, 4 * 4 ; correct stack only for fprintf
CALL fclose ; call fclose(), handle pushed before
add esp, 4 ; correct stack
%%endmacro:
popa ; restore back registers
%endmacro
requests.asm
GLOBAL processRequest
EXTERN strlen, printf, fprintf, strtok, malloc, memset, strchr, free, strncmp, send
EXTERN strDebugGeneral, maxHeaders, cgiBinFolder, handlerStatic
EXTERN kSocketCall
SEGMENT .data
header_sep: db 10, 13, 0
juststr: db "%s",10,0
SEGMENT .code
STRUC tRequest, -30
.filename: resd 1
.method: resd 1
.socket: resd 1
.request: resd 1
.requestLen: resd 1
ENDSTRUC
; Process user requests
; arguments: socket/(dword) request(dword,ptr)
processRequest:
push ebp ; preserve ebp
mov ebp, esp ; new local offset
sub esp, 30 ; space for structure in stack
%define lv_offset 30
sub esp, lv_offset
%define headers ebp - 30 - lv_offset + 0
%define i ebp - 30 - lv_offset + 4
%define j ebp - 30 - lv_offset + 8
%define buf ebp - 30 - lv_offset + 12
%define len ebp - 30 - lv_offset + 16
%define str ebp - 30 - lv_offset + 20
mov eax, dword [ebp + 8] ; get socket id from stack
mov dword [ebp + tRequest.socket], eax
mov eax, dword [ebp + 12] ; get request string
mov dword [ebp + tRequest.request], eax
push dword [ebp + tRequest.request] ; calculate
call strlen ; request length
add esp, 4 ; cleanup stack
mov dword [ebp + tRequest.requestLen], eax ; remember length
push dword 0
push '->> '
mov eax, esp
push dword 0
push '<<- '
push esp
push eax
push strDebugGeneral
call printf
add esp, 4 * 3
.allocBuffers:
;.. for header line
push dword 1 << 14 ; 16Kb
call malloc ; requre some memory
add esp, 4 ; correct stack
mov [buf], eax ; save pointer to memory block
mov [eax], dword 0 ; length = 0
; ..for array of pointers to string(enviroment)
mov eax, dword [maxHeaders]
mov bl, 4
mul bl
push eax
call malloc
mov [headers], eax
push dword 0
push dword [headers]
call memset
add esp, 4 * 3
; ..for url buffer
push dword 2048 ; maximum url length (rfc)
call malloc ; do allocate
add esp, 4 ; correct stack
mov [ebp + tRequest.filename], eax ; save point to url
.breakHeaders:
push dword header_sep
push dword [ebp + tRequest.request]
call strtok
add esp, 4 * 2
cmp eax, 0
je .breakHeadersEnd
.httpRequest:
mov [str], eax ; save pointer to header string
mov ebx, [str]
mov [ebp + tRequest.method], ebx
push dword ' '
push dword [str]
call strchr
add esp, 4 * 2
mov [eax], byte 0 ; break method and url
inc eax
mov [ebp + tRequest.filename], eax
push dword ' '
push dword [ebp + tRequest.filename]
call strchr
add esp, 4 * 2
mov [eax], byte 0 ; break url and http
push dword header_sep
push dword 0
call strtok ; AAAAAA strtok_r
add esp, 4 * 2
cmp eax, 0
je .breakHeadersEnd
.breakHeadersLoop:
mov [str], eax ; save pointer to header string
push dword [str] ; get current header length
push juststr
call printf
pop eax
pop eax
; jmp .breakHeadersEnd
push dword header_sep
push dword 0
call strtok ; AAAAAA strtok_r
add esp, 4 * 2
cmp eax, 0
jne .breakHeadersLoop
.breakHeadersEnd:
push dword [ebp + tRequest.filename]
push dword juststr
call printf
mov eax, ebp
add eax, tRequest
push eax
CALL preprocessRequest
.endproc:
.freeBuffers:
push dword [headers]
call free
push dword [buf]
call free
mov esp, ebp ; restore base stack pointer
pop ebp
ret 4 * 2
preprocessRequest:
push ebp ; preserve ebp
mov ebp, esp ; new local offset
%define reqFilename ebp+16
push dword 0
push dword 10 ; count of bytes
push dword [reqFilename]
push dword [ebp+8]
pop eax
add eax, 8
push eax
call send
add esp, 4*4
push dword cgiBinFolder ; get '/cgi-bin'
call strlen ; get length of string
add esp, 4 ; correct stack
push eax ; compare length is a last argument
push dword cgiBinFolder ; address of string
push dword [reqFilename]; addr of file name
call strncmp ; compare start of strings
add esp, 4 * 3 ; correct stack
cmp eax, 0 ; is start are same in both strings?
jz .cgiHere ; hey! there is cgi program!
.staticHere:
mov ebx, ebp ; filename - first field in structure
add ebx, 16
push dword [ebx]
call handlerStatic ; call static content processor
jmp .endproc
.cgiHere:
jmp .cgiHereNext
.cgiherestring:
db 'Hey! There is cgi interface!', 10, 0
.cgiHereNext:
push dword .cgiherestring
call printf
add esp, 4
jmp .endproc
.endproc:
mov esp, ebp ; restore base stack pointer
pop ebp
ret 4
procs.asm
EXTERN cfgBindPort, cfgBindIp, strStartMsg
EXTERN inet_ntoa, printf
GLOBAL startupMessage:FUNCTION
; Just output startup message
startupMessage:
xor eax, eax
mov ax, [cfgBindPort]
push eax
push dword [cfgBindIp]
call inet_ntoa
add esp, 4
push eax
push strStartMsg
call printf
add esp, 4 * 3
ret
Makefile
##
# SPbSU IFMO, Zaharov Ruslan, 3156.
# 11.03.2010 -
# Course work - simple web server writen in 80386 asm
# Using NASM syntax
##
################################
# Some variables
NASM = nasm -t -g -O0 -f elf
NASM_DBG = -F dwarf
GCC = gcc -march=i686 -m32 -nostartfiles -Wall -g
PRG = aweb
TARGETS = aweb.o kcall.o config.o vars.o strings.o procs.o \
consts.o sockets.o requests.o \
handler_static.o handler_cgi.o
################################
# Default target
prog: all
################################
# Objects
kcall.o:
$(NASM) kcall.asm -o $@ -l $@.lst $(NASM_DBG)
aweb.o:
$(NASM) aweb.asm -o $@ -l $@.lst $(NASM_DBG)
config.o:
$(NASM) config.asm -o $@ -l $@.lst $(NASM_DBG)
consts.o:
$(NASM) consts.asm -o $@ -l $@.lst $(NASM_DBG)
vars.o:
$(NASM) vars.asm -o $@ -l $@.lst $(NASM_DBG)
strings.o:
$(NASM) strings.asm -o $@ -l $@.lst $(NASM_DBG)
procs.o:
$(NASM) procs.asm -o $@ -l $@.lst $(NASM_DBG)
sockets.o:
$(NASM) sockets.asm -o $@ -l $@.lst $(NASM_DBG)
requests.o:
$(NASM) requests.asm -o $@ -l $@.lst $(NASM_DBG)
handler_static.o:
$(NASM) handler_static.asm -o $@ -l $@.lst $(NASM_DBG)
handler_cgi.o:
$(NASM) handler_cgi.asm -o $@ -l $@.lst $(NASM_DBG)
################################
# General targets
all: $(TARGETS)
$(GCC) $(TARGETS) -g -o $(PRG)
strip: all
strip $(PRG)
clean:
rm -rf $(TARGETS) $(PRG) *.o.lst
run: all
./$(PRG)
gdb: all
gdb ./$(PRG)
ddd: all
ddd ./$(PRG) > /dev/null
stat-code:
@echo Total lines in source: `cat *asm Makefile | wc -l`
@echo Chars in source: `cat *asm Makefile | wc -c`
stat: stat-code
Statistics
Сделал простую статистику по коду: количество строк и символов в исходниках:
$ make stat
Total lines in source: 1477
Chars in source: 40455
Просто иногда интересно посмотреть бывает :)
Download
Можно скачать всё сразу в архиве: aweb.tar.gz
Перед запуском не забудьте поменять DocumentRoot
Final
Для чего я выложил обрезок от веб сервера, в котором есть уязвимости и нет CGI и многопоточности? Уверен, те, кто пришёл к этому посту из поисковика найдут для себя ОЧЕНЬ много полезного, потому как информации в рунете псро программирование под линукс на NASM очень мало, и большенство примеров - всего лишь hello word. Кстати, англоязычные ресурсы не блещут количеством информации.