I claim that
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define FAVICON_BODY_LEN 123
static const unsigned char FAVICON_HEADER[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: image/x-icon\r\n"
"Content-Length: " STRINGIFY(FAVICON_BODY_LEN) "\r\n"
"Content-Encoding: gzip\r\n"
"Date: Sat, 01 Jan 2022 00:00:00 GMT\r\n"
"Cache-Control: max-age=473384600\r\n"
"\r\n";
#define FAVICON_HEADER_LEN ((sizeof FAVICON_HEADER) - 1)
static const unsigned char FAVICON_BODY[FAVICON_BODY_LEN] = {
0x1f, 0x8b, 0x08, 0x08, 0xea, 0xf3, 0xc3, 0x62, 0x02, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f,
0x6e, 0x2e, 0x69, 0x63, 0x6f, 0x00, 0x63, 0x60, 0x60, 0x04, 0x42, 0x01, 0x01, 0x06, 0x20, 0xa9,
0xc0, 0x90, 0xc1, 0xc2, 0xc0, 0x20, 0xc6, 0xc0, 0xc0, 0xa0, 0x01, 0xc4, 0x40, 0x21, 0xa0, 0x08,
0x44, 0x1c, 0x0c, 0x80, 0x72, 0xde, 0x31, 0x10, 0x0c, 0x03, 0xff, 0x87, 0x11, 0x60, 0x62, 0x12,
0x80, 0x63, 0x5c, 0x72, 0xc4, 0x98, 0x41, 0x8a, 0x38, 0x31, 0xea, 0x88, 0xd5, 0x8b, 0x4d, 0x2d,
0x29, 0x7a, 0xd1, 0xfd, 0x49, 0xaa, 0x5e, 0x64, 0x3d, 0xe4, 0xe8, 0x45, 0xd7, 0x47, 0x89, 0xfd,
0x94, 0xf8, 0x9f, 0x92, 0xb8, 0x43, 0x4f, 0x27, 0xc8, 0x62, 0xe4, 0x86, 0xc9, 0x60, 0x06, 0x0c,
0x14, 0x02, 0x00, 0xc2, 0xd1, 0x9b, 0xb4, 0x7e, 0x04, 0x00, 0x00,
};
should work, because the following program in Linux also works:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define FAVICON_BODY_LEN 123
static const unsigned char FAVICON_HEADER[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: image/x-icon\r\n"
"Content-Length: " STRINGIFY(FAVICON_BODY_LEN) "\r\n"
"Content-Encoding: gzip\r\n"
"Date: Sat, 01 Jan 2022 00:00:00 GMT\r\n"
"Cache-Control: max-age=473384600\r\n"
"\r\n";
#define FAVICON_HEADER_LEN ((sizeof FAVICON_HEADER) - 1)
static const unsigned char FAVICON_BODY[FAVICON_BODY_LEN] = {
0x1f, 0x8b, 0x08, 0x08, 0xea, 0xf3, 0xc3, 0x62, 0x02, 0x03, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f,
0x6e, 0x2e, 0x69, 0x63, 0x6f, 0x00, 0x63, 0x60, 0x60, 0x04, 0x42, 0x01, 0x01, 0x06, 0x20, 0xa9,
0xc0, 0x90, 0xc1, 0xc2, 0xc0, 0x20, 0xc6, 0xc0, 0xc0, 0xa0, 0x01, 0xc4, 0x40, 0x21, 0xa0, 0x08,
0x44, 0x1c, 0x0c, 0x80, 0x72, 0xde, 0x31, 0x10, 0x0c, 0x03, 0xff, 0x87, 0x11, 0x60, 0x62, 0x12,
0x80, 0x63, 0x5c, 0x72, 0xc4, 0x98, 0x41, 0x8a, 0x38, 0x31, 0xea, 0x88, 0xd5, 0x8b, 0x4d, 0x2d,
0x29, 0x7a, 0xd1, 0xfd, 0x49, 0xaa, 0x5e, 0x64, 0x3d, 0xe4, 0xe8, 0x45, 0xd7, 0x47, 0x89, 0xfd,
0x94, 0xf8, 0x9f, 0x92, 0xb8, 0x43, 0x4f, 0x27, 0xc8, 0x62, 0xe4, 0x86, 0xc9, 0x60, 0x06, 0x0c,
0x14, 0x02, 0x00, 0xc2, 0xd1, 0x9b, 0xb4, 0x7e, 0x04, 0x00, 0x00,
};
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
(void)signum; /* Generates no code, just silences unused parameter warning. */
done = 1;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&(act.sa_mask));
act.sa_handler = handle_done;
act.sa_flags = 0; /* We want interrupt delivery to block a waiting accept(). */
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static int write_all(int fd, const void *buf, size_t len)
{
const char *ptr = buf;
const char *const end = len + (const char *)buf;
ssize_t n;
while (ptr < end) {
n = write(fd, ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (n == -1) {
return errno;
} else {
return errno = EIO;
}
}
return 0;
}
static int serve(int fd)
{
size_t in_max = 4096;
size_t in_len = 0;
char *in_buf = malloc(in_max);
ssize_t n;
int err;
if (!in_buf)
return ENOMEM;
else
in_buf[0] = '\0';
/* Read until \r\n\r\n. */
while (in_len < 4 || !memmem(in_buf, in_len, "\r\n\r\n", 4)) {
if (in_len + 1 >= in_max) {
size_t new_max = (in_max | 4095) + 4097 - 32; /* Linear growth */
char *new_buf = realloc(in_buf, new_max);
if (!new_buf) {
free(in_buf);
return ENOMEM;
}
in_max = new_max;
in_buf = new_buf;
}
n = read(fd, in_buf + in_len, in_max - 1 - in_len);
if (n > 0) {
in_len += n;
in_buf[in_len] = '\0';
} else
if (!n) {
free(in_buf);
return ECONNABORTED;
} else
if (n != -1) {
free(in_buf);
return EIO;
} else {
const int saved_errno = errno;
free(in_buf);
return saved_errno;
}
}
if (!strncmp(in_buf, "GET ", 4)) {
printf("Received a GET request\n");
} else
if (!strncmp(in_buf, "HEAD ", 5)) {
printf("Received a HEAD request\n");
} else {
printf("Received an invalid request\n");
free(in_buf);
return ECONNREFUSED;
}
err = write_all(fd, FAVICON_HEADER, FAVICON_HEADER_LEN);
if (err) {
free(in_buf);
return err;
}
if (!strncmp(in_buf, "HEAD ", 5)) {
free(in_buf);
return 0;
}
err = write_all(fd, FAVICON_BODY, FAVICON_BODY_LEN);
if (err) {
free(in_buf);
return err;
}
free(in_buf);
return 0;
}
int main(int argc, char *argv[])
{
struct addrinfo hints, *result, *ai;
const char *node, *serv;
int sockfd, connfd, err;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s ADDRESS PORT-OR-SERVICE\n", argv0);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
node = argv[1];
if (!*node || !strcmp(node, "*") || !strcmp(node, "-"))
node = NULL;
serv = argv[2];
if (install_done(SIGHUP) ||
install_done(SIGINT) ||
install_done(SIGTERM) ||
install_done(SIGQUIT)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* TCP */
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
err = getaddrinfo(node, serv, &hints, &result);
if (err) {
fprintf(stderr, "%s.\n", gai_strerror(err));
return EXIT_FAILURE;
}
for (sockfd = -1, ai = result; ai != NULL; ai = ai->ai_next) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd == -1)
continue;
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
close(sockfd);
sockfd = -1;
continue;
}
if (listen(sockfd, 1)) {
close(sockfd);
sockfd = -1;
continue;
}
break;
}
if (sockfd == -1) {
fprintf(stderr, "Cannot bind to specified address and port.\n");
return EXIT_FAILURE;
}
while (!done) {
connfd = accept(sockfd, NULL, NULL);
if (connfd == -1) {
if (errno == EINTR)
continue;
fprintf(stderr, "Connection failure: %s.\n", strerror(errno));
continue;
}
err = serve(connfd);
if (err) {
fprintf(stderr, "Service failure: %s.\n", strerror(errno));
close(connfd);
continue;
}
fprintf(stderr, "Served successfully.\n");
close(connfd);
}
close(sockfd);
return EXIT_SUCCESS;
}
The data is gzipped favicon.ico (original 1150 bytes, sha256sum 2c01904c0df69815f18b28d2a7e8e438c353b5352b42fb3699726b332b507be9; gzipped to 123 bytes with sha256sum 1b566fe5012155da352dade788b9c4eac90f98a6facdab8c1610f7674b869db0). Browsers do support gzip transfer encoding (but we really should check that the request contained an "Accept-Encoding:" header with "gzip" as one of the comma-separated items).