<feed xmlns='http://www.w3.org/2005/Atom'>
<title>uhttpd, branch master</title>
<subtitle>Tiny HTTP server</subtitle>
<id>https://git.openwrt.org/project/uhttpd/atom?h=master</id>
<link rel='self' href='https://git.openwrt.org/project/uhttpd/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/'/>
<updated>2026-08-03T09:49:17Z</updated>
<entry>
<title>tls: add SNI-based redirect support</title>
<updated>2026-08-03T09:49:17Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:45:09Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=60f64bec40c8113cf09815ec377761b1f4f95f22'/>
<id>urn:sha1:60f64bec40c8113cf09815ec377761b1f4f95f22</id>
<content type='text'>
Add -Q option to configure SNI hostname to URL redirect mappings.
When a TLS client presents a matching SNI name, uhttpd responds
with 301 Moved Permanently and the configured Location URL.

Multiple -Q entries are supported for mapping different hostnames
to different targets.

Note: requires ustream-ssl backend populating ssl.server_name.

Closes: #19
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>lua, ucode: use buffered stdio for script output</title>
<updated>2026-08-03T09:38:25Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:38:25Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=c54ba119cf54427123087aed7955b05ca32d6ae7'/>
<id>urn:sha1:c54ba119cf54427123087aed7955b05ca32d6ae7</id>
<content type='text'>
Replace raw write() syscalls with fwrite() and enable full
buffering via setvbuf() to reduce the number of pipe writes from
script handlers to the parent process.

Expose uhttpd.flush() to both Lua and ucode so handlers can
explicitly flush the output buffer when needed.

Fixes: #17
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>file: dereference symlinks when looking up MIME type</title>
<updated>2026-08-03T09:28:20Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:28:20Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=067466662368cc13166301ccb4ecc8d0301a0db1'/>
<id>urn:sha1:067466662368cc13166301ccb4ecc8d0301a0db1</id>
<content type='text'>
uh_file_mime_lookup() determined Content-Type from the path string
extension, which for symlinks is the link name rather than the target.
Use lstat() and readlink() to resolve the target path before
inspecting the extension.

Also switch the serving path from pi-&gt;name (relative) to pi-&gt;phys
(absolute) so readlink() can resolve the link correctly.

Fixes: #14
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>utils: strip trailing whitespace from header field-values</title>
<updated>2026-08-03T09:17:14Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T09:17:14Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=d0b89eb7c70b0a048e1c8c6fb6d0c6e216159692'/>
<id>urn:sha1:d0b89eb7c70b0a048e1c8c6fb6d0c6e216159692</id>
<content type='text'>
RFC 9110 §5.5 defines received-field-value as OWS field-content OWS,
requiring both leading and trailing SP/HTAB to be stripped.

uh_split_header() already trimmed leading WSP; extend it to trim
trailing WSP as well by accepting the string length from callers,
avoiding an extra strlen() or memchr() scan.

Fixes: #11
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>ubus: release client refcount on successful /ubus/subscribe</title>
<updated>2026-08-03T08:13:52Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:13:52Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=3c48b9e17d2086f9756b97e5825c2ebdf3ed233a'/>
<id>urn:sha1:3c48b9e17d2086f9756b97e5825c2ebdf3ed233a</id>
<content type='text'>
uh_ubus_handle_get_subscribe() takes a client reference but never
releases it on the success path, so cl-&gt;refcount stays &gt;= 1 forever.
This prevents client_close() from freeing the client and unregistering
the ubus subscriber, eventually exhausting max_connections and wedging
the listener.

Fix by adding the missing uh_client_unref() before return.

Fixes: GHSA-wvgh-cm54-q6f6
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>client: move unread-body guard into uh_request_done()</title>
<updated>2026-08-03T08:11:20Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:11:20Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=d6205aad61763b5e5d86441164b3aeed83294e7f'/>
<id>urn:sha1:d6205aad61763b5e5d86441164b3aeed83294e7f</id>
<content type='text'>
uh_request_done() returns a keep-alive connection to CLIENT_STATE_INIT
without checking whether the request body was consumed. Handlers that
do not set d-&gt;data_send (static files, HEAD, GET /ubus, etc.) leave
the body in the ustream, causing it to be parsed as the next request
— classic CL.0 request smuggling.

Move the existing guard from uh_client_error() into uh_request_done()
so all success paths are covered, making the error-path copy redundant.

Fixes: GHSA-c2wg-hcff-hqrm
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>ubus: set d-&gt;free unconditionally to fix url_path leak</title>
<updated>2026-08-03T08:04:53Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:04:53Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=42e30caa704e4e6e28fc7412e0006959eb247c44'/>
<id>urn:sha1:42e30caa704e4e6e28fc7412e0006959eb247c44</id>
<content type='text'>
uh_ubus_handle_request() allocates du-&gt;url_path for every HTTP method,
but only the POST branch installed d-&gt;free = uh_ubus_request_free — the
sole code path that frees it. For all other methods, request_done()
zeroes the dispatch struct, NULLing url_path without freeing it.

