• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/web_server/
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*/
42static int websrv_destructor(struct websrv_context *web)
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*/
51static void websrv_timeout(struct tevent_context *event_context,
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*/
66void http_error(struct websrv_context *web, const char *status, const char *info)
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
79void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
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
99void websrv_output(struct websrv_context *web, void *data, size_t length)
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*/
111NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
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*/
145static void websrv_recv(struct stream_connection *conn, uint16_t flags)
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
212failed:
213	stream_terminate_connection(conn, "websrv_recv: failed");
214}
215
216
217
218/*
219  called when a web connection becomes writable
220*/
221static void websrv_send(struct stream_connection *conn, uint16_t flags)
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*/
252static void websrv_accept(struct stream_connection *conn)
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
285failed:
286	talloc_free(conn);
287}
288
289
290static 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*/
300static void websrv_task_init(struct task_server *task)
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
355failed:
356	task_server_terminate(task, "websrv_task_init: failed to startup web server task", true);
357}
358
359
360/* called at smbd startup - register ourselves as a server service */
361NTSTATUS server_service_web_init(void)
362{
363	return register_server_service("web", websrv_task_init);
364}
365