315 lines
5.9 KiB
ArmAsm
315 lines
5.9 KiB
ArmAsm
# HTTP GET/POST concurrent webserver targeting Linux ABI for x86_64 processors
|
|
# This code is public domain
|
|
# Author: xamidev
|
|
|
|
.intel_syntax noprefix
|
|
.global _start
|
|
|
|
.equ AF_INET, 2
|
|
.equ SOCK_STREAM, 1
|
|
.equ NULL, 0
|
|
.equ O_RDONLY, 0
|
|
.equ O_WRONLY, 1
|
|
.equ O_CREAT, 64
|
|
|
|
.section .text
|
|
|
|
_start:
|
|
# write(unsigned int fd, char* buf, size_t count)
|
|
mov rax, 1
|
|
mov rdi, 1
|
|
lea rsi, [rip + str_welcome]
|
|
mov rdx, 24
|
|
syscall
|
|
|
|
# socket(AF_INET, SOCK_STREAM, NULL)
|
|
mov rax, 41
|
|
mov rdi, AF_INET
|
|
mov rsi, SOCK_STREAM
|
|
mov rdx, NULL
|
|
syscall
|
|
|
|
# Save sockfd somewhere
|
|
mov r8, rax
|
|
|
|
# bind(int sockfd, struct sockaddr_in*, int addrlen)
|
|
mov rax, 49
|
|
mov rdi, r8
|
|
lea rsi, [rip + sockaddr_in]
|
|
mov rdx, 16
|
|
syscall
|
|
|
|
# listen(int sockfd, int backlog)
|
|
mov rax, 50
|
|
mov rdi, r8
|
|
mov rsi, 0
|
|
syscall
|
|
|
|
.accept_incoming_connections:
|
|
# accept(int sockfd, struct sockaddr_in*, int* addrlen)
|
|
mov rax, 43
|
|
mov rdi, r8
|
|
mov rsi, NULL
|
|
mov rdx, NULL
|
|
syscall
|
|
|
|
# Save sockfd to close it later
|
|
mov r14, rax
|
|
|
|
# fork() -> new child process for incoming connection; PID in rax
|
|
mov rax, 57
|
|
syscall
|
|
|
|
# If return value is zero, we're in the child process: we can process the connection
|
|
# If return value is non-zero, we're in the parent process: we have to get back to accepting incoming connections
|
|
|
|
test rax, rax
|
|
jz .continue
|
|
jmp .close_actual_sockfd
|
|
|
|
.close_actual_sockfd:
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r14
|
|
syscall
|
|
jmp .accept_incoming_connections
|
|
|
|
.continue:
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r8
|
|
syscall
|
|
|
|
# read(unsigned int fd, char* buf, size_t count)
|
|
mov rax, 0
|
|
mov rdi, r14
|
|
lea rsi, [rip + request_buf]
|
|
mov rdx, 1024
|
|
syscall
|
|
|
|
# Save read bytes (full request size)
|
|
mov r13, rax
|
|
|
|
.parse_http_path:
|
|
# Parse the HTTP request
|
|
# When one space is found, extract bytes until next space
|
|
lea rsi, [rip + request_buf]
|
|
xor rcx, rcx
|
|
.find_start:
|
|
mov al, byte [rsi + rcx]
|
|
cmp al, ' '
|
|
je .path_start
|
|
inc rcx
|
|
jmp .find_start
|
|
|
|
.path_start:
|
|
inc rcx
|
|
lea rdi, [rsi+rcx]
|
|
xor rdx, rdx
|
|
.path_loop:
|
|
mov al, byte [rdi + rdx]
|
|
cmp al, ' '
|
|
je .path_done
|
|
mov byte [path_buf + rdx], al
|
|
inc rdx
|
|
jmp .path_loop
|
|
.path_done:
|
|
|
|
# Detect if request is GET or POST (first char is G: GET, else POST)
|
|
.parse_get_post:
|
|
lea rsi, [rip + request_buf]
|
|
mov al, byte [rsi]
|
|
cmp al, 69 # 'E' (2nd letter, rsi+1)
|
|
je .get_request
|
|
jmp .post_request
|
|
|
|
.post_request:
|
|
# open(const char* filename, int flags, int mode)
|
|
mov rax, 2
|
|
lea rdi, [path_buf+1]
|
|
mov rsi, O_WRONLY | O_CREAT # O_RDONLY for GET requests
|
|
mov rdx, 0x1FF # All permissions
|
|
syscall
|
|
|
|
# Save fd
|
|
mov r10, rax
|
|
|
|
.parse_request_content:
|
|
# When we find \r\n\r\n we know that the content follows.
|
|
# request_buf already contains full request.
|
|
lea rsi, [rip + request_buf]
|
|
xor rcx, rcx
|
|
|
|
.find_body:
|
|
mov al, byte [rsi+rcx]
|
|
cmp al, 13 # \r
|
|
jne .next
|
|
mov al, byte [rsi+rcx+1]
|
|
cmp al, 10 # \n
|
|
jne .next
|
|
mov al, byte [rsi+rcx+2]
|
|
cmp al, 13
|
|
jne .next
|
|
mov al, byte[rsi+rcx+3]
|
|
cmp al, 10
|
|
jne .next
|
|
|
|
add rcx, 4
|
|
jmp .body_found
|
|
|
|
.next:
|
|
# Threshold to avoid infinite looping if request is malformed
|
|
inc rcx
|
|
cmp rcx, 512
|
|
jl .find_body
|
|
|
|
jmp .error_exit
|
|
|
|
.body_found:
|
|
lea rdi, [rsi+rcx]
|
|
lea rsi, [rip+request_content_buf]
|
|
mov rbx, r13
|
|
sub rbx, rcx
|
|
mov r13, rbx
|
|
|
|
# Byte amount of request content is now in r13
|
|
# Now we copy this to a buffer
|
|
xor rcx, rcx
|
|
|
|
.content_copy_loop:
|
|
cmp rcx, r13
|
|
jge .done_copy
|
|
|
|
mov al, byte [rdi+rcx]
|
|
mov byte [rsi+rcx], al
|
|
inc rcx
|
|
jmp .content_copy_loop
|
|
.done_copy:
|
|
dec r13
|
|
# Byte length in r13 (decrement cause of null-byte)
|
|
# Content in request_content_buf
|
|
|
|
# Here, as we have a POST request, we will have to write to filename instead of reading from it.
|
|
|
|
# write(unsigned int fd, const char* buf, size_t count)
|
|
mov rax, 1
|
|
mov rdi, r10
|
|
lea rsi, [rip + request_content_buf+1] # ADDRESS OF REQUEST CONTENT BUFFER
|
|
mov rdx, r13 # AMOUNT OF BYTES IN REQUEST CONTENT
|
|
syscall
|
|
|
|
# Save read bytes
|
|
mov r15, rax
|
|
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r10
|
|
syscall
|
|
|
|
# We simply return a 200 OK here
|
|
|
|
# write(unsigned int fd, const char* buf, size_t count)
|
|
mov rax, 1
|
|
mov rdi, r14
|
|
lea rsi, [rip + response]
|
|
mov rdx, 19
|
|
syscall
|
|
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r14
|
|
syscall
|
|
|
|
jmp .normal_exit
|
|
|
|
.get_request:
|
|
# open(const char* filename, int flags, int mode)
|
|
mov rax, 2
|
|
lea rdi, [path_buf+1]
|
|
mov rsi, O_RDONLY
|
|
mov rdx, NULL # who cares?
|
|
syscall
|
|
|
|
# Save fd
|
|
mov r10, rax
|
|
|
|
# read(unsigned int fd, char* buf, size_t count)
|
|
mov rax, 0
|
|
mov rdi, r10
|
|
lea rsi, [rip + file_buf]
|
|
mov rdx, 1024
|
|
syscall
|
|
|
|
# Save read bytes
|
|
mov r15, rax
|
|
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r10
|
|
syscall
|
|
|
|
# write(unsigned int fd, const char* buf, size_t count)
|
|
mov rax, 1
|
|
mov rdi, r14
|
|
lea rsi, [rip + response]
|
|
mov rdx, 19
|
|
syscall
|
|
|
|
# write(unsigned int fd, const char* buf, size_t count)
|
|
mov rax, 1
|
|
mov rdi, r14
|
|
lea rsi, [rip + file_buf]
|
|
mov rdx, r15
|
|
syscall
|
|
|
|
# close(unsigned int fd)
|
|
mov rax, 3
|
|
mov rdi, r14
|
|
syscall
|
|
|
|
.normal_exit:
|
|
# exit(0)
|
|
mov rax, 60
|
|
mov rdi, 0
|
|
syscall
|
|
|
|
.error_exit:
|
|
mov rax, 60
|
|
mov rdi, -1
|
|
syscall
|
|
|
|
.section .data
|
|
|
|
response:
|
|
.asciz "HTTP/1.0 200 OK\r\n\r\n"
|
|
|
|
str_welcome:
|
|
.asciz "Starting HTTP server...\n"
|
|
|
|
# Total size: 16 bytes
|
|
sockaddr_in:
|
|
.word AF_INET
|
|
# Port 80 = 0x0050 (unsigned short, 2 bytes)
|
|
# Big endian: bytes are in reverse order
|
|
.word 0x5000
|
|
# 32-bits of zeroes: 0.0.0.0
|
|
.int 0x00000000
|
|
.zero 8
|
|
|
|
.section .bss
|
|
|
|
path_buf:
|
|
.zero 64
|
|
|
|
request_buf:
|
|
.zero 1024
|
|
|
|
file_buf:
|
|
.zero 1024
|
|
|
|
stat_buf:
|
|
.zero 1024
|
|
|
|
request_content_buf:
|
|
.zero 1024
|