blob: 778ce9e25cd9c315f7c5b2c1f702b881e54dcde0 [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;
Youfu Zhang0c86fce2022-12-09 19:15:48 +080050 out->area[len++] = 0; /* rsv */
Christopher Faulet63bbf282019-08-11 23:08:53 +020051
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;
Youfu Zhang0c86fce2022-12-09 19:15:48 +080097 out->area[len++] = 0; /* rsv */
98 out->area[len++] = 0;
99 out->area[len++] = 0;
100 out->area[len++] = 0;
101 out->area[len++] = 0;
Christopher Faulet63bbf282019-08-11 23:08:53 +0200102
103 out->data = len;
104 return 1;
105}
106
107/* Encodes a parameter, part of the payload of a PARAM record, into the chunk
108 * <out>. It returns non-zero on success and 0 on failure (buffer full). <out>
109 * is a chunk, so the wrapping is not handled by this function. It is the caller
110 * responsibility to ensure enough contiguous space is available. The
111 * parameter's name is converted to upper case and non-alphanumeric character
112 * are replaced by an underscore.
113 */
114int fcgi_encode_param(struct buffer *out, const struct fcgi_param *p)
115{
116 size_t off, len = out->data;
117 int nbytes, vbytes;
118
119 nbytes = (!(p->n.len >> 7) ? 1 : 4);
120 vbytes = (!(p->v.len >> 7) ? 1 : 4);
121 if ((len + nbytes + p->n.len + vbytes + p->v.len) >= b_size(out))
122 return 0;
123
124 if (nbytes == 1)
125 out->area[len++] = (p->n.len & 0xff);
126 else {
127 out->area[len++] = (((p->n.len >> 24) & 0xff) | 0x80);
128 out->area[len++] = ((p->n.len >> 16) & 0xff);
129 out->area[len++] = ((p->n.len >> 8) & 0xff);
130 out->area[len++] = (p->n.len & 0xff);
131 }
132
133 if (vbytes == 1)
134 out->area[len++] = (p->v.len & 0xff);
135 else {
136 out->area[len++] = (((p->v.len >> 24) & 0xff) | 0x80);
137 out->area[len++] = ((p->v.len >> 16) & 0xff);
138 out->area[len++] = ((p->v.len >> 8) & 0xff);
139 out->area[len++] = (p->v.len & 0xff);
140 }
141
142 for (off = 0; off < p->n.len; off++) {
Willy Tarreau90807112020-02-25 08:16:33 +0100143 if (isalnum((unsigned char)p->n.ptr[off]))
Christopher Faulet63bbf282019-08-11 23:08:53 +0200144 out->area[len++] = ist_uc[(unsigned char)p->n.ptr[off]];
145 else
146 out->area[len++] = '_';
147 }
148 if (p->v.len) {
149 ist2bin(out->area + len, p->v);
150 len += p->v.len;
151 }
152
153 out->data = len;
154 return 1;
155}
156
157/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
158 * FCGI param <p>. The buffer may wrap so each byte read must be checked.
159 * Returns zero if some bytes are missing, otherwise the number of read bytes.
160 */
161size_t fcgi_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
162{
163 size_t data = b_data(in);
164 size_t nlen, vlen, len = 0;
165 uint8_t b0, b1, b2, b3;
166
167 if (data < o + 1)
168 return 0;
169 b0 = *b_peek(in, o++);
170 if (!(b0 >> 7)) {
171 nlen = b0;
172 len++;
173 }
174 else {
175 if (data < o + 3)
176 return 0;
177 b1 = *b_peek(in, o++);
178 b2 = *b_peek(in, o++);
179 b3 = *b_peek(in, o++);
180 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
181 len += 4;
182 }
183
184 if (data < o + 1)
185 return 0;
186 b0 = *b_peek(in, o++);
187 if (!(b0 >> 7)) {
188 vlen = b0;
189 len++;
190 }
191 else {
192 if (data < o + 3)
193 return 0;
194 b1 = *b_peek(in, o++);
195 b2 = *b_peek(in, o++);
196 b3 = *b_peek(in, o++);
197 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
198 len += 4;
199 }
200
201 if (data < nlen + vlen)
202 return 0;
203
204 p->n.ptr = b_peek(in, o);
205 p->n.len = nlen;
206 p->v.ptr = b_peek(in, o+nlen);
207 p->v.len = vlen;
208 len += nlen + vlen;
209
210 return len;
211}
212
213
214/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
215 * FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
216 * if some bytes are missing, otherwise the number of read bytes.
217 */
218size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
219{
220 size_t data = b_data(in);
221 size_t nlen, vlen, len = 0;
222 uint8_t b0, b1, b2, b3;
223
224 if (data < o + 1)
225 return 0;
226 b0 = in->area[o++];
227 if (!(b0 >> 7)) {
228 nlen = b0;
229 len++;
230 }
231 else {
232 if (data < o + 3)
233 return 0;
234 b1 = in->area[o++];
235 b2 = in->area[o++];
236 b3 = in->area[o++];
237 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
238 len += 4;
239 }
240
241 if (data < o + 1)
242 return 0;
243 b0 = in->area[o++];
244 if (!(b0 >> 7)) {
245 vlen = b0;
246 len++;
247 }
248 else {
249 if (data < o + 3)
250 return 0;
251 b1 = in->area[o++];
252 b2 = in->area[o++];
253 b3 = in->area[o++];
254 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
255 len += 4;
256 }
257
258 if (data < nlen + vlen)
259 return 0;
260
261 p->n.ptr = in->area + o;
262 p->n.len = nlen;
263 p->v.ptr = in->area + o + nlen;
264 p->v.len = vlen;
265 len += nlen + vlen;
266
267 return len;
268}
269
270/* Decodes payload of a END_REQUEST record from offset <o> of buffer <in> into
271 * the FCGI param <p>. The buffer may wrap so each byte read must be
272 * checked. Returns zero if some bytes are missing, otherwise the number of read
273 * bytes.
274 */
275size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *rec)
276{
277 uint8_t b0, b1, b2, b3;
278
279 if (b_data(in) < o + 8)
280 return 0;
281
282 b0 = *b_peek(in, o++);
283 b1 = *b_peek(in, o++);
284 b2 = *b_peek(in, o++);
285 b3 = *b_peek(in, o++);
286 rec->status = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
287 rec->errcode = *b_peek(in, o++);
288 o += 3; /* ignore rsv */
289
290 return 8;
291}
292
293/*
294 * Local variables:
295 * c-indent-level: 8
296 * c-basic-offset: 8
297 * End:
298 */