ZLA
AST

Iterative GET Server

Building a Web Server
Post Image
February 20, 2026
Read Time: 8 min

Problem

This problem comes from pwn.college - Building a Web Server ⤴.

Previously, your server served just one GET request before terminating. Now, you will modify it so that it can handle multiple GET requests sequentially. This involves wrapping the accept-read-write-close sequence in a loop. Each time a client connects, your server will accept the connection, process the GET request, and then cleanly close the client session while remaining active for the next request. This iterative approach is essential for building a persistent server.

Solution

Goal

Again, running /challenge/run server (using the program compiled in Dynamic Response) prints to the console. I’ll only be showing the relevant output.

Code

.intel_syntax noprefix
.global _start

# CONST: IP
.equ AF_INET,     2           # IPv4
.equ AF_INET6,   10           # IPv6
.equ SOCK_STREAM, 1           # TCP
.equ SOCK_DGRAM,  2           # UDP

# CONST: open() flags
.equ O_RDONLY, 0
.equ O_WRONLY, 1
.equ O_RDWR,   2

# CONST: syscall numbers
.equ READ,    0
.equ WRITE,   1
.equ OPEN,    2
.equ CLOSE,   3
.equ SOCKET, 41
.equ ACCEPT, 43
.equ BIND,   49
.equ LISTEN, 50
.equ EXIT,   60


.section .data
  sock_in:                    # struct sockaddr_in
    .word AF_INET
    .word 0x5000              # Port 80 in hex
    .long 0                   # IP address of 0.0.0.0
    .byte 0                   # Padding

  buf_w:
    .string "HTTP/1.0 200 OK\r\n\r\n"


.section .bss
  fd3: .quad 0
  fd4: .quad 0
  fd5: .quad 0
  path: .space 17


.section .text
_start:
  Socket:
    mov rdi, AF_INET
    mov rsi, SOCK_STREAM
    xor rdx, rdx               # 0 for protocol
    mov rax, SOCKET
    syscall
    mov qword [fd3], rax       # Save fd 3
  
  Bind:
    mov rdi, qword [fd3]
    lea rsi, [sock_in]         # Pointer to struct sockaddr_in
    mov rdx, 16                # 16 bytes expected
    mov rax, BIND
    syscall

  Listen:
    mov rdi, qword [fd3]
    mov rsi, 0                 # Expected backlog length
    mov rax, LISTEN
    syscall

  Accept:
    mov rdi, qword [fd3]
    mov rsi, 0x0               # Null
    mov rdx, 0x0               # Null
    mov rax, ACCEPT
    syscall
    mov qword [fd4], rax       # Save fd 4

  Read_1:
    sub rsp, 161               # Allocate 161 buf on stack
    mov rdi, qword [fd4]
    mov rsi, rsp               # buf location on the stack
    mov rdx, 161               # count
    mov rax, READ
    syscall

    # Extract Path
    lea rsi, [rsp+4]           # Start of path
    lea rdi, [path]            # Save path into "path" variable
    mov rcx, 16                # Amount of bytes to read
    rep movsb                  # Read rcx bytes starting from rsi to rdi
    mov byte ptr [path+16], 0  # Null terminate path
    add rsp, 161               # Remove buffer from stack

  Open:
    lea rdi, [path]            # Path: "/tmp/tmpxxxxxxxx"
    mov rsi, O_RDONLY
    mov rax, OPEN
    syscall
    mov qword [fd5], rax       # Save fd 5

  Read_2:
    sub rsp, 0x1000            # Allocate 4kB buf on stack
    mov rdi, qword [fd5]
    mov rsi, rsp               # buf location on stack
    mov rdx, 0x1000            # count (max 4kB)
    mov rax, READ
    syscall

  Close_1:
    mov rdi, qword [fd5]
    mov rax, CLOSE
    syscall

  Write_1:
    mov rdi, qword [fd4]
    lea rsi, [buf_w]           # Write data from buf
    mov rdx, 19                # Write 19 bytes from buf
    mov rax, WRITE
    syscall

  Write_2:
    lea rdi, [rsp]             # Argument of str_len (buf)
    call str_len
    mov rdi, qword [fd4]
    lea rsi, [rsp]             # Write data from buf
    mov rdx, rax               # Length of string
    mov rax, WRITE
    syscall
    add rsp, 0x1000            # Remove 4kB buf from stack

  Close_Socket:
    mov rdi, qword [fd4]
    mov rax, CLOSE
    syscall

  Exit:
    mov rdi, 0
    mov rax, EXIT
    syscall

# Function that finds string length
# Arguments (1): rdi accepts pointer to string location
# Return: Returns string length in bytes, stored in rax
# Example: 
#     lea rdi, [rsp]
#     call str_len
str_len:
  xor rax, rax

  loop:
    cmp byte ptr [rdi+rax], 0x0
    je end
    add rax, 1
    jmp loop
  end:
    ret