blob: 1d1a82b4c64484ef964aafef8f2e0b961b3024c0 [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 Zhang2e6bf0a2022-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 Zhang2e6bf0a2022-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
Tim Duesterhus77508502022-03-15 13:11:06 +0100204 p->n = ist2(b_peek(in, o), nlen);
205 p->v = ist2(b_peek(in, o + nlen), vlen);
Christopher Faulet63bbf282019-08-11 23:08:53 +0200206 len += nlen + vlen;
207
208 return len;
209}
210
211
212/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
213 * FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
214 * if some bytes are missing, otherwise the number of read bytes.
215 */
216size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
217{
218 size_t data = b_data(in);
219 size_t nlen, vlen, len = 0;
220 uint8_t b0, b1, b2, b3;
221
222 if (data < o + 1)
223 return 0;
224 b0 = in->area[o++];
225 if (!(b0 >> 7)) {
226 nlen = b0;
227 len++;
228 }
229 else {
230 if (data < o + 3)
231 return 0;
232 b1 = in->area[o++];
233 b2 = in->area[o++];
234 b3 = in->area[o++];
235 nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
236 len += 4;
237 }
238
239 if (data < o + 1)
240 return 0;
241 b0 = in->area[o++];
242 if (!(b0 >> 7)) {
243 vlen = b0;
244 len++;
245 }
246 else {
247 if (data < o + 3)
248 return 0;
249 b1 = in->area[o++];
250 b2 = in->area[o++];
251 b3 = in->area[o++];
252 vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
253 len += 4;
254 }
255
256 if (data < nlen + vlen)
257 return 0;
258
Tim Duesterhus77508502022-03-15 13:11:06 +0100259 p->n = ist2(in->area + o, nlen);
260 p->v = ist2(in->area + o + nlen, vlen);
Christopher Faulet63bbf282019-08-11 23:08:53 +0200261 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 */