root/source4/web_server/web_server.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. websrv_destructor
  2. websrv_timeout
  3. http_error
  4. websrv_output_headers
  5. websrv_output
  6. http_parse_header
  7. websrv_recv
  8. websrv_send
  9. websrv_accept
  10. websrv_task_init
  11. server_service_web_init

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    web server startup
   5 
   6    Copyright (C) Andrew Tridgell 2005
   7    Copyright (C) Jelmer Vernooij 2008
   8    
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 3 of the License, or
  12    (at your option) any later version.
  13    
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18    
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22 
  23 #include "includes.h"
  24 #include "smbd/service_task.h"
  25 #include "smbd/service_stream.h"
  26 #include "smbd/service.h"
  27 #include "web_server/web_server.h"
  28 #include "lib/events/events.h"
  29 #include "system/filesys.h"
  30 #include "system/network.h"
  31 #include "lib/socket/netif.h"
  32 #include "lib/tls/tls.h"
  33 #include "../lib/util/dlinklist.h"
  34 #include "param/param.h"
  35 
  36 /* don't allow connections to hang around forever */
  37 #define HTTP_TIMEOUT 120
  38 
  39 /*
  40   destroy a web connection
  41 */
  42 static int websrv_destructor(struct websrv_context *web)
     /* [<][>][^][v][top][bottom][index][help] */
  43 {
  44         return 0;
  45 }
  46 
  47 /*
  48   called when a connection times out. This prevents a stuck connection
  49   from hanging around forever
  50 */
  51 static void websrv_timeout(struct tevent_context *event_context, 
     /* [<][>][^][v][top][bottom][index][help] */
  52                            struct tevent_timer *te, 
  53                            struct timeval t, void *private_data)
  54 {
  55         struct websrv_context *web = talloc_get_type(private_data, struct websrv_context);
  56         struct stream_connection *conn = web->conn;
  57         web->conn = NULL;
  58         /* TODO: send a message to any running esp context on this connection
  59            to stop running */
  60         stream_terminate_connection(conn, "websrv_timeout: timed out"); 
  61 }
  62 
  63 /*
  64   setup for a raw http level error
  65 */
  66 void http_error(struct websrv_context *web, const char *status, const char *info)
     /* [<][>][^][v][top][bottom][index][help] */
  67 {
  68         char *s;
  69         s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n", 
  70                             status, status, info);
  71         if (s == NULL) {
  72                 stream_terminate_connection(web->conn, "http_error: out of memory");
  73                 return;
  74         }
  75         websrv_output_headers(web, status, NULL);
  76         websrv_output(web, s, strlen(s));
  77 }
  78 
  79 void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
     /* [<][>][^][v][top][bottom][index][help] */
  80 {
  81         char *s;
  82         DATA_BLOB b;
  83         struct http_header *hdr;
  84 
  85         s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
  86         if (s == NULL) return;
  87         for (hdr = headers; hdr; hdr = hdr->next) {
  88                 s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
  89         }
  90 
  91         s = talloc_asprintf_append_buffer(s, "\r\n");
  92 
  93         b = web->output.content;
  94         web->output.content = data_blob_string_const(s);
  95         websrv_output(web, b.data, b.length);
  96         data_blob_free(&b);
  97 }
  98 
  99 void websrv_output(struct websrv_context *web, void *data, size_t length)
     /* [<][>][^][v][top][bottom][index][help] */
 100 {
 101         data_blob_append(web, &web->output.content, data, length);
 102         EVENT_FD_NOT_READABLE(web->conn->event.fde);
 103         EVENT_FD_WRITEABLE(web->conn->event.fde);
 104         web->output.output_pending = true;
 105 }
 106 
 107 
 108 /*
 109   parse one line of header input
 110 */
 111 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
     /* [<][>][^][v][top][bottom][index][help] */
 112 {
 113         if (line[0] == 0) {
 114                 web->input.end_of_headers = true;
 115         } else if (strncasecmp(line,"GET ", 4)==0) {
 116                 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
 117         } else if (strncasecmp(line,"POST ", 5)==0) {
 118                 web->input.post_request = true;
 119                 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
 120         } else if (strchr(line, ':') == NULL) {
 121                 http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
 122                 return NT_STATUS_INVALID_PARAMETER;
 123         } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
 124                 web->input.content_length = strtoul(&line[16], NULL, 10);
 125         } else {
 126                 struct http_header *hdr = talloc_zero(web, struct http_header);
 127                 char *colon = strchr(line, ':');
 128                 if (colon == NULL) {
 129                         http_error(web, "500 Internal Server Error", "invalidly formatted header");
 130                         return NT_STATUS_INVALID_PARAMETER;
 131                 }
 132 
 133                 hdr->name = talloc_strndup(hdr, line, colon-line);
 134                 hdr->value = talloc_strdup(hdr, colon+1);
 135                 DLIST_ADD(web->input.headers, hdr);
 136         }
 137 
 138         /* ignore all other headers for now */
 139         return NT_STATUS_OK;
 140 }
 141 
 142 /*
 143   called when a web connection becomes readable
 144 */
 145 static void websrv_recv(struct stream_connection *conn, uint16_t flags)
     /* [<][>][^][v][top][bottom][index][help] */
 146 {
 147         struct web_server_data *wdata;
 148         struct websrv_context *web = talloc_get_type(conn->private_data,
 149                                                      struct websrv_context);
 150         NTSTATUS status;
 151         uint8_t buf[1024];
 152         size_t nread;
 153         uint8_t *p;
 154         DATA_BLOB b;
 155 
 156         /* not the most efficient http parser ever, but good enough for us */
 157         status = socket_recv(conn->socket, buf, sizeof(buf), &nread);
 158         if (NT_STATUS_IS_ERR(status)) goto failed;
 159         if (!NT_STATUS_IS_OK(status)) return;
 160 
 161         if (!data_blob_append(web, &web->input.partial, buf, nread))
 162                 goto failed;
 163 
 164         /* parse any lines that are available */
 165         b = web->input.partial;
 166         while (!web->input.end_of_headers &&
 167                (p=(uint8_t *)memchr(b.data, '\n', b.length))) {
 168                 const char *line = (const char *)b.data;
 169                 *p = 0;
 170                 if (p != b.data && p[-1] == '\r') {
 171                         p[-1] = 0;
 172                 }
 173                 status = http_parse_header(web, line);
 174                 if (!NT_STATUS_IS_OK(status)) return;
 175                 b.length -= (p - b.data) + 1;
 176                 b.data = p+1;
 177         }
 178 
 179         /* keep any remaining bytes in web->input.partial */
 180         if (b.length == 0) {
 181                 b.data = NULL;
 182         }
 183         b = data_blob_talloc(web, b.data, b.length);
 184         data_blob_free(&web->input.partial);
 185         web->input.partial = b;
 186 
 187         /* we finish when we have both the full headers (terminated by
 188            a blank line) and any post data, as indicated by the
 189            content_length */
 190         if (web->input.end_of_headers &&
 191             web->input.partial.length >= web->input.content_length) {
 192                 if (web->input.partial.length > web->input.content_length) {
 193                         web->input.partial.data[web->input.content_length] = 0;
 194                 }
 195                 EVENT_FD_NOT_READABLE(web->conn->event.fde);
 196 
 197                 /* the reference/unlink code here is quite subtle. It
 198                  is needed because the rendering of the web-pages, and
 199                  in particular the esp/ejs backend, is semi-async.  So
 200                  we could well end up in the connection timeout code
 201                  while inside http_process_input(), but we must not
 202                  destroy the stack variables being used by that
 203                  rendering process when we handle the timeout. */
 204                 if (!talloc_reference(web->task, web)) goto failed;
 205                 wdata = talloc_get_type(web->task->private_data, struct web_server_data);
 206                 if (wdata == NULL) goto failed;
 207                 wdata->http_process_input(wdata, web);
 208                 talloc_unlink(web->task, web);
 209         }
 210         return;
 211 
 212 failed:
 213         stream_terminate_connection(conn, "websrv_recv: failed");
 214 }
 215 
 216 
 217 
 218 /*
 219   called when a web connection becomes writable
 220 */
 221 static void websrv_send(struct stream_connection *conn, uint16_t flags)
     /* [<][>][^][v][top][bottom][index][help] */
 222 {
 223         struct websrv_context *web = talloc_get_type(conn->private_data,
 224                                                      struct websrv_context);
 225         NTSTATUS status;
 226         size_t nsent;
 227         DATA_BLOB b;
 228 
 229         b = web->output.content;
 230         b.data += web->output.nsent;
 231         b.length -= web->output.nsent;
 232 
 233         status = socket_send(conn->socket, &b, &nsent);
 234         if (NT_STATUS_IS_ERR(status)) {
 235                 stream_terminate_connection(web->conn, "socket_send: failed");
 236                 return;
 237         }
 238         if (!NT_STATUS_IS_OK(status)) {
 239                 return;
 240         }
 241 
 242         web->output.nsent += nsent;
 243 
 244         if (web->output.content.length == web->output.nsent) {
 245                 stream_terminate_connection(web->conn, "websrv_send: finished sending");
 246         }
 247 }
 248 
 249 /*
 250   establish a new connection to the web server
 251 */
 252 static void websrv_accept(struct stream_connection *conn)
     /* [<][>][^][v][top][bottom][index][help] */
 253 {
 254         struct task_server *task = talloc_get_type(conn->private_data, struct task_server);
 255         struct web_server_data *wdata = talloc_get_type(task->private_data, struct web_server_data);
 256         struct websrv_context *web;
 257         struct socket_context *tls_socket;
 258 
 259         web = talloc_zero(conn, struct websrv_context);
 260         if (web == NULL) goto failed;
 261 
 262         web->task = task;
 263         web->conn = conn;
 264         conn->private_data = web;
 265         talloc_set_destructor(web, websrv_destructor);
 266 
 267         event_add_timed(conn->event.ctx, web, 
 268                         timeval_current_ofs(HTTP_TIMEOUT, 0),
 269                         websrv_timeout, web);
 270 
 271         /* Overwrite the socket with a (possibly) TLS socket */
 272         tls_socket = tls_init_server(wdata->tls_params, conn->socket, 
 273                                      conn->event.fde, "GPHO");
 274         /* We might not have TLS, or it might not have initilised */
 275         if (tls_socket) {
 276                 talloc_unlink(conn, conn->socket);
 277                 talloc_steal(conn, tls_socket);
 278                 conn->socket = tls_socket;
 279         } else {
 280                 DEBUG(3, ("TLS not available for web_server connections\n"));
 281         }
 282 
 283         return;
 284 
 285 failed:
 286         talloc_free(conn);
 287 }
 288 
 289 
 290 static const struct stream_server_ops web_stream_ops = {
 291         .name                   = "web",
 292         .accept_connection      = websrv_accept,
 293         .recv_handler           = websrv_recv,
 294         .send_handler           = websrv_send,
 295 };
 296 
 297 /*
 298   startup the web server task
 299 */
 300 static void websrv_task_init(struct task_server *task)
     /* [<][>][^][v][top][bottom][index][help] */
 301 {
 302         NTSTATUS status;
 303         uint16_t port = lp_web_port(task->lp_ctx);
 304         const struct model_ops *model_ops;
 305         struct web_server_data *wdata;
 306 
 307         task_server_set_title(task, "task[websrv]");
 308 
 309         /* run the web server as a single process */
 310         model_ops = process_model_startup(task->event_ctx, "single");
 311         if (!model_ops) goto failed;
 312 
 313         if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
 314                 int num_interfaces;
 315                 int i;
 316                 struct interface *ifaces;
 317 
 318                 load_interfaces(NULL, lp_interfaces(task->lp_ctx), &ifaces);
 319 
 320                 num_interfaces = iface_count(ifaces);
 321                 for(i = 0; i < num_interfaces; i++) {
 322                         const char *address = iface_n_ip(ifaces, i);
 323                         status = stream_setup_socket(task->event_ctx, 
 324                                                      task->lp_ctx, model_ops, 
 325                                                      &web_stream_ops, 
 326                                                      "ipv4", address, 
 327                                                      &port, lp_socket_options(task->lp_ctx), 
 328                                                      task);
 329                         if (!NT_STATUS_IS_OK(status)) goto failed;
 330                 }
 331 
 332                 talloc_free(ifaces);
 333         } else {
 334                 status = stream_setup_socket(task->event_ctx, task->lp_ctx,
 335                                              model_ops, &web_stream_ops, 
 336                                              "ipv4", lp_socket_address(task->lp_ctx), 
 337                                              &port, lp_socket_options(task->lp_ctx), task);
 338                 if (!NT_STATUS_IS_OK(status)) goto failed;
 339         }
 340 
 341         /* startup the esp processor - unfortunately we can't do this
 342            per connection as that wouldn't allow for session variables */
 343         wdata = talloc_zero(task, struct web_server_data);
 344         if (wdata == NULL)goto failed;
 345 
 346         task->private_data = wdata;
 347         
 348         wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
 349         if (wdata->tls_params == NULL) goto failed;
 350 
 351         if (!wsgi_initialize(wdata)) goto failed;
 352 
 353         return;
 354 
 355 failed:
 356         task_server_terminate(task, "websrv_task_init: failed to startup web server task");
 357 }
 358 
 359 
 360 /* called at smbd startup - register ourselves as a server service */
 361 NTSTATUS server_service_web_init(void)
     /* [<][>][^][v][top][bottom][index][help] */
 362 {
 363         return register_server_service("web", websrv_task_init);
 364 }

/* [<][>][^][v][top][bottom][index][help] */