-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathio_cgi.c
130 lines (108 loc) · 3.64 KB
/
io_cgi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
* Copyright (c) 2004-2005 Sergey Lyubka <[email protected]>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#define MY_DEBUGGING 1
#include "defs.h"
static int write_cgi(struct stream *stream, const void *buf, size_t len)
{
int rc;
assert(stream->chan.sock != -1);
assert(stream->flags & FLAG_W);
rc = send(stream->chan.sock, buf, len, 0);
MY_DEBUG("%s rc=%d, buf=%.*s.\n", __func__, rc, len, buf);
return rc;
}
static int read_cgi(struct stream *stream, void *buf, size_t len)
{
struct headers parsed;
char status[4];
int n;
assert(stream->chan.sock != -1);
assert(stream->flags & FLAG_R);
stream->flags &= ~FLAG_DONT_CLOSE;
n = recv(stream->chan.sock, buf, len, 0);
MY_DEBUG("%s(,%p,%d)=%d\n", __func__, buf, len, n);
if (stream->flags & FLAG_HEADERS_PARSED)
return (n);
if (n <= 0 && ERRNO != EWOULDBLOCK) {
_shttpd_send_server_error(stream->conn, 500,
"Error running CGI");
return (n);
}
/*
* CGI script may output Status: and Location: headers, which
* may alter the status code. Buffer in headers, parse
* them, send correct status code and then forward all data
* from CGI script back to the remote end.
* Reply line was alredy appended to the IO buffer in
* decide_what_to_do(), with blank status code.
*/
stream->flags |= FLAG_DONT_CLOSE;
io_inc_head(&stream->io, n);
stream->headers_len = _shttpd_get_headers_len(stream->io.buf,
stream->io.head);
if (stream->headers_len < 0) {
stream->flags &= ~FLAG_DONT_CLOSE;
_shttpd_send_server_error(stream->conn, 500,
"Bad headers sent");
_shttpd_elog(E_LOG, stream->conn,
"CGI script sent invalid headers: "
"[%.*s]", stream->io.head - CGI_REPLY_LEN,
stream->io.buf + CGI_REPLY_LEN);
return (0);
}
/*
* If we did not received full headers yet, we must not send any
* data read from the CGI back to the client. Suspend sending by
* setting tail = head, which tells that there is no data in IO buffer
*/
if (stream->headers_len == 0) {
stream->io.tail = stream->io.head;
return (0);
}
/* Received all headers. Set status code for the connection. */
(void) memset(&parsed, 0, sizeof(parsed));
_shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed);
stream->content_len = parsed.cl.v_big_int;
stream->conn->status = (int) parsed.status.v_big_int;
/* If script outputs 'Location:' header, set status code to 302 */
if (parsed.location.v_vec.len > 0)
stream->conn->status = 302;
/*
* If script did not output neither 'Location:' nor 'Status' headers,
* set the default status code 200, which means 'success'.
*/
if (stream->conn->status == 0)
stream->conn->status = 200;
/* Append the status line to the beginning of the output */
(void) _shttpd_snprintf(status,
sizeof(status), "%3d", stream->conn->status);
(void) memcpy(stream->io.buf + 9, status, 3);
MY_DEBUG("read_cgi: content len %lu status %s\n",
stream->content_len, status);
/* Next time, pass output directly back to the client */
assert((big_int_t) stream->headers_len <= stream->io.total);
stream->io.total -= stream->headers_len;
stream->io.tail = 0;
stream->flags |= FLAG_HEADERS_PARSED;
/* Return 0 because we've already shifted the head */
return (0);
}
static void close_cgi(struct stream *stream)
{
MY_DEBUG("%s()\n", __func__);
assert(stream->chan.sock != -1);
(void) closesocket(stream->chan.sock);
}
const struct io_class _shttpd_io_cgi = {
"cgi",
read_cgi,
write_cgi,
close_cgi
};