/[hydra]/hydra/src/read.c
ViewVC logotype

Contents of /hydra/src/read.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.4 - (show annotations)
Tue Oct 1 22:38:24 2002 UTC (18 years, 11 months ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_0_6, hydra_0_0_7, hydra_0_0_5
Changes since 1.3: +1 -2 lines
File MIME type: text/plain
Several changes to allow Cookies and POST data, to work in HIC CGIs.
Check if a CGI is accessible before trying to execute it, and send not
found if it is not accesible.

1 /*
2 * Hydra, an http server
3 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4 * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
5 * Some changes Copyright (C) 1997,99 Jon Nelson <jnelson@boa.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 1, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 */
22
23 /* $Id: read.c,v 1.3 2002/09/28 16:32:37 nmav Exp $*/
24
25 #include "boa.h"
26 #include "socket.h"
27
28 /*
29 * Name: read_header
30 * Description: Reads data from a request socket. Manages the current
31 * status via a state machine. Changes status from READ_HEADER to
32 * READ_BODY or WRITE as necessary.
33 *
34 * Return values:
35 * -1: request blocked, move to blocked queue
36 * 0: request done, close it down
37 * 1: more to do, leave on ready list
38 */
39
40 int read_header(server_params * params, request * req)
41 {
42 int bytes, buf_bytes_left;
43 char *check, *buffer;
44
45 check = req->client_stream + req->parse_pos;
46 buffer = req->client_stream;
47 bytes = req->client_stream_pos;
48
49 #ifdef VERY_FASCIST_LOGGING
50 if (check < (buffer + bytes)) {
51 buffer[bytes] = '\0';
52 log_error_time();
53 fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n",
54 __FILE__, __LINE__, check);
55 }
56 #endif
57 while (check < (buffer + bytes)) {
58 switch (req->status) {
59 case READ_HEADER:
60 if (*check == '\r') {
61 req->status = ONE_CR;
62 req->header_end = check;
63 } else if (*check == '\n') {
64 req->status = ONE_LF;
65 req->header_end = check;
66 }
67 break;
68
69 case ONE_CR:
70 if (*check == '\n')
71 req->status = ONE_LF;
72 else if (*check != '\r')
73 req->status = READ_HEADER;
74 break;
75
76 case ONE_LF:
77 /* if here, we've found the end (for sure) of a header */
78 if (*check == '\r') /* could be end o headers */
79 req->status = TWO_CR;
80 else if (*check == '\n')
81 req->status = BODY_READ;
82 else
83 req->status = READ_HEADER;
84 break;
85
86 case TWO_CR:
87 if (*check == '\n')
88 req->status = BODY_READ;
89 else if (*check != '\r')
90 req->status = READ_HEADER;
91 break;
92
93 default:
94 break;
95 }
96
97 #ifdef VERY_FASCIST_LOGGING
98 log_error_time();
99 fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
100 #endif
101
102 req->parse_pos++; /* update parse position */
103 check++;
104
105 if (req->status == ONE_LF) {
106 *req->header_end = '\0';
107
108 /* terminate string that begins at req->header_line */
109
110 if (req->logline) {
111 if (process_option_line(req) == 0) {
112 return 0;
113 }
114 } else {
115 if (process_logline(req) == 0)
116 return 0;
117 if (req->simple)
118 return process_header_end(params, req);
119 }
120 /* set header_line to point to beginning of new header */
121 req->header_line = check;
122 } else if (req->status == BODY_READ) {
123 #ifdef VERY_FASCIST_LOGGING
124 int retval;
125 log_error_time();
126 fprintf(stderr, "%s:%d -- got to body read.\n",
127 __FILE__, __LINE__);
128 retval = process_header_end(params, req);
129 #else
130 int retval = process_header_end(params, req);
131 #endif
132 /* process_header_end inits non-POST cgi's */
133
134 if (retval && req->method == M_POST) {
135 /* for body_{read,write}, set header_line to start of data,
136 and header_end to end of data */
137 req->header_line = check;
138 req->header_end = req->client_stream + req->client_stream_pos;
139
140 req->status = BODY_WRITE;
141 /* so write it */
142 /* have to write first, or read will be confused
143 * because of the special case where the
144 * filesize is less than we have already read.
145 */
146
147 /*
148
149 As quoted from RFC1945:
150
151 A valid Content-Length is required on all HTTP/1.0 POST requests. An
152 HTTP/1.0 server should respond with a 400 (bad request) message if it
153 cannot determine the length of the request message's content.
154
155 */
156
157 if (req->content_length) {
158 int content_length;
159
160 content_length = boa_atoi(req->content_length);
161 /* Is a content-length of 0 legal? */
162 if (content_length <= 0) {
163 log_error_time();
164 fprintf(stderr,
165 "Invalid Content-Length [%s] on POST!\n",
166 req->content_length);
167 send_r_bad_request(req);
168 return 0;
169 }
170 if (single_post_limit && content_length > single_post_limit) {
171 log_error_time();
172 fprintf(stderr,
173 "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
174 content_length, single_post_limit);
175 send_r_bad_request(req);
176 return 0;
177 }
178 req->filesize = content_length;
179 req->filepos = 0;
180 if (req->header_end - req->header_line > req->filesize) {
181 req->header_end = req->header_line + req->filesize;
182 }
183 } else {
184 log_error_time();
185 fprintf(stderr, "Unknown Content-Length POST!\n");
186 send_r_bad_request(req);
187 return 0;
188 }
189 } /* either process_header_end failed or req->method != POST */
190 return retval; /* 0 - close it done, 1 - keep on ready */
191 } /* req->status == BODY_READ */
192 } /* done processing available buffer */
193
194 #ifdef VERY_FASCIST_LOGGING
195 log_error_time();
196 fprintf(stderr, "%s:%d - Done processing buffer. Status: %d\n",
197 __FILE__, __LINE__, req->status);
198 #endif
199
200 if (req->status < BODY_READ) {
201 /* only reached if request is split across more than one packet */
202
203 buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
204 if (buf_bytes_left < 1) {
205 log_error_time();
206 fputs("buffer overrun - read.c, read_header - closing\n", stderr);
207 req->status = DEAD;
208 return 0;
209 }
210
211 bytes =
212 socket_recv(req, buffer + req->client_stream_pos,
213 buf_bytes_left);
214
215 if (bytes < 0) {
216 if (bytes == BOA_E_INTR)
217 return 1;
218 if (bytes == BOA_E_AGAIN)
219 return -1;
220
221 log_error_doc(req);
222 perror("header read"); /* don't need to save errno because log_error_doc does */
223 return 0;
224 } else if (bytes == 0) {
225 /* this is an error. premature end of headers! */
226 return 0;
227 }
228
229 /* bytes is positive */
230 req->client_stream_pos += bytes;
231
232 #ifdef FASCIST_LOGGING1
233 log_error_time();
234 req->client_stream[req->client_stream_pos] = '\0';
235 fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
236 __FILE__, __LINE__, bytes,
237 #ifdef VERY_FASCIST_LOGGING2
238 req->client_stream + req->client_stream_pos - bytes);
239 #else
240 "");
241 #endif
242 #endif
243
244 return 1;
245 }
246 return 1;
247 }
248
249 /*
250 * Name: read_body
251 * Description: Reads body from a request socket for POST CGI
252 *
253 * Return values:
254 *
255 * -1: request blocked, move to blocked queue
256 * 0: request done, close it down
257 * 1: more to do, leave on ready list
258 *
259
260 As quoted from RFC1945:
261
262 A valid Content-Length is required on all HTTP/1.0 POST requests. An
263 HTTP/1.0 server should respond with a 400 (bad request) message if it
264 cannot determine the length of the request message's content.
265
266 */
267
268 int read_body(request * req)
269 {
270 int bytes_read, bytes_to_read, bytes_free;
271
272 bytes_free = BUFFER_SIZE - (req->header_end - req->header_line);
273 bytes_to_read = req->filesize - req->filepos;
274
275 if (bytes_to_read > bytes_free)
276 bytes_to_read = bytes_free;
277
278 if (bytes_to_read <= 0) {
279 req->status = BODY_WRITE; /* go write it */
280 return 1;
281 }
282
283 bytes_read = socket_recv(req, req->header_end, bytes_to_read);
284
285 if (bytes_read < 0) {
286 if (bytes_read == BOA_E_AGAIN) {
287 return -1;
288 } else {
289 boa_perror(req, "read body");
290 return 0;
291 }
292
293 } else if (bytes_read == 0) {
294 /* this is an error. premature end of body! */
295 log_error_time();
296 fprintf(stderr, "%s:%d - Premature end of body!!\n",
297 __FILE__, __LINE__);
298 send_r_bad_request(req);
299 return 0;
300 }
301
302 req->status = BODY_WRITE;
303
304 #ifdef FASCIST_LOGGING1
305 log_error_time();
306 fprintf(stderr, "%s:%d - read %d bytes.\n",
307 __FILE__, __LINE__, bytes_to_read);
308 #endif
309
310 req->header_end += bytes_read;
311
312 return 1;
313 }
314
315 /*
316 * Name: write_body
317 * Description: Writes a chunk of data to a file
318 *
319 * Return values:
320 * -1: request blocked, move to blocked queue
321 * 0: EOF or error, close it down
322 * 1: successful write, recycle in ready queue
323 */
324
325 int write_body(request * req)
326 {
327 int bytes_written, bytes_to_write = req->header_end - req->header_line;
328 if (req->filepos + bytes_to_write > req->filesize)
329 bytes_to_write = req->filesize - req->filepos;
330
331 if (bytes_to_write == 0) { /* nothing left in buffer to write */
332 req->header_line = req->header_end = req->buffer;
333 if (req->filepos >= req->filesize)
334 return init_cgi(req);
335 /* if here, we can safely assume that there is more to read */
336 req->status = BODY_READ;
337 return 1;
338 }
339 bytes_written =
340 write(req->post_data_fd, req->header_line, bytes_to_write);
341
342 if (bytes_written == -1) {
343 if (errno == EWOULDBLOCK || errno == EAGAIN)
344 return -1; /* request blocked at the pipe level, but keep going */
345 else if (errno == EINTR)
346 return 1;
347 else if (errno == ENOSPC) {
348 /* 20010520 - Alfred Fluckiger */
349 /* No test was originally done in this case, which might */
350 /* lead to a "no space left on device" error. */
351 boa_perror(req, "write body"); /* OK to disable if your logs get too big */
352 return 0;
353 } else {
354 boa_perror(req, "write body"); /* OK to disable if your logs get too big */
355 return 0;
356 }
357 }
358 #ifdef FASCIST_LOGGING
359 log_error_time();
360 fprintf(stderr, "%s:%d - wrote %d bytes. %ld of %ld\n",
361 __FILE__, __LINE__, bytes_written, req->filepos, req->filesize);
362 #endif
363
364 req->filepos += bytes_written;
365 req->header_line += bytes_written;
366
367 return 1; /* more to do */
368 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26