-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.asm
371 lines (343 loc) · 7.35 KB
/
server.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
bits 64
; Used to convert the port number to big endian when assembling
%define swap_word(x) ( (x<<8) | ((x&0xFFFF)>>>8) )
section .text
; Params
HTTP_PORT EQU 8080
BACKLOG EQU 5
STDOUT EQU 1
BUFFER_LEN EQU 200
FILE_BUFFER_LEN EQU 1000
; Syscall constants
AF_INET EQU 2
SOCK_STREAM EQU 1
SYS_SOCKET EQU 41
SYS_WRITE EQU 1
SYS_EXIT EQU 60
SYS_BIND EQU 49
SYS_LISTEN EQU 50
SYS_ACCEPT EQU 43
SYS_READ EQU 0
SYS_OPEN EQU 2
SYS_CLOSE EQU 3
SYS_LSEEK EQU 8
SYS_SETSOCKOPT EQU 54
SEEK_SET EQU 0
SEEK_END EQU 2
O_RDONLY EQU 0
SOL_SOCKET EQU 1
SO_REUSEADDR EQU 2
global _start
_start:
; Create a socket, fd will be stored in RBX
mov rax, SYS_SOCKET
mov rdi, AF_INET
mov rsi, SOCK_STREAM
mov rdx, 0
syscall
; RAX now contains fd
; Move it to RBX to avoid clobbering
mov rbx, rax
cmp rax, -1 ; Check for failure
jne .no_sock_err
.sock_err:
; Print errors and die
mov rax, socket_error_msg
call print_text
mov rax, 1
call exit
.no_sock_err:
; Set socket reuse addr flag so we can restart more quickly (no 30-120 second downtime between starts)
mov rax, SYS_SETSOCKOPT
mov rdi, rbx
mov rsi, SOL_SOCKET
mov rdx, SO_REUSEADDR
mov r10, sockopt
mov r8, 4
; Do bind
mov rax, SYS_BIND
mov rdi, rbx
mov rsi, sockaddr
mov rdx, 16
syscall
cmp rax, 0
jne .sock_err ; Failed to bind
; Listen on socket
mov rax, SYS_LISTEN
mov rdi, rbx
mov rsi, BACKLOG
syscall
cmp rax, 0
jne .sock_err ; Failed to listen
mov rax, bind_success_msg
call print_text
.accept:
; Accept an incoming connection
mov rax, SYS_ACCEPT
mov rdi, rbx
mov rsi, client_sock
mov rdx, client_addrlen
syscall
cmp rax, 0 ; Client socket fd is in RAX
jl .accept_error
push rax ; Print new connection message
mov rax, accept_success_msg
call print_text
pop rax
push rax ; Read HTTP request into buffer
mov rcx, rax
mov rax, SYS_READ
mov rdi, rcx
mov rsi, input_buffer
mov rdx, BUFFER_LEN
syscall
pop rax
call handle_request
jmp .accept
.accept_error:
mov rax, accept_error_msg
call print_text
jmp .accept
handle_request: ; RAX contains client file descriptor
; GET /test.txt HTTP 1.1
push r8
mov r8, rax
push rax
push rbx
push rcx
push rdx
mov rax, -1 ; RAX sentinel
mov rbx, input_buffer
mov rcx, 0
; This loops through the buffer until it finds the first space, then substrings that until the next space/EOL
.request_loop:
cmp rax, -1
jne .request_find_end
mov dl, [rbx]
cmp dl, 0x20 ; Check for space
jne .request_loop_inc
mov rax, rcx
inc rax ; Remove space
inc rax ; Remove leading slash
jmp .request_loop_inc
.request_find_end:
mov dl, [rbx]
cmp dl, 0x20 ; Check for space
je .request_found_end
cmp dl, 0xD ; Carriage return
je .request_found_end
cmp dl, 0xA ; New line
je .request_found_end
jmp .request_loop_inc
.request_found_end:
; Zero terminates the end of the requested file
mov rbx, input_buffer
add rbx, rax
sub rcx, rax
mov rax, rbx
mov rbx, rcx
push rdx
mov rdx, rax
add rdx, rbx
push r11
mov r11b, 0x0
mov [rdx], r11b
pop r11
pop rdx
call process_get ; Run the routine to actually fetch a file
pop rdx
pop rcx
pop rbx
pop rax
pop r8
ret
.request_loop_inc:
inc rbx
inc rcx
cmp rcx, 200
jl .request_loop
mov rax, request_fail_msg
call print_text
pop rdx
pop rcx
pop rbx
pop rax
pop r8
ret
process_get: ; Takes string in RAX, length in RBX, and file handle in R8
push rax
push rdi
push rsi
push rdx
push rbx
push r8
push r9
push r10
push r11
push r12
mov r9, rax
mov rax, get_message ; Print message with request details
call print_text
mov rax, r9
call print_text
mov rax, newline
call print_text
mov r10, rbx ; Try to open file
mov rax, SYS_OPEN
mov rdi, r9
mov rsi, O_RDONLY
mov rdx, 0
syscall
cmp rax, 0
jl .get_404 ; File doesn't exist, send 404
mov r12, rax ; Move file descriptor to r12 to avoid clobbering
; Seek to end of file to find its length
mov rax, SYS_LSEEK
mov rdi, r12
mov rsi, 0 ; 0 offset
mov rdx, SEEK_END
syscall
mov r11, rax
push r11
mov rax, SYS_LSEEK
mov rdi, r12
mov rsi, 0
mov rdx, SEEK_SET
syscall
; Send start of HTTP 200 response
mov rax, http_response_200
call strlen
mov rdx, rax
mov rsi, http_response_200
mov rdi, r8
mov rax, SYS_WRITE
syscall
.get_read: ; Loop through the file and read a buffer's worth of data
mov rax, SYS_READ
mov rdi, r12
mov rsi, file_buffer
mov rdx, FILE_BUFFER_LEN
syscall
pop r11
sub r11, rax
push r11
mov rdx, rax ; Write the data to the socket
mov rsi, file_buffer
mov rdi, r8
mov rax, SYS_WRITE
syscall
pop r11
cmp r11, 0
push r11
jg .get_read
pop r11 ; Close open file
mov rax, SYS_CLOSE
mov rdi, r12
syscall
jmp .get_end
.get_404:
mov rax, http_response_404 ; Send premade 404 response
call strlen
mov rdx, rax
mov rsi, http_response_404
mov rax, SYS_WRITE
mov rdi, r8
syscall
.get_end:
mov rax, SYS_CLOSE ; Close socket to trigger end of request
mov rdi, r8
syscall
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbx
pop rdx
pop rsi
pop rdi
pop rax
ret
strlen: ; Buffer location in RAX. Counts the number of bytes in a string up until a null terminator
push rbx
push rcx
push rdx
mov rcx, 0
.strlen_loop:
mov rdx, [rax]
cmp dl, 0x0
je .strlen_loopend
inc rax
inc rcx
jmp .strlen_loop
.strlen_loopend:
mov rax, rcx
pop rdx
pop rcx
pop rbx
ret
print_withlen: ; Buffer location in RAX, length in RBX. Prints a string with a given length to console
push rdi
push rsi
push rdx
push rax
mov rsi, rax
mov rdx, rbx
mov rax, SYS_WRITE
mov rdi, STDOUT
syscall
pop rax
pop rdx
pop rsi
pop rdi
ret
print_text: ; Buffer location in RAX. Prints a null terminated string to console
push rbx
push rax
call strlen
mov rbx, rax
pop rax
push rax
call print_withlen
pop rax
pop rbx
ret
exit: ; Error code in RAX. Exits the program
mov rdi, rax
mov rax, SYS_EXIT
syscall
section .data
socket_error_msg:
db "Failed to bind to socket!", 0xA, 0x0
bind_success_msg:
db "Successfully bound! Listening!", 0xA, 0x0
accept_error_msg:
db "Failed to accept connection!", 0xA, 0x0
accept_success_msg:
db "Accepted connection!", 0xA, 0x0
request_fail_msg:
db "Failed to parse request!", 0xA, 0x0
http_response_200:
db "HTTP/1.1 200 OK", 0xA, "Server: HTTPASM", 0xA, "Content-Type: text/html", 0xA, 0xA, 0x0
http_response_404:
db "HTTP/1.1 404 File Not Found", 0xA, "Server: HTTPASM", 0xA, "Content-Type: text/html", 0xA, 0xA, "404 File Not Found", 0xA, 0x0
newline:
db 0xA, 0x0
get_message:
db "Received request: ", 0x0
sockaddr:
dw AF_INET
dw swap_word(HTTP_PORT)
dd 0
dq 0
sockopt:
dd 1
section .bss
input_buffer:
resb BUFFER_LEN
file_buffer:
resb FILE_BUFFER_LEN
client_sock:
resb 8
client_addrlen:
resd 1