/* NETWOX Network toolbox Copyright(c) 1999-2006 Laurent Constantin ----- Main server : http://www.laurentconstantin.com/ Backup servers : http://go.to/laurentconstantin/ http://laurentconstantin.est-la.com/ http://laurentconstantin.free.fr/ http://membres.lycos.fr/lauconstantin/ [my current email address is on the web servers] ----- This file is part of Netwox. Netwox is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Netwox is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details (http://www.gnu.org/). ------------------------------------------------------------------------ */ /*-------------------------------------------------------------*/ #include "../netwox.h" /*-------------------------------------------------------------*/ netwib_conststring t000125_description[] = { "This tool is a simple web server.", "", NETWOX_DESC_rootdir, "Parameters --login and --password defines the login and password which", "will be needed to connect on this server.", "Parameter --allowed-clients defines the list of computer allowed to", "connect on the web server.", NETWOX_DESC_servertcpmul, NETWOX_DESC_toolpriv_port1024, NULL }; netwox_toolarg t000125_args[] = { NETWOX_SOCK_ARG_TCP_MULSERPORT("80"), NETWOX_TOOLARG_OPT_BUF_DIR_RD('r', "rootdir", "root directory", NULL), NETWOX_TOOLARG_OPT_BUF_LOGIN('l', "login", "login needed to access", NULL), NETWOX_TOOLARG_OPT_BUF_PASSWORD('L', "password", "password needed to access", NULL), NETWOX_TOOLARG_OPTA_UINT32('T', "timeout", "timeout in ms", "60000"), NETWOX_TOOLARG_OPTA_IPS_SU('c', "allowed-clients", "clients allowed to connect", NULL), NETWOX_TOOLARG_END }; netwox_tooltreenodetype t000125_nodes[] = { NETWOX_TOOLTREENODETYPE_SERVER_TCP_HTTP, NETWOX_TOOLTREENODETYPE_END }; netwox_tool_info t000125_info = { "HTTP server", t000125_description, "web, apache", t000125_args, t000125_nodes, }; /*-------------------------------------------------------------*/ static netwib_err t000125_send_error(netwox_httpserctx *pctx, netwib_io *pio, netwox_httphdr_statuscode statuscode, netwib_conststring statusmessage) { netwib_buf *pbuf; /* write headers */ netwib_er(netwox_httpserresphdrs_init(pctx, pio, statuscode, statusmessage)); netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pbuf)); netwib_er(netwox_httphdr_contenttype("text/html", pbuf)); netwib_er(netwox_httphdr_endheader(pbuf)); netwib_er(netwib_io_write(pio, pbuf)); /* write body */ netwib__buf_reinit(pbuf); netwib_er(netwib_buf_append_string("
", pbuf));
netwib_er(netwib_buf_append_fmt(pbuf, "Error %{uint32} : %s", statuscode,
statusmessage));
netwib_er(netwib_buf_append_string("\n", pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* close */
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pbuf));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_send_dir(netwox_httpserctx *pctx,
netwib_io *pio,
netwib_constbuf *puri,
netwib_constbuf *pdirname)
{
netwib_buf *pbuf, bufext, *pfilename, *pfullfilename;
netwib_pathstat pathstat;
netwib_dir *pdir;
netwib_cmp cmp;
netwib_err ret;
netwib_er(netwib_dir_init(pdirname, &pdir));
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pbuf));
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pfilename));
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pfullfilename));
/* write headers */
netwib_er(netwox_httpserresphdrs_init(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_OK, "Ok"));
netwib_er(netwox_httphdr_contenttype("text/html", pbuf));
netwib_er(netwox_httphdr_endheader(pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* write body */
netwib__buf_reinit(pbuf);
netwib_er(netwib_buf_append_string("", pbuf));
netwib_er(netwib_buf_init_ext_string("/", &bufext));
netwib_er(netwib_buf_cmp(puri, &bufext, &cmp));
if (cmp != NETWIB_CMP_EQ) {
netwib_er(netwib_buf_append_string("..\n", pbuf));
}
while (NETWIB_TRUE) {
netwib__buf_reinit(pfilename);
ret = netwib_dir_next(pdir, pfilename);
if (ret == NETWIB_ERR_DATAEND) break;
netwib__buf_reinit(pfullfilename);
netwib_er(netwib_path_init_concat(pdirname, pfilename, pfullfilename));
netwib_er(netwib_pathstat_init(pfullfilename, &pathstat));
if (pathstat.type == NETWIB_PATHSTAT_TYPE_DIR) {
netwib_er(netwib_buf_append_byte('/', pfilename));
}
netwib_er(netwib_buf_append_fmt(pbuf, "%{buf}\n",
pfilename, pfilename));
}
netwib_er(netwib_buf_append_string("\n", pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* close */
netwib_er(netwib_dir_close(&pdir));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pbuf));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pfilename));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pfullfilename));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_send_dir_redir(netwox_httpserctx *pctx,
netwib_io *pio,
netwib_constbuf *puri)
{
netwib_buf *pbuf;
/* write headers */
netwib_er(netwox_httpserresphdrs_init(pctx, pio, NETWOX_HTTPHDR_STATUSCODE_MOVEDPERMANENTLY, "Permanently moved"));
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pbuf));
netwib_er(netwox_httphdr_contenttype("text/html", pbuf));
netwib_er(netwox_httphdr_location(puri, pbuf));
netwib_er(netwox_httphdr_endheader(pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* write body */
netwib__buf_reinit(pbuf);
netwib_er(netwib_buf_append_string("", pbuf));
netwib_er(netwib_buf_append_fmt(pbuf, "Directory is at : %{buf}\n", puri, puri));
netwib_er(netwib_buf_append_string("\n", pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* close */
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pbuf));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_send_reg(netwox_httpserctx *pctx,
netwib_io *pio,
netwib_constbuf *pfilename)
{
netwib_buf *pbuf;
netwib_io *pfileio;
netwib_uint32 filesize;
netwib_er(netwib_filename_size(pfilename, &filesize));
netwib_er(netwib_io_init_file_read(pfilename, &pfileio));
/* write headers */
netwib_er(netwox_httpserresphdrs_init(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_OK, "Ok"));
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pbuf));
netwib_er(netwox_httphdr_endheader(pbuf));
netwib_er(netwib_io_write(pio, pbuf));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pbuf));
/* write body */
netwib_er(netwox_httpbody_write_io_close(pctx->pbufpool, pfileio, pio));
/* close */
netwib_er(netwib_io_close(&pfileio));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_send_link(netwox_httpserctx *pctx,
netwib_io *pio,
netwib_constbuf *pfilename)
{
netwib_buf *pbuf, *plink;
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pbuf));
/* write headers */
netwib_er(netwox_httpserresphdrs_init(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_OK, "Ok"));
netwib_er(netwox_httphdr_contenttype("text/html", pbuf));
netwib_er(netwox_httphdr_endheader(pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* do not bother creating a correct link : if it points outside
root, it will be unreachable */
/* write body */
netwib__buf_reinit(pbuf);
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &plink));
netwib_er(netwib_unix_readlink(pfilename, plink));
netwib_er(netwib_buf_append_string("", pbuf));
netwib_er(netwib_buf_append_fmt(pbuf, "Link points to : %{buf}\n", plink, plink));
netwib_er(netwib_buf_append_string("\n", pbuf));
netwib_er(netwib_io_write(pio, pbuf));
/* close */
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &plink));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pbuf));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_answer(netwox_httpserctx *pctx,
netwib_io *pio,
netwox_httpserreqhdrs *preqhdrs)
{
netwib_buf *puri, *pfilename;
netwib_data data;
netwib_uint32 datasize;
netwib_bool auth;
netwib_pathstat pathstat;
netwib_cmp cmp;
netwib_err ret;
/* check method */
if (preqhdrs->method != NETWOX_HTTPHDR_METHOD_GET) {
return(t000125_send_error(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_NOTIMPLEMENTED,
"Only GET method is implemented"));
}
/* check authentication */
if (netwib__buf_ref_data_size(&pctx->login)) {
auth = NETWIB_FALSE;
if (preqhdrs->authbasicset) {
netwib_er(netwib_buf_cmp(&pctx->login, &preqhdrs->authbasiclogin, &cmp));
if (cmp == NETWIB_CMP_EQ) {
netwib_er(netwib_buf_cmp(&pctx->password, &preqhdrs->authbasicpassword,
&cmp));
if (cmp == NETWIB_CMP_EQ) {
auth = NETWIB_TRUE;
}
}
}
if (!auth) {
return(t000125_send_error(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_UNAUTHORIZED,
"Bad login and password"));
}
}
/* check uri */
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &puri));
ret = netwox_url_pqf_decode(&preqhdrs->uri, puri, NULL, NULL);
if (ret != NETWIB_ERR_OK) {
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &puri));
return(t000125_send_error(pctx, pio, NETWOX_HTTPHDR_STATUSCODE_BADREQUEST,
"URI not decoded"));
}
netwib_er(netwib_bufpool_buf_init(pctx->pbufpool, &pfilename));
ret = netwib_path_init_jail(&pctx->rootdir, puri, pfilename);
if (ret != NETWIB_ERR_OK) {
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &puri));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pfilename));
return(t000125_send_error(pctx, pio, NETWOX_HTTPHDR_STATUSCODE_BADREQUEST,
"URI not canonized"));
}
/*netwib_er(netwib_fmt_display("%{buf} %{buf}\n", puricanon, pfilename));*/
/* determine filename type */
ret = netwib_pathstat_init(pfilename, &pathstat);
if (ret != NETWIB_ERR_OK) {
netwib_er(t000125_send_error(pctx, pio, NETWOX_HTTPHDR_STATUSCODE_NOTFOUND,
"not found"));
} else {
switch(pathstat.type) {
case NETWIB_PATHSTAT_TYPE_REG :
ret = t000125_send_reg(pctx, pio, pfilename);
break;
case NETWIB_PATHSTAT_TYPE_LINK :
ret = t000125_send_link(pctx, pio, pfilename);
break;
case NETWIB_PATHSTAT_TYPE_DIR :
data = netwib__buf_ref_data_ptr(puri);
datasize = netwib__buf_ref_data_size(puri);
if (data[datasize-1] == '/') {
ret = t000125_send_dir(pctx, pio, puri, pfilename);
} else {
netwib_er(netwib_buf_append_byte('/', puri));
ret = t000125_send_dir_redir(pctx, pio, puri);
}
break;
default :
netwib_er(t000125_send_error(pctx, pio,
NETWOX_HTTPHDR_STATUSCODE_NOTIMPLEMENTED,
"Unsupported filename type"));
break;
}
}
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &pfilename));
netwib_er(netwib_bufpool_buf_close(pctx->pbufpool, &puri));
return(ret);
}
/*-------------------------------------------------------------*/
static netwib_err t000125_srv(netwib_io *psockio,
netwib_ptr pinfos)
{
netwox_httpserctx *pctx = (netwox_httpserctx *)pinfos;
netwib_io *pio;
netwox_httpserreqhdrs reqhdrs;
netwib_ip remoteip;
netwib_port remoteport;
netwib_bool allowed;
netwib_err ret=NETWIB_ERR_OK;
/* check client */
if (pctx->pallowedclients != NULL) {
netwib_er(netwib_sock_ctl_get_remote(psockio, &remoteip, &remoteport));
netwib_er(netwib_ips_contains_ip(pctx->pallowedclients, &remoteip,
&allowed));
if (!allowed) {
netwib_fmt_display("IP address %{ip} is not allowed\n", &remoteip);
/*ignore*/t000125_send_error(pctx, psockio,
NETWOX_HTTPHDR_STATUSCODE_FORBIDDEN,
"Your address is forbidden");
return(NETWIB_ERR_OK);
}
}
/* prepare a read by line io */
netwib_er(netwib_io_init_data(NETWIB_IO_INIT_DATA_TYPE_LINE,
NETWIB_IO_INIT_DATA_TYPE_TRANSPARENT, &pio));
netwib_er(netwib_io_plug_rdwr(pio, psockio));
/* read request */
netwib_er(netwox_httpserreqhdrs_init(&reqhdrs));
ret = netwox_httpserreqhdrs_decode(pctx, pio, &reqhdrs);
/* answer */
if (ret == NETWIB_ERR_OK) {
ret = t000125_answer(pctx, pio, &reqhdrs);
}
if (ret == NETWIB_ERR_OK) {
/*
Cannot display it, because under Windows, this slows the server
(display is only 100 lines per second).
netwib_er(netwib_fmt_display("%{buf}\n", &reqhdrs.uri));
*/
} else {
netwib_er(netwox_err_display(ret, NETWIB_ERR_ENCODETYPE_TEXT));
}
/* close */
netwib_er(netwox_httpserreqhdrs_close(&reqhdrs));
netwib_er(netwib_io_unplug_next_supported(pio, NULL));
netwib_er(netwib_io_close(&pio));
return(NETWIB_ERR_OK);
}
/*-------------------------------------------------------------*/
netwib_err t000125_core(int argc, char *argv[])
{
netwox_arg *parg;
netwox_sockinfo sockinfo;
netwox_httpserctx ctx;
netwib_buf buf;
netwib_bool isset;
/* obtain parameters */
netwib_er(netwox_arg_init(argc, argv, &t000125_info, &parg));
netwib_er(netwox_sockinfo_init_arg_tcp_mulser(parg, &sockinfo));
netwib_er(netwox_httpserctx_init(&ctx));
netwib_er(netwox_arg_isset(parg, 'r', &isset));
if (isset) {
netwib__buf_reinit(&ctx.rootdir);
netwib_er(netwox_arg_buf(parg, 'r', &buf));
netwib_er(netwib_path_canon(&buf, &ctx.rootdir));
}
netwib_er(netwox_arg_buf(parg, 'l', &buf));
buf.flags |= NETWIB_BUF_FLAGS_SENSITIVE;
buf.flags |= NETWIB_BUF_FLAGS_SENSITIVE_READONLY;
netwib_er(netwib_buf_append_buf(&buf, &ctx.login));
netwib_er(netwox_arg_buf(parg, 'L', &buf));
buf.flags |= NETWIB_BUF_FLAGS_SENSITIVE;
buf.flags |= NETWIB_BUF_FLAGS_SENSITIVE_READONLY;
netwib_er(netwib_buf_append_buf(&buf, &ctx.password));
netwib_er(netwox_arg_uint32(parg, 'T', &ctx.timeoutms));
netwib_er(netwox_arg_ips(parg, 'c', &ctx.pallowedclients));
netwib_er(netwib_buf_append_string("HTTP Server", &ctx.realm));
/* main loop */
netwib_er(netwox_sock_tcp_mulser(&sockinfo, &t000125_srv, &ctx));
/* close */
netwib_er(netwox_httpserctx_close(&ctx));
netwib_er(netwox_sockinfo_close(&sockinfo));
netwib_er(netwox_arg_close(&parg));
return(NETWIB_ERR_OK);
}