Also use compound literals for immediate pointers we don't use later
(same as with setsockopt() in 32223c96bd).
Signed-off-by: Laslo Hunhold <dev@frign.de>
This turns the data-functions into the only functions "allowed"
to send body-data (called with http_send_body()). The previous (hacky)
approach of doing this in http_send_header() is not only out of place,
it's an easy source of bugs given, for instance, the sending of body
data is not expected with HEAD-requests.
Given html_escape() is now only used in data.c, we move it there from
util.c and make it a static method again.
Signed-off-by: Laslo Hunhold <dev@frign.de>
The methods in data.h only deal with the actual response data, not
the request handling itself, which has been formalized a bit more
in http.h. To avoid confusion, we rename it to data.h.
Signed-off-by: Laslo Hunhold <dev@frign.de>
Explicitly show that we set the status of the response struct to the
returned error status. This makes it clear that we are beyond the point
where the "form" of the response struct matters and it's now only about
the log-output.
Signed-off-by: Laslo Hunhold <dev@frign.de>
I don't like the juggling with status-values in serve. It makes
sense for http_recv_header() and http_parse_header(), because we
don't have a response-struct yet that we can "fill". We could pass
it to them, but that would make the usage a bit messy.
However, in http_prepare_response(), we are already entrusted with
a pointer to a response-struct, and just failing here (by returning
an error value) leaves the response-struct in an invalid state. Instead,
we make it a void function and reflect the status using the status field
in the passed response struct.
This way, there is no case where the response struct is in an
invalid state after calling a http_prepare_*()-method.
Signed-off-by: Laslo Hunhold <dev@frign.de>
This approach fits better in line of first initializing the response
struct and then sending the header with http_send_header() later.
Signed-off-by: Laslo Hunhold <dev@frign.de>
This improves readability a bit and helps iron out confusions with
status-variables called s in other methods.
Signed-off-by: Laslo Hunhold <dev@frign.de>
Of course URIs point at "targets", but the URIs themselves should
be called what they are, not only in the interest of clarity in terms
of nomenclature.
Signed-off-by: Laslo Hunhold <dev@frign.de>
The function http_send_response() did too much. It not only took
the request fields and built them together into a response, it
delegated too little and many functions were "hacked" into it, for
instance shady directory-changes for vhosts and hand-construction
of response structs.
The preparations for a rework were already made in previous commits,
including a tighter focus on the response-struct itself. Instead of
doing everything locally in the http_send_response() function, the
new http_prepare_response() only really takes the request-struct and
builds a response-struct. The response-struct is expanded such that
it's possible to do the data-sending simply with the response-struct
itself and not any other magic parameters that just drop out of the
function.
Another matter are the http_send_status()-calls. Because the
aforementioned function is so central, this refactoring has included
many areas. Instead of calling http_send_status() in every error-case,
which makes little sense now given we first delegate everything through
a response struct, errors are just sent as a return value and caught
centrally (in serve() in main.c), which centralizes the error handling
a bit.
It might look a bit strange now and it might not be clear in which
direction this is going, but subsequent commits will hopefully give
clarity in this regard.
Signed-off-by: Laslo Hunhold <dev@frign.de>
The function has become too long and basically did two things: Receiving
the header and parsing it. To better reflect this, we split it up into
the two functions http_recv_header() and http_parse_header(). This way,
we also obtain a better separation of concerns and can further reduce
the scope of each parameter-list.
http_recv_header() has been written in such a way that it can be
reentered and fill up the header-buffer bit by bit using a pointer to
an offset value.
The error handling was improved by only returning the immediate error
status codes and letting the caller do the error-handling with
http_send_status().
Signed-off-by: Laslo Hunhold <dev@frign.de>
The server-struct variable s was global, which made it readable and
modifiable from any point in the code. Making it a local variable in
main() instead and passing it as a pointer to constant memory to each
function needing it makes much more sense and allows the compiler to
warn us if we do try to modify it, which it wouldn't have before.
Signed-off-by: Laslo Hunhold <dev@frign.de>
As is there is no security issue, but _if_ we end up with a user
or group set to NULL after e.g. ARGEND, we would've hit a null-pointer-
dereference of grp in which is now line 311.
What we want to check instead is if user or group are NULL respectively
and throw an error. Consequently, we can remove the later checks in the
drop root section, as we now guarantee that grp and pwd are not NULL.
Signed-off-by: Laslo Hunhold <dev@frign.de>
I know that the effect of 'const' on compiler optimizations is smaller
than many believe, but it provides a good insight to the caller which
parameters are not modified and simplifies parallelization, in case
that is desired at a later point.
Throughout processing, the big structs mostly remained unmodified, with
the exception of parse_range(), which added a null-byte in the "Range"-
header to simplify its parsing. This commit refactors parse_range()
such that it won't modify this string anymore.
Additionally, the parser was made even stricter: Usually, strtoll()
(which is wrapped by strtonum()) allows whitespace and plus and minus
signs before the number, which is not part of the specification. The
stricter parser also better differentiates now between invalid requests
and range-lists. In that context, the switch in http_send_response()
was replaced for better readability.
Signed-off-by: Laslo Hunhold <dev@frign.de>
For me at least, the first valid configuration found by getaddrinfo
works fine most of the time. Obviously if this isn't the configuration
you want, you can specify the host explicitly.
Thanks to the feedback by z0lqLA! I forgot that unveil(NULL, NULL)
only locks further unveil calls when there has been at least _one_ prior
call to unveil!
To fix this, we reorder the calls and also make sure to call unveil()
before we disallow unveils via pledge.
Signed-off-by: Laslo Hunhold <dev@frign.de>
It has been on my todo-list for a long time. I tested it on
OpenBSD 6.5.
Thanks Richard Ulmer for the reminder.
Signed-off-by: Laslo Hunhold <dev@frign.de>
I didn't really like the use of a "yes"-variable for setsockopt().
A better way is to use compound literals (part of C99).
Another point are the structs. Instead of memsetting to zero we make
use of the standard which guarantees that "unmentioned" fields
are set to zero anyways. Just to note it here: The use of memset()
also sets padding to zero, which is not guaranteed with the method
of "unmentioned" fields.
Signed-off-by: Laslo Hunhold <dev@frign.de>
I wasn't happy with the tokenizer for the m- and v-flags, because it
was handling space-separated input and there was no way to have spaces
within the tokens themselves. This is a fine detail, but I didn't want
to impose this restriction where it could be solved (path prefixes or
folder names can very well contain spaces).
Given it's a bit quirky to handle multiple arguments to a single flag
in the command line, especially when parameters are optional, this
alternative wasn't further considered and I instead implemented a
tokenizer that allows escaping spaces with '\'.
While at it, I clarified the manual regarding this point.
Signed-off-by: Laslo Hunhold <dev@frign.de>
Put the chost-specification at the end and make it optional. This makes
more sense than having to give an arbitrary useless name in case you
weren't using virtual hosts in the first place.
While at it, clear up the wording in the manpage.
Signed-off-by: Laslo Hunhold <dev@frign.de>
When cleaning up after a caught signal, quark forwards the signal to all
processes in the process group with `kill(0, ...)`. If we do not open up a new
process group in the parent process, quarks parent will be sent a SIG... too,
resulting it to shut down (especially considering that the parent process might
run as root).
As a result, if we set up the service with djb's excellent daemontools,
`svc -d quark` will terminate the svscan-process and tear all other services
down with it.
See also <https://cr.yp.to/daemontools/faq/create.html#pgrphack>.
The previous parsing of the -v vhosts made sure there were 4 tokens.
If there was no prefix specified, usage() is called. Now, it only
checks for the firsts 3, with .prefix set to null if there are only
3 tokens.
Since now config.def.h has been reduced we don't have any more unused
variables and thus the manual fiddling with error-levels is no longer
necessary.
To get a completely clean result though we have to still cast some
variables here and there.
The config.h-interface has proven to be very effective for a lot of
suckless tools, but it just does not make too much sense for a web
server like quark.
$ quark
If you run multiple instances of it, you want to see in the command line
(or top) what it does, and given the amount of options it's logical to
just express them as options given in the command line.
It also is a problem if you can modify quark via the config.h,
contradicting the manual. Just saying "Well, then don't touch config.h"
is also not good, as the vhost and map options were only exposed via
this interface.
What is left in config.h are mime-types and two constants relating to
the incoming HTTP-header-limits.
In order to introduce these changes, some structs and safe utility
functions were added and imported from OpenBSD respectively.
To make the code a bit more flexible, let's get rid of the forking-code
in serve() and do it in main(). This way, we are more liberal in the
future to possibly handle it in a different way.
And many other things, too many to list here. For example, it now
properly logs uds instead of erroring out.
Separating concerns in many places definitely improves the readability.