Fix by setting d-&gt;free unconditionally; uh_ubus_request_free already
guards all other fields against uninitialized state.

Fixes: GHSA-83vv-qrc6-h3hx
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>ubus: guard params type in uh_ubus_send_request()</title>
<updated>2026-08-03T08:01:38Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-08-03T08:01:38Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=e76736e5676fb25536b27d45865eb4153c68aca3'/>
<id>urn:sha1:e76736e5676fb25536b27d45865eb4153c68aca3</id>
<content type='text'>
The JSON-RPC policy accepts params as any blobmsg type (BLOBMSG_TYPE_UNSPEC),
but uh_ubus_send_request() iterates it with blobmsg_for_each_attr() as if it
were always a container. A scalar params causes the iterator to reinterpret
payload bytes as a struct blob_attr header, leading to an out-of-bounds read
in strcmp() and a ~4 GiB out-of-bounds write in memcpy().

Fix by rejecting non-table params before entering the loop.

Fixes: GHSA-2mpg-6wp5-435p
Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>client: parse and discard chunked trailer sections, use enum chunked_state</title>
<updated>2026-07-24T10:04:03Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-07-24T10:04:03Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=f682ccabee9ed5f39dd3b0aaf96228975eeb478d'/>
<id>urn:sha1:f682ccabee9ed5f39dd3b0aaf96228975eeb478d</id>
<content type='text'>
client_poll_post_data() treated the terminal zero-length chunk as the
end of the request body, without reading the optional trailer section
RFC 9112 7.1.3 permits between the terminal chunk and the final CRLF.
Any trailer bytes a client sent were left unconsumed in the buffer;
once the connection was reset to CLIENT_STATE_INIT for keep-alive
reuse, those bytes were fed to client_init_cb() as if they were the
start of the next request. A well-formed trailer field line fails the
method/path/version split there and 400s the connection; a line
crafted to look like a request line would instead be parsed as a
genuine pipelined request, the same smuggling shape as the framing
bugs already closed in this file.

Add a trailer-draining sub-state that consumes trailer field lines one
at a time, discarding each (uhttpd exposes no trailer fields to
handlers), until the blank line ends the section, bounded by the same
UH_LIMIT_HEADER_COUNT/UH_LIMIT_HEADER_BYTES limits already applied to
regular headers. If a client blows through those limits mid-trailer,
the remaining framing is unknown, so the connection is closed instead
of reused, mirroring the existing invalid-chunk-length handling in
this function. The common case (no trailer, just the closing CRLF)
was already handled correctly by client_init_cb()'s existing leading
blank-line tolerance, confirmed by testing against a body-draining CGI
endpoint before and after this change.

Also replace the ad hoc 0/1/2(/3) values of http_request.transfer_chunked
with a proper enum chunked_state (CHUNKED_OFF/FIRST/NEXT/TRAILER),
matching the existing enum client_state convention on the same struct
instead of bare magic numbers.

Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
<entry>
<title>http: further consolidate character classification and header parsing</title>
<updated>2026-07-24T09:18:06Z</updated>
<author>
<name>Jo-Philipp Wich</name>
</author>
<published>2026-07-24T09:14:17Z</published>
<link rel='alternate' type='text/html' href='https://git.openwrt.org/project/uhttpd/commit/?id=003cd188b8f9460c7d603a1bbb98f9a3d583a648'/>
<id>urn:sha1:003cd188b8f9460c7d603a1bbb98f9a3d583a648</id>
<content type='text'>
Wire the already-added uh_is_digit()/uh_is_hexdig() helpers into
parse_chunksize() instead of leaving them unused, and drop
uh_is_owsl(), a same-semantics alias of uh_is_wsp() with a misleading
"leading" qualifier. Add a CT_CTL bit to the shared chartypes table so
control characters are distinguishable from unclassified bytes, and
renumber the flags to close the gap this leaves in the bit assignment.

client_parse_header() rescanned the header name twice: once for a
leading-whitespace check before the loop, and again inside the loop.
Whitespace bytes never carry the VCHAR bit, so !uh_is_tchar() already
rejects them; the standalone whitespace check was redundant and is
removed, leaving one pass over the name that validates and lowercases
it together.

Also validate header field-values against embedded control characters
(e.g. a bare CR or LF not part of the line terminator), using the new
CT_CTL bit. RFC 9110 field-content only permits VCHAR/obs-text plus
SP/HTAB; since header lines are currently located via strstr() for a
literal "\r\n", a lone CR or LF embedded in a value would previously
pass through unchecked. A reverse proxy that treats a bare LF as a
line terminator while uhttpd does not could use this to smuggle an
extra header or request past this parser, so such values are now
rejected with 400.

Signed-off-by: Jo-Philipp Wich &lt;jo@mein.io&gt;
</content>
</entry>
</feed>
