2 Unix SMB/CIFS implementation.
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/>.
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"
36 /* don't allow connections to hang around forever */
37 #define HTTP_TIMEOUT 120
40 destroy a web connection
42 static int websrv_destructor(struct websrv_context
*web
)
48 called when a connection times out. This prevents a stuck connection
49 from hanging around forever
51 static void websrv_timeout(struct tevent_context
*event_context
,
52 struct tevent_timer
*te
,
53 struct timeval t
, void *private_data
)
55 struct websrv_context
*web
= talloc_get_type(private_data
, struct websrv_context
);
56 struct stream_connection
*conn
= web
->conn
;
58 /* TODO: send a message to any running esp context on this connection
60 stream_terminate_connection(conn
, "websrv_timeout: timed out");
64 setup for a raw http level error
66 void http_error(struct websrv_context
*web
, const char *status
, const char *info
)
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
);
72 stream_terminate_connection(web
->conn
, "http_error: out of memory");
75 websrv_output_headers(web
, status
, NULL
);
76 websrv_output(web
, s
, strlen(s
));
79 void websrv_output_headers(struct websrv_context
*web
, const char *status
, struct http_header
*headers
)
83 struct http_header
*hdr
;
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
);
91 s
= talloc_asprintf_append_buffer(s
, "\r\n");
93 b
= web
->output
.content
;
94 web
->output
.content
= data_blob_string_const(s
);
95 websrv_output(web
, b
.data
, b
.length
);
99 void websrv_output(struct websrv_context
*web
, void *data
, size_t length
)
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;
109 parse one line of header input
111 NTSTATUS
http_parse_header(struct websrv_context
*web
, const char *line
)
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);
126 struct http_header
*hdr
= talloc_zero(web
, struct http_header
);
127 char *colon
= strchr(line
, ':');
129 http_error(web
, "500 Internal Server Error", "invalidly formatted header");
130 return NT_STATUS_INVALID_PARAMETER
;
133 hdr
->name
= talloc_strndup(hdr
, line
, colon
-line
);
134 hdr
->value
= talloc_strdup(hdr
, colon
+1);
135 DLIST_ADD(web
->input
.headers
, hdr
);
138 /* ignore all other headers for now */
143 called when a web connection becomes readable
145 static void websrv_recv(struct stream_connection
*conn
, uint16_t flags
)
147 struct web_server_data
*wdata
;
148 struct websrv_context
*web
= talloc_get_type(conn
->private_data
,
149 struct websrv_context
);
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;
161 if (!data_blob_append(web
, &web
->input
.partial
, buf
, nread
))
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
;
170 if (p
!= b
.data
&& p
[-1] == '\r') {
173 status
= http_parse_header(web
, line
);
174 if (!NT_STATUS_IS_OK(status
)) return;
175 b
.length
-= (p
- b
.data
) + 1;
179 /* keep any remaining bytes in web->input.partial */
183 b
= data_blob_talloc(web
, b
.data
, b
.length
);
184 data_blob_free(&web
->input
.partial
);
185 web
->input
.partial
= b
;
187 /* we finish when we have both the full headers (terminated by
188 a blank line) and any post data, as indicated by the
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;
195 EVENT_FD_NOT_READABLE(web
->conn
->event
.fde
);
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
);
213 stream_terminate_connection(conn
, "websrv_recv: failed");
219 called when a web connection becomes writable
221 static void websrv_send(struct stream_connection
*conn
, uint16_t flags
)
223 struct websrv_context
*web
= talloc_get_type(conn
->private_data
,
224 struct websrv_context
);
229 b
= web
->output
.content
;
230 b
.data
+= web
->output
.nsent
;
231 b
.length
-= web
->output
.nsent
;
233 status
= socket_send(conn
->socket
, &b
, &nsent
);
234 if (NT_STATUS_IS_ERR(status
)) {
235 stream_terminate_connection(web
->conn
, "socket_send: failed");
238 if (!NT_STATUS_IS_OK(status
)) {
242 web
->output
.nsent
+= nsent
;
244 if (web
->output
.content
.length
== web
->output
.nsent
) {
245 stream_terminate_connection(web
->conn
, "websrv_send: finished sending");
250 establish a new connection to the web server
252 static void websrv_accept(struct stream_connection
*conn
)
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
;
259 web
= talloc_zero(conn
, struct websrv_context
);
260 if (web
== NULL
) goto failed
;
264 conn
->private_data
= web
;
265 talloc_set_destructor(web
, websrv_destructor
);
267 event_add_timed(conn
->event
.ctx
, web
,
268 timeval_current_ofs(HTTP_TIMEOUT
, 0),
269 websrv_timeout
, web
);
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 */
276 talloc_unlink(conn
, conn
->socket
);
277 talloc_steal(conn
, tls_socket
);
278 conn
->socket
= tls_socket
;
280 DEBUG(3, ("TLS not available for web_server connections\n"));
290 static const struct stream_server_ops web_stream_ops
= {
292 .accept_connection
= websrv_accept
,
293 .recv_handler
= websrv_recv
,
294 .send_handler
= websrv_send
,
298 startup the web server task
300 static void websrv_task_init(struct task_server
*task
)
303 uint16_t port
= lp_web_port(task
->lp_ctx
);
304 const struct model_ops
*model_ops
;
305 struct web_server_data
*wdata
;
307 task_server_set_title(task
, "task[websrv]");
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
;
313 if (lp_interfaces(task
->lp_ctx
) && lp_bind_interfaces_only(task
->lp_ctx
)) {
316 struct interface
*ifaces
;
318 load_interfaces(NULL
, lp_interfaces(task
->lp_ctx
), &ifaces
);
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
,
327 &port
, lp_socket_options(task
->lp_ctx
),
329 if (!NT_STATUS_IS_OK(status
)) goto failed
;
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
;
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
;
346 task
->private_data
= wdata
;
348 wdata
->tls_params
= tls_initialise(wdata
, task
->lp_ctx
);
349 if (wdata
->tls_params
== NULL
) goto failed
;
351 if (!wsgi_initialize(wdata
)) goto failed
;
356 task_server_terminate(task
, "websrv_task_init: failed to startup web server task", true);
360 /* called at smbd startup - register ourselves as a server service */
361 NTSTATUS
server_service_web_init(void)
363 return register_server_service("web", websrv_task_init
);