|
| 1 | +#include <stdio.h> |
| 2 | +#include <stdlib.h> |
| 3 | +#include <string.h> |
| 4 | +#include <unistd.h> |
| 5 | +#include <sys/socket.h> |
| 6 | +#include <netinet/in.h> |
| 7 | +#include <arpa/inet.h> |
| 8 | + |
| 9 | +#define PORT 8080 |
| 10 | +#define BUFFER_SIZE 1024 |
| 11 | + |
| 12 | +int main() { |
| 13 | + int server_fd, new_socket; |
| 14 | + struct sockaddr_in6 address; |
| 15 | + int addrlen = sizeof(address); |
| 16 | + char buffer[BUFFER_SIZE] = {0}; |
| 17 | + const char *http_response = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 14\n\nHello, world!\n"; |
| 18 | + |
| 19 | + // Create an IPv6 socket |
| 20 | + if ((server_fd = socket(AF_INET6, SOCK_STREAM, 0)) == 0) { |
| 21 | + perror("socket failed"); |
| 22 | + exit(EXIT_FAILURE); |
| 23 | + } |
| 24 | + |
| 25 | + // Set SO_REUSEADDR to reuse the address immediately |
| 26 | + int opt = 1; |
| 27 | + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { |
| 28 | + perror("setsockopt SO_REUSEADDR failed"); |
| 29 | + close(server_fd); |
| 30 | + exit(EXIT_FAILURE); |
| 31 | + } |
| 32 | + |
| 33 | + // Set IPV6_V6ONLY to 0 to allow dual-stack operation. |
| 34 | + // This is the crucial part for the test. If this is 0, |
| 35 | + // the IPv6 socket can also accept IPv4 connections. |
| 36 | + // If it were 1, it would only accept IPv6 connections. |
| 37 | + int v6only = 0; |
| 38 | + if (setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))) { |
| 39 | + perror("setsockopt IPV6_V6ONLY failed"); |
| 40 | + close(server_fd); |
| 41 | + exit(EXIT_FAILURE); |
| 42 | + } |
| 43 | + int returned_v6only; |
| 44 | + socklen_t len = sizeof(returned_v6only); |
| 45 | + if (getsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &returned_v6only, &len) == 0) { |
| 46 | + printf("IPV6_V6ONLY is currently set to: %d\n", returned_v6only); |
| 47 | + } else { |
| 48 | + perror("getsockopt IPV6_V6ONLY failed"); |
| 49 | + } |
| 50 | + |
| 51 | + // Configure the server address |
| 52 | + // Use IN6ADDR_LOOPBACK_INIT to bind to [::1] |
| 53 | + memset(&address, 0, sizeof(address)); |
| 54 | + address.sin6_family = AF_INET6; |
| 55 | + address.sin6_addr = in6addr_loopback; // This is the [::1] address |
| 56 | + address.sin6_port = htons(PORT); |
| 57 | + |
| 58 | + // Bind the socket to the configured address |
| 59 | + if (bind(server_fd, (struct sockaddr *)&address, addrlen) < 0) { |
| 60 | + perror("bind failed"); |
| 61 | + close(server_fd); |
| 62 | + exit(EXIT_FAILURE); |
| 63 | + } |
| 64 | + |
| 65 | + // Start listening for incoming connections |
| 66 | + if (listen(server_fd, 3) < 0) { |
| 67 | + perror("listen failed"); |
| 68 | + close(server_fd); |
| 69 | + exit(EXIT_FAILURE); |
| 70 | + } |
| 71 | + |
| 72 | + printf("Server listening on [::1] port %d\n", PORT); |
| 73 | + printf("Test with curl: \n"); |
| 74 | + printf(" curl -6 http://[::1]:%d\n", PORT); |
| 75 | + printf(" curl -4 http://127.0.0.1:%d\n\n", PORT); |
| 76 | + |
| 77 | + while (1) { |
| 78 | + // Accept a new connection |
| 79 | + if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { |
| 80 | + perror("accept failed"); |
| 81 | + continue; |
| 82 | + } |
| 83 | + |
| 84 | + // Read the incoming request |
| 85 | + read(new_socket, buffer, BUFFER_SIZE); |
| 86 | + printf("Request received:\n---\n%s\n---\n", buffer); |
| 87 | + |
| 88 | + // Send the HTTP response |
| 89 | + write(new_socket, http_response, strlen(http_response)); |
| 90 | + |
| 91 | + // Close the client socket |
| 92 | + close(new_socket); |
| 93 | + } |
| 94 | + |
| 95 | + // Close the server socket (unreachable in this infinite loop) |
| 96 | + close(server_fd); |
| 97 | + |
| 98 | + return 0; |
| 99 | +} |
0 commit comments