emikulic / darkhttpd

When you need a web server in a hurry.
https://unix4lyfe.org/darkhttpd/
ISC License
1.03k stars 83 forks source link

Add a `--cors` option to allow setting of the `Access-Control-Allow-Origin` header #27

Closed kugland closed 1 year ago

kugland commented 1 year ago

On a second thought, I think it would be more precise to call this option --allow-origin instead of --cors.

emikulic commented 1 year ago

Looks good. This addresses #16?

Optional: do you feel like adding a test? :)

kugland commented 1 year ago

@emikulic, when I tried to write the test, I got this error from the address sanitizer. At first I thought this was caused by my change, but then I ran the test suite on the code on the master branch, and I got the same result. Any idea what can this be? Might it be some kernel hardening config I’m using? First it fails to intercept the stdlib calls, then it gets a segmentation fault.

==139728==AddressSanitizer: failed to intercept 'strlen'
==139728==AddressSanitizer: failed to intercept '__strndup'
==139728==AddressSanitizer: failed to intercept 'strcmp'
==139728==AddressSanitizer: failed to intercept 'strncmp'
==139728==AddressSanitizer: failed to intercept 'strstr'
==139728==AddressSanitizer: failed to intercept 'strchr'
==139728==AddressSanitizer: failed to intercept 'strrchr'
==139728==AddressSanitizer: failed to intercept 'strspn'
==139728==AddressSanitizer: failed to intercept 'strcspn'
==139728==AddressSanitizer: failed to intercept 'strtok'
==139728==AddressSanitizer: failed to intercept 'strpbrk'
==139728==AddressSanitizer: failed to intercept 'strxfrm'
==139728==AddressSanitizer: failed to intercept '__strxfrm_l'
==139728==AddressSanitizer: failed to intercept 'memset'
==139728==AddressSanitizer: failed to intercept 'memmove'
==139728==AddressSanitizer: failed to intercept 'memcpy'
==139728==AddressSanitizer: failed to intercept 'memchr'
==139728==AddressSanitizer: failed to intercept 'memcmp'
==139728==AddressSanitizer: failed to intercept 'read'
==139728==AddressSanitizer: failed to intercept 'preadv'
==139728==AddressSanitizer: failed to intercept 'preadv64'
==139728==AddressSanitizer: failed to intercept 'pwritev'
==139728==AddressSanitizer: failed to intercept 'pwritev64'
==139728==AddressSanitizer: failed to intercept 'localtime'
==139728==AddressSanitizer: failed to intercept 'gmtime'
==139728==AddressSanitizer: failed to intercept 'ctime'
==139728==AddressSanitizer: failed to intercept 'ctime_r'
==139728==AddressSanitizer: failed to intercept 'asctime'
==139728==AddressSanitizer: failed to intercept 'mktime'
==139728==AddressSanitizer: failed to intercept 'strptime'
==139728==AddressSanitizer: failed to intercept 'scanf'
==139728==AddressSanitizer: failed to intercept 'sscanf'
==139728==AddressSanitizer: failed to intercept 'fscanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_scanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_sscanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_fscanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vscanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vsscanf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vfscanf'
==139728==AddressSanitizer: failed to intercept 'printf'
==139728==AddressSanitizer: failed to intercept 'sprintf'
==139728==AddressSanitizer: failed to intercept 'fprintf'
==139728==AddressSanitizer: failed to intercept 'vprintf'
==139728==AddressSanitizer: failed to intercept 'vfprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_printf'
==139728==AddressSanitizer: failed to intercept '__isoc99_sprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_snprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_fprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vsprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vsnprintf'
==139728==AddressSanitizer: failed to intercept '__isoc99_vfprintf'
==139728==AddressSanitizer: failed to intercept 'getpwnam'
==139728==AddressSanitizer: failed to intercept 'getpwuid'
==139728==AddressSanitizer: failed to intercept 'getgrnam'
==139728==AddressSanitizer: failed to intercept 'getgrgid'
==139728==AddressSanitizer: failed to intercept 'getpwnam_r'
==139728==AddressSanitizer: failed to intercept 'getpwuid_r'
==139728==AddressSanitizer: failed to intercept 'getgrnam_r'
==139728==AddressSanitizer: failed to intercept 'getgrgid_r'
==139728==AddressSanitizer: failed to intercept 'getpwent'
==139728==AddressSanitizer: failed to intercept 'getgrent'
==139728==AddressSanitizer: failed to intercept 'fgetpwent'
==139728==AddressSanitizer: failed to intercept 'fgetgrent'
==139728==AddressSanitizer: failed to intercept 'getpwent_r'
==139728==AddressSanitizer: failed to intercept 'getgrent_r'
==139728==AddressSanitizer: failed to intercept 'setpwent'
==139728==AddressSanitizer: failed to intercept 'endpwent'
==139728==AddressSanitizer: failed to intercept 'setgrent'
==139728==AddressSanitizer: failed to intercept 'endgrent'
==139728==AddressSanitizer: failed to intercept 'clock_getres'
==139728==AddressSanitizer: failed to intercept 'clock_gettime'
==139728==AddressSanitizer: failed to intercept 'clock_settime'
==139728==AddressSanitizer: failed to intercept 'clock_getcpuclockid'
==139728==AddressSanitizer: failed to intercept 'pthread_getcpuclockid'
==139728==AddressSanitizer: failed to intercept 'time'
==139728==AddressSanitizer: failed to intercept 'glob'
==139728==AddressSanitizer: failed to intercept 'glob64'
==139728==AddressSanitizer: failed to intercept 'posix_spawn'
==139728==AddressSanitizer: failed to intercept 'posix_spawnp'
==139728==AddressSanitizer: failed to intercept 'inet_ntop'
==139728==AddressSanitizer: failed to intercept 'pthread_getschedparam'
==139728==AddressSanitizer: failed to intercept 'getaddrinfo'
==139728==AddressSanitizer: failed to intercept 'getnameinfo'
==139728==AddressSanitizer: failed to intercept 'gethostent'
==139728==AddressSanitizer: failed to intercept 'gethostbyaddr'
==139728==AddressSanitizer: failed to intercept 'gethostbyname'
==139728==AddressSanitizer: failed to intercept 'gethostbyname2'
==139728==AddressSanitizer: failed to intercept 'gethostbyname_r'
==139728==AddressSanitizer: failed to intercept 'gethostbyname2_r'
==139728==AddressSanitizer: failed to intercept 'gethostbyaddr_r'
==139728==AddressSanitizer: failed to intercept 'gethostent_r'
==139728==AddressSanitizer: failed to intercept 'accept'
==139728==AddressSanitizer: failed to intercept 'accept4'
==139728==AddressSanitizer: failed to intercept 'ptrace'
==139728==AddressSanitizer: failed to intercept 'setlocale'
==139728==AddressSanitizer: failed to intercept 'get_current_dir_name'
==139728==AddressSanitizer: failed to intercept 'mbstowcs'
==139728==AddressSanitizer: failed to intercept 'wcstombs'
==139728==AddressSanitizer: failed to intercept 'wctomb'
==139728==AddressSanitizer: failed to intercept 'realpath'
==139728==AddressSanitizer: failed to intercept 'confstr'
==139728==AddressSanitizer: failed to intercept 'sched_getaffinity'
==139728==AddressSanitizer: failed to intercept 'strerror'
==139728==AddressSanitizer: failed to intercept '__xpg_strerror_r'
==139728==AddressSanitizer: failed to intercept 'ppoll'
==139728==AddressSanitizer: failed to intercept 'wordexp'
==139728==AddressSanitizer: failed to intercept 'sigemptyset'
==139728==AddressSanitizer: failed to intercept 'sigfillset'
==139728==AddressSanitizer: failed to intercept 'sigandset'
==139728==AddressSanitizer: failed to intercept 'sigorset'
==139728==AddressSanitizer: failed to intercept 'sigpending'
==139728==AddressSanitizer: failed to intercept 'pthread_sigmask'
==139728==AddressSanitizer: failed to intercept '_exit'
==139728==AddressSanitizer: failed to intercept 'pthread_mutex_lock'
==139728==AddressSanitizer: failed to intercept 'pthread_mutex_unlock'
==139728==AddressSanitizer: failed to intercept '__pthread_mutex_lock'
==139728==AddressSanitizer: failed to intercept '__pthread_mutex_unlock'
==139728==AddressSanitizer: failed to intercept 'getmntent'
==139728==AddressSanitizer: failed to intercept 'initgroups'
==139728==AddressSanitizer: failed to intercept 'ether_ntoa'
==139728==AddressSanitizer: failed to intercept 'ether_aton'
==139728==AddressSanitizer: failed to intercept 'ether_ntohost'
==139728==AddressSanitizer: failed to intercept 'ether_hostton'
==139728==AddressSanitizer: failed to intercept 'ether_line'
==139728==AddressSanitizer: failed to intercept 'ether_ntoa_r'
==139728==AddressSanitizer: failed to intercept 'ether_aton_r'
==139728==AddressSanitizer: failed to intercept 'shmctl'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getdetachstate'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getguardsize'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getscope'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getstacksize'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getstack'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getschedparam'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getschedpolicy'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getinheritsched'
==139728==AddressSanitizer: failed to intercept 'pthread_attr_getaffinity_np'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_getpshared'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_gettype'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_getprotocol'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_getprioceiling'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_getrobust'
==139728==AddressSanitizer: failed to intercept 'pthread_mutexattr_getrobust_np'
==139728==AddressSanitizer: failed to intercept 'pthread_rwlockattr_getpshared'
==139728==AddressSanitizer: failed to intercept 'pthread_rwlockattr_getkind_np'
==139728==AddressSanitizer: failed to intercept 'pthread_condattr_getpshared'
==139728==AddressSanitizer: failed to intercept 'pthread_condattr_getclock'
==139728==AddressSanitizer: failed to intercept 'pthread_barrierattr_getpshared'
==139728==AddressSanitizer: failed to intercept 'tmpnam'
==139728==AddressSanitizer: failed to intercept 'tmpnam_r'
==139728==AddressSanitizer: failed to intercept 'ptsname'
==139728==AddressSanitizer: failed to intercept 'ttyname'
==139728==AddressSanitizer: failed to intercept 'tempnam'
==139728==AddressSanitizer: failed to intercept 'pthread_setname_np'
==139728==AddressSanitizer: failed to intercept 'pthread_getname_np'
==139728==AddressSanitizer: failed to intercept 'lgamma'
==139728==AddressSanitizer: failed to intercept 'lgammaf'
==139728==AddressSanitizer: failed to intercept 'lgammal'
==139728==AddressSanitizer: failed to intercept 'drand48_r'
==139728==AddressSanitizer: failed to intercept 'lrand48_r'
==139728==AddressSanitizer: failed to intercept 'rand_r'
==139728==AddressSanitizer: failed to intercept '__getdelim'
==139728==AddressSanitizer: failed to intercept 'iconv'
==139728==AddressSanitizer: failed to intercept '__tls_get_addr'
==139728==AddressSanitizer: failed to intercept 'listxattr'
==139728==AddressSanitizer: failed to intercept 'llistxattr'
==139728==AddressSanitizer: failed to intercept 'flistxattr'
==139728==AddressSanitizer: failed to intercept 'getxattr'
==139728==AddressSanitizer: failed to intercept 'lgetxattr'
==139728==AddressSanitizer: failed to intercept 'fgetxattr'
==139728==AddressSanitizer: failed to intercept 'getresuid'
==139728==AddressSanitizer: failed to intercept 'getresgid'
==139728==AddressSanitizer: failed to intercept 'capget'
==139728==AddressSanitizer: failed to intercept 'capset'
==139728==AddressSanitizer: failed to intercept '__bzero'
==139728==AddressSanitizer: failed to intercept 'ftime'
==139728==AddressSanitizer: failed to intercept 'xdrmem_create'
==139728==AddressSanitizer: failed to intercept 'xdrstdio_create'
==139728==AddressSanitizer: failed to intercept 'xdr_short'
==139728==AddressSanitizer: failed to intercept 'xdr_u_short'
==139728==AddressSanitizer: failed to intercept 'xdr_int'
==139728==AddressSanitizer: failed to intercept 'xdr_u_int'
==139728==AddressSanitizer: failed to intercept 'xdr_long'
==139728==AddressSanitizer: failed to intercept 'xdr_u_long'
==139728==AddressSanitizer: failed to intercept 'xdr_hyper'
==139728==AddressSanitizer: failed to intercept 'xdr_u_hyper'
==139728==AddressSanitizer: failed to intercept 'xdr_longlong_t'
==139728==AddressSanitizer: failed to intercept 'xdr_u_longlong_t'
==139728==AddressSanitizer: failed to intercept 'xdr_int8_t'
==139728==AddressSanitizer: failed to intercept 'xdr_uint8_t'
==139728==AddressSanitizer: failed to intercept 'xdr_int16_t'
==139728==AddressSanitizer: failed to intercept 'xdr_uint16_t'
==139728==AddressSanitizer: failed to intercept 'xdr_int32_t'
==139728==AddressSanitizer: failed to intercept 'xdr_uint32_t'
==139728==AddressSanitizer: failed to intercept 'xdr_int64_t'
==139728==AddressSanitizer: failed to intercept 'xdr_uint64_t'
==139728==AddressSanitizer: failed to intercept 'xdr_quad_t'
==139728==AddressSanitizer: failed to intercept 'xdr_u_quad_t'
==139728==AddressSanitizer: failed to intercept 'xdr_bool'
==139728==AddressSanitizer: failed to intercept 'xdr_enum'
==139728==AddressSanitizer: failed to intercept 'xdr_char'
==139728==AddressSanitizer: failed to intercept 'xdr_u_char'
==139728==AddressSanitizer: failed to intercept 'xdr_float'
==139728==AddressSanitizer: failed to intercept 'xdr_double'
==139728==AddressSanitizer: failed to intercept 'xdr_bytes'
==139728==AddressSanitizer: failed to intercept 'xdr_string'
==139728==AddressSanitizer: failed to intercept 'xdrrec_create'
==139728==AddressSanitizer: failed to intercept 'xdr_destroy'
==139728==AddressSanitizer: failed to intercept '__uflow'
==139728==AddressSanitizer: failed to intercept '__underflow'
==139728==AddressSanitizer: failed to intercept '__overflow'
==139728==AddressSanitizer: failed to intercept '__wuflow'
==139728==AddressSanitizer: failed to intercept '__wunderflow'
==139728==AddressSanitizer: failed to intercept '__woverflow'
==139728==AddressSanitizer: failed to intercept 'fopen'
==139728==AddressSanitizer: failed to intercept 'fdopen'
==139728==AddressSanitizer: failed to intercept 'freopen'
==139728==AddressSanitizer: failed to intercept 'freopen64'
==139728==AddressSanitizer: failed to intercept 'open_wmemstream'
==139728==AddressSanitizer: failed to intercept 'fmemopen'
==139728==AddressSanitizer: failed to intercept '_obstack_begin_1'
==139728==AddressSanitizer: failed to intercept '_obstack_begin'
==139728==AddressSanitizer: failed to intercept '_obstack_newchunk'
==139728==AddressSanitizer: failed to intercept 'fclose'
==139728==AddressSanitizer: failed to intercept 'dlopen'
==139728==AddressSanitizer: failed to intercept 'dlclose'
==139728==AddressSanitizer: failed to intercept 'getpass'
==139728==AddressSanitizer: failed to intercept 'timerfd_settime'
==139728==AddressSanitizer: failed to intercept 'timerfd_gettime'
==139728==AddressSanitizer: failed to intercept 'mlock'
==139728==AddressSanitizer: failed to intercept 'munlock'
==139728==AddressSanitizer: failed to intercept 'mlockall'
==139728==AddressSanitizer: failed to intercept 'munlockall'
==139728==AddressSanitizer: failed to intercept 'fopencookie'
==139728==AddressSanitizer: failed to intercept 'sem_init'
==139728==AddressSanitizer: failed to intercept 'sem_destroy'
==139728==AddressSanitizer: failed to intercept 'sem_wait'
==139728==AddressSanitizer: failed to intercept 'sem_trywait'
==139728==AddressSanitizer: failed to intercept 'sem_timedwait'
==139728==AddressSanitizer: failed to intercept 'sem_post'
==139728==AddressSanitizer: failed to intercept 'sem_getvalue'
==139728==AddressSanitizer: failed to intercept 'sem_open'
==139728==AddressSanitizer: failed to intercept 'sem_unlink'
==139728==AddressSanitizer: failed to intercept 'mincore'
==139728==AddressSanitizer: failed to intercept 'process_vm_readv'
==139728==AddressSanitizer: failed to intercept 'process_vm_writev'
==139728==AddressSanitizer: failed to intercept 'ctermid'
==139728==AddressSanitizer: failed to intercept 'eventfd_read'
==139728==AddressSanitizer: failed to intercept 'eventfd_write'
==139728==AddressSanitizer: failed to intercept '__xstat'
==139728==AddressSanitizer: failed to intercept '__xstat64'
==139728==AddressSanitizer: failed to intercept '__lxstat'
==139728==AddressSanitizer: failed to intercept '__lxstat64'
==139728==AddressSanitizer: failed to intercept 'getutxent'
==139728==AddressSanitizer: failed to intercept 'getutxid'
==139728==AddressSanitizer: failed to intercept 'getutxline'
==139728==AddressSanitizer: failed to intercept 'pututxline'
==139728==AddressSanitizer: failed to intercept 'getloadavg'
==139728==AddressSanitizer: failed to intercept 'wcsncat'
==139728==AddressSanitizer: failed to intercept 'wcsdup'
==139728==AddressSanitizer: failed to intercept 'wcsxfrm'
==139728==AddressSanitizer: failed to intercept '__wcsxfrm_l'
==139728==AddressSanitizer: failed to intercept 'getgrouplist'
==139728==AddressSanitizer: failed to intercept 'readlinkat'
==139728==AddressSanitizer: failed to intercept 'name_to_handle_at'
==139728==AddressSanitizer: failed to intercept 'open_by_handle_at'
==139728==AddressSanitizer: failed to intercept 'getprotoent'
==139728==AddressSanitizer: failed to intercept 'getprotobyname'
==139728==AddressSanitizer: failed to intercept 'getprotobynumber'
==139728==AddressSanitizer: failed to intercept 'getprotoent_r'
==139728==AddressSanitizer: failed to intercept 'getprotobyname_r'
==139728==AddressSanitizer: failed to intercept 'getprotobynumber_r'
==139728==AddressSanitizer: failed to intercept 'getnetent'
==139728==AddressSanitizer: failed to intercept 'getnetbyname'
==139728==AddressSanitizer: failed to intercept 'getnetbyaddr'
==139728==AddressSanitizer: failed to intercept 'setbuf'
==139728==AddressSanitizer: failed to intercept 'setlinebuf'
==139728==AddressSanitizer: failed to intercept 'regexec@@GLIBC_2.3.4' or 'regexec'
==139728==AddressSanitizer: failed to intercept 'popen'
==139728==AddressSanitizer: failed to intercept 'pclose'
==139728==AddressSanitizer: failed to intercept 'getusershell'
==139728==AddressSanitizer: failed to intercept 'crypt'
==139728==AddressSanitizer: failed to intercept 'crypt_r'
==139728==AddressSanitizer: failed to intercept 'qsort'
==139728==AddressSanitizer: failed to intercept 'bsearch'
==139728==AddressSanitizer: failed to intercept '__sprintf_chk'
==139728==AddressSanitizer: failed to intercept '__snprintf_chk'
==139728==AddressSanitizer: failed to intercept '__vsprintf_chk'
==139728==AddressSanitizer: failed to intercept '__vsnprintf_chk'
==139728==AddressSanitizer: failed to intercept '__fprintf_chk'
==139728==AddressSanitizer: failed to intercept 'strcat'
==139728==AddressSanitizer: failed to intercept 'strcpy'
==139728==AddressSanitizer: failed to intercept 'strncat'
==139728==AddressSanitizer: failed to intercept 'strncpy'
==139728==AddressSanitizer: failed to intercept '__strdup'
==139728==AddressSanitizer: failed to intercept 'atoi'
==139728==AddressSanitizer: failed to intercept 'atol'
==139728==AddressSanitizer: failed to intercept 'atoll'
==139728==AddressSanitizer: failed to intercept '__longjmp_chk'
==139728==AddressSanitizer: failed to intercept '__cxa_throw'
==139728==AddressSanitizer: failed to intercept 'pthread_create'
==139728==AddressSanitizer: failed to intercept 'pthread_join'
==139728==AddressSanitizer: failed to intercept '__cxa_atexit'
==139728==AddressSanitizer: libc interceptors initialized
|| `[0x10007fff8000, 0x7fffffffffff]` || HighMem    ||
|| `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
|| `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap  ||
|| `[0x00007fff8000, 0x00008fff6fff]` || LowShadow  ||
|| `[0x000000000000, 0x00007fff7fff]` || LowMem     ||
MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x02008fff6fff
redzone=16
max_redzone=2048
quarantine_size_mb=256M
thread_local_quarantine_size_kb=1024K
malloc_context_size=30
SHADOW_SCALE: 3
SHADOW_GRANULARITY: 8
SHADOW_OFFSET: 0x7fff8000
==139728==Installed the sigaction for signal 11
==139728==Installed the sigaction for signal 7
==139728==Installed the sigaction for signal 8
AddressSanitizer:DEADLYSIGNAL
=================================================================
==139728==ERROR: AddressSanitizer: SEGV on unknown address 0x61b2dc825d68 (pc 0x768a135591e7 bp 0x000000000000 sp 0x7ffd76c7c870 T0)
==139728==The signal is caused by a READ memory access.
AddressSanitizer:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting.
emikulic commented 1 year ago

