Repository navigation
Expand file tree
/
Copy pathChangeLog
More file actions
1298 lines (1180 loc) · 66.8 KB
/
Copy pathChangeLog
File metadata and controls
1298 lines (1180 loc) · 66.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
2026-10-23 Renaud Allard <renaud@allard.it>
* Release 0.24.1
- config: new idle_timeout directive, whole seconds from 0 to
86400, default 60, 0 disables it.
- connection: each connection keeps the idle timeout in force when
it was accepted, so a reload only applies to new connections.
- debian: the control file lists the current maintainer.
- docs: idle_timeout and the 5 second request timeout.
2026-10-23 Renaud Allard <renaud@allard.it>
* Release 0.24.0
- udp_connection: a source client dtls session drops the datagrams
that come back to it; an ICMP error on a backend socket no longer
ends its session.
- connection: a DTLS session the rate limiter has no room for opens
without being rate limited.
- seccomp: the binder opens directories only, creates unix stream
sockets only, sends on its IPC socket only and may not move or
close descriptor 0; the resolver sets only the socket options
c-ares uses; the logger may call capset().
- resolv: kernel TLS is turned off for DNS over TLS.
- logger: a forked child reconnects to syslog; capabilities are
dropped after setuid().
- sniproxy: the main process drops its capabilities but CAP_NET_RAW
at a start without root and after setuid().
- hostname_sanitize: a hostname ending in white space is refused.
- tests: clang string concatenation warning in xmpp_test.
- docs: routing per connection, CAP_NET_RAW, forged DNS replies,
sanitizer false alarms.
2026-10-21 Renaud Allard <renaud@allard.it>
* Release 0.23.1
- connection: backend affinity hashes with a seed of its own; a DTLS
session only starts a per-IP count entry in the first half of a
chain.
- seccomp: IP sockets must be TCP or UDP; the main process may not
set SO_MARK or SO_PRIORITY; the binder only receives on its IPC
socket and may not read.
- binder: multicast addresses are refused.
- http: a CONNECT request is routed on the host its target names.
- sniproxy: on OpenBSD only the socket of a unix backend or fallback
is unveiled, not its directory.
- udp_connection: a dtls listener opens no session for datagrams from
the backend socket of a session a '*' entry routed.
- docs: the rate limiter as 0.23.0 made it.
2026-10-20 Renaud Allard <renaud@allard.it>
* Release 0.23.0
- resolv: an answer to a lookup still waiting to be sent to the
resolver process is ignored.
- binder: outside Linux, IP sockets are created as the run user and
bound as root; a binder started as root is pledged id on OpenBSD.
- seccomp: bind, listen and accept are allowed to the main process
only; the utime calls are refused in every filter.
- sniproxy, listener: a root start and -t refuse a pidfile or unix
listener path through a directory others may write or a symlink
root does not own.
- connection: DTLS backend sockets take at most half of
max_connections; SIGUSR1 dumps are created relative to the checked
directory, which is checked again when its path changed; a full
rate limiter forgets the least recently seen source of a chain and
holds about 524288 sources at most.
- binder: its error replies are logged with non-printable bytes
escaped.
- http: a request line must start with a method token and a space.
- configure: no -pie on Darwin.
- docs: BungeeCord forwarding warning, macOS process names.
2026-10-13 Renaud Allard <renaud@allard.it>
* Release 0.22.1
- resolv: lookups are timed on the monotonic clock; one never
answered is given up 50 seconds after it was sent, checked every
5 seconds, whether a client still waits for it or not.
- http: up to 8 leading CR and LF bytes are skipped before the
request line is read, more make the request invalid.
- logger: the OpenBSD main process reopens a log file it can still
reach when the logger process is lost.
- connection: a client reset before accept() returned is logged at
debug level, and its access log line names the listener.
- ipc_crypto: at most one warning a second about refused messages.
- scripts: OpenRC logs the startup output at daemon.notice.
- docs: the fallback and a client's own PROXY header, the 50 to 55
second lookup bound, how a lost first logger is reported.
2026-10-11 Renaud Allard <renaud@allard.it>
* Release 0.22.0
- config: a configuration without a listener, reuseport or an acl on
a unix socket listener, a unix fallback on a dtls listener and a
FIFO reached through a symlink are refused; a literal entry's
trailing dot is left out; -t refuses, when not run as root, a user
or group other than its own, and keeps its refusals on stderr.
- logger: on a root start only log files created by it, or owned by
the run user, are handed over; an unknown user or group leaves no
root-owned file; startup refusals reach the error log; SIGXFSZ is
ignored; a failed logger restart is retried; dropped messages are
counted; the health check waits for its ping's answer; a replaced
file is reported as ESTALE on reopen.
- resolv: a lookup is given up 60 seconds after it was sent; at most
64 frames are read per wakeup; DoT setup failures set errno.
- tls: a ClientHello whose extensions are not all read before
supported_versions is refused; versions above TLS 1.3 do not count
against -T.
- http: an absolute-form target is routed on its host and must match
the Host header; a client's PROXY header is refused, as on xmpp; a
request departing from the HTTP/2 preface is parsed again; an
empty :authority port is refused.
- connection, listener: wall clock steps back no longer affect the
rate limiter, buffer shrinking or throttled warnings; accepting
pauses when a connection cannot be allocated; DTLS sessions to a
unix socket are refused; helpers killed by a signal are logged.
- backend: the regex step limit grows with the pattern's length.
- sniproxy: the OpenBSD main process no longer pledges chown.
- scripts, packaging: the FreeBSD rc script passes -g; OpenRC logs
stderr to syslog and keeps an empty SNIPROXY_OPTS; descriptions
name every protocol; the example config no longer loops.
- docs and tests: IPv6 Host entries, OpenBSD process names, the
systemd dump location, BSD log directories, -T; new tests and CI
runs under Valgrind and the sanitizers.
2026-10-10 Renaud Allard <renaud@allard.it>
* Release 0.21.1
- resolv: each IPC message is sent with MSG_EOR on a SOCK_SEQPACKET
socket, so FreeBSD 15 no longer returns two in one read.
- http2: the Huffman decoding tree is a static array.
2026-10-09 Renaud Allard <renaud@allard.it>
* Release 0.21.0
- config: io_collect_interval defaults to 0; a bracketed address
must be IPv6; a configuration path that is a FIFO is refused; -t
looks up the user and group, and refuses ID 0 for a root start.
- build: seccomp.h is found through pkg-config; --enable-seccomp
makes a missing libseccomp an error and the packages pass it;
--disable-seccomp builds without it.
- connection: entries made for UDP sources in the rate limiter are
limited; a full chain is logged as no room for the client; a
listener's source address is only bound for backends of its
family; the SIGUSR1 dump is mode 0200 after a root start.
- udp: up to 64 datagrams are read per wakeup.
- tls: supported_versions is checked against every -T minimum.
- http2: blocks whose fields decode past 64 KiB are routed on the
fields before the limit.
- resolv: a lookup keeps its place under the DNS limits until both
address families are done; DoT socket errors are logged with their
errno.
- logger: a log file's directory is only reached through symlinks
owned by root after a root start.
- binder: CAP_DAC_OVERRIDE is kept only for unix socket listeners
under /run or /var/run.
- rc: the FreeBSD script reloads through rc.subr.
- tests: backend ACL, TLS refusals with a fallback, per-IP rate,
DTLS session and the 503 of bad_dns_request_test; CI runs the
tests that need root.
- docs: IP literals matched by a '*' entry go to the fallback; a
pidfile in a root-only directory stays behind; each DoT lookup
opens a new connection; capabilities kept after the drop.
2026-10-09 Renaud Allard <renaud@allard.it>
* Release 0.20.0
- tls: a first record that is not a handshake and a ClientHello that
outgrows the client buffer are refused instead of going to the
fallback, so -T cannot be bypassed through it.
- table: a hostname whose pattern match fails is refused instead of
trying the later entries and the fallback.
- connection: multicast backend addresses are refused; UDP lookups
have their own per-client DNS count and at most half of
max_concurrent_queries.
- udp: only client datagrams restart a session's idle timer; the
session key includes the IPv6 scope.
- logger: after a start as root the log files are write-only for the
user sniproxy runs as.
- sandbox: the Linux binder runs as the configured user, and IP
listeners are created as that user; the main process cannot create
namespaces, clone3 fails with ENOSYS, netlink is NETLINK_ROUTE only;
TIOCLINUX is refused like TIOCSTI.
- docs: allow_except advice adds 100.64.0.0/10 and the host's own
addresses; a dtls acl only checks forgeable sources; a reload keeps
the acl of accepted connections.
2026-10-08 Renaud Allard <renaud@allard.it>
* Release 0.19.2
- resolver: every socket c-ares opens is watched, instead of the first
16, so lookups no longer time out once c-ares 1.34.6 opens more of
them after a nameserver failure; OpenBSD: the resolver no longer
pledges "unix".
- docs: the allow_except advice lists IPv6 unique local addresses and
the NAT64 prefix.
2026-10-07 Renaud Allard <renaud@allard.it>
* Release 0.19.1
- udp: sessions have their own per-IP allowance and count, apart from
TCP connections; the session hash is keyed with an arc4random value.
- tls: a ClientHello whose version cannot be checked against -T is
refused instead of going to the fallback; a handshake header split
over records is read.
- connection: PROXY header trust and source client are taken at
accept, so a reload does not change them for open connections.
- sandbox: Linux processes are non-dumpable; the resolver and binder
only open files for reading; helpers only create the sockets they
use and only signal themselves; a root binder keeps only
CAP_NET_BIND_SERVICE and CAP_DAC_OVERRIDE; unused file system calls
are refused to the main process and the logger; OpenBSD unveils the
files themselves and makes the dump directory at startup.
- sniproxy: the main process closes inherited descriptors, except the
one a -c path names; address: no uninitialised bytes in a logged
unix socket path cut short.
2026-10-07 Renaud Allard <renaud@allard.it>
* Release 0.19.0
- resolver: dot:// servers keep their port with c-ares before 1.24
instead of being queried in cleartext on port 53, and DoT works with
c-ares 1.18 and 1.19; servers given by name are set once c-ares has
returned and keep up to four addresses; the 64 address limit of a
lookup applies to each family.
- connection: a fast open backend gets the client's end of data once
connected; no abort message after the backend's end of data, nor
counted as received from it; peers that went away and clients that
reset before accept() are not logged; a large max_connections no
longer wraps; PROXY headers that do not follow the protocol are
refused; on OpenBSD a spliced backend failure is handled as an
unspliced one, and splicing is used on OpenBSD only.
- udp: sessions are charged to the per-IP limits once validated, with
an allowance of their own until then; wildcard dtls listeners answer
from the address the client sent to.
- parsers: only server_name is limited to 4096 bytes; a ClientHello
may span several records; HTTP/2 header blocks over 64 KiB are
routed; the Minecraft 1.6 server list ping goes to the fallback.
- config: a port after a unix address, a unix source address, portless
entries for unix listeners, a protocol or log setting given twice
and a directory are refused; words up to 4096 bytes; refused values
named in the errors.
- sniproxy: a relative -c path keeps its symbolic link; a daemon moves
to / before starting the logger; a reload or dump requested during
startup is acted on; the descriptor limit is raised up to the hard
limit; open connections are logged at shutdown; stopped helpers and
a binder that does not answer no longer hold up shutdown or reloads;
the binder and logger report crashes; riscv64 allows the instruction
cache flush; the OpenBSD logger unveils before dropping privileges.
- packaging: the Debian service is not enabled or started on install,
debhelper compat 12; the Debian init script restarts with ENABLED=0;
the systemd unit splits SNIPROXY_OPTS into words; the unused Red Hat
init script is removed.
- tests and docs: bad_request, fallback, bind_source and
transparent_proxy tests able to fail, new half_close_test, TLS and
DTLS fuzzers seeded, test objects built apart from src/; manual,
README and ARCHITECTURE.md corrections.
2026-09-25 Renaud Allard <renaud@allard.it>
* Release 0.18.0
- connection: TCP half-close is passed on instead of closing both
sides, on spliced OpenBSD connections too; a backend failing before
answering gets the client the abort message, one failing mid answer
resets the client.
- connection: backend_affinity seeds no longer all odd; dtls backend
sockets count against max_connections; idle timer on the monotonic
clock; access log fixes for incoming PROXY headers and header
timeouts.
- listener: numeric names refused as a '*' target; address: no port
on unix socket addresses; config: second access_log in a listener
refused; sniproxy: pidfile pid range checked.
- logger: first logger detached from the caller's output when
daemonizing; control messages wait a second for a busy logger;
restart reasons logged; helpers ignore SIGINT; binder warns when
restarted without root.
- packaging: systemd unit gets /run/sniproxy and KillMode=mixed;
Debian init stop fixed; sniproxy-dbg dropped; RPM changelog and
build requirements; SELinux policy removed.
- tests and docs: curl --fail, crash and sanitizer detection, working
slow_client, hostname and wildcard tests; fuzzing fails on UB; CI on
every branch; manual, README and ARCHITECTURE.md corrections.
2026-09-25 Renaud Allard <renaud@allard.it>
* Release 0.17.0
- listener: reloads pair listeners by address and socket type, and
close dropped listeners before adding new ones.
- connection: idle connections are closed a full idle timeout after
their last activity.
- listener, binder: no SO_REUSEADDR on dtls sockets on Linux.
- minecraft: handshakes up to the largest BungeeCord server address.
- logger: FreeBSD syslog kept across SIGHUP in capability mode; no
fchmod after the OpenBSD pledge; rotated files handed to the logger;
stuck, lost and non-child loggers replaced.
- listener: stale unix socket probe only removes sockets and does
not block; dtls on unix sockets refused.
- connection: large incoming PROXY v2 headers, outgoing PROXY header
on a full buffer, per-IP counts when a reload enables the limit,
source client for IPv4 clients of dual-stack listeners, 64-bit
byte counts.
- sniproxy: connection ceiling from the file limit in force, two
descriptors per connection.
- config: relative unix paths, unix nameservers and a bare directive
at end of file refused; error_log removal on reload; tokenizer
works on pipes; one boolean parser, tcp_fastopen takes true/false.
- http: trailing whitespace in Host; resolver: *_first spread over
fallback records; FreeBSD: Capsicum limits on restarted helpers.
2026-09-24 Renaud Allard <renaud@allard.it>
* Release 0.16.1
- resolver: a lookup that finds the channel to the resolver process
full waits for room instead of failing with a 503.
- resolver: on macOS the resolver process retries an answer the full
channel refused with ENOBUFS instead of dropping it, and pauses when
the channel reports room it does not have.
2026-09-24 Renaud Allard <renaud@allard.it>
* Release 0.16.0
- logger: log files are opened without blocking and must be regular
files with a single link, so a hard link in the log directory can
no longer give a root file to the service user and a FIFO no longer
hangs the start. The logger starts its own session.
- http: an incomplete HTTP/1 request is only parsed again once a new
line arrives, only the first HTTP/2 header block is searched for the
host, and an HTTP/2 host value holding a NUL is refused.
- sniproxy: "*" is not accepted as the hostname of a request.
- sniproxy: a DNS lookup counts against max_concurrent_queries and
max_concurrent_queries_per_client until the resolver has finished
it, not only until its client leaves.
- resolver: the delayed restart is not throttled again, and only IPv4
and IPv6 addresses are accepted from the child.
- acl: 0.0.0.0 and :: are refused as backends under a backend_acl, and
a range written as IPv4-mapped IPv6 is read as the IPv4 range.
- binder: a unix socket is bound in the directory whose real path was
checked, and a path too long for sun_path is refused.
- seccomp: the 32-bit forms of system calls are allowed, TIOCSTI is
refused, and the main process cannot open raw or packet sockets.
- proxy protocol: a PROXY v1 header is only used once all of it is
valid, and ports above 65535 are refused.
- dtls: a renegotiation hello drops the session, and a full session
table drops the oldest unvalidated session to admit a new one.
- config: an address given to two listener blocks of the same type,
and a config file owned by anyone but root or the running user, are
refused.
- docs: a proxy_protocol listener should only admit the load
balancer; where a wildcard backend can lead.
2026-09-23 Renaud Allard <renaud@allard.it>
* Release 0.15.1
- logger: at exit the main process waits up to a second for the
logger process to write what is queued before killing it, so the
reason for a fatal exit reaches a file error log.
2026-09-23 Renaud Allard <renaud@allard.it>
* Release 0.15.0
- freebsd: only the logger enters capability mode; connect() is not
permitted there, so proxying and resolving failed. CI now builds,
tests and runs sniproxy on FreeBSD.
- sniproxy: every helper process ignores SIGHUP and SIGUSR1, and one
restarted while running stops on SIGTERM again.
- openbsd: the logger unveils its log files, a restarted logger comes
up with a reduced pledge, and the main process reopens its log
files on rotation.
- sniproxy: server buffers grow up to server_buffer_limit, but not
while connections use more than 64 MiB.
- sniproxy: setproctitle_init() is called where libbsd provides it.
- config: an unknown syslog facility and PROXY options on a dtls
listener are errors; a dtls listener's table with PROXY entries
warns. ipv6_v6only no clears IPV6_V6ONLY.
- packaging: the Alpine package ships the default config, the log
directory, the logrotate file and an OpenRC service.
- build: configure keeps -Werror=format-security with gcc.
- tests: TestUtils stops leftover children when a test dies.
test-sanitizer-build.sh is removed.
- docs: man pages, ARCHITECTURE.md and SANITIZERS.md corrected.
2026-09-23 Renaud Allard <renaud@allard.it>
* Release 0.14.5
- openbsd: the resolver process can read /etc/resolv.conf and
/etc/hosts. Under unveil it read neither and asked 127.0.0.1, so
nothing resolved without a resolver block unless a local resolver
was running.
- sniproxy: source client works again after the privilege drop. On
Linux the main process keeps CAP_NET_RAW alone when a listener
uses it, and the helper processes started later give it up.
- resolver: dot:// servers given by hostname are looked up through
c-ares once the sandbox is in place, and queries are only read
after those lookups succeeded.
- resolver: the DNSSEC code that could never compile in is gone.
dnssec_validation strict and its aliases warn when the config is
read and act as relaxed.
- packaging: the systemd unit starts as root and passes -g. The
Debian package installs /etc/sniproxy.conf 0640 root:daemon and
keeps /var/log/sniproxy daemon:adm 0750, and declares
Rules-Requires-Root. The RPM ships the default config, the log
directory and the logrotate file, which moved to scripts/ and no
longer names the adm group.
- build: configure warns without libseccomp. setver.sh is gone, so
autogen.sh no longer rewrites tracked files.
- tests: the integration tests run without root. Their configs turn
off the per-IP rate limit, and reload, functional, binder,
reuseport and transparent proxy tests were fixed. Fuzzing keeps
its crash files.
- docs: README and man pages corrected throughout.
2026-09-23 Renaud Allard <renaud@allard.it>
* Release 0.14.4
- tests: the SIGCHLD handler in TestUtils.pm could record -1 as the
status of a child it had just reaped when a second SIGCHLD was
handled in between, so the run failed with "died with signal 127"
for a child that exited normally. $! and $? are localised in the
handler now.
- tests: fd_limit_test no longer requires every request to succeed
while the proxy is deliberately out of descriptors. It counts the
refused ones and checks instead that the limit was reached and that
the proxy serves a request once the pressure is off.
- README: add a comparison with upstream sniproxy, HAProxy, nginx
stream and Envoy, say that macOS runs without a sandbox, and stop
calling the DTLS source check a HelloVerify exchange.
- No change to the daemon itself.
2026-09-18 Renaud Allard <renaud@allard.it>
* Release 0.14.3
- sniproxy: the check for a stale pidfile no longer blocks on a
FIFO left at the pidfile path. fopen() waits there for a writer,
so a daemonized start hung instead of refusing the path. It is
opened with O_NONBLOCK and O_NOFOLLOW now and only read once
fstat() says it is a regular file; anything else is left for
write_pidfile() to refuse.
2026-09-09 Renaud Allard <renaud@allard.it>
* Release 0.14.2
Follow-up fixes from the regression pass over 0.14.1.
- sniproxy: open the standard descriptors at startup. A daemon
started with stdout or stderr closed let the event loop's
descriptors take their place, and the helper children, which
keep fd 1 and 2 since 0.14.1, inherited them.
- connection: the SIGUSR1 connection dump never worked on OpenBSD.
mkostemp() was given O_NOFOLLOW, which OpenBSD rejects with
EINVAL; the flag added nothing since the file is created with
O_EXCL.
- connection: TCP_NODELAY and TCP_FASTOPEN_CONNECT are only set on
TCP sockets. On a unix listener or a unix backend the failing
call logged a warning for every connection.
- ipc_crypto: zero the control buffer used to pass a descriptor
over the logger channel; its padding was sent uninitialised.
- address: initialise the buffer used while splitting off a port.
The compiler may compare more bytes than the string holds, which
valgrind reported as a use of uninitialised memory.
2026-09-09 Renaud Allard <renaud@allard.it>
* Release 0.14.1
Fixes from another audit of the whole source tree. Two of them
only affect OpenBSD, where they were severe.
- sniproxy: starting as root with a file based error_log or
access_log aborted on OpenBSD with a pledge violation. The
privilege drop chowns the log files to the target user and none
of the pledge sets in force at that point allowed chown.
- connection: on OpenBSD a spliced connection was closed after 60
seconds of silence in one direction while data still flowed in
the other, so any download longer than the idle timeout died.
The kernel idle timeout is no longer used; the connection idle
timer polls the splice byte counters of both directions instead.
- connection: the access log counted only the bytes relayed before
a splice was set up. The kernel byte counters are added when the
splice ends.
- logger: the resolver and binder children wrote their own
messages to a descriptor number the parent had used for the log
file and the child had closed, so with a file error_log the
resolver's DNS-over-TLS failures and its -d output were lost.
Helper processes log through syslog when the error log is a file
and keep the parent's stderr otherwise.
- connection: the per-IP rate limiter and connection counter hash
tables grow from 1024 to 65536 buckets. A chain longer than 32
entries refuses connections, and with buckets kept for five
minutes a busy instance started refusing legitimate clients at a
few tens of thousands of distinct sources.
- tls: a TLS 1.3 minimum set with -T was satisfied by any
supported_versions value of 0x0304 or higher, including the
GREASE values sent by clients that only speak 1.2. Only real TLS
versions count now.
- connection: a ClientHello attempting a renegotiation is rejected
even when a fallback is configured, as the log already claimed,
and the message for hellos refused by the version policy no
longer says they cannot carry SNI.
- listener, binder: unix socket nodes are created with mode 0666
regardless of the umask. In the background the daemon's umask of
077 produced a node only its own user could connect to.
- address: only one trailing port is accepted, and a bracketed
IPv6 address must end at its bracket, so 1.2.3.4:80:90 is a
configuration error instead of silently meaning 1.2.3.4:80.
- doc: the fallback is not used when the address of a matching
table entry cannot be resolved; the manual claimed it was.
2026-08-05 Renaud Allard <renaud@allard.it>
* Release 0.14.0
Fixes from a deep audit of the whole source tree, plus stricter
configuration validation that rejects files earlier releases
accepted. Check an existing configuration with "sniproxy -t"
before upgrading.
- resolver: refuse to start when a dot:// nameserver given as a
hostname cannot be resolved. The entry was skipped instead,
which left c-ares with no configured server, so it fell back to
the cleartext nameservers in resolv.conf and every lookup ran
over plain UDP for the lifetime of the resolver child.
- config: warn when dot:// and cleartext nameservers are mixed.
They share one failover pool, so a failed TLS handshake silently
falls back to unauthenticated DNS.
- resolver: restart the child from a timer instead of sleeping in
the event loop, and keep retrying after a failed restart. A
crash-looping child froze all proxy traffic one second at a
time, and a restart whose initialisation failed was never
retried, leaving DNS down until the daemon was restarted.
- resolver: wait for room on the IPC socket instead of dropping a
result frame, bounded so a socket reported writable that cannot
take the frame does not spin. A dropped result left the client
stuck until its idle timeout, or forever with the timeout off.
- resolver: set the child's own signal disposition rather than
inheriting the parent's libev state, which left the child deaf
to SIGTERM, and signal it before waiting on shutdown so a child
still resolving a dot:// hostname cannot hold up exit.
- backend: match table patterns case insensitively. Hostnames
reach the table lowercased, so a pattern containing an uppercase
letter could never match and its traffic fell through to the
fallback.
- config: compile backend patterns while parsing, so a pattern
that does not compile is a configuration error rather than a
backend silently dropped at startup.
- cfg_parser: reject a directive that is missing its argument
instead of silently keeping the default.
- config: reject duplicate error_log, access_log and resolver
blocks, which previously replaced the first block silently.
- connection: keep the per-IP rate limit buckets when the rate is
unchanged, so a reload no longer hands every throttled client a
full token bucket, and re-count open connections when
per_ip_ipv6_prefix changes so their close cannot decrement
buckets they never incremented.
- listener: bind a replacement listener before closing the old
one, so a failure to rebind after a socket type change leaves
the address served.
- config: adopt the new error log on reload instead of pinning the
one from startup, and warn about pidfile and tcp_fastopen
changes that a reload cannot apply.
- binder: drop the IPC channel after a partial send, which leaves
the stream desynced.
- ipc_crypto: set errno on every send failure path so callers can
tell a droppable failure from one needing a teardown.
- sniproxy: on OpenBSD unveil the configuration file after parsing
it. Unveiling beforehand hid every other path while the log
files named in the configuration were being opened, so a file
based error_log or access_log made startup fail.
- doc: explain that a backslash meant for a table pattern must be
doubled, since the parser consumes one as an escape, and record
that per_ip_ipv6_prefix also groups the per-client DNS query
budget and the backend_affinity hash.
2026-07-02 Renaud Allard <renaud@allard.it>
* Release 0.13.5
Fixes and one new option from a deep security audit:
- connection: add the per_ip_ipv6_prefix option (default 64) to
group native IPv6 clients by prefix for the per-IP rate limit and
connection cap. Keying on the full /128 let a client rotate
through its allocation (a /64 is the usual assignment) to evade
both limits. IPv4 clients keep exact matching; set the prefix to
128 to restore the old per-address behaviour.
- connection: reject connections when the per-IP connection-count
hash chain is full. The count check previously failed open while
the rate limiter failed closed, so once a chain saturated the
per-IP connection cap could be bypassed by colliding addresses.
- ipc_crypto: report a distinct error on a partial send to the
logger so a desynced encrypted channel is torn down and restarted
instead of being written to again.
- table: swap backend lists on reload with STAILQ_CONCAT instead of
copying the list head by value, which left a dangling tail pointer
when a reloaded table ended up with no backends.
2026-06-11 Renaud Allard <renaud@allard.it>
* Release 0.13.4
Fixes from a deep source, documentation, build and CI audit:
- seccomp: allow the seccomp syscall in the main process filter;
helper children forked at runtime to replace a dead resolver,
logger or binder inherited the main filter and were killed with
SIGSYS before they could install their own, so a single
resolver crash permanently disabled DNS resolution.
- resolver: create c-ares sockets non-blocking; with DNS over TLS
the single-threaded resolver child could stall for the whole
kernel SYN retry cycle in connect(), or indefinitely inside TLS
calls against a stalling server, freezing all DNS resolution,
failover and even shutdown.
- binder: confine root-created unix socket nodes to /run and
/var/run, applied only when actually binding so unix listeners
in user-writable directories outside those prefixes still
start; set SO_REUSEPORT before bind in the binder child so port
sharing works for privileged listeners; treat an IPC end of
stream as a dead binder child and restart it instead of
consulting a stale errno.
- listener: remove unix socket files on shutdown and replace
stale leftovers at the next start instead of failing with
EADDRINUSE until the file is deleted by hand.
- config: finalize the last directive of a file without a
trailing newline instead of silently dropping it; reject
duplicate table names, which flipped routing between the
duplicates on reload; validate listener table references at
load; switch the error log only after a successful parse so a
failed reload keeps the running error log; validate dot://
nameserver entries so an entry the resolver child refuses
fails config test instead of crash-looping the child, and
throttle resolver child restart storms.
- logger: keep the working log file when a reopen fails instead
of silently disabling logging; tolerate transient EAGAIN on
control messages instead of killing the healthy logger child;
send privileges to a restarted logger child so it tightens its
sandbox like the original one.
- http2: do not reject clients advertising a header table larger
than 64 KiB; the setting describes the client's own decoder
and places no constraint on parsing its requests.
- connection: use ev_timer_remaining() for the idle timer
hysteresis; comparing the watcher's private monotonic deadline
against the wall clock disabled it and caused timer churn on
every read and write event.
- buffer: honor the documented 1 GiB connection buffer limit;
configured values above 10 MiB were silently clamped.
- pidfile: check liveness instead of unconditionally removing
the pidfile at startup, so a second instance no longer deletes
the running instance's pidfile.
- resolver: drop the dead res_init() call; the guarding macro
was never defined and the call cannot be compiled as written.
- docs: correct the SIGUSR1 dump location in the README, drop
the false directive abbreviation claim from sniproxy.conf.5
and document that tcp_fastopen is a fatal configuration error
on builds without TFO support.
- tests: distribute the Perl integration tests and harness
modules in the tarball so make check works from a release and
make distcheck passes; flag unexpected signal deaths of test
children; make connection_reset_test reset during the transfer
again; stop tracking compiled test binaries.
- ci: fix openSUSE Leap version detection for package builds,
carry the fuzzing corpus across nightly runs and use the bare
major.minor Ubuntu version for package builds.
2026-05-30 Renaud Allard <renaud@allard.it>
* Release 0.13.3
Security and correctness fixes from a multi-pass source audit:
- ipc: close received file descriptors on the frame recvmsg
truncation and short-read error paths so a misbehaving IPC peer
cannot leak a descriptor per crafted message.
- ipc: derive per-instance keys from a fresh salt so a restarted
logger, resolver or binder child never reuses the previous
instance's ChaCha20-Poly1305 (key, nonce) keystream.
- listener: reject "source client" combined with proxy_protocol;
the untrusted PROXY header must not drive the transparent source
bind, which would allow source spoofing toward backends.
- udp: enforce the global and per-client DNS query limits on the
UDP/DTLS resolution path, match the receiving listener when
looking up sessions, and fail closed when the transparent or
source bind fails.
- resolver: free queries orphaned at shutdown, assign
in-flight-unique query ids, include the affinity seed in query
coalescing, watch only the direction the TLS layer needs during
the DoT handshake (fixes a CPU busy-loop), and clear the OpenSSL
error queue before each DoT SSL operation.
- seccomp: give the binder a minimal network syscall allowlist and
grant it fs_read so its own error logging does not hit SIGSYS.
- OpenBSD: keep wpath in the main pledge so the SIGUSR1 connection
dump can write its file, and always tighten the logger pledge
after privilege handover.
- address: reject a sockaddr shorter than sa_family to avoid a
heap over-read from a hostile resolver result.
- pidfile: do not remove a pidfile this process did not create.
- listener: set a deterministic errno when a connection allocation
fails so the accept backoff is not driven by a stale value.
- children: redirect stdio to /dev/null after closing inherited
descriptors so a stray log write cannot corrupt a reused fd.
- connection: record the memory-pressure run time before shrinking
so the cooldown suppresses reentrant passes.
- tests: give slow_client_test more timeout headroom under load.
2026-05-22 Renaud Allard <renaud@allard.it>
* Release 0.13.2
- OpenBSD: initialize the IPC master key in the parent before
forking the logger and binder children. Previously the children
pledged before calling ipc_crypto_channel_init(), which on first
invocation tries to mlock the master key. mlock() is not covered
by any pledge promise, so the child was aborted. Pre-initializing
in the parent lets the children inherit the locked state and
skip the mlock path.
2026-05-04 Renaud Allard <renaud@allard.it>
* Release 0.13.1
- Build: include libgen.h unconditionally for dirname()/basename().
0.13.0 left the include inside #ifdef __OpenBSD__, so the new
Capsicum pidfile-cleanup code failed to build on FreeBSD.
- Build: require seccomp.h to be present before defining
HAVE_SECCOMP. Distros that split libseccomp into separate
header and library packages (notably SUSE BCI) no longer fail
the seccomp.c compile when only the library is installed.
- Tests: add a logger_post_fork_child_disinherit stub to
resolver_response_fuzz so the resolver fuzz harness links
cleanly without pulling in logger.c.
- CI: continuous-fuzzing only files an issue when actual
crash/leak/timeout artifacts exist; build or link failures no
longer create false-positive 'fuzzer crash' issues.
2026-05-03 Renaud Allard <renaud@allard.it>
* Release 0.13.0
- Security: TLS parser now rejects renegotiation_info even when SNI
appears earlier in the extension list. Previously the parser
returned on the SNI extension and never inspected later
extensions, allowing a client-initiated renegotiation attempt
alongside a valid SNI to be routed instead of rejected.
- Security: Reject empty or mis-sized SNI server_name list per
RFC 6066 section 3.
- Security: PROXY protocol v2 rejects undefined command values.
- Security: IPC channel closes orphan fds in SCM_RIGHTS ancillary
data, validates cmsg_len before extracting fd, checks MSG_CTRUNC
on the prefix recvmsg, and wipes partial ciphertext on seal
failure.
- Reliability: Binder and resolver children no longer SIGKILL the
parent's logger child via inherited IPC state; the resolver
child's startup notice() previously triggered this on every
launch.
- Reliability: disable_logger_process and logger_process_shutdown
no longer SIGKILL on waitpid ECHILD - the PID may have been
recycled.
- Reliability: HTTP/2 returns -1 (incomplete) instead of -2 (no
host) when the buffer ends mid-request, so clients that split
preface, SETTINGS and HEADERS across TCP segments are not
aborted.
- Reliability: Configuration tokenizer rejects embedded NUL bytes.
- Configuration: dot://ADDRESS/SNI/tls1.2|tls1.3 syntax now
parses correctly (previously rejected before reaching the
resolver child).
- FreeBSD: Connect to syslog at openlog() time with LOG_NDELAY so
the connection is open before cap_enter().
- FreeBSD: Pre-open pidfile parent directory and remove on
shutdown via unlinkat. Register cleanup via atexit.
- FreeBSD: Use unlinkat for connection-dump cleanup paths.
- FreeBSD: Limit cap_rights on parent-side IPC sockets.
- FreeBSD: Warn when logger child cannot open a new file sink in
capability mode.
- OpenBSD: Keep cpath in the post-tightening pledge so the pidfile
can be removed at exit (otherwise daemon SIGABRTed on shutdown).
- Packaging: Add libseccomp build dependency to debian/control,
redhat/sniproxy.spec, alpine/APKBUILD and the matching install
lines in release-packages.yml. Without this, every Linux package
release was being built without the seccomp sandbox.
- Packaging: Detect libssl in configure instead of hardcoding
-lssl. Point package metadata at the renaudallard fork.
- Build: Actually clear hardening flags when sanitizers are
enabled (the conflict-resolution branch had no effect).
- Tests: Tighten resolv_test, ipc_crypto_debug_test and tls_test
assertions so silent regressions are caught. The lenient
tls_test assertion is what previously masked the
renegotiation_info bypass.
- Tests: Wire ipc_crypto_debug_test, ipc_crypto_rekey_test and
ipc_crypto_time_rekey_test into the build.
- Tests: Cover dtls and ipc_crypto tests in the valgrind workflow.
- Tests: Fix functional_test connection-dump file lookup,
xmpp_test off-by-one length, config_test TMPDIR honoring.
- Other: Rate limit buckets now track is_v4 to mirror
ConnCountBucket and avoid v4/v6 cross-match on hash collision.
- Other: Skip destructive sink swap when REOPEN produced no new
file. Avoid double ev_timer_again in UDP validating fast-path.
Use _exit in manual daemonize fork-and-exit fallback.
2026-03-20 Renaud Allard <renaud@allard.it>
* Release 0.12.0
- Add Capsicum capability mode sandboxing for FreeBSD
- Add FreeBSD rc.d startup script (scripts/sniproxy.rc)
- Fix build system: add AC_CANONICAL_HOST for seccomp/Capsicum
detection and link libm for isfinite() on FreeBSD
- Add capsicum.c to test binaries that link logger.c
- Add FreeBSD package build to release workflow
2026-03-17 Renaud Allard <renaud@allard.it>
* Release 0.11.1
- Fix IPV6_V6ONLY not effective when binding privileged ports via
binder child process (setsockopt after bind rejected by Linux)
2026-03-16 Renaud Allard <renaud@allard.it>
* Release 0.11.0
- Add DTLS (TLS over UDP) protocol support for proxying WebRTC,
OpenConnect VPN, CoAP, and other UDP/DTLS protocols by hostname
- Shared SNI extension parsing between TLS and DTLS parsers
- UDP session manager with per-session connected server sockets,
DNS resolution, idle timeouts, source address binding, and
transparent proxy support
- Binder support for UDP (SOCK_DGRAM) privileged port binding
- DTLS parser unit tests and fuzz harness
- DTLS source validation to prevent UDP reflection/amplification
- Per-IP rate and connection limiting for UDP sessions
- Cap UDP pending datagram size to 4KB to limit memory under load
- Add missing sendfd/recvfd pledge promises on OpenBSD, including
sendfd in parent pledge set for binder child inheritance
- Tighten seccomp: restrict logger to syslog and IPC-only syscalls
- Disable TLS session tickets for DNS-over-TLS connections
- Detect explicit_bzero in configure for reliable secret wiping
- Wipe IPC send buffer before free
- Set SOCK_CLOEXEC on DoT sockets in resolver child
- Add int16_t bounds check in Huffman tree builder
- Add compiler hardening flags: -Wshadow, -Wimplicit-fallthrough,
-Wnull-dereference, -Wundef, -fno-delete-null-pointer-checks
- Harden systemd service with additional restrictions
- Use memcpy for IPv6 address hashing in UDP session lookup
- Annotate intentional switch fallthroughs for clang compatibility
- Fix conn_count_free_count underflow guard in connection rate limiter
- Fix missing partial write check in resolver child send
- Fix atomicity violation in resolver query resubmission after restart
- Fix unsafe EV_DEFAULT fallback in resolver child after fork
- Add early sock->failed check in DoT read/write callbacks
- Warn when reuseport or ipv6_v6only changes on SIGHUP reload
- Warn when resolver config changes on SIGHUP reload
2026-03-14 Renaud Allard <renaud@allard.it>
* Release 0.10.0
- Add per_ip_max_connections directive to limit concurrent connections per IP
- Add tcp_fastopen directive for TCP Fast Open on listeners and backends
- Add backend_acl directive to restrict outbound connection subnets
- Randomly select from multiple DNS results for load distribution
- Add backend_affinity table directive for consistent backend selection
- Auto-anchor literal hostname patterns in table entries
- Accept incoming PROXY protocol v1/v2 headers on listeners
- Add PROXY protocol v2 (binary) output support
- Add Minecraft Java Edition protocol support
- Add SIGCHLD handler to reap zombie child processes
- Document HTTP/2 connection coalescing limitation
- Fix broken binary search and missing host header detection in HPACK
decoder
- Enforce RFC 7541 dynamic table size update limits in HPACK decoder
- Fix ensure_logger_process return value after resend failure
- Add missing recvfd pledge promise in logger child on OpenBSD
- Restart logger child on health check failure instead of fatal exit
- Use non-blocking poll in logger health check to avoid stalling event
loop
- Close unexpected fd in logger child NEW_SINK handler
- Handle ECHILD when killing logger child after daemonize
- Fix stderr log messages silently lost via logger child
- Resolve relative config path before daemonize and check pidfile write
- Use logger for write_pidfile errors instead of stderr
- Fix per-IP conn_count mismatch with PROXY protocol
- Fix -T 1.3 rejecting all TLS connections
- Fix fallback address losing port on SIGHUP reload
- Reject stray closing brace at config top level
- Fix EAGAIN on logger IPC permanently killing logger child
- Fix use-after-free in resolve_server_address on sync DNS failure
- Fix server buffer growing 4x instead of intended 2x on resize
- Fix IPC crypto context leak in resolver init on socketpair/fork failure
- Preserve errno across accept cleanup so EMFILE backoff triggers
- Fix resolver IPC max payload length to include header
- Use TOCTOU-safe directory validation for /var/run/sniproxy
- Wipe all derived keys on channel init failure
- Fix resource leak on strdup failure in DoT server registration
- Add overflow check to iovec total length in DoT send path
- Snapshot protocol at accept time to prevent mismatch during reload
- Fail backend init when literal pattern anchoring malloc fails
- Chown log files before dropping privileges for SIGHUP rotation
- Emit PROXY TCP4 for IPv4-mapped IPv6 addresses
- Unify fallback proxy_protocol naming with table backend syntax
- Use reallocarray() for overflow-safe allocation
- Replace assert() with proper runtime error handling
- Check setsockopt/fcntl/inet_ntop return values throughout
- Clean up unused functions, dead code, and redundant includes
- Add closefrom to AC_CHECK_FUNCS to enable efficient fd cleanup
- Add missing build dependencies for OpenSSL and libbsd
- Fix use-after-free in resolve_server_address on synchronous DNS failure
- Fix use-after-free when c-ares calls callback synchronously
- Fix use-after-free in resolver callback on config reload
- Fix PROXY protocol header sent after client data instead of before
- Fix HTTP Host header smuggling with bare Host: line
- Fix HTTP/2 empty HEADERS frame blocking CONTINUATION frames
- Fix HTTP parser returning wrong error for incomplete header lines
- Fix dead Huffman padding validation in HPACK decoder
- Reject EOS symbol in HPACK Huffman decoder per RFC 7541
- Fix double timestamp in syslog messages when logger process is enabled
- Clean up logger child sinks on abnormal parent exit
- Ignore SIGPIPE in logger child process
- Fix blocking waitpid in logger that can stall the mainloop
- Fix NULL dereference in err() during default logger initialization
- Fix potential NULL dereference on logger->sink in vlog_msg
- Fix potential NULL dereference in timestamp formatting
- Fix fd leak in recv_logger_message on validation failure
- Fix file descriptor leak in logger REOPEN handler
- Fix double-close of file descriptor in obtain_file_sink
- Fix recursive fork in logger child on pledge and crypto failure
- Fix zombie and repeated forks in logger on crypto init failure
- Fix obtain_file_sink fallback ignoring logger_parent_fs_locked
- Fix connection leak when server buffer expansion fails
- Fix assert and connection leak in reactivate_watchers for empty abort
- Fix double close_server_socket regression in buffer overflow guard
- Fix IPC crypto recv state corruption on AEAD verification failure
- Fix IPC send channel state corruption on rekey derivation failure
- Fix IPC recv channel corruption on rekey derivation failure
- Fix undefined behavior in ipc_crypto_open for zero-length payloads
- Fix reading uninitialized control buffer on recvmsg error
- Fix resource leak in ipc_crypto_channel_init and check return values
- Wipe base_key on derive_base_key failure in ipc_crypto_channel_init
- Add defensive NULL check for CMSG_FIRSTHDR in ipc_crypto_send_msg
- Fix zombie process in binder on crypto initialization failure
- Use _exit in binder child on seccomp filter failure
- Fix fatal() on config file permission change during SIGHUP reload
- Use consistent -1 error returns instead of -errno in config parsing
- Handle EOF without trailing newline in config tokenizer
- Use exact match for bad_requests action and reject unknown values
- Use exact match for protocol and reject unknown protocol names
- Reject negative values in config parsers regardless of leading whitespace
- Reject negative values in max_connections and resolver limit config
- Reject negative values for -n file descriptor limit option
- Check fcntl return values when setting sockets nonblocking
- Check ipc_crypto_channel_set_role return in resolver child
- Retry resolver child send on EINTR
- Use strict < in TLS extension parsing loop conditions
- Fix XMPP parser returning incomplete for malformed unclosed quotes
- Fix integer overflow in Minecraft parser on 32-bit platforms
- Fix heap over-read in buffer_coalesce on malloc failure
- Tighten OpenBSD pledge promises after privilege drop
2026-03-08 Renaud Allard <renaud@allard.it>
* Release 0.9.26
- Set logger IPC socket non-blocking to prevent mainloop stall
- Combine logger header and payload into a single IPC message
- Cache EVP_CIPHER_CTX in IpcCryptoState to avoid per-message allocation
- Use cached send buffer in ipc_crypto_send_msg to avoid per-message malloc
- Add missing limits.h include in resolv.c for INT_MAX
- Fix autorelease glob expansion of release notes containing asterisks
2026-03-08 Renaud Allard <renaud@allard.it>
* Release 0.9.25
- Fix infinite loop in shrink_idle_buffers under memory pressure
- Fix DNS per-client tracking bypass for IPv4-mapped IPv6 addresses
- Fix fd leak on short prefix read in IPC crypto receive
- Fix HTTP/2 to HTTP/1.1 fallthrough on full preface match
- Fix ACL policy validation for implicit default mode
- Fix logger priority name accepting partial matches
- Replace select() with poll() in logger health check
- Handle SSL_ERROR_ZERO_RETURN in DNS-over-TLS read
- Drain OpenSSL error queue after DoT TLS errors
- Clamp SSL_read/SSL_write length to INT_MAX
- Cap IPC payload length to INT_MAX for OpenSSL API
- Add size validation to binder AF_UNIX sockaddr comparison
- Add NULL guard to copy_address
- Fix size_t to int narrowing in config tokenizer
- Fix misleading logger stanza error messages
- Remove incorrect const from table_lookup_server_address
- Remove dead code in connection_cb buffer reserve path
- Update mutex nesting documentation in resolver
2026-03-04 Renaud Allard <renaud@allard.it>
* Release 0.9.24
- Feature: Add -t flag to test configuration and exit without
starting the proxy, similar to nginx -t.
- Feature: Add -g flag to allow group-read (0640) config file
permissions, enabling SIGHUP config reload after privilege drop.
A security warning is logged at startup when this flag is used.
- Improved permission error messages to show allowed modes and
hint about the -g flag.
2026-03-02 Renaud Allard <renaud@allard.it>
* Release 0.9.23
- Security: Normalize IPv4-mapped IPv6 addresses in rate limiter
so the same client gets one bucket regardless of address family.
- Security: Log warning when SNIPROXY_DISABLE_SECCOMP environment
variable bypasses the seccomp sandbox.
- Security: Verify privilege drop succeeded in logger child with
getuid/geteuid/getgid/getegid checks after setuid.
- Reliability: Register new listener addresses with binder allowlist
on config reload so privileged port binding works after SIGHUP.
- Reliability: Warn when user or group directive changes on config
reload since privilege drop is irreversible.
2026-03-01 Renaud Allard <renaud@allard.it>
* Release 0.9.22
- Resolver: Fix deadlock when resolver send fails during query emit
by deferring restart until after the mutex is released.
- Resolver: Fix DNS timeout timer not being rescheduled when already