MINOR: fcgi: Add code related to FCGI protocol
This code is independant and is only responsible to encode and decode part of the FCGI protocol.
This commit is contained in:
parent
86d144c74b
commit
63bbf284a1
2
Makefile
2
Makefile
|
@ -787,7 +787,7 @@ OBJS = src/http_ana.o src/cfgparse-listen.o src/stream.o \
|
|||
src/protocol.o src/arg.o src/hpack-huff.o src/base64.o src/ring.o \
|
||||
src/hash.o src/mailers.o src/activity.o src/version.o src/trace.o \
|
||||
src/mworker.o src/mworker-prog.o src/debug.o src/wdt.o src/dict.o \
|
||||
src/xprt_handshake.o src/h1_htx.o
|
||||
src/xprt_handshake.o src/h1_htx.o src/fcgi.o
|
||||
|
||||
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \
|
||||
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* include/common/fcgi.h
|
||||
* This file contains FastCGI protocol definitions.
|
||||
*
|
||||
* Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, version 2.1
|
||||
* exclusively.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_FCGI_H
|
||||
#define _COMMON_FCGI_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <common/config.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/buf.h>
|
||||
#include <common/ist.h>
|
||||
|
||||
/* FCGI protocol version */
|
||||
#define FCGI_VERSION 0x1
|
||||
|
||||
/* flags for FCGI_BEGIN_REQUEST records */
|
||||
#define FCGI_KEEP_CONN 0x01
|
||||
|
||||
/* FCGI record's type */
|
||||
enum fcgi_record_type {
|
||||
FCGI_BEGIN_REQUEST = 1,
|
||||
FCGI_ABORT_REQUEST = 2,
|
||||
FCGI_END_REQUEST = 3,
|
||||
FCGI_PARAMS = 4,
|
||||
FCGI_STDIN = 5,
|
||||
FCGI_STDOUT = 6,
|
||||
FCGI_STDERR = 7,
|
||||
FCGI_DATA = 8,
|
||||
FCGI_GET_VALUES = 9,
|
||||
FCGI_GET_VALUES_RESULT = 10,
|
||||
FCGI_UNKNOWN_TYPE = 11,
|
||||
FCGI_ENTRIES
|
||||
} __attribute__((packed));
|
||||
|
||||
enum fcgi_role {
|
||||
FCGI_RESPONDER = 1,
|
||||
FCGI_AUTHORIZER = 2, /* Unsupported */
|
||||
FCGI_FILTER = 3, /* Unsupported */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Protocol status */
|
||||
enum fcgi_proto_status {
|
||||
FCGI_PS_REQUEST_COMPLETE = 0,
|
||||
FCGI_PS_CANT_MPX_CONN = 1,
|
||||
FCGI_PS_OVERLOADED = 2,
|
||||
FCGI_PS_UNKNOWN_ROLE = 3,
|
||||
FCGI_PS_ENTRIES,
|
||||
} __attribute__((packed));
|
||||
|
||||
struct fcgi_header {
|
||||
uint8_t vsn;
|
||||
uint8_t type;
|
||||
uint16_t id;
|
||||
uint16_t len;
|
||||
uint8_t padding;
|
||||
uint8_t rsv;
|
||||
};
|
||||
|
||||
struct fcgi_param {
|
||||
struct ist n;
|
||||
struct ist v;
|
||||
};
|
||||
|
||||
struct fcgi_begin_request {
|
||||
enum fcgi_role role;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct fcgi_end_request {
|
||||
uint32_t status;
|
||||
uint8_t errcode;
|
||||
};
|
||||
|
||||
struct fcgi_unknown_type {
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int fcgi_encode_record_hdr(struct buffer *out, const struct fcgi_header *h);
|
||||
size_t fcgi_decode_record_hdr(const struct buffer *in, size_t o, struct fcgi_header *h);
|
||||
|
||||
int fcgi_encode_begin_request(struct buffer *out, const struct fcgi_begin_request *r);
|
||||
|
||||
int fcgi_encode_param(struct buffer *out, const struct fcgi_param *p);
|
||||
size_t fcgi_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p);
|
||||
size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p);
|
||||
|
||||
size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *r);
|
||||
|
||||
#endif /* _COMMON_FCGI_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* FastCGI protocol processing
|
||||
*
|
||||
* Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <common/fcgi.h>
|
||||
|
||||
|
||||
/* Encodes header of a FCGI record into the chunk <out>. It returns non-zero on
|
||||
* success and 0 on failure (buffer full). <out> is a chunk, so the wrapping is
|
||||
* not handled by this function. It is the caller responsibility to ensure
|
||||
* enough contiguous space is available
|
||||
*/
|
||||
int fcgi_encode_record_hdr(struct buffer *out, const struct fcgi_header *h)
|
||||
{
|
||||
size_t len = out->data;
|
||||
|
||||
if (len + 8 >= b_size(out))
|
||||
return 0;
|
||||
|
||||
out->area[len++] = h->vsn;
|
||||
out->area[len++] = h->type;
|
||||
out->area[len++] = ((h->id >> 8) & 0xff);
|
||||
out->area[len++] = (h->id & 0xff);
|
||||
out->area[len++] = ((h->len >> 8) & 0xff);
|
||||
out->area[len++] = (h->len & 0xff);
|
||||
out->area[len++] = h->padding;
|
||||
len++; /* rsv */
|
||||
|
||||
out->data = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Decodes a FCGI record header from offset <o> of buffer <in> into descriptor
|
||||
* <h>. The buffer may wrap so each byte read must be checked. The header is
|
||||
* formed like this :
|
||||
*
|
||||
* b0 b1 b2 b3 b4 b5 b6 b7
|
||||
* +-----+------+-----+-----+------+------+--------+-----+
|
||||
* | vsn | type | id1 | id0 | len1 | len0 | padlen | rsv |
|
||||
* +-----+------+-----+-----+------+------+--------+-----+
|
||||
*
|
||||
* Returns zero if some bytes are missing, otherwise the number of read bytes.
|
||||
*/
|
||||
size_t fcgi_decode_record_hdr(const struct buffer *in, size_t o, struct fcgi_header *h)
|
||||
{
|
||||
if (b_data(in) < o + 8)
|
||||
return 0;
|
||||
|
||||
h->vsn = (uint8_t)(*b_peek(in, o));
|
||||
h->type = (uint8_t)(*b_peek(in, o+1));
|
||||
h->id = ((uint8_t)(*b_peek(in, o+2)) << 8) + (uint8_t)(*b_peek(in, o+3));
|
||||
h->len = ((uint8_t)(*b_peek(in, o+4)) << 8) + (uint8_t)(*b_peek(in, o+5));
|
||||
h->padding = (uint8_t)(*b_peek(in, o+6));
|
||||
/* ignore rsv */
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
/* Encodes the payload part of a BEGIN_REQUEST record into the chunk <out>. It
|
||||
* returns non-zero on success and 0 on failure (buffer full). <out> is a chunk,
|
||||
* so the wrapping is not handled by this function. It is the caller
|
||||
* responsibility to ensure enough contiguous space is available
|
||||
*/
|
||||
int fcgi_encode_begin_request(struct buffer *out, const struct fcgi_begin_request *r)
|
||||
{
|
||||
size_t len = out->data;
|
||||
|
||||
if (len + 8 >= b_size(out))
|
||||
return 0;
|
||||
|
||||
out->area[len++] = ((r->role >> 8) & 0xff);
|
||||
out->area[len++] = (r->role & 0xff);
|
||||
out->area[len++] = r->flags;
|
||||
len += 5; /* rsv */
|
||||
|
||||
out->data = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Encodes a parameter, part of the payload of a PARAM record, into the chunk
|
||||
* <out>. It returns non-zero on success and 0 on failure (buffer full). <out>
|
||||
* is a chunk, so the wrapping is not handled by this function. It is the caller
|
||||
* responsibility to ensure enough contiguous space is available. The
|
||||
* parameter's name is converted to upper case and non-alphanumeric character
|
||||
* are replaced by an underscore.
|
||||
*/
|
||||
int fcgi_encode_param(struct buffer *out, const struct fcgi_param *p)
|
||||
{
|
||||
size_t off, len = out->data;
|
||||
int nbytes, vbytes;
|
||||
|
||||
nbytes = (!(p->n.len >> 7) ? 1 : 4);
|
||||
vbytes = (!(p->v.len >> 7) ? 1 : 4);
|
||||
if ((len + nbytes + p->n.len + vbytes + p->v.len) >= b_size(out))
|
||||
return 0;
|
||||
|
||||
if (nbytes == 1)
|
||||
out->area[len++] = (p->n.len & 0xff);
|
||||
else {
|
||||
out->area[len++] = (((p->n.len >> 24) & 0xff) | 0x80);
|
||||
out->area[len++] = ((p->n.len >> 16) & 0xff);
|
||||
out->area[len++] = ((p->n.len >> 8) & 0xff);
|
||||
out->area[len++] = (p->n.len & 0xff);
|
||||
}
|
||||
|
||||
if (vbytes == 1)
|
||||
out->area[len++] = (p->v.len & 0xff);
|
||||
else {
|
||||
out->area[len++] = (((p->v.len >> 24) & 0xff) | 0x80);
|
||||
out->area[len++] = ((p->v.len >> 16) & 0xff);
|
||||
out->area[len++] = ((p->v.len >> 8) & 0xff);
|
||||
out->area[len++] = (p->v.len & 0xff);
|
||||
}
|
||||
|
||||
for (off = 0; off < p->n.len; off++) {
|
||||
if (isalnum((int)p->n.ptr[off]))
|
||||
out->area[len++] = ist_uc[(unsigned char)p->n.ptr[off]];
|
||||
else
|
||||
out->area[len++] = '_';
|
||||
}
|
||||
if (p->v.len) {
|
||||
ist2bin(out->area + len, p->v);
|
||||
len += p->v.len;
|
||||
}
|
||||
|
||||
out->data = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
|
||||
* FCGI param <p>. The buffer may wrap so each byte read must be checked.
|
||||
* Returns zero if some bytes are missing, otherwise the number of read bytes.
|
||||
*/
|
||||
size_t fcgi_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
|
||||
{
|
||||
size_t data = b_data(in);
|
||||
size_t nlen, vlen, len = 0;
|
||||
uint8_t b0, b1, b2, b3;
|
||||
|
||||
if (data < o + 1)
|
||||
return 0;
|
||||
b0 = *b_peek(in, o++);
|
||||
if (!(b0 >> 7)) {
|
||||
nlen = b0;
|
||||
len++;
|
||||
}
|
||||
else {
|
||||
if (data < o + 3)
|
||||
return 0;
|
||||
b1 = *b_peek(in, o++);
|
||||
b2 = *b_peek(in, o++);
|
||||
b3 = *b_peek(in, o++);
|
||||
nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
len += 4;
|
||||
}
|
||||
|
||||
if (data < o + 1)
|
||||
return 0;
|
||||
b0 = *b_peek(in, o++);
|
||||
if (!(b0 >> 7)) {
|
||||
vlen = b0;
|
||||
len++;
|
||||
}
|
||||
else {
|
||||
if (data < o + 3)
|
||||
return 0;
|
||||
b1 = *b_peek(in, o++);
|
||||
b2 = *b_peek(in, o++);
|
||||
b3 = *b_peek(in, o++);
|
||||
vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
len += 4;
|
||||
}
|
||||
|
||||
if (data < nlen + vlen)
|
||||
return 0;
|
||||
|
||||
p->n.ptr = b_peek(in, o);
|
||||
p->n.len = nlen;
|
||||
p->v.ptr = b_peek(in, o+nlen);
|
||||
p->v.len = vlen;
|
||||
len += nlen + vlen;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* Decodes a parameter of a PARAM record from offset <o> of buffer <in> into the
|
||||
* FCGI param <p>. To call this function, the buffer must not wrap. Returns zero
|
||||
* if some bytes are missing, otherwise the number of read bytes.
|
||||
*/
|
||||
size_t fcgi_aligned_decode_param(const struct buffer *in, size_t o, struct fcgi_param *p)
|
||||
{
|
||||
size_t data = b_data(in);
|
||||
size_t nlen, vlen, len = 0;
|
||||
uint8_t b0, b1, b2, b3;
|
||||
|
||||
if (data < o + 1)
|
||||
return 0;
|
||||
b0 = in->area[o++];
|
||||
if (!(b0 >> 7)) {
|
||||
nlen = b0;
|
||||
len++;
|
||||
}
|
||||
else {
|
||||
if (data < o + 3)
|
||||
return 0;
|
||||
b1 = in->area[o++];
|
||||
b2 = in->area[o++];
|
||||
b3 = in->area[o++];
|
||||
nlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
len += 4;
|
||||
}
|
||||
|
||||
if (data < o + 1)
|
||||
return 0;
|
||||
b0 = in->area[o++];
|
||||
if (!(b0 >> 7)) {
|
||||
vlen = b0;
|
||||
len++;
|
||||
}
|
||||
else {
|
||||
if (data < o + 3)
|
||||
return 0;
|
||||
b1 = in->area[o++];
|
||||
b2 = in->area[o++];
|
||||
b3 = in->area[o++];
|
||||
vlen = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
len += 4;
|
||||
}
|
||||
|
||||
if (data < nlen + vlen)
|
||||
return 0;
|
||||
|
||||
p->n.ptr = in->area + o;
|
||||
p->n.len = nlen;
|
||||
p->v.ptr = in->area + o + nlen;
|
||||
p->v.len = vlen;
|
||||
len += nlen + vlen;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Decodes payload of a END_REQUEST record from offset <o> of buffer <in> into
|
||||
* the FCGI param <p>. The buffer may wrap so each byte read must be
|
||||
* checked. Returns zero if some bytes are missing, otherwise the number of read
|
||||
* bytes.
|
||||
*/
|
||||
size_t fcgi_decode_end_request(const struct buffer *in, size_t o, struct fcgi_end_request *rec)
|
||||
{
|
||||
uint8_t b0, b1, b2, b3;
|
||||
|
||||
if (b_data(in) < o + 8)
|
||||
return 0;
|
||||
|
||||
b0 = *b_peek(in, o++);
|
||||
b1 = *b_peek(in, o++);
|
||||
b2 = *b_peek(in, o++);
|
||||
b3 = *b_peek(in, o++);
|
||||
rec->status = ((b0 & 0x7f) << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||
rec->errcode = *b_peek(in, o++);
|
||||
o += 3; /* ignore rsv */
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
Loading…
Reference in New Issue