/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software .
*
* Copyright 2000-2024 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
* .
*/
#include "portable.h"
#include "slap.h"
#ifdef HAVE_STDINT_H
#include
#endif
#ifdef HAVE_INTTYPES_H
#include
#endif
#include
#include
#include
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;
}