mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-27 03:20:22 +08:00
227 lines
5.6 KiB
C
227 lines
5.6 KiB
C
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 2000-2020 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[LUTIL_ADDRLEN];
|
|
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 */
|
|
lutil_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
|
|
lutil_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;
|
|
}
|