source: rtems/cpukit/httpd/default.c @ 58ce601

4.104.114.84.95
Last change on this file since 58ce601 was ee3afa2, checked in by Joel Sherrill <joel.sherrill@…>, on 04/11/03 at 14:46:55

2002-04-10 Mike Siers <mikes@…>

  • rtems_webserver/NOTES, rtems_webserver/asp.c, rtems_webserver/balloc.c, rtems_webserver/default.c, rtems_webserver/ej.h, rtems_webserver/ejIntrn.h, rtems_webserver/ejlex.c, rtems_webserver/ejparse.c, rtems_webserver/emfdb.c, rtems_webserver/emfdb.h, rtems_webserver/form.c, rtems_webserver/h.c, rtems_webserver/handler.c, rtems_webserver/license.txt, rtems_webserver/md5.h, rtems_webserver/md5c.c, rtems_webserver/mime.c, rtems_webserver/misc.c, rtems_webserver/ringq.c, rtems_webserver/rom.c, rtems_webserver/security.c, rtems_webserver/sock.c, rtems_webserver/sym.c, rtems_webserver/uemf.c, rtems_webserver/uemf.h, rtems_webserver/um.c, rtems_webserver/um.h, rtems_webserver/url.c, rtems_webserver/value.c, rtems_webserver/wbase64.c, rtems_webserver/webcomp.c, rtems_webserver/webpage.c, rtems_webserver/webrom.c, rtems_webserver/webs.c, rtems_webserver/webs.h, rtems_webserver/websuemf.c, rtems_webserver/wsIntrn.h: Update to GoAhead? Webserver 2.1.4. The following URL is the release notes from GoAhead?.

http://data.goahead.com/Software/Webserver/2.1.4/release.htm

