/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * bb_http.c : bearerbox http adminstration commands * * NOTE: this is a special bearerbox module - it does call * functions from core module! (other modules are fully * encapsulated, and only called outside) * * Kalle Marjola 2000 for project Kannel */ #include #include #include #include "gwlib/gwlib.h" #include "bearerbox.h" /* passed from bearerbox core */ extern volatile sig_atomic_t bb_status; /* our own thingies */ static volatile sig_atomic_t httpadmin_running; static long ha_port; static Octstr *ha_interface; static Octstr *ha_password; static Octstr *ha_status_pw; static Octstr *ha_allow_ip; static Octstr *ha_deny_ip; /*--------------------------------------------------------- * static functions */ /* * check if the password matches. Return NULL if * it does (or is not required) */ static Octstr *httpd_check_authorization(List *cgivars, int status) { Octstr *password; static double sleep = 0.01; password = http_cgi_variable(cgivars, "password"); if (status) { if (ha_status_pw == NULL) return NULL; if (password == NULL) goto denied; if (octstr_compare(password, ha_password)!=0 && octstr_compare(password, ha_status_pw)!=0) goto denied; } else { if (password == NULL || octstr_compare(password, ha_password)!=0) goto denied; } sleep = 0.0; return NULL; /* allowed */ denied: gwthread_sleep(sleep); sleep += 1.0; /* little protection against brute force * password cracking */ return octstr_create("Denied"); } /* * check if we still have time to do things */ static Octstr *httpd_check_status(void) { if (bb_status == BB_SHUTDOWN || bb_status == BB_DEAD) return octstr_create("Avalanche has already started, too late to " "save the sheeps"); return NULL; } static Octstr *httpd_status(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 1))!= NULL) return reply; return bb_print_status(status_type); } static Octstr *httpd_store_status(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 1))!= NULL) return reply; return store_status(status_type); } static Octstr *httpd_loglevel(List *cgivars, int status_type) { Octstr *reply; Octstr *level; int new_loglevel; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; /* check if new loglevel is given */ level = http_cgi_variable(cgivars, "level"); if (level) { new_loglevel = atoi(octstr_get_cstr(level)); log_set_log_level(new_loglevel); return octstr_format("log-level set to %d", new_loglevel); } else { return octstr_create("New level not given"); } } static Octstr *httpd_shutdown(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if (bb_status == BB_SHUTDOWN) bb_status = BB_DEAD; else { bb_shutdown(); gwthread_wakeup(MAIN_THREAD_ID); } return octstr_create("Bringing system down"); } static Octstr *httpd_isolate(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; if (bb_isolate() == -1) return octstr_create("Already isolated"); else return octstr_create(GW_NAME " isolated from message providers"); } static Octstr *httpd_suspend(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; if (bb_suspend() == -1) return octstr_create("Already suspended"); else return octstr_create(GW_NAME " suspended"); } static Octstr *httpd_resume(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; if (bb_resume() == -1) return octstr_create("Already running"); else return octstr_create("Running resumed"); } static Octstr *httpd_restart(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; if (bb_status == BB_SHUTDOWN) { bb_status = BB_DEAD; gwthread_wakeup_all(); return octstr_create("Trying harder to restart"); } bb_restart(); return octstr_create("Restarting....."); } static Octstr *httpd_flush_dlr(List *cgivars, int status_type) { Octstr *reply; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; if (bb_flush_dlr() == -1) return octstr_create("Suspend " GW_NAME " before trying to flush DLR queue"); else return octstr_create("DLR queue flushed"); } static Octstr *httpd_stop_smsc(List *cgivars, int status_type) { Octstr *reply; Octstr *smsc; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; /* check if the smsc id is given */ smsc = http_cgi_variable(cgivars, "smsc"); if (smsc) { if (bb_stop_smsc(smsc) == -1) return octstr_format("Could not shut down smsc-id `%s'", octstr_get_cstr(smsc)); else return octstr_format("SMSC `%s' shut down", octstr_get_cstr(smsc)); } else return octstr_create("SMSC id not given"); } static Octstr *httpd_restart_smsc(List *cgivars, int status_type) { Octstr *reply; Octstr *smsc; if ((reply = httpd_check_authorization(cgivars, 0))!= NULL) return reply; if ((reply = httpd_check_status())!= NULL) return reply; /* check if the smsc id is given */ smsc = http_cgi_variable(cgivars, "smsc"); if (smsc) { if (bb_restart_smsc(smsc) == -1) return octstr_format("Could not re-start smsc-id `%s'", octstr_get_cstr(smsc)); else return octstr_format("SMSC `%s' re-started", octstr_get_cstr(smsc)); } else return octstr_create("SMSC id not given"); } /* Known httpd commands and their functions */ static struct httpd_command { const char *command; Octstr * (*function)(List *cgivars, int status_type); } httpd_commands[] = { { "status", httpd_status }, { "store-status", httpd_store_status }, { "log-level", httpd_loglevel }, { "shutdown", httpd_shutdown }, { "suspend", httpd_suspend }, { "isolate", httpd_isolate }, { "resume", httpd_resume }, { "restart", httpd_restart }, { "flush-dlr", httpd_flush_dlr }, { "stop-smsc", httpd_stop_smsc }, { "start-smsc", httpd_restart_smsc }, { NULL , NULL } /* terminate list */ }; static void httpd_serve(HTTPClient *client, Octstr *ourl, List *headers, Octstr *body, List *cgivars) { Octstr *reply, *final_reply, *url; char *content_type; char *header, *footer; int status_type; int i; long pos; reply = final_reply = NULL; /* for compiler please */ url = octstr_duplicate(ourl); /* Set default reply format according to client * Accept: header */ if (http_type_accepted(headers, "text/vnd.wap.wml")) { status_type = BBSTATUS_WML; content_type = "text/vnd.wap.wml"; } else if (http_type_accepted(headers, "text/html")) { status_type = BBSTATUS_HTML; content_type = "text/html"; } else if (http_type_accepted(headers, "text/xml")) { status_type = BBSTATUS_XML; content_type = "text/xml"; } else { status_type = BBSTATUS_TEXT; content_type = "text/plain"; } /* kill '/cgi-bin' prefix */ pos = octstr_search(url, octstr_imm("/cgi-bin/"), 0); if (pos != -1) octstr_delete(url, pos, 9); else if (octstr_get_char(url, 0) == '/') octstr_delete(url, 0, 1); /* look for type and kill it */ pos = octstr_search_char(url, '.', 0); if (pos != -1) { Octstr *tmp = octstr_copy(url, pos+1, octstr_len(url) - pos - 1); octstr_delete(url, pos, octstr_len(url) - pos); if (octstr_str_compare(tmp, "txt") == 0) status_type = BBSTATUS_TEXT; else if (octstr_str_compare(tmp, "html") == 0) status_type = BBSTATUS_HTML; else if (octstr_str_compare(tmp, "xml") == 0) status_type = BBSTATUS_XML; else if (octstr_str_compare(tmp, "wml") == 0) status_type = BBSTATUS_WML; octstr_destroy(tmp); } for (i=0; httpd_commands[i].command != NULL; i++) { if (octstr_str_compare(url, httpd_commands[i].command) == 0) { reply = httpd_commands[i].function(cgivars, status_type); break; } } /* check if command found */ if (httpd_commands[i].command == NULL) { char *lb = bb_status_linebreak(status_type); reply = octstr_format("Unknown command `%S'.%sPossible commands are:%s", ourl, lb, lb); for (i=0; httpd_commands[i].command != NULL; i++) octstr_format_append(reply, "%s%s", httpd_commands[i].command, lb); } gw_assert(reply != NULL); if (status_type == BBSTATUS_HTML) { header = "\n" "\n" GW_NAME "\n\n

