Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Copy `env.example` to `.env` and edit as needed. All keys are optional and defau
| `SERVE_DOTS` | `false` | Whether `.dotfiles` and `.dotfolders/` should be served |
| `MAX_REQUESTS` | `20` | Max simultaneous connections (1–65535) |
| `SERVER_NAME` | `NASMServer/ver` | Value for the `Server:` response header |
| `USE_X_REAL_IP` | `false` | Use the `X-Real-IP` header for logging instead of the remote socket address |
| `ERRORDOC_400` | *(empty)* | Error page path, relative to `DOCUMENT_ROOT`, must start with `/` |
| `ERRORDOC_401` | *(empty)* | Same, for 401 |
| `ERRORDOC_403` | *(empty)* | Same, for 403 |
Expand Down
5 changes: 5 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ DOCUMENT_ROOT=./www
# Default: (empty)
#LOG_FILE=<path>

# Use the X-Real-IP header for logging instead of the remote socket address
# Useful when running the server behind a reverse proxy (like Nginx)
# Default: false
#USE_X_REAL_IP=<true|false>

# Error document paths, relative to DOCUMENT_ROOT
# Must start with a slash. Leave empty to send headers only (no body).
# Default: (empty)
Expand Down
83 changes: 55 additions & 28 deletions labels/initialsetup.asm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ section .data
key_logfile db "LOG_FILE", 0
default_logfile db "", 0

key_use_xri db "USE_X_REAL_IP", 0 ; if we should use 'X-Real-Ip' to display the IP address in the logs
default_use_xri db "false", 0

; errordocs files, relatively to the document_root (empty = none)
; start them with a slash !

Expand All @@ -48,39 +51,50 @@ section .data
default_errordoc_400 db "", 0

section .bss
; config (loaded from .env at startup)
; all custom paths are 128 chars max for consistency (129 for the null byte)

env_path_buf resb 129
word_str_buf resb 8 ; ascii port/max requests from .env before ATOI
port resw 1 ; port number (host byte order)
interface resd 1 ; 0 = 0.0.0.0
max_age_str resb 12 ; enough for "4294967295\0" (max value of resd 1)
max_age resd 1
max_requests resw 1 ; max simultaneous connections (max 65535)
document_root resb 129 ; document root, no trailing slash !
index_file resb 129 ; default index file
server_w_ver resb 24 ; The default server with the version (24 chars should be enough)
; env / strings
env_path_buf resb 129 ; Path to .env file
word_str_buf resb 8 ; Temp buffer for ASCII to Integer conversion
max_age_str resb 12 ; Buffer for "4294967295\0"
serve_dots_str resb 5 ; Buffer for "true\0"
use_xri_str resb 5 ; Buffer for "true\0"

; network
interface resd 1 ; IP Address (0 = 0.0.0.0)
port resw 1 ; Port number (host byte order)
max_requests resw 1 ; Max simultaneous connections (0-65535)
max_age resd 1 ; Cache-Control Max-Age value
use_xri resb 1 ; Toggle for X-Real-IP

; server
server_name resb 129 ; Server: header value
auth_username resb 129 ; for HTTP 1.0 authentication
auth_password resb 129
serve_dots_str resb 5 ; "true\0"
serve_dots resb 1
errordoc_405 resb 129 ; relative to document_root, start with /
errordoc_404 resb 129
errordoc_403 resb 129
server_w_ver resb 24 ; "ServerName/1.0" combined string

; serve configs
document_root resb 129 ; Root directory (no trailing slash)
index_file resb 129 ; Default index (e.g., index.html)
serve_dots resb 1 ; Toggle serving hidden files

; auth
auth_username resb 129 ; HTTP 1.0 Basic Auth User
auth_password resb 129 ; HTTP 1.0 Basic Auth Pass

; logs
log_file_path resb 129 ; Path to access/error log
log_file resq 1 ; Log file descriptor (64-bit)

; errordocs
errordoc_400 resb 129
errordoc_401 resb 129
errordoc_403 resb 129
errordoc_404 resb 129
errordoc_405 resb 129

; error doc paths (built at startup from document_root + errordoc_* + NUL)
errordoc_405_path resb 257
errordoc_404_path resb 257
errordoc_403_path resb 257
errordoc_401_path resb 257
; document_root + errordoc_XXX + NULL = 257 bytes
errordoc_400_path resb 257

log_file_path resb 129
log_file resq 1 ; log file descriptor
errordoc_401_path resb 257
errordoc_403_path resb 257
errordoc_404_path resb 257
errordoc_405_path resb 257

