1 #ifndef LNANOHTMLTILEDMAP_HTTP_C
2 #define LNANOHTMLTILEDMAP_HTTP_C
4 * this code is protected by the GNU affero GPLv3 license
5 * author:Sylvain BERTRAND
8 #include <ulinux/compiler_types.h>
9 #include <ulinux/types.h>
10 #include <ulinux/start.h>
11 #include <ulinux/error.h>
12 #include <ulinux/sysc.h>
13 #include <ulinux/file.h>
14 #include <ulinux/time.h>
15 #include <ulinux/mmap.h>
16 #include <ulinux/signal/signal.h>
17 #include <ulinux/socket/socket.h>
19 #include <ulinux/socket/in.h>
21 #include <ulinux/socket/in6.h>
23 #include <ulinux/epoll.h>
24 #include <ulinux/utils/endian.h>
27 #include "common_cpp.h"
29 #include "exit_codes.h"
30 /*============================================================================*/
31 #include "namespace/ulinux.h"
32 /*============================================================================*/
33 #define SIGBIT(sig) (1<<(sig-1))
35 static struct sockaddr_in srv_addr
;
37 static struct sockaddr_in6 srv_addr
;
39 static sl page_sz
; /* from linux auxiliary vector */
40 static si srv_sock
; /* the main listening socket */
41 static si sigs_fd
; /* synchronous signal handling */
42 static si epfd
; /* the main epoll file descriptor */
43 static si cnx_sock
; /* a connection socket from accept */
44 static si idletimerfd
; /* the idle timer for the connection */
45 static u64 urandom
; /* to try to workaround client cache */
47 #define RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ 8000
48 static u8
*req
; /* buffer for the request line buffer */
49 static u8
*req_e
; /* pointer on the byte past the last mmaped byte */
50 static u8
*req_recv
; /* pointer on the byte past the last received byte */
51 static u32 req_sz_max
; /* the size of buffer for the request line */
54 #define STATE_RDY 0x00
55 #define STATE_RECV_REQ_LINE 0x01
56 #define STATE_DISCARD_REQ_REM 0x02
57 #define STATE_SEND_RESP 0x03
59 /* data storage for states */
60 static u8
*req_line_crlf_lookup
; /* tracker pointer for the request line CRLF */
61 static u8
*req_line_e
; /* pointer on the '\r' of the request line CRLF */
63 * count how much of the crlf crlf sequence we have seen in order to locate
64 * the end of a request without a body. If we get a weird request with a
65 * huge payload, the idle timeout will get rid of the connection
67 static u8 req_crlf_crlf_lookup
;
69 static u8
*aux
; /* auxiliary buffer of page_sz bytes */
70 static u8
*aux_e
;/* pointer on the byte past the last mmaped byte */
73 static u8
*resp_p
; /* track what we have sent already */
74 static u8
*resp_html_e
; /* pointer past the last byte of the generated response */
76 static u32 resp_sz_max
;
77 /*----------------------------------------------------------------------------*/
84 static void pagesize_get(u8
*abi_stack
)
86 abi_stack
+= sizeof(ul
); /* skip argc */
94 abi_stack
+= sizeof(ul
);
96 abi_stack
+= sizeof(ul
); /* skip argv terminator */
103 abi_stack
+= sizeof(ul
);
105 abi_stack
+= sizeof(ul
); /* skip envp terminator */
109 auxv
= (struct auxv_t
*)abi_stack
;
110 if (auxv
->type
== AT_NULL
)
111 exit(SETUP_AUXVEC_PAGESIZE_NOT_FOUND
);
112 else if (auxv
->type
== AT_PAGESIZE
) {
113 page_sz
= (u32
)auxv
->data
;
116 abi_stack
+= 2 * sizeof(ul
);
122 static void urandom_init(void)
128 r
= openat(0, "/dev/urandom", O_RDONLY
, 0);
130 if (ISERR(r
)) /* ignored */
136 read(fd
, &urandom
, sizeof(urandom
));
139 static void globals_init(u8
*abi_stack
)
141 /* XXX: sockaddr_in? structures are of different sizes */
142 memset(&srv_addr
, 0, sizeof(srv_addr
));
144 srv_addr
.family
= AF_INET
;
145 /* big endian port */
146 srv_addr
.port
= cpu_to_be16_const(CONFIG_LISTENING_PORT
);
147 /* zero address = any ipv4 address */
149 srv_addr
.family
= AF_INET6
;
150 /* big endian port */
151 srv_addr
.port
= cpu_to_be16_const(CONFIG_LISTENING_PORT
);
152 /* zero address = any ipv6 address */
159 crlf_crlf
= "\r\n\r\n";
161 pagesize_get(abi_stack
);
165 * handling of synchronous signals with signalfd
166 * cannot change SIGKILL, neither SIGSTOP
168 static void sigs_setup(void)
174 r
= rt_sigprocmask(SIG_BLOCK
, &mask
, 0, sizeof(mask
));
176 exit(SIGS_SETUP_BLOCKING_FAILURE
);
177 mask
= SIGBIT(SIGTERM
);
178 sigs_fd
= (si
)signalfd4(-1, &mask
, sizeof(mask
), SFD_NONBLOCK
);
180 exit(SIGS_SETUP_HANDLERS_FAILURE
);
182 static void epoll_sigs_setup(void)
184 struct epoll_event ep_evt
;
187 memset(&ep_evt
, 0, sizeof(ep_evt
));
188 ep_evt
.events
= EPOLLET
| EPOLLIN
;
189 ep_evt
.data
.fd
= sigs_fd
;
190 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, sigs_fd
, &ep_evt
);
192 exit(EPOLL_SIGS_SETUP_EPOLL_ADD_FAILURE
);
194 static void srv_sock_create(void)
199 r
= socket(AF_INET
, SOCK_STREAM
| SOCK_NONBLOCK
| SOCK_CLOEXEC
, 0);
201 r
= socket(AF_INET6
, SOCK_STREAM
| SOCK_NONBLOCK
| SOCK_CLOEXEC
, 0);
204 exit(SRV_SOCK_CREATE_FAILURE
);
207 r
= setsockopt(srv_sock
, SOL_SOCKET
, SO_REUSEADDR
, &bool_true
,
210 exit(SRV_SOCK_SET_SOCK_OPTION_FAILURE
);
211 r
= bind(srv_sock
, &srv_addr
, sizeof(srv_addr
));
213 exit(SRV_SOCK_BIND_FAILURE
);
214 r
= listen(srv_sock
, 0);
216 exit(SRV_SOCK_LISTEN_FAILURE
);
218 static void epoll_srv_sock_setup(void)
220 struct epoll_event ep_evt
;
223 memset(&ep_evt
, 0, sizeof(ep_evt
));
224 ep_evt
.events
= EPOLLIN
| EPOLLPRI
;
225 ep_evt
.data
.fd
= srv_sock
;
226 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, srv_sock
, &ep_evt
);
228 exit(SRV_SOCK_SETUP_EPOLL_CTL_ADD_FAILURE
);
230 static void req_mmap(void)
234 req_sz_max
= RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ
/ page_sz
+ page_sz
;
235 addr
= mmap(req_sz_max
, RD
| WR
, PRIVATE
| ANONYMOUS
);
236 if(addr
== 0 || ISERR(addr
))
237 exit(PAGE_MMAP_FAILURE
);
239 req_e
= req
+ req_sz_max
;
241 /* random initial content */
243 static void aux_mmap(void)
247 addr
= mmap(page_sz
, RD
| WR
, PRIVATE
| ANONYMOUS
);
248 if(addr
== 0 || ISERR(addr
))
249 exit(PAGE_MMAP_FAILURE
);
251 aux_e
= req
+ page_sz
;
252 /* random initial content */
254 static void resp_mmap(void)
258 /* enough room for our template, don't need much more */
259 resp_sz_max
= html_sz_max();
260 resp_sz_max
= resp_sz_max
/ page_sz
+ page_sz
;
261 addr
= mmap(resp_sz_max
, RD
| WR
, PRIVATE
| ANONYMOUS
);
262 if(addr
== 0 || ISERR(addr
))
263 exit(PAGE_MMAP_FAILURE
);
265 resp_e
= resp
+ resp_sz_max
;
266 /* random initial content */
268 static void idletimerfd_create(void)
272 r
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
| TFD_CLOEXEC
);
274 exit(IDLETIMERFD_UNABLE_TO_CREATE_FD
);
277 static void epoll_idletimerfd_setup(void)
279 struct epoll_event ep_evt
;
282 memset(&ep_evt
, 0, sizeof(ep_evt
));
283 ep_evt
.events
= EPOLLIN
;
284 ep_evt
.data
.fd
= idletimerfd
;
285 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, idletimerfd
, &ep_evt
);
287 exit(EPOLL_IDLETIMERFD_SETUP_EPOLL_ADD_FAILURE
);
289 static void setup(void)
292 epfd
=(si
)epoll_create1(0);
294 exit(SETUP_EPOLL_CREATE_FAILURE
);
297 epoll_srv_sock_setup();
298 idletimerfd_create();
299 epoll_idletimerfd_setup();
304 /* consuming synchronous signals */
305 static void sigs_consume(void)
307 struct signalfd_siginfo info
;
312 memset(&info
, 0, sizeof(info
));
313 /* atomic read / no short reads last time we checked */
314 r
= read(sigs_fd
, &info
, sizeof(info
));
318 /* not supposed to happen, but we do it anyway */
319 if (r
!= -EAGAIN
&& ((ISERR(r
) || (r
> 0
320 && r
!= sizeof(info
)))))
321 exit(SIGS_CONSUME_SIGINFO_READ_FAILURE
);
322 if (r
== 0 || r
== -EAGAIN
)
324 switch (info
.ssi_signo
) {
328 /* please, do add the ones you like */
332 /* reasonable and generic idle timer, for now */
333 static void idletimer_arm(void)
338 memset(&t
, 0, sizeof(t
));
339 t
.value
.sec
= CONFIG_IDLETIMER_TIMEOUT_SECONDS
;
340 r
= timerfd_settime(idletimerfd
, 0, &t
, 0);
342 exit(IDLETIMERFD_FAILED_TO_ARM
);
344 static void idletimer_disarm(void)
349 memset(&t
, 0, sizeof(t
));
350 r
= timerfd_settime(idletimerfd
, 0, &t
, 0);
352 exit(IDLETIMERFD_FAILED_TO_DISARM
);
354 /* may log any pb one day */
355 static void state_rdy_force(void)
357 struct epoll_event ep_evt
;
359 * XXX: events can be reported for a "closed" file descriptor: *ALL*
360 * file descriptors related to the underlaying objects must be closed
361 * in order to avoid spurious events
363 epoll_ctl(epfd
, EPOLL_CTL_DEL
, cnx_sock
, 0);
366 memset(&ep_evt
, 0, sizeof(ep_evt
));
367 /* switch on the listening socket */
368 ep_evt
.events
= EPOLLIN
| EPOLLPRI
;
369 ep_evt
.data
.fd
= srv_sock
;
370 epoll_ctl(epfd
, EPOLL_CTL_MOD
, srv_sock
, &ep_evt
);
374 static bool state_recv_req_line(si cnx_sock_from_accept
)
376 struct epoll_event ep_evt
;
379 /* must be in ready state */
380 if (state
!= STATE_RDY
)
382 /* install the connection socket */
383 memset(&ep_evt
, 0, sizeof(ep_evt
));
384 ep_evt
.events
= EPOLLIN
| EPOLLPRI
; /* EPOLLPRI = TCP urgent data */
385 ep_evt
.data
.fd
= cnx_sock_from_accept
;
386 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, cnx_sock_from_accept
, &ep_evt
);
387 if(ISERR(r
)) /* since we are from the ready state, no EEXIST */
389 /* ignore events from the listening socket */
390 memset(&ep_evt
, 0, sizeof(ep_evt
));
391 ep_evt
.data
.fd
= srv_sock
;
392 r
= epoll_ctl(epfd
, EPOLL_CTL_MOD
, srv_sock
, &ep_evt
);
395 cnx_sock
= cnx_sock_from_accept
;
397 req_line_crlf_lookup
= req
;
399 state
= STATE_RECV_REQ_LINE
;
405 static void state_send_resp(struct html_ctx_t
*c
)
407 struct epoll_event ep_evt
;
410 resp_html_e
= c
->p
; /* point on the byte past the last generated byte */
412 memset(&ep_evt
, 0, sizeof(ep_evt
));
413 ep_evt
.events
= EPOLLOUT
| EPOLLPRI
;
414 ep_evt
.data
.fd
= cnx_sock
;
415 epoll_ctl(epfd
, EPOLL_CTL_MOD
, cnx_sock
, &ep_evt
);
417 state
= STATE_SEND_RESP
;
420 static void req_decode(struct html_query_t
*q
)
427 /* skip the http verb */
435 /* skip the spaces between the verb and the request-target */
443 /* from here we are in the request-target */
445 * reach the http scheme URI '?' query delimiter (which should be uniq)
446 * or the various ends
449 if (p
== req_line_e
|| *p
== ' ')
455 /* here, we have a html query marker '?' */
456 ++p
; /* skip the marker '?' */
460 if (p
== req_line_e
|| *p
== '#' || *p
== ' ') {
466 html_query_decode(q
, q_s
, q_e
);
469 * we use such function because scanning of CRLFCRLF can happen across 2
470 * buffers: the request-line buffer and the auxiliary buffer used to discard
471 * the remainder of the request
473 static bool crlf_crlf_scan(u8
*start
, u8
*end
)
477 if (*start
== crlf_crlf
[req_crlf_crlf_lookup
]) {
478 if (req_crlf_crlf_lookup
== 3)
480 ++req_crlf_crlf_lookup
;
481 } else /* not the CRLFCRLF sequence */
482 req_crlf_crlf_lookup
= 0;
485 static void resp_compute(void)
487 struct html_query_t q
;
493 html_gen_ctx_init_from_query(&c
, &q
, resp
, urandom
);
495 html_majortbl_gen(&c
);
499 static void state_discard_req_rem(u8
*req_line_cr
)
501 /* we will use data from this state */
502 if (state
!= STATE_RECV_REQ_LINE
) {
506 /* we are still reading from the connection socket */
507 req_line_e
= req_line_cr
;
508 /* we have seen already the crlf from the request-line */
509 req_crlf_crlf_lookup
= 2;
510 /* scan what we have already received in the request line buffer */
511 if (crlf_crlf_scan(req_line_e
+ 2, req_recv
)) {
512 /* got the request terminationg CRLFCRLF */
516 /* the request terminating CRLFCRLF were not received yet */
518 state
= STATE_DISCARD_REQ_REM
;
520 /* we may get more or less */
521 static void state_discard_req_rem_sock_evts(u32 sock_evts
)
525 r
= read(cnx_sock
, aux
, page_sz
);
527 continue; /* SIGSTOP? */
528 else if (r
== -EAGAIN
|| r
== -EWOULDBLOCK
)
529 break; /* no more available */
531 * error or the client did shutdown the connection while in the
532 * syscall (r = 0 with page_sz being non zero)
534 else if (ISERR(r
) || r
== 0) {
539 if (crlf_crlf_scan(aux
, aux
+ r
)) {
544 /* we may get more or less */
545 static void state_recv_req_line_sock_evts(u32 sock_evts
)
549 loop
{ /* read of much as we can */
551 ul buf_remaining_bytes_n
;
553 /* here, we must have req_e != req_recv */
554 buf_remaining_bytes_n
= (ul
)(req_e
- req_recv
);
555 r
= read(cnx_sock
, req_recv
, buf_remaining_bytes_n
);
557 continue; /* SIGSTOP? */
558 else if (r
== -EAGAIN
|| r
== -EWOULDBLOCK
)
559 break; /* no more available, do something with that */
561 * error or the client did shutdown the connection while in the
564 else if (ISERR(r
) || (buf_remaining_bytes_n
!= 0 && r
== 0)) {
570 if (req_recv
== req_e
)
571 break; /* no more room, try to do something with that */
573 /* lookup for the request-line terminating CRLF */
574 p
= req_line_crlf_lookup
;
577 req_line_crlf_lookup
= p
;
578 break; /* nothing yet */
581 if (((p
- 1) >= req
) && (p
[-1] == '\r')) {
582 state_discard_req_rem(&p
[-1]);
585 /* \n without a previous \r, don't like that */
591 /* nothing interesting and no more room :(, bail out */
592 if (req_e
== req_recv
)
595 static void state_send_resp_sock_evts(u32 evts
)
600 r
= write(cnx_sock
, resp_p
, resp_html_e
- resp_p
);
606 if (ISERR(r
)) { /* ignore and reset */
610 /* account for the bytes sent and acked */
612 if (resp_p
== resp_html_e
) /* whole response sent and acked */
615 static void cnxs_consume(void)
617 /* we service synchronously 1 connection at a time, for now */
618 if (state
!= STATE_RDY
)
623 struct sockaddr_in peer
;
625 struct sockaddr_in6 peer
;
629 peer_len
= sizeof(peer
);
632 * from the linux man page, already pending network
633 * errors for the was-about-to-be-returned network
634 * socket may be returned by the accept syscall, namely
635 * the was-about-to-be-returned network socket is
636 * trashed. It means, if we get errors related to
637 * TCP/IP from the accept syscall, those have to be
638 * considered like EAGAIN.
640 r
= accept4(srv_sock
, &peer
, &peer_len
, SOCK_NONBLOCK
647 * no clearcut and accurate return errors for this syscall:
648 * we will consider all errors as retry-able like EAGAIN
649 * I guess, reading the code would help to sort out error codes
650 * which are fatal from those which would be retry-able.
652 if (ISERR(r
) || peer_len
!= sizeof(peer
))
655 * from here, we should have a valid socket file descriptor with
656 * an expected peer address size
658 if (state_recv_req_line((si
)r
))
661 * failed state switch, then back in ready state , next accept
666 static void cnx_sock_evts(u32 evts
)
668 /* common to all states, TCP urgent data is not expected with EPOLLPRI */
669 if ((evts
& (EPOLLERR
| EPOLLHUP
| EPOLLPRI
)) != 0) {
673 /* ofc, will get an interface as a function table if going bigger */
675 case STATE_RECV_REQ_LINE
:
676 state_recv_req_line_sock_evts(evts
);
678 case STATE_DISCARD_REQ_REM
:
679 state_discard_req_rem_sock_evts(evts
);
681 case STATE_SEND_RESP
:
682 state_send_resp_sock_evts(evts
);
691 static void state_discard_req_rem_idletimerfd_expiration(void)
693 /* trash the connection */
696 static void state_recv_req_line_idletimerfd_expiration(void)
698 /* trash the connection */
701 static void state_send_resp_idletimerfd_expiration(void)
703 /* trash the connection */
707 * XXX: must read the idletimer file descriptor count of expirations in order to
710 static void idletimerfd_evts(u32 evts
)
714 if ((evts
& EPOLLIN
) == 0)
715 return; /* spurious, may log */
716 /* from here EPOLLIN */
722 r
= read(idletimerfd
, &expirations_n
, sizeof(expirations_n
));
724 continue; /* SIGSTOP? */
725 /* we are not supposed to get that in EPOLLIN, may log */
728 if (ISERR(r
) || r
!= sizeof(expirations_n
))
729 exit(IDLETIMERFD_READ_FAILURE
);
733 if (expirations_n
== 0) /* spurious, may log */
735 /* ofc, will get an interface as a function table if going bigger */
737 case STATE_DISCARD_REQ_REM
:
738 state_discard_req_rem_idletimerfd_expiration();
740 case STATE_RECV_REQ_LINE
:
741 state_recv_req_line_idletimerfd_expiration();
743 case STATE_SEND_RESP
:
744 state_send_resp_idletimerfd_expiration();
752 static void main_loop(void)
755 struct epoll_event evts
[2]; /* sigs_fd and srv_sock */
760 memset(evts
, 0, sizeof(evts
));
761 r
= epoll_pwait(epfd
, evts
, 2, -1, 0);
762 if (r
!= -EINTR
) /* SIGSTOP? */
766 exit(MAIN_LOOP_EPOLL_WAIT_GENERIC_FAILURE
);
767 /* r >= 0 from here */
772 if (evts
[j
].data
.fd
== sigs_fd
) {
773 if((evts
[j
].events
& EPOLLIN
) != 0)
776 exit(MAIN_LOOP_EPOLL_WAIT_SIGS_FD_EVENT_IS_NOT_EPOLLIN
);
777 } else if (evts
[j
].data
.fd
== srv_sock
) {
778 if ((evts
[j
].events
& (EPOLLERR
| EPOLLHUP
780 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNEXPECTED_EVENT
);
781 else if ((evts
[j
].events
& EPOLLIN
) != 0) {
784 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNKNOWN_FAILURE
);
785 } else if (evts
[j
].data
.fd
== cnx_sock
)
786 cnx_sock_evts(evts
[j
].events
);
787 else if (evts
[j
].data
.fd
== idletimerfd
)
788 idletimerfd_evts(evts
[j
].events
);
794 * daemon-ization, not done, maybe one day, in the meantime, usage of
795 * setsuid/env/etc commands is recommended
797 void ulinux_start(u8
*abi_stack
)
802 globals_init(abi_stack
);
806 #undef RFC7230_REQ_LINE_RECOMMENDED_MIN_SZ
809 #undef STATE_RECV_REQ_LINE
810 #undef STATE_DISCARD_REQ_REM
812 #include "common_cpp.h"
813 /*============================================================================*/
814 #include "namespace/ulinux.h"
815 /*============================================================================*/