04_tcp_client
Example 04: TCP HTTP Client
Utilizing lwIP to establish an outbound TCP connection.
This advanced example demonstrates the steps required to use the raw network system calls to establish a connection with an external HTTP server and dump the response over the terminal.
📝 Concepts Introduced
- Verifying the network state (
sys_network_is_initialized,sys_network_has_ip). - Performing DNS lookups manually via
sys_dns_lookup. - Managing strict TCP flow logic (
sys_tcp_connect, send, block for receive). - Using the terminal
SYS_WRITEoutput for debugging. - Declaring app metadata via source annotations.
The Code (src/userland/cli/http_get.c)
// BOREDOS_APP_DESC: HTTP GET client — fetches a webpage over TCP.
// BOREDOS_APP_ICONS: /Library/images/icons/colloid/network-wired.png
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
int main(void) {
if (!sys_network_is_initialized() || !sys_network_has_ip()) {
printf("Network is unreachable! Make sure you inited the network first!\n");
return 1;
}
// 1. Resolve host name to IP
const char *target_host = "boreddev.nl";
net_ipv4_address_t server_ip;
printf("Resolving %s...\n", target_host);
if (sys_dns_lookup(target_host, &server_ip) < 0) {
printf("DNS Lookup failed.\n");
return 1;
}
printf("Resolved to: %d.%d.%d.%d\n", server_ip.bytes[0], server_ip.bytes[1],
server_ip.bytes[2], server_ip.bytes[3]);
// 2. Establish a TCP connection on port 80 (HTTP)
printf("Connecting...\n");
if (sys_tcp_connect(&server_ip, 80) < 0) {
printf("Connection failed.\n");
return 1;
}
printf("Connected! Sending GET request...\n");
// 3. Format and send the raw HTTP Request
char request[256];
strcpy(request, "GET / HTTP/1.1\r\nHost: ");
strcat(request, target_host);
strcat(request, "\r\nConnection: close\r\n\r\n");
if (sys_tcp_send(request, strlen(request)) < 0) {
printf("Failed to send data.\n");
sys_tcp_close();
return 1;
}
// 4. Block and wait for response data
char recv_buf[512];
int bytes_received;
printf("\n--- RESPONSE ---\n");
while ((bytes_received = sys_tcp_recv(recv_buf, sizeof(recv_buf) - 1)) > 0) {
recv_buf[bytes_received] = '\0'; // Null-terminate the chunk
printf("%s", recv_buf); // Print the chunk to stdout
}
// 5. Cleanup
printf("\n--- END RESPONSE ---\n");
sys_tcp_close();
printf("Connection closed.\n");
return 0;
}
How it Works
- Network Setup: First, we must ensure the host machine or QEMU environment gave BoredOS a valid IP address via DHCP. The
sys_network_has_ip()check prevents our app from hanging trying to route data to nowhere. - DNS (
sys_dns_lookup): Since we want to connect to a domain name, not a raw IP, we query the DNS server configured by the OS (which it received via DHCP). - Connection (
sys_tcp_connect): We block the application thread while the OS performs the 3-way TCP handshake over port 80. - Payload (
sys_tcp_send): We format a compliant HTTP/1.1 payload representing a simple GET request for the root directory/. - Chunked Receiving (
sys_tcp_recv): The server's response might be larger than ourrecv_buf(512 bytes). Therefore, we loop.sys_tcp_recvblocks execution until data arrives. If it returns0, the remote server cleanly closed the connection (which happens automatically because we specifiedConnection: closein our request payload!). BOREDOS_APP_DESC/BOREDOS_APP_ICONS: Embedded into the compiled.elfas a BoredOS NOTE section. The Desktop and File Explorer read this to display the app's icon. Seeelf_metadata.mdfor full details.
Running It
Make sure QEMU is running with networking enabled. Launch the terminal and type http_get. You will see the raw headers and HTML source of the target webpage scroll down the CLI interface!