blob: 1c2543def6b04436495a44f285f070b45ae66714 [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
200 p->n.ptr = b_peek(in, o);
201 p->n.len = nlen;
202 p->v.ptr = b_peek(in, o+nlen);
203 p->v.len = vlen;
204 len += nlen + vlen;
205
206 return len;
207}
208
209
210/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
211 * FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
212 * if some bytes are missing, otherwise the number of read bytes.
213 */
214size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
215{
216 size_t data = b_data(in);
217 size_t nlen, vlen, len = 0;
218 uint8_t b0, b1, b2, b3;
219
220 if (data < o + 1)
221 return 0;
222 b0 = in->area[o++];
223 if (!(b0 >> 7)) {
224 nlen = b0;
225 len++;
226 }
227 else {
228 if (data < o + 3)
229 return 0;
230 b1 = in->area[o++];
231 b2 = in->area[o++];
232 b3 = in->area[o++];
233 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
234 len += 4;
235 }
236
237 if (data < o + 1)
238 return 0;
239 b0 = in->area[o++];
240 if (!(b0 >> 7)) {
241 vlen = b0;
242 len++;
243 }
244 else {
245 if (data < o + 3)
246 return 0;
247 b1 = in->area[o++];
248 b2 = in->area[o++];
249 b3 = in->area[o++];
250 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
251 len += 4;
252 }
253
254 if (data < nlen + vlen)
255 return 0;
256
257 p->n.ptr = in->area + o;
258 p->n.len = nlen;
259 p->v.ptr = in->area + o + nlen;
260 p->v.len = vlen;
261 len += nlen + vlen;
262
263 return len;
264}
265
266/* Decodes payload of a END_REQUEST record from offset <o> of buffer <in> into
267 * the FCGI param <p>. The buffer may wrap so each byte read must be
268 * checked. Returns zero if some bytes are missing, otherwise the number of read
269 * bytes.
270 */
271size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *rec)
272{
273 uint8_t b0, b1, b2, b3;
274
275 if (b_data(in) < o + 8)
276 return 0;
277
278 b0 = *b_peek(in, o++);
279 b1 = *b_peek(in, o++);
280 b2 = *b_peek(in, o++);
281 b3 = *b_peek(in, o++);
282 rec->status = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
283 rec->errcode = *b_peek(in, o++);
284 o += 3; /* ignore rsv */
285
286 return 8;
287}
288
289/*
290 * Local variables:
291 * c-indent-level: 8
292 * c-basic-offset: 8
293 * End:
294 */