src/server/safs/cgi.c

changeset 118
38bf6dd8f4e7
child 119
155cbab9eefd
equal deleted inserted replaced
117:a94cf2e94492 118:38bf6dd8f4e7
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2016 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "cgi.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #include "../util/util.h"
36 #include "../../ucx/string.h"
37
38 #include "cgiutils.h"
39
40 #define CGI_VARS 32
41
42 #define CGI_RESPONSE_PARSER_BUFLEN 2048
43 #define CGI_RESPONSE_MAX_LINE_LENGTH 512
44
45 int send_cgi(pblock *pb, Session *sn, Request *rq) {
46 char *path = pblock_findval("path", rq->vars);
47
48 struct stat s;
49 if(stat(path, &s)) {
50 int statuscode = util_errno2status(errno);
51 protocol_status(sn, rq, statuscode, NULL);
52 return REQ_ABORTED;
53 }
54 if(S_ISDIR(s.st_mode)) {
55 protocol_status(sn, rq, 403, NULL);
56 return REQ_ABORTED;
57 }
58
59 param_free(pblock_remove("content-type", rq->srvhdrs));
60
61 const char *args = pblock_findval("query", rq->reqpb);
62 char **argv = cgi_create_argv(path, NULL, args);
63
64 char **env = http_hdrs2env(rq->headers);
65 env = cgi_common_vars(sn, rq, env);
66 env = cgi_specific_vars(sn, rq, args, env, 1);
67
68 CGIProcess cgip;
69 int ret = cgi_start(&cgip, path, argv, env);
70 if(ret != REQ_PROCEED) {
71 return ret;
72 }
73
74 // TODO: send http body
75 close(cgip.in[1]);
76
77 // read from child
78 CGIResponseParser *parser = cgi_parser_new(sn, rq);
79 WSBool cgiheader = TRUE;
80
81 char buf[4096];
82 ssize_t r;
83 while((r = read(cgip.out[0], buf, 4096)) > 0) {
84 if(cgiheader) {
85 size_t pos;
86 int ret = cgi_parse_response(parser, buf, r, &pos);
87 if(ret == -1) {
88 protocol_status(sn, rq, 500, NULL);
89 return REQ_ABORTED;
90 } else if(ret == 0) {
91
92 } else if(ret == 1) {
93 cgiheader = FALSE;
94 http_start_response(sn, rq);
95 if(pos < r) {
96 net_write(sn->csd, buf+pos, r-pos);
97 }
98 }
99 } else {
100 net_write(sn->csd, buf, r);
101 }
102 }
103
104 return REQ_PROCEED;
105 }
106
107 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) {
108 if(pipe(p->in) || pipe(p->out)) {
109 log_ereport(
110 LOG_FAILURE,
111 "send-cgi: cannot create pipe: %s",
112 strerror(errno));
113 return REQ_ABORTED;
114 }
115
116 p->pid = fork();
117 if(p->pid == 0) {
118 // child
119 if(dup2(p->in[0], STDIN_FILENO) == -1) {
120 perror("cgi_start: dup2");
121 exit(EXIT_FAILURE);
122 }
123 if(dup2(p->out[1], STDOUT_FILENO) == -1) {
124 perror("cgi_start: dup2");
125 exit(EXIT_FAILURE);
126 }
127
128 // we need to close this unused pipe
129 // otherwise stdin cannot reach EOF
130 close(p->in[1]);
131
132 // execute program
133 exit(execve(path, argv, envp));
134 } else {
135 // parent
136 close(p->out[1]);
137 }
138
139 return REQ_PROCEED;
140 }
141
142 CGIResponseParser* cgi_parser_new(Session *sn, Request *rq) {
143 CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser));
144 parser->sn = sn;
145 parser->rq = rq;
146 parser->tmp = ucx_buffer_new(NULL, 64, UCX_BUFFER_AUTOEXTEND);
147 return parser;
148 }
149
150 /*
151 * parses a cgi response line and adds the response header to rq->srvhdrs
152 * returns 0: incomplete line
153 * 1: successfully parsed lines
154 * 2: cgi response header complete (empty line)
155 * -1: error
156 */
157 static int parse_lines(CGIResponseParser *parser, char *buf, size_t len, int *pos) {
158 sstr_t name;
159 sstr_t value;
160 WSBool space = TRUE;
161 int i;
162
163 int line_begin = 0;
164 int value_begin = 0;
165 for(i=0;i<len;i++) {
166 char c = buf[i];
167 if(value_begin == line_begin && c == ':') {
168 name = sstrn(buf + line_begin, i - line_begin);
169 value_begin = i + 1;
170 } else if(c == '\n') {
171 if(value_begin == line_begin) {
172 if(space) {
173 *pos = i + 1;
174 return 2;
175 } else {
176 // line ends with content but without ':' -> error
177 return -1;
178 }
179 }
180 value = sstrn(buf + value_begin, i - value_begin);
181
182 name = sstrtrim(name);
183 value = sstrtrim(value);
184
185 if(name.length == 0 || value.length == 0) {
186 return -1;
187 }
188 pblock_nvlinsert(
189 name.ptr,
190 name.length,
191 value.ptr,
192 value.length,
193 parser->rq->srvhdrs);
194
195 line_begin = i+1;
196 value_begin = line_begin;
197 space = TRUE;
198 } else if(c != ' ') {
199 space = FALSE;
200 }
201 }
202
203 if(i < len) {
204 *pos = i;
205 return 0;
206 }
207 return 1;
208 }
209
210 /*
211 * returns -1: error
212 * 0: response header incomplete
213 * 1: complete
214 */
215 int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos) {
216 *bpos = 0;
217 int pos = 0;
218 if(parser->tmp->pos > 0) {
219 // the tmp buffer contains an unfinished line
220 // fill up the buffer until the line is complete
221 WSBool nb = FALSE;
222 for(pos=0;pos<len;pos++) {
223 if(buf[pos] == '\n') {
224 nb = TRUE;
225 break;
226 }
227 }
228 ucx_buffer_write(buf, 1, pos, parser->tmp);
229
230 if(nb) {
231 // line complete
232 int npos;
233 int r = parse_lines(parser, parser->tmp->space, parser->tmp->pos, &npos);
234 switch(r) {
235 case -1: return -1;
236 case 0: return -1;
237 case 1: break;
238 case 2: {
239 *bpos = pos + 1;
240 return 1;
241 }
242 }
243 // reset tmp buffer
244 parser->tmp->pos = 0;
245 } else {
246 if(parser->tmp->pos > CGI_RESPONSE_MAX_LINE_LENGTH) {
247 return -1;
248 }
249 }
250 }
251
252 int npos = 0;
253 int r = parse_lines(parser, buf + pos, len - pos, &npos);
254 switch(r) {
255 default: return -1;
256 case 0:
257 case 1: {
258 int newlen = len - npos;
259 if(npos > 0) {
260 ucx_buffer_write(buf + npos, 1, newlen, parser->tmp);
261 }
262 return 0;
263 }
264 case 2: {
265 *bpos = pos + npos;
266 return 1;
267 }
268 }
269 }
270

mercurial