1 /* this code is protected by the GNU affero GPLv3
2 author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
3 <sylware AT legeek dot net> */
4 /*----------------------------------------------------------------------------*/
7 /*----------------------------------------------------------------------------*/
9 /*----------------------------------------------------------------------------*/
11 #include <ulinux/compiler_types.h>
12 #include <ulinux/types.h>
13 #include <ulinux/sysc.h>
15 #include <ulinux/file.h>
16 #include <ulinux/socket/socket.h>
17 #include <ulinux/socket/in.h>
18 #include <ulinux/signal/signal.h>
19 #include <ulinux/error.h>
20 #include <ulinux/epoll.h>
21 #include <ulinux/utils/mem.h>
22 #include <ulinux/utils/endian.h>
23 #include <ulinux/mmap.h>
24 #include <ulinux/time.h>
25 #include <ulinux/select.h>
26 #include <ulinux/stat.h>
28 #include <ulinux/utils/ascii/string/vsprintf.h>
30 #include "ulinux_namespace.h"
31 #include "exit_codes.h"
32 /*----------------------------------------------------------------------------*/
34 /******************************************************************************/
36 /* 16 bits value for the port (below 1024, must be root, that you must be for
38 #define LISTENING_PORT 80
39 /* 32 bits value for the IPv4, can be INADDR_ANY */
40 #define LISTENING_IPV4 INADDR_ANY
41 /* the chroot patch used upon start */
42 #define CHROOT_PATH "/root/http/chroot"
43 /* time out for a socket read/write, in seconds. 4 secs is huge */
44 #define CNX_WAIT_TIMEOUT 4
46 #define DEFAULT_FILE (u8*)"/index.xhtml"
47 #define DEFAULT_FILE_MIME (u8*)"/index.xhtml.mime"
49 #define RESP_HDR_FMT (u8*)"\
51 content-length:%u\r\n\r\n"
53 #define RESP_HDR_CONTENT_TYPE_FMT (u8*)"\
55 content-length:%u\r\n\
56 content-type:%s\r\n\r\n"
57 /******************************************************************************/
59 #define SIGBIT(sig) (1<<(sig-1))
61 /*----------------------------------------------------------------------------*/
63 static struct sockaddr_in srv_addr
;
64 static si srv_sock
; /* the main listening socket */
65 static si cnx_sock
; /* a cnx socket from accept */
67 static ul cnx_sock_fd_set
[FD_SET_ULS_N
];
68 static u8 cnx_sock_fd_set_ul_idx
;
69 static u8 cnx_sock_fd_ul_shift
;
70 /*----------------------------------------------------------------------------*/
72 /*----------------------------------------------------------------------------*/
74 static si sigs_fd
; /* fd for signals */
75 static si epfd
; /* epoll fd */
76 /*----------------------------------------------------------------------------*/
78 /*----------------------------------------------------------------------------*/
79 #define HTTP_METHOD_GET 0
80 #define HTTP_METHOD_HEAD 1
81 static u8 http_method
;
82 /*----------------------------------------------------------------------------*/
84 /*----------------------------------------------------------------------------*/
87 static ul page_bytes_rd_n
; /* keep an eye on how much was read */
89 static u8
*method_target_space
;
90 static u8
*target_start
;
91 static u8
*target_end
; /* point the space char right after */
92 /*----------------------------------------------------------------------------*/
94 /*----------------------------------------------------------------------------*/
95 static u8 target_file_is_default
;
96 static si target_file_fd
;
97 static ul target_file_sz
;
98 static u8 target_file_mime
[255 + 1]; /* actually the content-type */
99 /*----------------------------------------------------------------------------*/
101 /*----------------------------------------------------------------------------*/
102 static sl resp_hdr_sz
; /* the generated response header */
103 /*----------------------------------------------------------------------------*/
106 /******************************************************************************/
107 /******************************************************************************/
108 /******************************************************************************/
111 static void epoll_srv_sock_setup(void)
113 struct epoll_event ep_evt
;
116 memset(&ep_evt
, 0, sizeof(ep_evt
));
117 ep_evt
.events
= EPOLLIN
| EPOLLPRI
;
118 ep_evt
.data
.fd
= srv_sock
;
120 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, srv_sock
, &ep_evt
);
122 exit(SRV_SOCK_SETUP_EPOLL_CTL_ADD_FAILURE
);
125 /*---------------------------------------------------------------------------*/
126 /* http method can request to close the connexion exiting, we are violent: we close the tcp cnx
127 though we should send a response in order to be rfc correct */
130 /* PAGE_SZ is way bigger than 4 bytes */
131 static u8
http_method_is_get(void)
141 /* PAGE_SZ is way bigger than 4 bytes */
142 static u8
http_method_is_head(void)
152 static u8
http_method_match(void)
154 if (http_method_is_get())
155 http_method
= HTTP_METHOD_GET
;
156 else if (http_method_is_head())
157 http_method
= HTTP_METHOD_HEAD
;
159 return HTTP_CLOSE
; /* garbage or method not handled */
163 /* if we have an error or we time out, notify for socket closing */
164 #define CNX_SOCK_RD_WAIT_FAILURE 1
165 #define fd_set cnx_sock_fd_set
166 #define ul_idx cnx_sock_fd_set_ul_idx
167 #define ul_shift cnx_sock_fd_ul_shift
168 static u8
cnx_sock_rd_wait(void)
173 fd_set
[ul_idx
] = (1UL << ul_shift
); /* was zero-ed in global_init */
175 tv
.sec
= CNX_WAIT_TIMEOUT
;
179 r
= select(cnx_sock
+ 1, &cnx_sock_fd_set
[0], 0, 0, &tv
);
184 if (ISERR(r
) || r
== 0) /* r != 1 */
185 return CNX_SOCK_RD_WAIT_FAILURE
;
192 #define CNX_SOCK_RD_FAILURE -1
193 static sl
cnx_sock_rd(void)
198 bytes_rd_n
= read(cnx_sock
, page
+ page_bytes_rd_n
,
199 PAGE_SZ
- page_bytes_rd_n
);
200 if (bytes_rd_n
!= -EINTR
) {
201 if (ISERR(bytes_rd_n
))
202 bytes_rd_n
= CNX_SOCK_RD_FAILURE
;
209 static u8
http_method_rd(void)
219 r
= cnx_sock_rd_wait();
220 if (r
== CNX_SOCK_RD_WAIT_FAILURE
)
223 bytes_rd_n
= cnx_sock_rd();
224 if (bytes_rd_n
== CNX_SOCK_RD_FAILURE
|| bytes_rd_n
== 0)
227 page_bytes_rd_n
+= bytes_rd_n
;
230 if (bytes_rd_n
-- == 0)
234 /* got the space separator:method' 'target */
235 method_target_space
= c
;
242 if (page_bytes_rd_n
== PAGE_SZ
) /* no more page room */
248 /* XXX:must be called with room in the page or it will loop with 0 read bytes
249 till the other end decides to shutdown the connection */
250 #define UNABLE_TO_READ_AT_LEAST_ONE_BYTE 1
251 static u8
http_rd_at_least_one_byte(void)
257 r
= cnx_sock_rd_wait();
258 if (r
== CNX_SOCK_RD_WAIT_FAILURE
)
261 bytes_rd_n
= cnx_sock_rd();
262 if (bytes_rd_n
== CNX_SOCK_RD_FAILURE
|| bytes_rd_n
== 0)
265 if (bytes_rd_n
!= 0) {
266 page_bytes_rd_n
+= bytes_rd_n
;
270 return UNABLE_TO_READ_AT_LEAST_ONE_BYTE
;
273 static u8
http_target_end_locate(void)
277 c
= method_target_space
+ 1;
280 /* need at least 1 more byte to keep going */
281 if (c
== (page
+ page_bytes_rd_n
)) {
284 if (page_bytes_rd_n
== PAGE_SZ
)
285 break;/* but no more room in our page */
287 /* we have still room in our page */
289 r
= http_rd_at_least_one_byte();
290 if (r
== UNABLE_TO_READ_AT_LEAST_ONE_BYTE
)
303 static u8
http_target_file_open(void)
308 r
= open(target_start
, O_RDONLY
, 0);
315 target_file_fd
= (si
)r
;
319 static u8
http_target_file_sz_get(void)
321 struct stat target_stat
;
324 memset(&target_stat
, 0, sizeof(target_stat
));
326 r
= fstat(target_file_fd
, &target_stat
);
330 target_file_sz
= target_stat
.sz
;
334 /* if we have an error or we time out, notify for socket closing */
335 #define CNX_SOCK_SEND_WAIT_FAILURE 1
336 #define fd_set cnx_sock_fd_set
337 #define ul_idx cnx_sock_fd_set_ul_idx
338 #define ul_shift cnx_sock_fd_ul_shift
339 static u8
cnx_sock_send_wait(void)
344 fd_set
[ul_idx
] = (1UL << ul_shift
); /* was zero-ed in global_init */
346 tv
.sec
= CNX_WAIT_TIMEOUT
;
350 r
= select(cnx_sock
+ 1, 0, &cnx_sock_fd_set
[0], 0, &tv
);
355 if (ISERR(r
) || r
== 0) /* r != 1 */
356 return CNX_SOCK_SEND_WAIT_FAILURE
;
363 #define CNX_SOCK_SEND_RESP_HDR_FAILURE -1
364 static sl
cnx_sock_send_resp_hdr(void)
369 bytes_sent_n
= write(cnx_sock
, page
, resp_hdr_sz
);
370 if (bytes_sent_n
!= -EINTR
) {
371 if (ISERR(bytes_sent_n
))
372 bytes_sent_n
= CNX_SOCK_SEND_RESP_HDR_FAILURE
;
379 static u8
http_resp_hdr_send(void)
381 if (target_file_mime
[0] == 0)
382 resp_hdr_sz
= (sl
)snprintf(page
, PAGE_SZ
, RESP_HDR_FMT
,
385 resp_hdr_sz
= (sl
)snprintf(page
, PAGE_SZ
,
386 RESP_HDR_CONTENT_TYPE_FMT
,
388 &target_file_mime
[0]);
390 if (resp_hdr_sz
== 0)
397 r
= cnx_sock_send_wait();
398 if (r
== CNX_SOCK_SEND_WAIT_FAILURE
)
401 bytes_sent_n
= cnx_sock_send_resp_hdr();
402 if (bytes_sent_n
== CNX_SOCK_SEND_RESP_HDR_FAILURE
)
405 resp_hdr_sz
-= bytes_sent_n
;
407 if (resp_hdr_sz
== 0)
408 return 0; /* resp hrd was sent */
413 #define CNX_SOCK_SENDFILE_FAILURE -1
414 static sl
cnx_sock_sendfile(void)
419 bytes_sent_n
= sendfile(cnx_sock
, target_file_fd
, 0,
421 if (bytes_sent_n
!= -EINTR
) {
422 if (ISERR(bytes_sent_n
))
423 bytes_sent_n
= CNX_SOCK_SENDFILE_FAILURE
;
430 static u8
http_sendfile(void)
436 if (target_file_sz
== 0)
439 r
= cnx_sock_send_wait();
440 if (r
== CNX_SOCK_SEND_WAIT_FAILURE
)
443 bytes_sent_n
= cnx_sock_sendfile();
444 if (bytes_sent_n
== CNX_SOCK_SENDFILE_FAILURE
)
447 target_file_sz
-= bytes_sent_n
;
452 static u8
*http_target_mime_file_path(void)
454 /* We have room in the page. target_end has not be modified */
464 static void http_target_mime_file(void)
469 struct stat mime_file_stat
;
472 if (target_file_is_default
)
473 mime_file_path
= DEFAULT_FILE_MIME
;
475 mime_file_path
= http_target_mime_file_path();
477 /*--------------------------------------------------------------------*/
480 r
= open(mime_file_path
, O_RDONLY
, 0);
487 mime_file_fd
= (si
)r
;
488 /*--------------------------------------------------------------------*/
490 /*--------------------------------------------------------------------*/
492 memset(&mime_file_stat
, 0, sizeof(mime_file_stat
));
494 r
= fstat(mime_file_fd
, &mime_file_stat
);
498 /* mime_file_stat.sz */
499 /*--------------------------------------------------------------------*/
501 /*--------------------------------------------------------------------*/
503 if ((mime_file_stat
.sz
+ 1) > sizeof(target_file_mime
)) {
507 bytes_to_read_n
= mime_file_stat
.sz
;
508 /*--------------------------------------------------------------------*/
510 /*--------------------------------------------------------------------*/
513 r
= read(mime_file_fd
, &target_file_mime
[0] + mime_file_stat
.sz
514 - bytes_to_read_n
, bytes_to_read_n
);
517 goto close_mime_file
;
519 bytes_to_read_n
-= r
;
521 if (bytes_to_read_n
== 0)
525 /*--------------------------------------------------------------------*/
527 target_file_mime
[mime_file_stat
.sz
] = 0;
534 target_file_mime
[0] = 0; /* no mime */
537 /*---------------------------------------------------------------------------*/
539 static void cnx_handle(void)
545 /* read till we find the first space */
546 r
= http_method_rd();
550 /* is this space defining a reasonable method name? */
551 if (method_target_space
== (page
+ PAGE_SZ
)) /* huge method name */
554 r
= http_method_match();
558 r
= http_target_end_locate();
562 /* now we check we have room for the ".mime" extension */
563 if ((target_end
- 1 + sizeof(".mime")) >= (page
+ PAGE_SZ
))
566 /* target is exactly "/" or prepare the path */
567 if ((target_end
- (method_target_space
+ 1)) == 1
568 && method_target_space
[1] == '/') {
569 target_file_is_default
= 1;
570 target_start
= DEFAULT_FILE
;
572 target_file_is_default
= 0;
573 target_start
= method_target_space
+ 1;
577 r
= http_target_file_open();
581 r
= http_target_file_sz_get();
583 goto close_target_file
;
585 http_target_mime_file();
587 r
= http_resp_hdr_send();
589 goto close_target_file
;
591 if (http_method
== HTTP_METHOD_GET
)
592 (void)http_sendfile();
595 close(target_file_fd
);
600 static void cnx_sock_close(void)
606 if (r
!= -EINTR
) /* ignores errors */
611 static void cnx_sock_fd_set_params(void)
615 ul_bits_n
= 8 * sizeof(ul
);
617 cnx_sock_fd_set_ul_idx
= cnx_sock
/ ul_bits_n
;
618 cnx_sock_fd_ul_shift
= cnx_sock
% ul_bits_n
;
621 static void cnxs_consume(void)
625 struct sockaddr_in peer
;
628 peer_len
= sizeof(peer
);
630 r
= accept(srv_sock
, &peer
, &peer_len
);
631 if (r
!= -EINTR
&& r
!= ECONNABORTED
) /* based on man page */
633 /* SIGSTOP will generate a EINTR */
636 if (r
!= -EAGAIN
&& ISERR(r
))
637 exit(CNXS_CONSUME_ACCEPT_GENERIC_FAILURE
);
638 if (peer_len
!= sizeof(peer
))
639 exit(CNXS_CONSUME_ACCEPT_WRONG_PEER
);
641 break; /* no more connexion pending */
645 cnx_sock_fd_set_params();
652 static void sigs_consume(void)
654 struct signalfd_siginfo info
;
660 memset(&info
, 0, sizeof(info
));
661 r
= read(sigs_fd
, &info
, sizeof(info
));
665 if (r
!= -EAGAIN
&& ((ISERR(r
) || (r
> 0
666 && r
!= sizeof(info
)))))
667 exit(SIGS_CONSUME_SIGINFO_READ_FAILURE
);
668 if (r
== 0 || r
== -EAGAIN
)
671 switch (info
.ssi_signo
) {
675 /* please, do add the ones you like */
680 static void main_loop(void)
683 struct epoll_event evts
[2]; /*sigs_fd and srv_sock */
688 memset(evts
, 0, sizeof(evts
));
689 r
= epoll_wait(epfd
, evts
, 2, -1);
694 exit(MAIN_LOOP_EPOLL_WAIT_GENERIC_FAILURE
);
701 if (evts
[j
].data
.fd
== sigs_fd
) {
702 if(evts
[j
].events
& EPOLLIN
)
705 exit(MAIN_LOOP_EPOLL_WAIT_SIGS_FD_EVENT_IS_NOT_EPOLLIN
);
706 } else if (evts
[j
].data
.fd
== srv_sock
) {
707 if (evts
[j
].events
& (EPOLLERR
| EPOLLHUP
| EPOLLPRI
))
708 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNEXPECTED_EVENT
);
709 else if (evts
[j
].events
& EPOLLIN
)
712 exit(MAIN_LOOP_EPOLL_WAIT_SRV_SOCK_UNKNOWN_FAILURE
);
720 static void srv_sock_create(void)
725 /* TCP on IPv4... erk! */
726 r
= socket(PF_INET
, SOCK_STREAM
| SOCK_NONBLOCK
, 0);
728 exit(SRV_SOCK_CREATE_FAILURE
);
732 r
= setsockopt(srv_sock
, SOL_SOCKET
, SO_REUSEADDR
, &bool_true
,
735 exit(SRV_SOCK_SET_SOCK_OPTION_FAILURE
);
737 r
= bind(srv_sock
, &srv_addr
, sizeof(srv_addr
));
739 exit(SRV_SOCK_BIND_FAILURE
);
741 r
= listen(srv_sock
, 0);
743 exit(SRV_SOCK_LISTEN_FAILURE
);
746 static void epoll_sigs_setup(void)
748 struct epoll_event ep_evt
;
751 memset(&ep_evt
, 0, sizeof(ep_evt
));
752 ep_evt
.events
= EPOLLET
| EPOLLIN
;
753 ep_evt
.data
.fd
= sigs_fd
;
754 r
= epoll_ctl(epfd
, EPOLL_CTL_ADD
, sigs_fd
, &ep_evt
);
756 exit(EPOLL_SIGS_SETUP_EPOLL_ADD_FAILURE
);
759 static void sigs_setup(void)
760 {/* synchronous treatement of signals with signalfd
761 cannot change SIGKILL, neither SIGSTOP */
766 r
= rt_sigprocmask(SIG_BLOCK
, &mask
, 0, sizeof(mask
));
768 exit(SIGS_SETUP_BLOCKING_FAILURE
);
770 mask
= SIGBIT(SIGTERM
) | SIGBIT(SIGCHLD
);
771 sigs_fd
= (si
)signalfd4(-1, &mask
, sizeof(mask
), SFD_NONBLOCK
);
773 exit(SIGS_SETUP_HANDLERS_FAILURE
);
776 static void page_mmap(void)
780 addr
= mmap(PAGE_SZ
, RD
| WR
, PRIVATE
| ANONYMOUS
);
781 if(addr
== 0 || ISERR(addr
))
782 exit(RCV_PAGE_MMAP_FAILURE
);
787 static void setup(void)
791 epfd
=(si
)epoll_create1(0);
793 exit(SETUP_EPOLL_CREATE_FAILURE
);
797 epoll_srv_sock_setup();
802 static void globals_init(void)
804 srv_addr
.sin_family
= AF_INET
;
806 /* big endian port */
807 srv_addr
.sin_port
= cpu2be16(LISTENING_PORT
);
808 srv_addr
.sin_addr
.s_addr
= cpu2be32(LISTENING_IPV4
);
810 srv_sock
= -1; /* our listening socket */
811 cnx_sock
= -1; /* a cnx socket from accept */
813 sigs_fd
= -1; /* fd for signals */
814 epfd
= -1; /* epoll fd */
816 memset(&cnx_sock_fd_set
[0], 0, sizeof(cnx_sock_fd_set
));
819 static void chroot_do(void)
823 r
= chroot(CHROOT_PATH
);
825 exit(CHROOT_FAILURE
);
833 /******************************************************************************/
834 /******************************************************************************/
835 /******************************************************************************/
838 /* XXX:may do the daemonic stuff if _really_ we need it */