0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
};
+#define PROXY2_IP6_ADDR_SIZE 16
+#define PROXY2_UNIX_ADDR_SIZE 108
+
+#define TLV_TYPE_SSL 0x20
+
+enum proxy2_family {
+ PROXY2_AF_UNSPEC = 0x0,
+ PROXY2_AF_INET = 0x1,
+ PROXY2_AF_INET6 = 0x2,
+ PROXY2_AF_UNIX = 0x3
+};
+
+enum proxy2_protocol {
+ PROXY2_PROTOCOL_UNSPEC = 0x0,
+ PROXY2_PROTOCOL_STREAM = 0x1,
+ PROXY2_PROTOCOL_DGRAM = 0x2
+};
+
+/** PROXYv2 protocol header section */
+struct proxy2_header {
+ uint8_t signature[sizeof(PROXY2_SIGNATURE)];
+ uint8_t version_command;
+ uint8_t family_protocol;
+ uint16_t length; /**< Length of the address section */
+};
+
+/** PROXYv2 additional information in Type-Length-Value (TLV) format. */
+struct proxy2_tlv {
+ uint8_t type;
+ uint8_t length_hi;
+ uint8_t length_lo;
+ uint8_t value[];
+};
+
+/** PROXYv2 protocol address section */
+union proxy2_address {
+ struct {
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv4_addr;
+ struct {
+ uint8_t src_addr[PROXY2_IP6_ADDR_SIZE];
+ uint8_t dst_addr[PROXY2_IP6_ADDR_SIZE];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ipv6_addr;
+ struct {
+ uint8_t src_addr[PROXY2_UNIX_ADDR_SIZE];
+ uint8_t dst_addr[PROXY2_UNIX_ADDR_SIZE];
+ } unix_addr;
+};
+
/** Gets protocol version from the specified PROXYv2 header. */
static inline unsigned char proxy2_header_version(const struct proxy2_header* h)
return (union proxy2_address *) ((uint8_t *) h + sizeof(struct proxy2_header));
}
+static inline struct proxy2_tlv *get_tlvs(const struct proxy2_header *h, size_t addr_len)
+{
+ return (struct proxy2_tlv *) ((uint8_t *) proxy2_get_address(h) + addr_len);
+}
+
+/** Gets the length of the TLV's `value` attribute. */
+static inline uint16_t proxy2_tlv_length(const struct proxy2_tlv *tlv)
+{
+ return ((uint16_t) tlv->length_hi << 16) | tlv->length_lo;
+}
+
+static inline bool has_tlv(const struct proxy2_header *h,
+ const struct proxy2_tlv *tlv)
+{
+ uint64_t addr_length = ntohs(h->length);
+ ptrdiff_t hdr_len = sizeof(struct proxy2_header) + addr_length;
+
+ uint8_t *tlv_hdr_end = (uint8_t *) tlv + sizeof(struct proxy2_tlv);
+ ptrdiff_t distance = tlv_hdr_end - (uint8_t *) h;
+ if (hdr_len < distance)
+ return false;
+
+ uint8_t *tlv_end = tlv_hdr_end + proxy2_tlv_length(tlv);
+ distance = tlv_end - (uint8_t *) h;
+ return hdr_len >= distance;
+}
+
+static inline void next_tlv(struct proxy2_tlv **tlv)
+{
+ uint8_t *next = ((uint8_t *) *tlv + sizeof(struct proxy2_tlv) + proxy2_tlv_length(*tlv));
+ *tlv = (struct proxy2_tlv *) next;
+}
+
bool proxy_allowed(const struct network *net, const struct sockaddr *saddr)
{
const struct proxy2_header *hdr = (struct proxy2_header *) buf;
- uint64_t addr_length = ntohs(hdr->length);
- ssize_t hdr_len = sizeof(struct proxy2_header) + addr_length;
+ uint64_t content_length = ntohs(hdr->length);
+ ssize_t hdr_len = sizeof(struct proxy2_header) + content_length;
/* PROXYv2 requires the header to be received all at once */
if (nread < hdr_len) {
/* Parse addresses */
union proxy2_address* addr = proxy2_get_address(hdr);
+ size_t addr_length = 0;
switch(out->family) {
case AF_INET:
- if (addr_length < sizeof(addr->ipv4_addr))
+ addr_length = sizeof(addr->ipv4_addr);
+ if (content_length < addr_length)
return kr_error(KNOT_EMALF);
out->src_addr.ip4 = (struct sockaddr_in) {
};
break;
case AF_INET6:
- if (addr_length < sizeof(addr->ipv6_addr))
+ addr_length = sizeof(addr->ipv6_addr);
+ if (content_length < addr_length)
return kr_error(KNOT_EMALF);
out->src_addr.ip6 = (struct sockaddr_in6) {
break;
}
+ /* Process additional information */
+ for (struct proxy2_tlv *tlv = get_tlvs(hdr, addr_length); has_tlv(hdr, tlv); next_tlv(&tlv)) {
+ switch (tlv->type) {
+ case TLV_TYPE_SSL:
+ out->has_tls = true;
+ break;
+ /* TODO: add more TLV types if needed */
+ }
+ }
+
fill_wirebuf:
return session_wirebuf_trim(s, hdr_len);
}
extern const char PROXY2_SIGNATURE[12];
#define PROXY2_MIN_SIZE 16
-#define PROXY2_IP6_ADDR_SIZE 16
-#define PROXY2_UNIX_ADDR_SIZE 108
enum proxy2_command {
PROXY2_CMD_LOCAL = 0x0,
PROXY2_CMD_PROXY = 0x1
};
-enum proxy2_family {
- PROXY2_AF_UNSPEC = 0x0,
- PROXY2_AF_INET = 0x1,
- PROXY2_AF_INET6 = 0x2,
- PROXY2_AF_UNIX = 0x3
-};
-
-enum proxy2_protocol {
- PROXY2_PROTOCOL_UNSPEC = 0x0,
- PROXY2_PROTOCOL_STREAM = 0x1,
- PROXY2_PROTOCOL_DGRAM = 0x2
-};
-
-/** PROXYv2 protocol header section */
-struct proxy2_header {
- uint8_t signature[sizeof(PROXY2_SIGNATURE)];
- uint8_t version_command;
- uint8_t family_protocol;
- uint16_t length; /**< Length of the address section */
-};
-
-/** PROXYv2 protocol address section */
-union proxy2_address {
- struct {
- uint32_t src_addr;
- uint32_t dst_addr;
- uint16_t src_port;
- uint16_t dst_port;
- } ipv4_addr;
- struct {
- uint8_t src_addr[PROXY2_IP6_ADDR_SIZE];
- uint8_t dst_addr[PROXY2_IP6_ADDR_SIZE];
- uint16_t src_port;
- uint16_t dst_port;
- } ipv6_addr;
- struct {
- uint8_t src_addr[PROXY2_UNIX_ADDR_SIZE];
- uint8_t dst_addr[PROXY2_UNIX_ADDR_SIZE];
- } unix_addr;
-};
-
/** Parsed result of the PROXY protocol */
struct proxy_result {
enum proxy2_command command; /**< Proxy command - PROXY or LOCAL. */
int protocol; /**< Protocol type from socket library (e.g. SOCK_STREAM). */
union kr_sockaddr src_addr; /**< Parsed source address and port. */
union kr_sockaddr dst_addr; /**< Parsed destination address and port. */
+ bool has_tls : 1; /**< `true` = client has used TLS with the proxy.
+ If TLS padding is enabled, it will be used even if
+ the proxy did not use TLS with kresd. */
};
/** Checks for a PROXY protocol version 2 signature in the specified buffer. */
* wire buffer. The function assumes that the PROXYv2 signature is present
* and has been already checked by the caller (like `udp_recv` or `tcp_recv`). */
ssize_t proxy_process_header(struct proxy_result *out, struct session *s,
- const void *buf, ssize_t nread);
+ const void *buf, ssize_t nread);
if (session->handle && session->handle->type == UV_TCP) {
free(session->wire_buf);
}
- if (session->proxy) {
- free(session->proxy);
- }
+ free(session->proxy);
#if ENABLE_DOH2
http_free(session->http_ctx);
#endif
kr_request_set_extended_error(req, KNOT_EDNS_EDE_NONE, NULL);
array_init(req->qsource.headers);
if (session) {
- const struct sockaddr *src_addr = NULL;
- const struct sockaddr *comm_addr = NULL;
- const struct sockaddr *dst_addr = NULL;
- const struct proxy_result *proxy = NULL;
- if (comm) {
- src_addr = comm->src_addr;
- comm_addr = comm->comm_addr;
- dst_addr = comm->dst_addr;
- proxy = comm->proxy;
- }
+ kr_require(comm);
+
+ const struct sockaddr *src_addr = comm->src_addr;
+ const struct sockaddr *comm_addr = comm->comm_addr;
+ const struct sockaddr *dst_addr = comm->dst_addr;
+ const struct proxy_result *proxy = comm->proxy;
req->qsource.comm_flags.tcp = session_get_handle(session)->type == UV_TCP;
req->qsource.comm_flags.tls = session_flags(session)->has_tls;
req->qsource.flags = req->qsource.comm_flags;
if (proxy) {
req->qsource.flags.tcp = proxy->protocol == SOCK_STREAM;
+ req->qsource.flags.tls = proxy->has_tls;
}
req->qsource.stream_id = -1;
#if ENABLE_DOH2
- if (req->qsource.flags.http) {
+ if (req->qsource.comm_flags.http) {
struct http_ctx *http_ctx = session_http_get_server_ctx(session);
struct http_stream stream = queue_head(http_ctx->streams);
req->qsource.stream_id = stream.id;
}
#endif
/* We need to store a copy of peer address. */
- if (src_addr) {
- memcpy(&ctx->source.addr.ip, src_addr, kr_sockaddr_len(src_addr));
- req->qsource.addr = &ctx->source.addr.ip;
- } else {
- req->qsource.addr = NULL;
- }
+ memcpy(&ctx->source.addr.ip, src_addr, kr_sockaddr_len(src_addr));
+ req->qsource.addr = &ctx->source.addr.ip;
if (!comm_addr)
comm_addr = src_addr;
- if (comm_addr) {
- memcpy(&ctx->source.comm_addr.ip, comm_addr, kr_sockaddr_len(comm_addr));
- req->qsource.comm_addr = &ctx->source.comm_addr.ip;
- } else {
- req->qsource.comm_addr = NULL;
- }
+ memcpy(&ctx->source.comm_addr.ip, comm_addr, kr_sockaddr_len(comm_addr));
+ req->qsource.comm_addr = &ctx->source.comm_addr.ip;
if (!dst_addr) /* We wouldn't have to copy in this case, but for consistency. */
dst_addr = session_get_sockname(session);
- if (dst_addr) {
- memcpy(&ctx->source.dst_addr.ip, dst_addr, kr_sockaddr_len(dst_addr));
- req->qsource.dst_addr = &ctx->source.dst_addr.ip;
- } else {
- req->qsource.dst_addr = NULL;
- }
+ memcpy(&ctx->source.dst_addr.ip, dst_addr, kr_sockaddr_len(dst_addr));
+ req->qsource.dst_addr = &ctx->source.dst_addr.ip;
}
req->selection_context.is_tls_capable = is_tls_capable;
wire_size += KR_COOKIE_OPT_MAX_LEN;
}
#endif /* ENABLE_COOKIES */
- if (req->qsource.flags.tls) {
+ if (req->qsource.flags.tls || req->qsource.comm_flags.tls) {
wire_size += edns_padding_option_size(req->ctx->tls_padding);
}
return knot_pkt_reserve(pkt, wire_size);
{
if (kr_fails_assert(request && request->answer && request->ctx))
return kr_error(EINVAL);
- if (!request->qsource.flags.tls) {
+ if (!request->qsource.flags.tls && !request->qsource.comm_flags.tls) {
/* Not meaningful to pad without encryption. */
return kr_ok();
}
// Find answer_max: limit on DNS wire length.
uint16_t answer_max;
const struct kr_request_qsource_flags *qs_flags = &request->qsource.flags;
- if (kr_fails_assert((qs_flags->tls || qs_flags->http) ? qs_flags->tcp : true))
+ const struct kr_request_qsource_flags *qs_cflags = &request->qsource.comm_flags;
+ if (kr_fails_assert(!(qs_flags->tls || qs_cflags->tls || qs_cflags->http) || qs_flags->tcp))
goto fail;
- if (!request->qsource.addr || qs_flags->tcp) {
+ if (!request->qsource.addr || qs_flags->tcp || qs_cflags->tcp) {
// not on UDP
answer_max = KNOT_WIRE_MAX_PKTSIZE;
} else if (knot_pkt_has_edns(qs_pkt)) {