blob: dcf2db219656d51f7f66717c4ce7e1d2232a500f [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
Willy Tarreau8dabda72020-05-27 17:22:10 +020027#include <haproxy/buf.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020028#include <haproxy/fcgi.h>
Willy Tarreauf0f1c802020-05-27 17:32:53 +020029#include <haproxy/istbuf.h>
Christopher Faulet63bbf282019-08-11 23:08:53 +020030
31/* Encodes header of a FCGI record into the chunk <out>. It returns non-zero on
32 * success and 0 on failure (buffer full). <out> is a chunk, so the wrapping is
33 * not handled by this function. It is the caller responsibility to ensure
34 * enough contiguous space is available
35 */
36int fcgi_encode_record_hdr(struct buffer *out, const struct fcgi_header *h)
37{
38 size_t len = out->data;
39
40 if (len + 8 >= b_size(out))
41 return 0;
42
43 out->area[len++] = h->vsn;
44 out->area[len++] = h->type;
45 out->area[len++] = ((h->id >> 8) & 0xff);
46 out->area[len++] = (h->id & 0xff);
47 out->area[len++] = ((h->len >> 8) & 0xff);
48 out->area[len++] = (h->len & 0xff);
49 out->area[len++] = h->padding;
50 len++; /* rsv */
51
52 out->data = len;
53 return 1;
54}
55
56/* Decodes a FCGI record header from offset <o> of buffer <in> into descriptor
57 * <h>. The buffer may wrap so each byte read must be checked. The header is
58 * formed like this :
59 *
60 * b0 b1 b2 b3 b4 b5 b6 b7
61 * +-----+------+-----+-----+------+------+--------+-----+
62 * | vsn | type | id1 | id0 | len1 | len0 | padlen | rsv |
63 * +-----+------+-----+-----+------+------+--------+-----+
64 *
65 * Returns zero if some bytes are missing, otherwise the number of read bytes.
66 */
67size_t fcgi_decode_record_hdr(const struct buffer *in, size_t o, struct fcgi_header *h)
68{
69 if (b_data(in) < o + 8)
70 return 0;
71
72 h->vsn = (uint8_t)(*b_peek(in, o));
73 h->type = (uint8_t)(*b_peek(in, o+1));
74 h->id = ((uint8_t)(*b_peek(in, o+2)) << 8) + (uint8_t)(*b_peek(in, o+3));
75 h->len = ((uint8_t)(*b_peek(in, o+4)) << 8) + (uint8_t)(*b_peek(in, o+5));
76 h->padding = (uint8_t)(*b_peek(in, o+6));
77 /* ignore rsv */
78
79 return 8;
80}
81
82/* Encodes the payload part of a BEGIN_REQUEST record into the chunk <out>. It
83 * returns non-zero on success and 0 on failure (buffer full). <out> is a chunk,
84 * so the wrapping is not handled by this function. It is the caller
85 * responsibility to ensure enough contiguous space is available
86 */
87int fcgi_encode_begin_request(struct buffer *out, const struct fcgi_begin_request *r)
88{
89 size_t len = out->data;
90
91 if (len + 8 >= b_size(out))
92 return 0;
93
94 out->area[len++] = ((r->role >> 8) & 0xff);
95 out->area[len++] = (r->role & 0xff);
96 out->area[len++] = r->flags;
97 len += 5; /* rsv */
98
99 out->data = len;
100 return 1;
101}
102
103/* Encodes a parameter, part of the payload of a PARAM record, into the chunk
104 * <out>. It returns non-zero on success and 0 on failure (buffer full). <out>
105 * is a chunk, so the wrapping is not handled by this function. It is the caller
106 * responsibility to ensure enough contiguous space is available. The
107 * parameter's name is converted to upper case and non-alphanumeric character
108 * are replaced by an underscore.
109 */
110int fcgi_encode_param(struct buffer *out, const struct fcgi_param *p)
111{
112 size_t off, len = out->data;
113 int nbytes, vbytes;
114
115 nbytes = (!(p->n.len >> 7) ? 1 : 4);
116 vbytes = (!(p->v.len >> 7) ? 1 : 4);
117 if ((len + nbytes + p->n.len + vbytes + p->v.len) >= b_size(out))
118 return 0;
119
120 if (nbytes == 1)
121 out->area[len++] = (p->n.len & 0xff);
122 else {
123 out->area[len++] = (((p->n.len >> 24) & 0xff) | 0x80);
124 out->area[len++] = ((p->n.len >> 16) & 0xff);
125 out->area[len++] = ((p->n.len >> 8) & 0xff);
126 out->area[len++] = (p->n.len & 0xff);
127 }
128
129 if (vbytes == 1)
130 out->area[len++] = (p->v.len & 0xff);
131 else {
132 out->area[len++] = (((p->v.len >> 24) & 0xff) | 0x80);
133 out->area[len++] = ((p->v.len >> 16) & 0xff);
134 out->area[len++] = ((p->v.len >> 8) & 0xff);
135 out->area[len++] = (p->v.len & 0xff);
136 }
137
138 for (off = 0; off < p->n.len; off++) {
Willy Tarreau90807112020-02-25 08:16:33 +0100139 if (isalnum((unsigned char)p->n.ptr[off]))
Christopher Faulet63bbf282019-08-11 23:08:53 +0200140 out->area[len++] = ist_uc[(unsigned char)p->n.ptr[off]];
141 else
142 out->area[len++] = '_';
143 }
144 if (p->v.len) {
145 ist2bin(out->area + len, p->v);
146 len += p->v.len;
147 }
148
149 out->data = len;
150 return 1;
151}
152
153/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
154 * FCGI param <p>. The buffer may wrap so each byte read must be checked.
155 * Returns zero if some bytes are missing, otherwise the number of read bytes.
156 */
157size_t fcgi_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
158{
159 size_t data = b_data(in);
160 size_t nlen, vlen, len = 0;
161 uint8_t b0, b1, b2, b3;
162
163 if (data < o + 1)
164 return 0;
165 b0 = *b_peek(in, o++);
166 if (!(b0 >> 7)) {
167 nlen = b0;
168 len++;
169 }
170 else {
171 if (data < o + 3)
172 return 0;
173 b1 = *b_peek(in, o++);
174 b2 = *b_peek(in, o++);
175 b3 = *b_peek(in, o++);
176 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
177 len += 4;
178 }
179
180 if (data < o + 1)
181 return 0;
182 b0 = *b_peek(in, o++);
183 if (!(b0 >> 7)) {
184 vlen = b0;
185 len++;
186 }
187 else {
188 if (data < o + 3)
189 return 0;
190 b1 = *b_peek(in, o++);
191 b2 = *b_peek(in, o++);
192 b3 = *b_peek(in, o++);
193 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
194 len += 4;
195 }
196
197 if (data < nlen + vlen)
198 return 0;
199
Tim Duesterhus77508502022-03-15 13:11:06 +0100200 p->n = ist2(b_peek(in, o), nlen);
201 p->v = ist2(b_peek(in, o + nlen), vlen);
Christopher Faulet63bbf282019-08-11 23:08:53 +0200202 len += nlen + vlen;
203
204 return len;
205}
206
207
208/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
209 * FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
210 * if some bytes are missing, otherwise the number of read bytes.
211 */
212size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
213{
214 size_t data = b_data(in);
215 size_t nlen, vlen, len = 0;
216 uint8_t b0, b1, b2, b3;
217
218 if (data < o + 1)
219 return 0;
220 b0 = in->area[o++];
221 if (!(b0 >> 7)) {
222 nlen = b0;
223 len++;
224 }
225 else {
226 if (data < o + 3)
227 return 0;
228 b1 = in->area[o++];
229 b2 = in->area[o++];
230 b3 = in->area[o++];
231 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
232 len += 4;
233 }
234
235 if (data < o + 1)
236 return 0;
237 b0 = in->area[o++];
238 if (!(b0 >> 7)) {
239 vlen = b0;
240 len++;
241 }
242 else {
243 if (data < o + 3)
244 return 0;
245 b1 = in->area[o++];
246 b2 = in->area[o++];
247 b3 = in->area[o++];
248 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
249 len += 4;
250 }
251
252 if (data < nlen + vlen)
253 return 0;
254
Tim Duesterhus77508502022-03-15 13:11:06 +0100255 p->n = ist2(in->area + o, nlen);
256 p->v = ist2(in->area + o + nlen, vlen);
Christopher Faulet63bbf282019-08-11 23:08:53 +0200257 len += nlen + vlen;
258
259 return len;
260}
261
262/* Decodes payload of a END_REQUEST record from offset <o> of buffer <in> into
263 * the FCGI param <p>. The buffer may wrap so each byte read must be
264 * checked. Returns zero if some bytes are missing, otherwise the number of read
265 * bytes.
266 */
267size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *rec)
268{
269 uint8_t b0, b1, b2, b3;
270
271 if (b_data(in) < o + 8)
272 return 0;
273
274 b0 = *b_peek(in, o++);
275 b1 = *b_peek(in, o++);
276 b2 = *b_peek(in, o++);
277 b3 = *b_peek(in, o++);
278 rec->status = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
279 rec->errcode = *b_peek(in, o++);
280 o += 3; /* ignore rsv */
281
282 return 8;
283}
284
285/*
286 * Local variables:
287 * c-indent-level: 8
288 * c-basic-offset: 8
289 * End:
290 */