I'm not sure what causes that. I wouldn't block this PR on ASAN, if the test passes on an uninstrumented build I'm happy to merge it.

Alternatively: please upload the test and I'll run it on my end.

emikulic commented 1 year ago

Alternatively: merge this PR as-is and deal with tests later. What would you prefer?

kugland commented 1 year ago

Alternatively: merge this PR as-is and deal with tests later. What would you prefer?

I’d prefer to change this PR, add the tests, change --cors to --allow-origin and add some sanity check for the argument. Because, as it is now, it would allow messing with the header. For example, --cors $'*\r\nX-I-Can-Pass-Another-Field: value\r\n\r\nAnd even the body can be messed with'.

Maybe even implement an option to pass arbitrary headers? What you think? And if going this way, how do you think the headers should be stored in memory? As a linked list? As an array expanded with realloc?

Something like:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

static unsigned int xvasprintf(char **ret, const char *format, va_list ap) {
    int len = vasprintf(ret, format, ap);
    if (ret == NULL || len == -1)
        abort();
    return (unsigned int)len;
}

static unsigned int xasprintf(char **ret, const char *format, ...) {
    va_list va;
    unsigned int len;

    va_start(va, format);
    len = xvasprintf(ret, format, va);
    va_end(va);
    return len;
}

size_t headers_len = 0;
char *headers = NULL;

void add_header(const char* field, const char *value)
{
    /* Omitted: some sanity on the variables `field` and `value` */
    char* headers_cpy = headers != NULL ? strdup(headers) : NULL;
    headers_len = xasprintf(&headers, "%s%s: %s\r\n", headers_cpy != NULL ? headers_cpy : "", field, value);
    if (headers_cpy != NULL) {
        free(headers_cpy);
    }
}

int main(void)
{
    add_header("X-First-Header", "Abcdef");
    add_header("Z-Second-Header", "Ghijklmn");
    printf("%s", headers);
}
emikulic commented 1 year ago

I like the idea of arbitrary headers. I like the approach in your code.

Suggestion: If headers starts as strdup("") you can drop the NULL checks.

Also, how about add_header("Pragma: whatever") for an even simpler API.