blob: 33aa2a24916b0647cc154ce53a6697a4e4b71b44 [file] [log] [blame]
Christopher Faulet63bbf282019-08-11 23:08:53 +02001/*
2 * FastCGI protocol processing
3 *
4 * Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27#include <common/fcgi.h>
28
29
30/* Encodes header of a FCGI record into the chunk <out>. It returns non-zero on
31 * success and 0 on failure (buffer full). <out> is a chunk, so the wrapping is
32 * not handled by this function. It is the caller responsibility to ensure
33 * enough contiguous space is available
34 */
35int fcgi_encode_record_hdr(struct buffer *out, const struct fcgi_header *h)
36{
37 size_t len = out->data;
38
39 if (len + 8 >= b_size(out))
40 return 0;
41
42 out->area[len++] = h->vsn;
43 out->area[len++] = h->type;
44 out->area[len++] = ((h->id >> 8) & 0xff);
45 out->area[len++] = (h->id & 0xff);
46 out->area[len++] = ((h->len >> 8) & 0xff);
47 out->area[len++] = (h->len & 0xff);
48 out->area[len++] = h->padding;
49 len++; /* rsv */
50
51 out->data = len;
52 return 1;
53}
54
55/* Decodes a FCGI record header from offset <o> of buffer <in> into descriptor
56 * <h>. The buffer may wrap so each byte read must be checked. The header is
57 * formed like this :
58 *
59 * b0 b1 b2 b3 b4 b5 b6 b7
60 * +-----+------+-----+-----+------+------+--------+-----+
61 * | vsn | type | id1 | id0 | len1 | len0 | padlen | rsv |
62 * +-----+------+-----+-----+------+------+--------+-----+
63 *
64 * Returns zero if some bytes are missing, otherwise the number of read bytes.
65 */
66size_t fcgi_decode_record_hdr(const struct buffer *in, size_t o, struct fcgi_header *h)
67{
68 if (b_data(in) < o + 8)
69 return 0;
70
71 h->vsn = (uint8_t)(*b_peek(in, o));
72 h->type = (uint8_t)(*b_peek(in, o+1));
73 h->id = ((uint8_t)(*b_peek(in, o+2)) << 8) + (uint8_t)(*b_peek(in, o+3));
74 h->len = ((uint8_t)(*b_peek(in, o+4)) << 8) + (uint8_t)(*b_peek(in, o+5));
75 h->padding = (uint8_t)(*b_peek(in, o+6));
76 /* ignore rsv */
77
78 return 8;
79}
80
81/* Encodes the payload part of a BEGIN_REQUEST record into the chunk <out>. It
82 * returns non-zero on success and 0 on failure (buffer full). <out> is a chunk,
83 * so the wrapping is not handled by this function. It is the caller
84 * responsibility to ensure enough contiguous space is available
85 */
86int fcgi_encode_begin_request(struct buffer *out, const struct fcgi_begin_request *r)
87{
88 size_t len = out->data;
89
90 if (len + 8 >= b_size(out))
91 return 0;
92
93 out->area[len++] = ((r->role >> 8) & 0xff);
94 out->area[len++] = (r->role & 0xff);
95 out->area[len++] = r->flags;
96 len += 5; /* rsv */
97
98 out->data = len;
99 return 1;
100}
101
102/* Encodes a parameter, part of the payload of a PARAM record, into the chunk
103 * <out>. It returns non-zero on success and 0 on failure (buffer full). <out>
104 * is a chunk, so the wrapping is not handled by this function. It is the caller
105 * responsibility to ensure enough contiguous space is available. The
106 * parameter's name is converted to upper case and non-alphanumeric character
107 * are replaced by an underscore.
108 */
109int fcgi_encode_param(struct buffer *out, const struct fcgi_param *p)
110{
111 size_t off, len = out->data;
112 int nbytes, vbytes;
113
114 nbytes = (!(p->n.len >> 7) ? 1 : 4);
115 vbytes = (!(p->v.len >> 7) ? 1 : 4);
116 if ((len + nbytes + p->n.len + vbytes + p->v.len) >= b_size(out))
117 return 0;
118
119 if (nbytes == 1)
120 out->area[len++] = (p->n.len & 0xff);
121 else {
122 out->area[len++] = (((p->n.len >> 24) & 0xff) | 0x80);
123 out->area[len++] = ((p->n.len >> 16) & 0xff);
124 out->area[len++] = ((p->n.len >> 8) & 0xff);
125 out->area[len++] = (p->n.len & 0xff);
126 }
127
128 if (vbytes == 1)
129 out->area[len++] = (p->v.len & 0xff);
130 else {
131 out->area[len++] = (((p->v.len >> 24) & 0xff) | 0x80);
132 out->area[len++] = ((p->v.len >> 16) & 0xff);
133 out->area[len++] = ((p->v.len >> 8) & 0xff);
134 out->area[len++] = (p->v.len & 0xff);
135 }
136
137 for (off = 0; off < p->n.len; off++) {
138 if (isalnum((int)p->n.ptr[off]))
139 out->area[len++] = ist_uc[(unsigned char)p->n.ptr[off]];
140 else
141 out->area[len++] = '_';
142 }
143 if (p->v.len) {
144 ist2bin(out->area + len, p->v);
145 len += p->v.len;
146 }
147
148 out->data = len;
149 return 1;
150}
151
152/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
153 * FCGI param <p>. The buffer may wrap so each byte read must be checked.
154 * Returns zero if some bytes are missing, otherwise the number of read bytes.
155 */
156size_t fcgi_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
157{
158 size_t data = b_data(in);
159 size_t nlen, vlen, len = 0;
160 uint8_t b0, b1, b2, b3;
161
162 if (data < o + 1)
163 return 0;
164 b0 = *b_peek(in, o++);
165 if (!(b0 >> 7)) {
166 nlen = b0;
167 len++;
168 }
169 else {
170 if (data < o + 3)
171 return 0;
172 b1 = *b_peek(in, o++);
173 b2 = *b_peek(in, o++);
174 b3 = *b_peek(in, o++);
175 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
176 len += 4;
177 }
178
179 if (data < o + 1)
180 return 0;
181 b0 = *b_peek(in, o++);
182 if (!(b0 >> 7)) {
183 vlen = b0;
184 len++;
185 }
186 else {
187 if (data < o + 3)
188 return 0;
189 b1 = *b_peek(in, o++);
190 b2 = *b_peek(in, o++);
191 b3 = *b_peek(in, o++);
192 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
193 len += 4;
194 }
195
196 if (data < nlen + vlen)
197 return 0;
198
199 p->n.ptr = b_peek(in, o);
200 p->n.len = nlen;
201 p->v.ptr = b_peek(in, o+nlen);
202 p->v.len = vlen;
203 len += nlen + vlen;
204
205 return len;
206}
207
208
209/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
210 * FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
211 * if some bytes are missing, otherwise the number of read bytes.
212 */
213size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
214{
215 size_t data = b_data(in);
216 size_t nlen, vlen, len = 0;
217 uint8_t b0, b1, b2, b3;
218
219 if (data < o + 1)
220 return 0;
221 b0 = in->area[o++];
222 if (!(b0 >> 7)) {
223 nlen = b0;
224 len++;
225 }
226 else {
227 if (data < o + 3)
228 return 0;
229 b1 = in->area[o++];
230 b2 = in->area[o++];
231 b3 = in->area[o++];
232 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
233 len += 4;
234 }
235
236 if (data < o + 1)
237 return 0;
238 b0 = in->area[o++];
239 if (!(b0 >> 7)) {
240 vlen = b0;
241 len++;
242 }
243 else {
244 if (data < o + 3)
245 return 0;
246 b1 = in->area[o++];
247 b2 = in->area[o++];
248 b3 = in->area[o++];
249 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
250 len += 4;
251 }
252
253 if (data < nlen + vlen)
254 return 0;
255
256 p->n.ptr = in->area + o;
257 p->n.len = nlen;
258 p->v.ptr = in->area + o + nlen;
259 p->v.len = vlen;
260 len += nlen + vlen;
261
262 return len;
263}
264
265/* Decodes payload of a END_REQUEST record from offset <o> of buffer <in> into
266 * the FCGI param <p>. The buffer may wrap so each byte read must be
267 * checked. Returns zero if some bytes are missing, otherwise the number of read
268 * bytes.
269 */
270size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *rec)
271{
272 uint8_t b0, b1, b2, b3;
273
274 if (b_data(in) < o + 8)
275 return 0;
276
277 b0 = *b_peek(in, o++);
278 b1 = *b_peek(in, o++);
279 b2 = *b_peek(in, o++);
280 b3 = *b_peek(in, o++);
281 rec->status = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
282 rec->errcode = *b_peek(in, o++);
283 o += 3; /* ignore rsv */
284
285 return 8;
286}
287
288/*
289 * Local variables:
290 * c-indent-level: 8
291 * c-basic-offset: 8
292 * End:
293 */