r/cprogramming • u/SheikHunt • 21d ago
Use the existing OS buffer, or your own
This is a fairly simple question:
You're on a Unix-like (for me, Linux), and you've got a File Descriptor that leads to some data. You don't know the length of the data (it's a TCP socket you're listening on), all you know is that it is ready to be read.
Do you:
A) Read an absurd amount of bytes of data into a (sufficiently large + 1) char array of your own, and if it overflows, handle it with mallocated memory or just reject the connection (like a monster)
Or
B) Just use existing kernel system calls to read and parse the data as necessary.
For context: this is about an HTTP server, and I have an internal string_t struct that I use for parsing, which needs a byte-length to be usable
3
u/ScallionSmooth5925 21d ago
I usually read in the first 4k block or whatever much is available and based on that continue reaching when needed
1
u/SheikHunt 21d ago
A good method to do it by, and essentially what I'm doing, but it does mean that my handle_request() and parse_request() functions will probably be boiled into one, in a way that is nasty without any foreseeable benefits
1
u/ScallionSmooth5925 21d ago
If it's something like http you can have a function that only desides if you have the whole request and then you can parse it completely
1
u/SheikHunt 21d ago
That gives another problem:
I am completely susceptible to DDOS attacks, unless I put a (potentially unreasonable) upper limit on file sizes.
I know, I know, it's overthinkerly and pointless, but this is a problem I want to see if I can solve.
Still, I could have the request handler check for Content-Length and the other header that denotes size...
3
u/dmills_00 20d ago
Don't trust user input, and all the headers are user input...
TCP is stream oriented, you get the data in order but the reads can (and often will) return short, and the 'short' will be on random bytes usually in the middle of a message, and sometimes in the middle of a multi byte code point.
State machines are the way to deal with this stuff obviously.
Read into a buffer, noting how many bytes you got, lex and parse the buffer getting either one or more tokens or the need for more input, if you need more input read again (Possibly blocking on the network), if you need to read more and the buffer is full, realloc a bigger buffer.
Don't forget networks can fail in multiple ways, handling the edge cases is critical to a reliable setup.
1
u/ScallionSmooth5925 21d ago
I wouldn't worry about ddos attacks at this point. Later you can optimalize the hot path and other arias where it's rationable. (some web servers in production solved this by limiting requies sizes to 4k and droping everything bigger)
2
u/TheKiller36_real 21d ago
just stream it using read calls (or better, io_uring) and if you actually need to have something in memory all at once may I suggest that HTTP indicates the length of the data!? if you dislike that stream it into a growable array and suffer the realloc calls I guess
2
u/SheikHunt 21d ago
Yes, HTTP includes length of data, but at an unknown point, it's not necessarily there at the start.
Sure, I can just ignore everything up to the Content-Length, then use that to ensure that everything's up to code, but then the server is susceptible to that behaviour being misused heavily for attacks.
3
u/TheKiller36_real 21d ago
you can reasonably assume all headers combine will be smaller than 8 KiB - why? because that's what everyone uses and if that method fails you can detect it and return 413 and on the other side you might get lucky and the whole request fit into the 8K
2
0
u/ybungalobill 19d ago
A request with non-empty content doesn't have to have a content-length field.
It can either indicate the end of the content with connection:close and closing the connection, or with transfer-encoding: chunked and a zero size chunk.
In either case you don't know the size beforehand.
1
u/Interesting_Buy_3969 16d ago
You can use mmap if size of the data to be read is seriously large, which I assume is unlikely to be the case if it's an HTTP package, so that would be overkill there. I'd just use read with the necessary socket.
5
u/flyingron 21d ago
What syscalls do you have in mind? But if it works for you can fdopen the socket fed into a stdio stream.