2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 #include <sys/types.h>
25 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <sys/signal.h>
29 #include <arpa/inet.h>
33 #include <asterisk/cli.h>
34 #include <asterisk/http.h>
35 #include <asterisk/utils.h>
36 #include <asterisk/strings.h>
39 #define DEFAULT_PREFIX "asterisk"
41 /* This program implements a tiny http server supporting the "get" method
42 only and was inspired by micro-httpd by Jef Poskanzer */
44 struct ast_http_server_instance {
47 struct sockaddr_in requestor;
48 ast_http_callback callback;
51 static struct ast_http_uri *uris;
53 static int httpfd = -1;
54 static pthread_t master = AST_PTHREADT_NULL;
55 static char prefix[MAX_PREFIX];
56 static int prefix_len = 0;
57 static struct sockaddr_in oldsin;
60 static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
63 int reslen = sizeof(result);
65 struct ast_variable *v;
66 char iabuf[INET_ADDRSTRLEN];
68 ast_build_string(&c, &reslen,
70 "<title>Asterisk HTTP Status</title>\r\n"
71 "<body bgcolor=\"#ffffff\">\r\n"
72 "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
73 "<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
75 ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
76 ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
77 ast_inet_ntoa(iabuf, sizeof(iabuf), oldsin.sin_addr));
78 ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
79 ntohs(oldsin.sin_port));
80 ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
83 ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
86 ast_build_string(&c, &reslen, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
87 return strdup(result);
90 static struct ast_http_uri statusuri = {
91 .callback = httpstatus_callback,
92 .description = "Asterisk HTTP General Status",
97 char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
101 "Content-type: text/html\r\n"
104 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
106 "<title>%d %s</title>\r\n"
111 "<address>Asterisk Server</address>\r\n"
112 "</body></html>\r\n",
113 (extra_header ? extra_header : ""), status, title, title, text);
117 int ast_http_uri_link(struct ast_http_uri *urih)
119 struct ast_http_uri *prev=uris;
120 if (!uris || strlen(uris->uri) <= strlen(urih->uri)) {
124 while (prev->next && (strlen(prev->next->uri) > strlen(urih->uri)))
127 urih->next = prev->next;
133 void ast_http_uri_unlink(struct ast_http_uri *urih)
135 struct ast_http_uri *prev = uris;
142 if (prev->next == urih) {
143 prev->next = urih->next;
150 static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength)
157 struct ast_http_uri *urih=NULL;
159 struct ast_variable *vars=NULL, *v, *prev = NULL;
164 params = strchr(uri, '?');
168 while ((var = strsep(¶ms, "&"))) {
169 val = strchr(var, '=');
177 if ((v = ast_variable_new(var, val))) {
187 if (!strncasecmp(uri, prefix, prefix_len)) {
189 if (!*uri || (*uri == '/')) {
194 len = strlen(urih->uri);
195 if (!strncasecmp(urih->uri, uri, len)) {
196 if (!uri[len] || uri[len] == '/') {
200 if (!*turi || urih->has_subtree) {
211 c = urih->callback(sin, uri, vars, status, title, contentlength);
212 ast_variables_destroy(vars);
214 c = ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this serer.");
216 *title = strdup("Not Found");
221 static void *ast_httpd_helper_thread(void *data)
225 struct ast_http_server_instance *ser = data;
226 char *uri, *c, *title=NULL;
227 int status = 200, contentlength = 0;
230 if (fgets(buf, sizeof(buf), ser->f)) {
233 while(*uri && (*uri > 32)) uri++;
239 /* Skip white space */
240 while (*uri && (*uri < 33)) uri++;
244 while (*c && (*c > 32)) c++;
250 if (!strcasecmp(buf, "get"))
251 c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength);
253 c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
255 c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
257 c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
260 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
261 ast_cli(ser->fd, "HTTP/1.1 GET %d %s\r\n", status, title ? title : "OK");
262 ast_cli(ser->fd, "Server: Asterisk\r\n");
263 ast_cli(ser->fd, "Date: %s\r\n", timebuf);
265 ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
266 ast_cli(ser->fd, "Connection: close\r\n");
267 ast_cli(ser->fd, "%s", c);
278 static void *http_root(void *data)
281 struct sockaddr_in sin;
283 struct ast_http_server_instance *ser;
286 ast_wait_for_input(httpfd, -1);
287 sinlen = sizeof(sin);
288 fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen);
290 if ((errno != EAGAIN) && (errno != EINTR))
291 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
294 ser = calloc(1, sizeof(*ser));
297 if ((ser->f = fdopen(ser->fd, "w+"))) {
298 if (ast_pthread_create(&launched, NULL, ast_httpd_helper_thread, ser)) {
299 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
304 ast_log(LOG_WARNING, "fdopen failed!\n");
309 ast_log(LOG_WARNING, "Out of memory!\n");
316 static void http_server_start(struct sockaddr_in *sin)
318 char iabuf[INET_ADDRSTRLEN];
322 /* Do nothing if nothing has changed */
323 if (!memcmp(&oldsin, sin, sizeof(oldsin))) {
324 ast_log(LOG_DEBUG, "Nothing changed in http\n");
328 memcpy(&oldsin, sin, sizeof(oldsin));
330 /* Shutdown a running server if there is one */
331 if (master != AST_PTHREADT_NULL) {
332 pthread_cancel(master);
333 pthread_kill(master, SIGURG);
334 pthread_join(master, NULL);
340 /* If there's no new server, stop here */
341 if (!sin->sin_family)
345 httpfd = socket(AF_INET, SOCK_STREAM, 0);
347 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
351 setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
352 if (bind(httpfd, (struct sockaddr *)sin, sizeof(*sin))) {
353 ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n",
354 ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
360 if (listen(httpfd, 10)) {
361 ast_log(LOG_NOTICE, "Unable to listen!\n");
366 flags = fcntl(httpfd, F_GETFL);
367 fcntl(httpfd, F_SETFL, flags | O_NONBLOCK);
368 if (ast_pthread_create(&master, NULL, http_root, NULL)) {
369 ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n",
370 ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
377 static int __ast_http_load(int reload)
379 struct ast_config *cfg;
380 struct ast_variable *v;
382 struct sockaddr_in sin;
384 struct ast_hostent ahp;
385 char newprefix[MAX_PREFIX];
386 memset(&sin, 0, sizeof(sin));
388 strcpy(newprefix, DEFAULT_PREFIX);
389 cfg = ast_config_load("http.conf");
391 v = ast_variable_browse(cfg, "general");
393 if (!strcasecmp(v->name, "enabled"))
394 enabled = ast_true(v->value);
395 else if (!strcasecmp(v->name, "bindport"))
396 sin.sin_port = ntohs(atoi(v->value));
397 else if (!strcasecmp(v->name, "bindaddr")) {
398 if ((hp = ast_gethostbyname(v->value, &ahp))) {
399 memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
401 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
403 } else if (!strcasecmp(v->name, "prefix"))
404 ast_copy_string(newprefix, v->value, sizeof(newprefix));
407 ast_config_destroy(cfg);
410 sin.sin_family = AF_INET;
411 if (strcmp(prefix, newprefix)) {
412 ast_copy_string(prefix, newprefix, sizeof(prefix));
413 prefix_len = strlen(prefix);
415 http_server_start(&sin);
419 static int handle_show_http(int fd, int argc, char *argv[])
421 char iabuf[INET_ADDRSTRLEN];
422 struct ast_http_uri *urih;
424 return RESULT_SHOWUSAGE;
425 ast_cli(fd, "HTTP Server Status:\n");
426 ast_cli(fd, "Prefix: %s\n", prefix);
427 if (oldsin.sin_family)
428 ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n",
429 ast_inet_ntoa(iabuf, sizeof(iabuf), oldsin.sin_addr),
430 ntohs(oldsin.sin_port));
432 ast_cli(fd, "Server Disabled\n\n");
433 ast_cli(fd, "Enabled URI's:\n");
436 ast_cli(fd, "/%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
440 ast_cli(fd, "None.\n");
441 return RESULT_SUCCESS;
444 int ast_http_reload(void)
446 return __ast_http_load(1);
449 static char show_http_help[] =
451 " Shows status of internal HTTP engine\n";
453 static struct ast_cli_entry http_cli[] = {
454 { { "show", "http", NULL }, handle_show_http,
455 "Display HTTP status", show_http_help },
458 int ast_http_init(void)
460 ast_http_uri_link(&statusuri);
461 ast_cli_register_multiple(http_cli, sizeof(http_cli) / sizeof(http_cli[0]));
462 return __ast_http_load(0);