I have only done a minimal amount of testing (i.e. the network
demo program works fine). Please try this out and let me know
if it works. The patch needs to be applied on the
c/src/libnetworking/rtems_webserver directory.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 * default.c -- Default URL handler. Includes support for ASP.
3 *
4 * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 *
8 * $Id$
9 */
10
11/******************************** Description *********************************/
12
13/*
14 *      This module provides default URL handling and Active Server Page support.
15 *
16 *      In many cases we don't check the return code of calls to websWrite as
17 *      it is easier, smaller and non-fatal to continue even when the requesting
18 *      browser has gone away.
19 */
20
21/********************************* Includes ***********************************/
22
23#include        "wsIntrn.h"
24
25/*********************************** Locals ***********************************/
26
27static char_t   *websDefaultPage;                       /* Default page name */
28static char_t   *websDefaultDir;                        /* Default Web page directory */
29
30/**************************** Forward Declarations ****************************/
31
32static void websDefaultWriteEvent(webs_t wp);
33
34/*********************************** Code *************************************/
35/*
36 *      Process a default URL request. This will validate the URL and handle "../"
37 *      and will provide support for Active Server Pages. As the handler is the
38 *      last handler to run, it always indicates that it has handled the URL
39 *      by returning 1.
40 */
41
42int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
43                                                char_t *url, char_t *path, char_t *query)
44{
45        websStatType    sbuf;
46        char_t                  *lpath, *tmp, *date;
47        int                             bytes, flags, nchars;
48
49        a_assert(websValid(wp));
50        a_assert(url && *url);
51        a_assert(path);
52        a_assert(query);
53
54/*
55 *      Validate the URL and ensure that ".."s don't give access to unwanted files
56 */
57        flags = websGetRequestFlags(wp);
58
59        if (websValidateUrl(wp, path) < 0) {
60                websError(wp, 500, T("Invalid URL %s"), url);
61                return 1;
62        }
63        lpath = websGetRequestLpath(wp);
64        nchars = gstrlen(lpath) - 1;
65        if (lpath[nchars] == '/' || lpath[nchars] == '\\') {
66                lpath[nchars] = '\0';
67        }
68
69/*
70 *      If the file is a directory, redirect using the nominated default page
71 */
72        if (websPageIsDirectory(lpath)) {
73                nchars = gstrlen(path);
74                if (path[nchars-1] == '/' || path[nchars-1] == '\\') {
75                        path[--nchars] = '\0';
76                }
77                nchars += gstrlen(websDefaultPage) + 2;
78                fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage);
79                websRedirect(wp, tmp);
80                bfreeSafe(B_L, tmp);
81                return 1;
82        }
83
84/*
85 *      Open the document. Stat for later use.
86 */
87        if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY,
88                0666) < 0) {
89                websError(wp, 400, T("Cannot open URL <b>%s</b>"), url);
90                return 1;
91        }
92
93        if (websPageStat(wp, lpath, path, &sbuf) < 0) {
94                websError(wp, 400, T("Cannot stat page for URL <b>%s</b>"), url);
95                return 1;
96        }
97
98/*
99 *      If the page has not been modified since the user last received it and it
100 *      is not dynamically generated each time (ASP), then optimize request by
101 *      sending a 304 Use local copy response
102 */
103        websStats.localHits++;
104#ifdef WEBS_IF_MODIFIED_SUPPORT
105        if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) {
106                if (sbuf.mtime <= wp->since) {
107                        websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n"));
108
109/*
110 *                      by license terms the following line of code must
111 *                      not be modified.
112 */
113                        websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
114
115                        if (flags & WEBS_KEEP_ALIVE) {
116                                websWrite(wp, T("Connection: keep-alive\r\n"));
117                        }
118                        websWrite(wp, T("\r\n"));
119                        websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE);
120                        websDone(wp, 304);
121                        return 1;
122                }
123        }
124#endif
125
126/*
127 *      Output the normal HTTP response header
128 */
129        if ((date = websGetDateString(NULL)) != NULL) {
130                websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date);
131
132/*
133 *              By license terms the following line of code must not be modified.
134 */
135                websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
136                bfree(B_L, date);
137        }
138        flags |= WEBS_HEADER_DONE;
139
140/*
141 *      If this is an ASP request, ensure the remote browser doesn't cache it.
142 *      Send back both HTTP/1.0 and HTTP/1.1 cache control directives
143 */
144        if (flags & WEBS_ASP) {
145                bytes = 0;
146                websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
147
148        } else {
149                if ((date = websGetDateString(&sbuf)) != NULL) {
150                        websWrite(wp, T("Last-modified: %s\r\n"), date);
151                        bfree(B_L, date);
152                }
153                bytes = sbuf.size;
154        }
155
156        if (bytes) {
157                websWrite(wp, T("Content-length: %d\r\n"), bytes);
158                websSetRequestBytes(wp, bytes);
159        }
160        websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp));
161
162        if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) {
163                websWrite(wp, T("Connection: keep-alive\r\n"));
164        }
165        websWrite(wp, T("\r\n"));
166
167/*
168 *      All done if the browser did a HEAD request
169 */
170        if (flags & WEBS_HEAD_REQUEST) {
171                websDone(wp, 200);
172                return 1;
173        }
174
175/*
176 *      Evaluate ASP requests
177 */
178        if (flags & WEBS_ASP) {
179                if (websAspRequest(wp, lpath) < 0) {
180                        return 1;
181                }
182                websDone(wp, 200);
183                return 1;
184        }
185
186#ifdef WEBS_SSL_SUPPORT
187        if (wp->flags & WEBS_SECURE) {
188                websDefaultWriteEvent(wp);
189        } else {
190                websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);
191        }
192#else
193/*
194 *      For normal web documents, return the data via background write
195 */
196        websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent);
197#endif
198        return 1;
199}
200
201/******************************************************************************/
202/*
203 *      Validate the URL path and process ".." path segments. Return -1 if the URL
204 *      is bad.
205 */
206
207int websValidateUrl(webs_t wp, char_t *path)
208{
209        char_t  *parts[64];                                     /* Array of ptr's to URL parts */
210        char_t  *token, *dir, *lpath;
211        int             i, len, npart;
212
213        a_assert(websValid(wp));
214        a_assert(path);
215
216        dir = websGetRequestDir(wp);
217        if (dir == NULL || *dir == '\0') {
218                return -1;
219        }
220
221/*
222 *      Copy the string so we don't destroy the original
223 */
224        path = bstrdup(B_L, path);
225        websDecodeUrl(path, path, gstrlen(path));
226
227        len = npart = 0;
228        parts[0] = NULL;
229
230   /*
231    * 22 Jul 02 -- there were reports that a directory traversal exploit was
232    * possible in the WebServer running under Windows if directory paths
233    * outside the server's specified root web were given by URL-encoding the
234    * backslash character, like:
235    *
236    *  GoAhead is vulnerable to a directory traversal bug. A request such as
237    * 
238    *  GoAhead-server/../../../../../../../ results in an error message
239    *  'Cannot open URL'.
240
241    *  However, by encoding the '/' character, it is possible to break out of
242    *  the
243    *  web root and read arbitrary files from the server.
244    *  Hence a request like:
245    *
246    *  GoAhead-server/..%5C..%5C..%5C..%5C..%5C..%5C/winnt/win.ini returns the
247    *  contents of the win.ini file.
248    * (Note that the description uses forward slashes (0x2F), but the example
249    * uses backslashes (0x5C). In my tests, forward slashes are correctly
250    * trapped, but backslashes are not. The code below substitutes forward
251    * slashes for backslashes before attempting to validate that there are no
252    * unauthorized paths being accessed.
253    */
254   token = gstrchr(path, '\\');
255   while (token != NULL)
256   {
257      *token = '/';
258      token = gstrchr(token, '\\');
259   }
260   
261        token = gstrtok(path, T("/"));
262
263/*
264 *      Look at each directory segment and process "." and ".." segments
265 *      Don't allow the browser to pop outside the root web.
266 */
267        while (token != NULL) {
268                if (gstrcmp(token, T("..")) == 0) {
269                        if (npart > 0) {
270                                npart--;
271                        }
272
273                } else if (gstrcmp(token, T(".")) != 0) {
274                        parts[npart] = token;
275                        len += gstrlen(token) + 1;
276                        npart++;
277                }
278                token = gstrtok(NULL, T("/"));
279        }
280
281/*
282 *      Create local path for document. Need extra space all "/" and null.
283 */
284        if (npart || (gstrcmp(path, T("/")) == 0) || (path[0] == '\0')) {
285                lpath = balloc(B_L, (gstrlen(dir) + 1 + len + 1) * sizeof(char_t));
286                gstrcpy(lpath, dir);
287
288                for (i = 0; i < npart; i++) {
289                        gstrcat(lpath, T("/"));
290                        gstrcat(lpath, parts[i]);
291                }
292                websSetRequestLpath(wp, lpath);
293                bfree(B_L, path);
294                bfree(B_L, lpath);
295
296        } else {
297                bfree(B_L, path);
298                return -1;
299        }
300        return 0;
301}
302
303/******************************************************************************/
304/*
305 *      Do output back to the browser in the background. This is a socket
306 *      write handler.
307 */
308
309static void websDefaultWriteEvent(webs_t wp)
310{
311        int             len, wrote, flags, bytes, written;
312        char    *buf;
313
314        a_assert(websValid(wp));
315
316        flags = websGetRequestFlags(wp);
317
318        websSetTimeMark(wp);
319
320        wrote = bytes = 0;
321        written = websGetRequestWritten(wp);
322
323/*
324 *      We only do this for non-ASP documents
325 */
326        if ( !(flags & WEBS_ASP)) {
327                bytes = websGetRequestBytes(wp);
328/*
329 *              Note: websWriteDataNonBlock may return less than we wanted. It will
330 *              return -1 on a socket error
331 */
332                if ((buf = balloc(B_L, PAGE_READ_BUFSIZE)) == NULL) {
333                        websError(wp, 200, T("Can't get memory"));
334                } else {
335                        while ((len = websPageReadData(wp, buf, PAGE_READ_BUFSIZE)) > 0) {
336                                if ((wrote = websWriteDataNonBlock(wp, buf, len)) < 0) {
337                                        break;
338                                }
339                                written += wrote;
340                                if (wrote != len) {
341                                        websPageSeek(wp, - (len - wrote));
342                                        break;
343                                }
344                        }
345/*
346 *                      Safety. If we are at EOF, we must be done
347 */
348                        if (len == 0) {
349                                a_assert(written >= bytes);
350                                written = bytes;
351                        }
352                        bfree(B_L, buf);
353                }
354        }
355
356/*
357 *      We're done if an error, or all bytes output
358 */
359        websSetRequestWritten(wp, written);
360        if (wrote < 0 || written >= bytes) {
361                websDone(wp, 200);
362        }
363}
364
365/******************************************************************************/
366/*
367 *      Closing down. Free resources.
368 */
369
370void websDefaultClose()
371{
372        if (websDefaultPage) {
373                bfree(B_L, websDefaultPage);
374                websDefaultPage = NULL;
375        }
376        if (websDefaultDir) {
377                bfree(B_L, websDefaultDir);
378                websDefaultDir = NULL;
379        }
380}
381
382/******************************************************************************/
383/*
384 *      Get the default page for URL requests ending in "/"
385 */
386
387char_t *websGetDefaultPage()
388{
389        return websDefaultPage;
390}
391
392/******************************************************************************/
393/*
394 *      Get the default web directory
395 */
396
397char_t *websGetDefaultDir()
398{
399        return websDefaultDir;
400}
401
402/******************************************************************************/
403/*
404 *      Set the default page for URL requests ending in "/"
405 */
406
407void websSetDefaultPage(char_t *page)
408{
409        a_assert(page && *page);
410
411        if (websDefaultPage) {
412                bfree(B_L, websDefaultPage);
413        }
414        websDefaultPage = bstrdup(B_L, page);
415}
416
417/******************************************************************************/
418/*
419 *      Set the default web directory
420 */
421
422void websSetDefaultDir(char_t *dir)
423{
424        a_assert(dir && *dir);
425        if (websDefaultDir) {
426                bfree(B_L, websDefaultDir);
427        }
428        websDefaultDir = bstrdup(B_L, dir);
429}
430
431/******************************************************************************/
432
Note: See TracBrowser for help on using the repository browser.