selftest: vfs.fruit: add xattr_tdb where possible
[Samba.git] / source4 / web_server / web_server.c
blobc3516890eb72b6cfca4617c3349994b92242b10f
1 /*
2 Unix SMB/CIFS implementation.
4 web server startup
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Jelmer Vernooij 2008
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.
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.
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/>.
23 #include "includes.h"
24 #include "web_server/web_server.h"
25 #include "../lib/util/dlinklist.h"
26 #include "lib/tls/tls.h"
27 #include "lib/events/events.h"
28 #include "lib/socket/netif.h"
29 #include "param/param.h"
31 NTSTATUS server_service_web_init(TALLOC_CTX *);
33 /* don't allow connections to hang around forever */
34 #define HTTP_TIMEOUT 120
37 destroy a web connection
39 static int websrv_destructor(struct websrv_context *web)
41 return 0;
45 called when a connection times out. This prevents a stuck connection
46 from hanging around forever
48 static void websrv_timeout(struct tevent_context *event_context,
49 struct tevent_timer *te,
50 struct timeval t, void *private_data)
52 struct websrv_context *web = talloc_get_type_abort(private_data, struct websrv_context);
53 struct stream_connection *conn = web->conn;
54 web->conn = NULL;
55 /* TODO: send a message to any running esp context on this connection
56 to stop running */
57 stream_terminate_connection(conn, "websrv_timeout: timed out");
61 setup for a raw http level error
63 void http_error(struct websrv_context *web, const char *status, const char *info)
65 char *s;
66 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",
67 status, status, info);
68 if (s == NULL) {
69 stream_terminate_connection(web->conn, "http_error: out of memory");
70 return;
72 websrv_output_headers(web, status, NULL);
73 websrv_output(web, s, strlen(s));
76 void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
78 char *s;
79 DATA_BLOB b;
80 struct http_header *hdr;
82 s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
83 if (s == NULL) return;
84 for (hdr = headers; hdr; hdr = hdr->next) {
85 s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
88 s = talloc_asprintf_append_buffer(s, "\r\n");
90 b = web->output.content;
91 web->output.content = data_blob_string_const(s);
92 websrv_output(web, b.data, b.length);
93 data_blob_free(&b);
96 void websrv_output(struct websrv_context *web, const void *data, size_t length)
98 data_blob_append(web, &web->output.content, data, length);
99 TEVENT_FD_NOT_READABLE(web->conn->event.fde);
100 TEVENT_FD_WRITEABLE(web->conn->event.fde);
101 web->output.output_pending = true;
106 parse one line of header input
108 NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
110 if (line[0] == 0) {
111 web->input.end_of_headers = true;
112 } else if (strncasecmp(line,"GET ", 4)==0) {
113 web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
114 } else if (strncasecmp(line,"POST ", 5)==0) {
115 web->input.post_request = true;
116 web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
117 } else if (strchr(line, ':') == NULL) {
118 http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
119 return NT_STATUS_INVALID_PARAMETER;
120 } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
121 web->input.content_length = strtoul(&line[16], NULL, 10);
122 } else {
123 struct http_header *hdr = talloc_zero(web, struct http_header);
124 char *colon = strchr(line, ':');
125 if (colon == NULL) {
126 http_error(web, "500 Internal Server Error", "invalidly formatted header");
127 return NT_STATUS_INVALID_PARAMETER;
130 hdr->name = talloc_strndup(hdr, line, colon-line);
131 hdr->value = talloc_strdup(hdr, colon+1);
132 DLIST_ADD(web->input.headers, hdr);
135 /* ignore all other headers for now */
136 return NT_STATUS_OK;
140 called when a web connection becomes readable
142 static void websrv_recv(struct stream_connection *conn, uint16_t flags)
144 struct web_server_data *wdata;
145 struct websrv_context *web = talloc_get_type_abort(conn->private_data,
146 struct websrv_context);
147 NTSTATUS status;
148 uint8_t buf[1024];
149 size_t nread;
150 uint8_t *p;
151 DATA_BLOB b;
153 /* not the most efficient http parser ever, but good enough for us */
154 status = socket_recv(conn->socket, buf, sizeof(buf), &nread);
155 if (NT_STATUS_IS_ERR(status)) goto failed;
156 if (!NT_STATUS_IS_OK(status)) return;
158 if (!data_blob_append(web, &web->input.partial, buf, nread))
159 goto failed;
161 /* parse any lines that are available */
162 b = web->input.partial;
163 while (!web->input.end_of_headers &&
164 (p=(uint8_t *)memchr(b.data, '\n', b.length))) {
165 const char *line = (const char *)b.data;
166 *p = 0;
167 if (p != b.data && p[-1] == '\r') {
168 p[-1] = 0;
170 status = http_parse_header(web, line);
171 if (!NT_STATUS_IS_OK(status)) return;
172 b.length -= (p - b.data) + 1;
173 b.data = p+1;
176 /* keep any remaining bytes in web->input.partial */
177 if (b.length == 0) {
178 b.data = NULL;
180 b = data_blob_talloc(web, b.data, b.length);
181 data_blob_free(&web->input.partial);
182 web->input.partial = b;
184 /* we finish when we have both the full headers (terminated by
185 a blank line) and any post data, as indicated by the
186 content_length */
187 if (web->input.end_of_headers &&
188 web->input.partial.length >= web->input.content_length) {
189 if (web->input.partial.length > web->input.content_length) {
190 web->input.partial.data[web->input.content_length] = 0;
192 TEVENT_FD_NOT_READABLE(web->conn->event.fde);
194 /* the reference/unlink code here is quite subtle. It
195 is needed because the rendering of the web-pages, and
196 in particular the esp/ejs backend, is semi-async. So
197 we could well end up in the connection timeout code
198 while inside http_process_input(), but we must not
199 destroy the stack variables being used by that
200 rendering process when we handle the timeout. */
201 if (!talloc_reference(web->task, web)) goto failed;
202 wdata = talloc_get_type_abort(web->task->private_data, struct web_server_data);
203 if (wdata == NULL) goto failed;
204 wdata->http_process_input(wdata, web);
205 talloc_unlink(web->task, web);
207 return;
209 failed:
210 stream_terminate_connection(conn, "websrv_recv: failed");
216 called when a web connection becomes writable
218 static void websrv_send(struct stream_connection *conn, uint16_t flags)
220 struct websrv_context *web = talloc_get_type_abort(conn->private_data,
221 struct websrv_context);
222 NTSTATUS status;
223 size_t nsent;
224 DATA_BLOB b;
226 b = web->output.content;
227 b.data += web->output.nsent;
228 b.length -= web->output.nsent;
230 status = socket_send(conn->socket, &b, &nsent);
231 if (NT_STATUS_IS_ERR(status)) {
232 stream_terminate_connection(web->conn, "socket_send: failed");
233 return;
235 if (!NT_STATUS_IS_OK(status)) {
236 return;
239 web->output.nsent += nsent;
241 if (web->output.content.length == web->output.nsent) {
242 stream_terminate_connection(web->conn, "websrv_send: finished sending");
247 establish a new connection to the web server
249 static void websrv_accept(struct stream_connection *conn)
251 struct task_server *task = talloc_get_type_abort(conn->private_data, struct task_server);
252 struct web_server_data *wdata = talloc_get_type_abort(task->private_data, struct web_server_data);
253 struct websrv_context *web;
254 struct socket_context *tls_socket;
256 web = talloc_zero(conn, struct websrv_context);
257 if (web == NULL) goto failed;
259 web->task = wdata->task;
260 web->conn = conn;
261 conn->private_data = web;
262 talloc_set_destructor(web, websrv_destructor);
264 tevent_add_timer(conn->event.ctx, web,
265 timeval_current_ofs(HTTP_TIMEOUT, 0),
266 websrv_timeout, web);
268 /* Overwrite the socket with a (possibly) TLS socket */
269 tls_socket = tls_init_server(wdata->tls_params, conn->socket,
270 conn->event.fde, "GPHO");
271 /* We might not have TLS, or it might not have initilised */
272 if (tls_socket) {
273 talloc_unlink(conn, conn->socket);
274 talloc_steal(conn, tls_socket);
275 conn->socket = tls_socket;
276 } else {
277 DEBUG(3, ("TLS not available for web_server connections\n"));
280 return;
282 failed:
283 talloc_free(conn);
287 static const struct stream_server_ops web_stream_ops = {
288 .name = "web",
289 .accept_connection = websrv_accept,
290 .recv_handler = websrv_recv,
291 .send_handler = websrv_send,
295 startup the web server task
297 static void websrv_task_init(struct task_server *task)
299 NTSTATUS status;
300 uint16_t port = lpcfg_web_port(task->lp_ctx);
301 struct web_server_data *wdata;
303 task_server_set_title(task, "task[websrv]");
305 /* startup the Python processor - unfortunately we can't do this
306 per connection as that wouldn't allow for session variables */
307 wdata = talloc_zero(task, struct web_server_data);
308 if (wdata == NULL) goto failed;
310 wdata->task = task;
311 task->private_data = wdata;
313 if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) {
314 int num_interfaces;
315 int i;
316 struct interface *ifaces;
318 load_interface_list(NULL, task->lp_ctx, &ifaces);
320 num_interfaces = iface_list_count(ifaces);
321 for(i = 0; i < num_interfaces; i++) {
322 const char *address = iface_list_n_ip(ifaces, i);
323 status = stream_setup_socket(task,
324 task->event_ctx,
325 task->lp_ctx,
326 task->model_ops,
327 &web_stream_ops,
328 "ip", address,
329 &port,
330 lpcfg_socket_options(task->lp_ctx),
331 task,
332 task->process_context);
333 if (!NT_STATUS_IS_OK(status)) goto failed;
336 talloc_free(ifaces);
337 } else {
338 char **wcard;
339 int i;
340 wcard = iface_list_wildcard(task);
341 if (wcard == NULL) {
342 DEBUG(0,("No wildcard addresses available\n"));
343 goto failed;
345 for (i=0; wcard[i]; i++) {
346 status = stream_setup_socket(task, task->event_ctx,
347 task->lp_ctx,
348 task->model_ops,
349 &web_stream_ops,
350 "ip", wcard[i],
351 &port, lpcfg_socket_options(task->lp_ctx),
352 wdata,
353 task->process_context);
354 if (!NT_STATUS_IS_OK(status)) goto failed;
356 talloc_free(wcard);
359 wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
360 if (wdata->tls_params == NULL) goto failed;
362 if (!wsgi_initialize(wdata)) goto failed;
365 return;
367 failed:
368 task_server_terminate(task, "websrv_task_init: failed to startup web server task", true);
372 /* called at smbd startup - register ourselves as a server service */
373 NTSTATUS server_service_web_init(TALLOC_CTX *ctx)
375 struct service_details details = {
376 .inhibit_fork_on_accept = true,
377 .inhibit_pre_fork = true
379 return register_server_service(ctx, "web", websrv_task_init, &details);