Network Protocols
Introduction
Network protocols define the rules and conventions for communication between network devices. Understanding and implementing common network protocols is essential for developing robust network applications. This chapter covers the implementation of widely-used protocols such as HTTP, FTP, DNS, and others in C.
HTTP Protocol Implementation
HTTP (HyperText Transfer Protocol) is the foundation of data communication on the World Wide Web. Understanding HTTP is crucial for developing web applications and services.
HTTP Request Structure
An HTTP request consists of: 1. Request Line - Method, URI, and HTTP version 2. Headers - Key-value pairs providing metadata 3. Body - Optional data (for POST, PUT requests)
Example HTTP GET request:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Connection: close
HTTP Response Structure
An HTTP response consists of: 1. Status Line - HTTP version, status code, and reason phrase 2. Headers - Key-value pairs providing metadata 3. Body - Response content
Example HTTP response:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
Connection: close
<HTML>...</HTML>
Simple HTTP Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUFFER_SIZE 4096
int create_socket(const char *hostname, int port) {
int sockfd;
struct hostent *server;
struct sockaddr_in serv_addr;
// Create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
return -1;
}
// Get server IP address
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr, "ERROR, no such host\n");
return -1;
}
// Configure server address
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(port);
// Connect to server
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
return -1;
}
return sockfd;
}
int http_get(const char *hostname, const char *path) {
int sockfd;
char request[BUFFER_SIZE];
char response[BUFFER_SIZE];
int bytes_sent, bytes_received;
// Create socket and connect
sockfd = create_socket(hostname, 80);
if (sockfd < 0) {
return -1;
}
// Create HTTP GET request
snprintf(request, BUFFER_SIZE,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"\r\n",
path, hostname);
// Send request
bytes_sent = send(sockfd, request, strlen(request), 0);
if (bytes_sent < 0) {
perror("ERROR sending request");
close(sockfd);
return -1;
}
printf("HTTP Request sent:\n%s", request);
// Receive response
while ((bytes_received = recv(sockfd, response, BUFFER_SIZE - 1, 0)) > 0) {
response[bytes_received] = '\0';
printf("%s", response);
}
if (bytes_received < 0) {
perror("ERROR receiving response");
close(sockfd);
return -1;
}
close(sockfd);
return 0;
}
int main() {
const char *hostname = "httpbin.org";
const char *path = "/get";
printf("Sending HTTP GET request to %s%s\n", hostname, path);
http_get(hostname, path);
return 0;
}Simple HTTP Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#define PORT 8080
#define BUFFER_SIZE 4096
void send_http_response(int client_fd, const char *status, const char *content_type, const char *body) {
char response[BUFFER_SIZE];
time_t now;
char time_str[100];
// Get current time
time(&now);
strftime(time_str, sizeof(time_str), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
// Create HTTP response
int content_length = strlen(body);
snprintf(response, BUFFER_SIZE,
"HTTP/1.1 %s\r\n"
"Date: %s\r\n"
"Server: SimpleHTTPServer/1.0\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"\r\n"
"%s",
status, time_str, content_type, content_length, body);
// Send response
send(client_fd, response, strlen(response), 0);
}
void handle_request(int client_fd, const char *request) {
printf("Received request:\n%s\n", request);
// Parse request line (first line)
char method[16], path[256], version[16];
if (sscanf(request, "%15s %255s %15s", method, path, version) != 3) {
send_http_response(client_fd, "400 Bad Request", "text/plain", "Bad Request");
return;
}
// Handle different HTTP methods and paths
if (strcmp(method, "GET") == 0) {
if (strcmp(path, "/") == 0 || strcmp(path, "/index.html") == 0) {
const char *html =
"<!DOCTYPE html>\n"
"<html>\n"
"<head><title>Simple HTTP Server</title></head>\n"
"<body><h1>Welcome to Simple HTTP Server</h1>\n"
"<p>This is a simple HTTP server implemented in C.</p>\n"
"</body>\n"
"</html>";
send_http_response(client_fd, "200 OK", "text/html", html);
} else if (strcmp(path, "/api/time") == 0) {
time_t now = time(NULL);
char time_str[100];
snprintf(time_str, sizeof(time_str), "{\"time\": \"%s\"}", ctime(&now));
send_http_response(client_fd, "200 OK", "application/json", time_str);
} else {
const char *not_found =
"<!DOCTYPE html>\n"
"<html>\n"
"<head><title>404 Not Found</title></head>\n"
"<body><h1>404 Not Found</h1>\n"
"<p>The requested resource was not found.</p>\n"
"</body>\n"
"</html>";
send_http_response(client_fd, "404 Not Found", "text/html", not_found);
}
} else {
send_http_response(client_fd, "405 Method Not Allowed", "text/plain", "Method Not Allowed");
}
}
int main() {
int server_fd, client_fd;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
// Create socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Set socket options
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// Configure address
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Bind socket
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Listen for connections
if (listen(server_fd, 10) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("HTTP Server listening on port %d\n", PORT);
while (1) {
// Accept connection
if ((client_fd = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen)) < 0) {
perror("accept");
continue;
}
// Read request
int bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
handle_request(client_fd, buffer);
}
// Close client connection
close(client_fd);
}
close(server_fd);
return 0;
}FTP Protocol Implementation
FTP (File Transfer Protocol) is a standard network protocol used for transferring files between a client and server on a computer network.
FTP Client Implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUFFER_SIZE 1024
#define FTP_PORT 21
int ftp_connect(const char *hostname, int port) {
int sockfd;
struct hostent *server;
struct sockaddr_in serv_addr;
// Create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
return -1;
}
// Get server IP address
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr, "ERROR, no such host\n");
return -1;
}
// Configure server address
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(port);
// Connect to server
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
return -1;
}
return sockfd;
}
int ftp_login(int sockfd, const char *username, const char *password) {
char buffer[BUFFER_SIZE];
int bytes_received;
// Read welcome message
bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("%s", buffer);
}
// Send USER command
snprintf(buffer, BUFFER_SIZE, "USER %s\r\n", username);
send(sockfd, buffer, strlen(buffer), 0);
// Read response
bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("%s", buffer);
}
// Send PASS command
snprintf(buffer, BUFFER_SIZE, "PASS %s\r\n", password);
send(sockfd, buffer, strlen(buffer), 0);
// Read response
bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("%s", buffer);
// Check if login was successful
if (strncmp(buffer, "230", 3) == 0) {
return 0; // Success
}
}
return -1; // Failure
}
int ftp_list(int sockfd) {
char buffer[BUFFER_SIZE];
// Send LIST command
snprintf(buffer, BUFFER_SIZE, "LIST\r\n");
send(sockfd, buffer, strlen(buffer), 0);
// Read response
int bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("%s", buffer);
return 0;
}
return -1;
}
int main() {
int sockfd;
const char *hostname = "ftp.example.com"; // Replace with actual FTP server
const char *username = "anonymous";
const char *password = "user@example.com";
printf("Connecting to FTP server: %s\n", hostname);
// Connect to FTP server
sockfd = ftp_connect(hostname, FTP_PORT);
if (sockfd < 0) {
exit(EXIT_FAILURE);
}
// Login to FTP server
if (ftp_login(sockfd, username, password) < 0) {
printf("FTP login failed\n");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("FTP login successful\n");
// List directory contents
printf("Directory listing:\n");
ftp_list(sockfd);
// Close connection
close(sockfd);
return 0;
}DNS Protocol Implementation
DNS (Domain Name System) translates domain names to IP addresses. Understanding DNS is important for network programming.
Simple DNS Resolver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
// DNS header structure
struct dns_header {
unsigned short id; // identification number
unsigned short flags; // flags
unsigned short qdcount; // number of question entries
unsigned short ancount; // number of answer entries
unsigned short nscount; // number of authority entries
unsigned short arcount; // number of resource entries
};
// DNS question structure
struct dns_question {
unsigned short qtype;
unsigned short qclass;
};
// Function to convert domain name to DNS format
void dns_format(unsigned char* dns, unsigned char* host) {
int lock = 0, i;
strcat((char*)host, ".");
for(i = 0; i < strlen((char*)host); i++) {
if(host[i] == '.') {
*dns++ = i - lock;
for(; lock < i; lock++) {
*dns++ = host[lock];
}
lock++;
}
}
*dns++ = '\0';
}
int dns_query(const char *hostname) {
int sockfd;
struct sockaddr_in dest;
unsigned char buf[65536];
struct dns_header *dns = NULL;
struct dns_question *qinfo = NULL;
// Create UDP socket
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
perror("socket creation failed");
return -1;
}
// Configure destination address (Google DNS)
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr("8.8.8.8");
// Set up DNS header
dns = (struct dns_header *)&buf;
dns->id = (unsigned short)htons(getpid());
dns->flags = htons(0x0100); // Standard query
dns->qdcount = htons(1); // One question
dns->ancount = 0;
dns->nscount = 0;
dns->arcount = 0;
// Set up question section
unsigned char *qname = (unsigned char*)&buf[sizeof(struct dns_header)];
dns_format(qname, (unsigned char*)hostname);
qinfo = (struct dns_question*)&buf[sizeof(struct dns_header) + strlen((char*)qname) + 1];
qinfo->qtype = htons(1); // Type A (IPv4 address)
qinfo->qclass = htons(1); // Class IN (Internet)
// Send DNS query
int packet_size = sizeof(struct dns_header) + strlen((char*)qname) + 1 + sizeof(struct dns_question);
if (sendto(sockfd, (char*)buf, packet_size, 0, (struct sockaddr*)&dest, sizeof(dest)) < 0) {
perror("sendto failed");
close(sockfd);
return -1;
}
// Receive DNS response
socklen_t dest_len = sizeof(dest);
int received_bytes = recvfrom(sockfd, (char*)buf, 65536, 0, (struct sockaddr*)&dest, &dest_len);
if (received_bytes < 0) {
perror("recvfrom failed");
close(sockfd);
return -1;
}
// Parse DNS response
dns = (struct dns_header*)buf;
printf("DNS Response:\n");
printf(" ID: %d\n", ntohs(dns->id));
printf(" Questions: %d\n", ntohs(dns->qdcount));
printf(" Answers: %d\n", ntohs(dns->ancount));
// Extract IP addresses from answer section
unsigned char *reader = &buf[sizeof(struct dns_header) + strlen((char*)qname) + 1 + sizeof(struct dns_question)];
for(int i = 0; i < ntohs(dns->ancount); i++) {
// Skip name (for simplicity, assuming it's a pointer)
reader += 2; // Skip name pointer
unsigned short type = ntohs(*(unsigned short*)reader);
reader += 2;
unsigned short class = ntohs(*(unsigned short*)reader);
reader += 2;
unsigned int ttl = ntohl(*(unsigned int*)reader);
reader += 4;
unsigned short data_len = ntohs(*(unsigned short*)reader);
reader += 2;
if (type == 1 && class == 1) { // Type A, Class IN
unsigned char ip[4];
for(int j = 0; j < 4; j++) {
ip[j] = *reader++;
}
printf(" IP Address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
} else {
reader += data_len;
}
}
close(sockfd);
return 0;
}
int main() {
const char *hostname = "google.com";
printf("Resolving hostname: %s\n", hostname);
dns_query(hostname);
return 0;
}SMTP Protocol Implementation
SMTP (Simple Mail Transfer Protocol) is used for sending emails. Here’s a simple SMTP client implementation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUFFER_SIZE 1024
#define SMTP_PORT 25
int smtp_connect(const char *smtp_server) {
int sockfd;
struct hostent *server;
struct sockaddr_in serv_addr;
// Create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
return -1;
}
// Get server IP address
server = gethostbyname(smtp_server);
if (server == NULL) {
fprintf(stderr, "ERROR, no such host\n");
return -1;
}
// Configure server address
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(SMTP_PORT);
// Connect to server
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
return -1;
}
return sockfd;
}
int smtp_send_command(int sockfd, const char *command) {
char buffer[BUFFER_SIZE];
int bytes_sent, bytes_received;
// Send command
bytes_sent = send(sockfd, command, strlen(command), 0);
if (bytes_sent < 0) {
perror("ERROR sending command");
return -1;
}
// Receive response
bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("Server: %s", buffer);
return 0;
}
return -1;
}
int smtp_send_email(const char *smtp_server, const char *from, const char *to,
const char *subject, const char *body) {
int sockfd;
char command[BUFFER_SIZE];
// Connect to SMTP server
sockfd = smtp_connect(smtp_server);
if (sockfd < 0) {
return -1;
}
// Read server greeting
char buffer[BUFFER_SIZE];
int bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("Server: %s", buffer);
}
// Send HELO command
snprintf(command, BUFFER_SIZE, "HELO localhost\r\n");
smtp_send_command(sockfd, command);
// Send MAIL FROM command
snprintf(command, BUFFER_SIZE, "MAIL FROM:<%s>\r\n", from);
smtp_send_command(sockfd, command);
// Send RCPT TO command
snprintf(command, BUFFER_SIZE, "RCPT TO:<%s>\r\n", to);
smtp_send_command(sockfd, command);
// Send DATA command
snprintf(command, BUFFER_SIZE, "DATA\r\n");
smtp_send_command(sockfd, command);
// Send email content
snprintf(command, BUFFER_SIZE,
"From: %s\r\n"
"To: %s\r\n"
"Subject: %s\r\n"
"\r\n"
"%s\r\n"
".\r\n",
from, to, subject, body);
send(sockfd, command, strlen(command), 0);
// Read response
bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("Server: %s", buffer);
}
// Send QUIT command
snprintf(command, BUFFER_SIZE, "QUIT\r\n");
smtp_send_command(sockfd, command);
close(sockfd);
return 0;
}
int main() {
const char *smtp_server = "smtp.example.com"; // Replace with actual SMTP server
const char *from = "sender@example.com";
const char *to = "recipient@example.com";
const char *subject = "Test Email";
const char *body = "This is a test email sent from a C program.";
printf("Sending email via SMTP...\n");
smtp_send_email(smtp_server, from, to, subject, body);
return 0;
}Practical Examples
Protocol Analyzer
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
void analyze_ip_packet(const unsigned char *buffer, int size) {
struct iphdr *iph = (struct iphdr*)buffer;
printf("IP Header\n");
printf(" |-Version : %d\n", (unsigned int)iph->version);
printf(" |-Internet Header Length : %d DWORDS or %d Bytes\n",
(unsigned int)iph->ihl, ((unsigned int)(iph->ihl))*4);
printf(" |-Type Of Service : %d\n", (unsigned int)iph->tos);
printf(" |-Total Length : %d Bytes\n", ntohs(iph->tot_len));
printf(" |-Identification : %d\n", ntohs(iph->id));
printf(" |-Time To Live : %d\n", (unsigned int)iph->ttl);
printf(" |-Protocol : %d\n", (unsigned int)iph->protocol);
printf(" |-Header Checksum : %d\n", ntohs(iph->check));
printf(" |-Source IP : %s\n", inet_ntoa(*(struct in_addr*)&iph->saddr));
printf(" |-Destination IP : %s\n", inet_ntoa(*(struct in_addr*)&iph->daddr));
// Analyze protocol-specific headers
switch (iph->protocol) {
case 6: // TCP
printf("TCP Protocol\n");
break;
case 17: // UDP
printf("UDP Protocol\n");
break;
case 1: // ICMP
printf("ICMP Protocol\n");
break;
default:
printf("Other Protocol\n");
break;
}
}
int main() {
int sockfd;
unsigned char buffer[65536];
struct sockaddr_in dest;
socklen_t dest_len = sizeof(dest);
// Create raw socket (requires root privileges)
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd < 0) {
perror("socket creation failed (try running as root)");
exit(EXIT_FAILURE);
}
printf("Protocol Analyzer started...\n");
// Capture packets
while (1) {
int packet_size = recvfrom(sockfd, buffer, 65536, 0, (struct sockaddr*)&dest, &dest_len);
if (packet_size < 0) {
perror("recvfrom failed");
continue;
}
printf("\n----------------------------------------\n");
analyze_ip_packet(buffer, packet_size);
printf("----------------------------------------\n");
}
close(sockfd);
return 0;
}Summary
Network protocol implementation in C involves understanding and working with various standard protocols:
- HTTP - The foundation of web communication with request/response patterns
- FTP - File transfer protocol for uploading and downloading files
- DNS - Domain name resolution system
- SMTP - Email transmission protocol
- Protocol Analysis - Examining network packets at the protocol level
Key concepts for protocol implementation: - Understanding protocol message formats and structures - Properly formatting requests and parsing responses - Handling different protocol states and error conditions - Managing connections and data flow - Implementing security considerations
These implementations form the basis for more complex network applications and services. The next chapter will cover network security considerations and best practices.