"; footer = "

\n\n"; content_type = "text/html"; } else if (status_type == BBSTATUS_WML) { header = "\n" "\n" "\n\n \n

"; footer = "

\n
\n
\n"; content_type = "text/vnd.wap.wml"; } else if (status_type == BBSTATUS_XML) { header = "\n" "\n"; footer = "\n"; } else { header = ""; footer = ""; content_type = "text/plain"; } final_reply = octstr_create(header); octstr_append(final_reply, reply); octstr_append_cstr(final_reply, footer); /* debug("bb.http", 0, "Result: '%s'", octstr_get_cstr(final_reply)); */ http_destroy_headers(headers); headers = gwlist_create(); http_header_add(headers, "Content-Type", content_type); http_send_reply(client, HTTP_OK, headers, final_reply); octstr_destroy(url); octstr_destroy(ourl); octstr_destroy(body); octstr_destroy(reply); octstr_destroy(final_reply); http_destroy_headers(headers); http_destroy_cgiargs(cgivars); } static void httpadmin_run(void *arg) { HTTPClient *client; Octstr *ip, *url, *body; List *headers, *cgivars; while(bb_status != BB_DEAD) { if (bb_status == BB_SHUTDOWN) bb_shutdown(); client = http_accept_request(ha_port, &ip, &url, &headers, &body, &cgivars); if (client == NULL) break; if (is_allowed_ip(ha_allow_ip, ha_deny_ip, ip) == 0) { info(0, "HTTP admin tried from denied host <%s>, disconnected", octstr_get_cstr(ip)); http_close_client(client); continue; } httpd_serve(client, url, headers, body, cgivars); octstr_destroy(ip); } httpadmin_running = 0; } /*------------------------------------------------------------- * public functions * */ int httpadmin_start(Cfg *cfg) { CfgGroup *grp; int ssl = 0; #ifdef HAVE_LIBSSL Octstr *ssl_server_cert_file; Octstr *ssl_server_key_file; #endif /* HAVE_LIBSSL */ if (httpadmin_running) return -1; grp = cfg_get_single_group(cfg, octstr_imm("core")); if (cfg_get_integer(&ha_port, grp, octstr_imm("admin-port")) == -1) panic(0, "Missing admin-port variable, cannot start HTTP admin"); ha_interface = cfg_get(grp, octstr_imm("admin-interface")); ha_password = cfg_get(grp, octstr_imm("admin-password")); if (ha_password == NULL) panic(0, "You MUST set HTTP admin-password"); ha_status_pw = cfg_get(grp, octstr_imm("status-password")); ha_allow_ip = cfg_get(grp, octstr_imm("admin-allow-ip")); ha_deny_ip = cfg_get(grp, octstr_imm("admin-deny-ip")); #ifdef HAVE_LIBSSL cfg_get_bool(&ssl, grp, octstr_imm("admin-port-ssl")); /* * check if SSL is desired for HTTP servers and then * load SSL client and SSL server public certificates * and private keys */ ssl_server_cert_file = cfg_get(grp, octstr_imm("ssl-server-cert-file")); ssl_server_key_file = cfg_get(grp, octstr_imm("ssl-server-key-file")); if (ssl_server_cert_file != NULL && ssl_server_key_file != NULL) { /* we are fine here, the following call is now in conn_config_ssl(), * so there is no reason to do this twice. use_global_server_certkey_file(ssl_server_cert_file, ssl_server_key_file); */ } else if (ssl) { panic(0, "You MUST specify cert and key files within core group for SSL-enabled HTTP servers!"); } octstr_destroy(ssl_server_cert_file); octstr_destroy(ssl_server_key_file); #endif /* HAVE_LIBSSL */ http_open_port_if(ha_port, ssl, ha_interface); if (gwthread_create(httpadmin_run, NULL) == -1) panic(0, "Failed to start a new thread for HTTP admin"); httpadmin_running = 1; return 0; } void httpadmin_stop(void) { http_close_all_ports(); gwthread_join_every(httpadmin_run); octstr_destroy(ha_interface); octstr_destroy(ha_password); octstr_destroy(ha_status_pw); octstr_destroy(ha_allow_ip); octstr_destroy(ha_deny_ip); ha_password = NULL; ha_status_pw = NULL; ha_allow_ip = NULL; ha_deny_ip = NULL; }