ZLA
AST

Listen

Building a Web Server
Post Image
February 3, 2026
Read Time: 4 min

Problem

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

With your socket bound to an address, you now need to prepare it to accept incoming connections. The listen ⤴ syscall transforms your socket into a passive one that awaits client connection requests. It requires the socket’s file descriptor and a backlog parameter, which sets the maximum number of queued connections. This step is vital because without marking the socket as listening, your server wouldn’t be able to receive any connection attempts.

Solution

Goal

Running /challenge/run server (using the program compiled in bind) prints to the console

===== Expected: Parent Process =====
[ ] execve(<execve_args>) = 0
[ ] socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
[ ] bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
    - Bind to port 80
    - Bind to address 0.0.0.0
[ ] listen(3, 0) = 0
[ ] exit(0) = ?

Which, uh, doesn’t say very much. But it does appear that the listen function is quite simple, so let’s look at that.

Listen Function

The listen function has the following function definition

int listen(int sockfd, int backlog)

It is quite simple! It takes two parameters

  • sockfd, being the socket file descriptor that we got from calling the socket function.
  • backlog, which defines the maximum queue length for awaiting connections. Looking at the console output in the Goal section, we’ll only be handling one connection at a time for this challenge, which means 0 backlog.

The listen function returns an int with either a 0 on success or -1 on failure. See the listen man page ⤴ for more details.

Code

I’ve made a few changes to the code compared to the previous section

  • Function calls are now labels instead of comments. It looks cleaner to me and shows the flow of the program more clearly.
  • sockfd is saved to rbx since we need rax for other things.
.intel_syntax noprefix
.global _start

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

.section .text
_start:
  Socket:
    mov rdi, 2         # AF_INET = 2, for IPv4
    mov rsi, 1         # SOCK_STREAM = 1, for TCP
    xor rdx, rdx       # 0 for protocol
    mov rax, 41        # socket syscall number: 41
    syscall
    mov rbx, rax       # Save sockfd
  
  Bind:
    mov rdi, rax       # Send file descriptor to sockfd
    lea rsi, [sock_in] # Pointer to struct sockaddr
    mov rdx, 16        # 16 bytes expected
    mov rax, 49        # bind syscall number: 49
    syscall

  Listen:
    mov rdi, rbx       # Send file descriptor to sockfd
    mov rsi, 0         # Expected backlog/queue length
    mov rax, 50        # listen syscall number: 50
    syscall

  Exit:
    mov rdi, 0
    mov rax, 60        # exit syscall number: 60
    syscall