summaryrefslogtreecommitdiffstats
path: root/servers/slapd/proxyp.c
diff options
context:
space:
mode:
Diffstat (limited to 'servers/slapd/proxyp.c')
-rw-r--r--servers/slapd/proxyp.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/servers/slapd/proxyp.c b/servers/slapd/proxyp.c
new file mode 100644
index 0000000..c548bca
--- /dev/null
+++ b/servers/slapd/proxyp.c
@@ -0,0 +1,226 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <lber_types.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+typedef struct {
+ uint8_t sig[12]; /* hex 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a */
+ uint8_t ver_cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and address */
+ uint16_t len; /* length of address data */
+} proxyp_header;
+
+typedef union {
+ struct { /* for TCP/UDP over IPv4, len = 12 */
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip4;
+ struct { /* for TCP/UDP over IPv6, len = 36 */
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip6;
+ struct { /* for AF_UNIX sockets, len = 216 */
+ uint8_t src_addr[108];
+ uint8_t dst_addr[108];
+ } unx;
+} proxyp_addr;
+
+static const uint8_t proxyp_sig[12] = {
+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a,
+};
+
+int
+proxyp( ber_socket_t sfd, Sockaddr *from ) {
+ proxyp_header pph;
+ proxyp_addr ppa;
+ char peername[LDAP_IPADDRLEN];
+ struct berval peerbv = BER_BVC(peername);
+ /* Maximum size of header minus static component size is max option size */
+ uint8_t proxyp_options[536 - 16];
+ int pph_len;
+ int ret;
+
+ peername[0] = '\0';
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK( sfd ), &pph, sizeof(pph) );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "header read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != sizeof(pph) ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "header read insufficient data %d\n",
+ (long)sfd, ret );
+ return 0;
+ }
+
+ if ( memcmp( pph.sig, proxyp_sig, 12 ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "invalid header signature\n", (long)sfd );
+ return 0;
+ }
+
+ if ( ( pph.ver_cmd & 0xF0 ) != 0x20 ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "invalid header version %x\n",
+ (long)sfd, pph.ver_cmd & 0xF0 );
+ return 0;
+ }
+
+ pph_len = ntohs( pph.len );
+ if ( ( pph.ver_cmd & 0x0F ) == 0x01 ) { /* PROXY command */
+ int addr_len;
+ switch ( pph.fam ) {
+ case 0x11: /* TCPv4 */
+ addr_len = sizeof( ppa.ip4 );
+ break;
+ case 0x21: /* TCPv6 */
+ addr_len = sizeof( ppa.ip6 );
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "unsupported protocol %x\n",
+ (long)sfd, pph.fam );
+ return 0;
+ }
+
+ if ( pph_len < addr_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address length %d too small, expecting %d\n",
+ (long)sfd, pph_len, addr_len );
+ return 0;
+ }
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK (sfd), &ppa, addr_len );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != addr_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address read insufficient data, expecting %d, read %d\n",
+ (long)sfd, addr_len, ret );
+ return 0;
+ }
+
+ pph_len -= addr_len;
+ }
+
+ switch ( pph.ver_cmd & 0x0F ) {
+ case 0x01: /* PROXY command */
+ switch ( pph.fam ) {
+ case 0x11: /* TCPv4 */
+ ldap_pvt_sockaddrstr( from, &peerbv );
+ Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
+ (long)sfd, peername );
+
+ from->sa_in_addr.sin_family = AF_INET;
+ from->sa_in_addr.sin_addr.s_addr = ppa.ip4.src_addr;
+ from->sa_in_addr.sin_port = ppa.ip4.src_port;
+ break;
+
+ case 0x21: /* TCPv6 */
+#ifdef LDAP_PF_INET6
+ ldap_pvt_sockaddrstr( from, &peerbv );
+ Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
+ (long)sfd, peername );
+ from->sa_in6_addr.sin6_family = AF_INET6;
+ memcpy( &from->sa_in6_addr.sin6_addr, ppa.ip6.src_addr,
+ sizeof(ppa.ip6.src_addr) );
+ from->sa_in6_addr.sin6_port = ppa.ip6.src_port;
+#else
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "IPv6 proxied addresses disabled\n",
+ (long)sfd );
+ return 0;
+#endif
+ break;
+ }
+
+ break;
+
+ case 0x00: /* LOCAL command */
+ Debug( LDAP_DEBUG_CONNS, "proxyp(%ld): "
+ "local connection, ignoring proxy data\n",
+ (long)sfd );
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): invalid command %x\n",
+ (long)sfd, pph.ver_cmd & 0x0F );
+ return 0;
+ }
+
+ /* Clear out any options left in proxy packet */
+ if ( pph_len > 0 ) {
+ if (pph_len > sizeof( proxyp_options ) ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options size %d too big\n",
+ (long)sfd, pph_len );
+ return 0;
+ }
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK (sfd), &proxyp_options, pph_len );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != pph_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options read insufficient data, expecting %d, read %d\n",
+ (long)sfd, pph_len, ret );
+ return 0;
+ }
+ }
+
+ return 1;
+}