section .text
global initial_setup
Expand Down Expand Up @@ -166,6 +180,9 @@ initial_setup:

ENV_DEFAULT env_path_buf, key_servedots, serve_dots_str, 5, default_servedots
call .is_servedot_true

ENV_DEFAULT env_path_buf, key_use_xri, use_xri_str, 5, default_use_xri
call .is_xri_true

; open the log file
ENV_DEFAULT env_path_buf, key_logfile, log_file_path, 129, default_logfile
Expand Down Expand Up @@ -204,6 +221,16 @@ initial_setup:
mov byte [serve_dots], 1
ret

.is_xri_true:
cmp dword [use_xri_str], 0x65757274 ; "true"
je .set_xri_true

ret

.set_xri_true:
mov byte [use_xri], 1
ret

.open_logfile:
cmp byte [log_file_path], 0
je .no_log_file
Expand Down
84 changes: 84 additions & 0 deletions macros/httputils.asm
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,90 @@ section .bss
%%done:
%endmacro

; PARSE_XRI_HEADER buffer, length, out_buf, max_len
; Scans headers for "X-Real-IP: " and copies the value into out_buf.
; Args:
; %1: buffer address
; %2: buffer length
; %3: output buffer, zeroed on failure
; %4: max bytes to copy (should be resb size - 1)
; Clobbers: rax, rsi, rdi, r8, r9
%macro PARSE_XRI_HEADER 4
mov rsi, %1
xor r8, r8

%%xri_scan:
mov rax, r8
add rax, 12 ; "X-REal-Ip: " = 11 bytes + 1 byte value

cmp rax, %2
jg %%xri_not_found

cmp byte [rsi + r8], 'X'
jne %%xri_next

; Ensure we are at the start of a line
cmp r8, 2
jl %%xri_next
cmp word [rsi + r8 - 2], 0x0a0d
jne %%xri_next

; "X-Re" = 0x65522d58
; "al-I" = 0x492d6c61
; "p: " = 0x203a50
cmp dword [rsi + r8 + 0], 0x65522d58
jne %%xri_next
cmp dword [rsi + r8 + 4], 0x492d6c61
jne %%xri_next
cmp word [rsi + r8 + 8], 0x3a70 ; "p:"
jne %%xri_next
cmp byte [rsi + r8 + 10], 0x20 ; " "
jne %%xri_next

add r8, 11 ; skip past "X-Real-IP: "
xor r9, r9
lea rdi, [%3]

%%xri_copy:
mov rax, r8
add rax, r9

cmp rax, %2
jge %%xri_done

movzx rax, byte [rsi + rax]

cmp al, 0x0d ; \r = end of header value
je %%xri_done

cmp al, 0x0a ; \n = also end
je %%xri_done

cmp al, 0x20 ; stop at space or control chars
jl %%xri_done

mov [rdi + r9], al
inc r9

cmp r9, %4
jge %%xri_done

jmp %%xri_copy

%%xri_done:
mov byte [rdi + r9], 0
jmp %%done

%%xri_next:
inc r8
jmp %%xri_scan

%%xri_not_found:
mov byte [%3], 0

%%done:
%endmacro

; HTTP_EXPIRE_DATE offset_sec, out_buf
; Builds a null-terminated RFC 7231 GMT date string for use in HTTP headers.
; Takes the current wall-clock time, adds offset_sec seconds, then formats it.
Expand Down
22 changes: 16 additions & 6 deletions program.asm
Original file line number Diff line number Diff line change
Expand Up @@ -755,12 +755,7 @@ _start:
mov rdi, r14
syscall

; parse UA and referer for the logs
PARSE_UA_HEADER request, 8192, user_agent, 1024
PARSE_REFERER_HEADER request, 8192, referer, 1024

mov r8, qword [log_file]
LOG_REQUEST_CLFE r8
call .log_request

add rsp, 16
EXIT 0 ; child exits
Expand Down Expand Up @@ -799,6 +794,21 @@ _start:
.reap_done:
ret

.log_request:
; parse other headers for the logs
PARSE_UA_HEADER request, 8192, user_agent, 1024
PARSE_REFERER_HEADER request, 8192, referer, 1024

cmp byte [use_xri], 1
jne .__log_req ; check if we need to use the X-Real-Ip header

PARSE_XRI_HEADER request, 8192, client_ip_str, 15

.__log_req:
mov r8, qword [log_file]
LOG_REQUEST_CLFE r8

ret

.fail_socket:
LOG_ERR log_fail_socket, log_fail_socket_len
Expand Down
Loading