Home | History | Annotate | Line # | Download | only in slapd
      1  1.1  christos /*	$NetBSD: proxyp.c,v 1.3 2025/09/05 21:16:25 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /* $OpenLDAP$ */
      4  1.1  christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  1.1  christos  *
      6  1.3  christos  * Copyright 2000-2024 The OpenLDAP Foundation.
      7  1.1  christos  * All rights reserved.
      8  1.1  christos  *
      9  1.1  christos  * Redistribution and use in source and binary forms, with or without
     10  1.1  christos  * modification, are permitted only as authorized by the OpenLDAP
     11  1.1  christos  * Public License.
     12  1.1  christos  *
     13  1.1  christos  * A copy of this license is available in the file LICENSE in the
     14  1.1  christos  * top-level directory of the distribution or, alternatively, at
     15  1.1  christos  * <http://www.OpenLDAP.org/license.html>.
     16  1.1  christos  */
     17  1.1  christos 
     18  1.1  christos #include <sys/cdefs.h>
     19  1.1  christos __RCSID("$NetBSD: proxyp.c,v 1.3 2025/09/05 21:16:25 christos Exp $");
     20  1.1  christos 
     21  1.1  christos #include "portable.h"
     22  1.1  christos #include "slap.h"
     23  1.1  christos 
     24  1.1  christos #ifdef HAVE_STDINT_H
     25  1.1  christos #include <stdint.h>
     26  1.1  christos #endif
     27  1.1  christos #ifdef HAVE_INTTYPES_H
     28  1.1  christos #include <inttypes.h>
     29  1.1  christos #endif
     30  1.1  christos 
     31  1.1  christos #include <lber_types.h>
     32  1.1  christos #include <ac/string.h>
     33  1.1  christos #include <ac/errno.h>
     34  1.1  christos 
     35  1.1  christos typedef struct {
     36  1.1  christos 	uint8_t  sig[12];	/* hex 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a */
     37  1.1  christos 	uint8_t  ver_cmd;	/* protocol version and command */
     38  1.1  christos 	uint8_t  fam;		/* protocol family and address */
     39  1.1  christos 	uint16_t len;		/* length of address data */
     40  1.1  christos } proxyp_header;
     41  1.1  christos 
     42  1.1  christos typedef union {
     43  1.1  christos 	struct {	/* for TCP/UDP over IPv4, len = 12 */
     44  1.1  christos 		uint32_t src_addr;
     45  1.1  christos 		uint32_t dst_addr;
     46  1.1  christos 		uint16_t src_port;
     47  1.1  christos 		uint16_t dst_port;
     48  1.1  christos 	} ip4;
     49  1.1  christos 	struct {	/* for TCP/UDP over IPv6, len = 36 */
     50  1.1  christos 		uint8_t  src_addr[16];
     51  1.1  christos 		uint8_t  dst_addr[16];
     52  1.1  christos 		uint16_t src_port;
     53  1.1  christos 		uint16_t dst_port;
     54  1.1  christos 	} ip6;
     55  1.1  christos 	struct {	/* for AF_UNIX sockets, len = 216 */
     56  1.1  christos 		uint8_t src_addr[108];
     57  1.1  christos 		uint8_t dst_addr[108];
     58  1.1  christos 	} unx;
     59  1.1  christos } proxyp_addr;
     60  1.1  christos 
     61  1.1  christos static const uint8_t proxyp_sig[12] = {
     62  1.1  christos 	0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a,
     63  1.1  christos };
     64  1.1  christos 
     65  1.1  christos int
     66  1.1  christos proxyp( ber_socket_t sfd, Sockaddr *from ) {
     67  1.1  christos 	proxyp_header pph;
     68  1.1  christos 	proxyp_addr ppa;
     69  1.1  christos 	char peername[LDAP_IPADDRLEN];
     70  1.1  christos 	struct berval peerbv = BER_BVC(peername);
     71  1.1  christos 	/* Maximum size of header minus static component size is max option size */
     72  1.1  christos 	uint8_t proxyp_options[536 - 16];
     73  1.1  christos 	int pph_len;
     74  1.1  christos 	int ret;
     75  1.1  christos 
     76  1.1  christos 	peername[0] = '\0';
     77  1.1  christos 
     78  1.1  christos 	do {
     79  1.1  christos 		ret = tcp_read( SLAP_FD2SOCK( sfd ), &pph, sizeof(pph) );
     80  1.1  christos 	} while ( ret == -1 && errno == EINTR );
     81  1.1  christos 
     82  1.1  christos 	if ( ret == -1 ) {
     83  1.1  christos 		char ebuf[128];
     84  1.1  christos 		int save_errno = errno;
     85  1.1  christos 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
     86  1.1  christos 				"header read failed %d (%s)\n",
     87  1.1  christos 				(long)sfd, save_errno,
     88  1.1  christos 				AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
     89  1.1  christos 		return 0;
     90  1.1  christos 	} else if ( ret != sizeof(pph) ) {
     91  1.1  christos 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
     92  1.1  christos 				"header read insufficient data %d\n",
     93  1.1  christos 				(long)sfd, ret );
     94  1.1  christos 		return 0;
     95  1.1  christos 	}
     96  1.1  christos 
     97  1.1  christos 	if ( memcmp( pph.sig, proxyp_sig, 12 ) != 0 ) {
     98  1.1  christos 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
     99  1.1  christos 				"invalid header signature\n", (long)sfd );
    100  1.1  christos 		return 0;
    101  1.1  christos 	}
    102  1.1  christos 
    103  1.1  christos 	if ( ( pph.ver_cmd & 0xF0 ) != 0x20 ) {
    104  1.1  christos 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    105  1.1  christos 				"invalid header version %x\n",
    106  1.1  christos 				(long)sfd, pph.ver_cmd & 0xF0 );
    107  1.1  christos 		return 0;
    108  1.1  christos 	}
    109  1.1  christos 
    110  1.1  christos 	pph_len = ntohs( pph.len );
    111  1.1  christos 	if ( ( pph.ver_cmd & 0x0F ) == 0x01 ) { /* PROXY command */
    112  1.1  christos 		int addr_len;
    113  1.1  christos 		switch ( pph.fam ) {
    114  1.1  christos 		case 0x11: /* TCPv4 */
    115  1.1  christos 			addr_len = sizeof( ppa.ip4 );
    116  1.1  christos 			break;
    117  1.1  christos 		case 0x21: /* TCPv6 */
    118  1.1  christos 			addr_len = sizeof( ppa.ip6 );
    119  1.1  christos 			break;
    120  1.1  christos 		default:
    121  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    122  1.1  christos 					"unsupported protocol %x\n",
    123  1.1  christos 					(long)sfd, pph.fam );
    124  1.1  christos 			return 0;
    125  1.1  christos 		}
    126  1.1  christos 
    127  1.1  christos 		if ( pph_len < addr_len ) {
    128  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    129  1.1  christos 					"address length %d too small, expecting %d\n",
    130  1.1  christos 					(long)sfd, pph_len, addr_len );
    131  1.1  christos 			return 0;
    132  1.1  christos 		}
    133  1.1  christos 
    134  1.1  christos 		do {
    135  1.1  christos 			ret = tcp_read( SLAP_FD2SOCK (sfd), &ppa, addr_len );
    136  1.1  christos 		} while ( ret == -1 && errno == EINTR );
    137  1.1  christos 
    138  1.1  christos 		if ( ret == -1 ) {
    139  1.1  christos 			char ebuf[128];
    140  1.1  christos 			int save_errno = errno;
    141  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    142  1.1  christos 					"address read failed %d (%s)\n",
    143  1.1  christos 					(long)sfd, save_errno,
    144  1.1  christos 					AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
    145  1.1  christos 			return 0;
    146  1.1  christos 		} else if ( ret != addr_len ) {
    147  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    148  1.1  christos 					"address read insufficient data, expecting %d, read %d\n",
    149  1.1  christos 					(long)sfd, addr_len, ret );
    150  1.1  christos 			return 0;
    151  1.1  christos 		}
    152  1.1  christos 
    153  1.1  christos 		pph_len -= addr_len;
    154  1.1  christos 	}
    155  1.1  christos 
    156  1.1  christos 	switch ( pph.ver_cmd & 0x0F ) {
    157  1.1  christos 	case 0x01: /* PROXY command */
    158  1.1  christos 		switch ( pph.fam ) {
    159  1.1  christos 		case 0x11: /* TCPv4 */
    160  1.1  christos 			ldap_pvt_sockaddrstr( from, &peerbv );
    161  1.1  christos 			Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
    162  1.1  christos 					(long)sfd, peername );
    163  1.1  christos 
    164  1.1  christos 			from->sa_in_addr.sin_family = AF_INET;
    165  1.1  christos 			from->sa_in_addr.sin_addr.s_addr = ppa.ip4.src_addr;
    166  1.1  christos 			from->sa_in_addr.sin_port = ppa.ip4.src_port;
    167  1.1  christos 			break;
    168  1.1  christos 
    169  1.1  christos 		case 0x21: /* TCPv6 */
    170  1.1  christos #ifdef LDAP_PF_INET6
    171  1.1  christos 			ldap_pvt_sockaddrstr( from, &peerbv );
    172  1.1  christos 			Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
    173  1.1  christos 					(long)sfd, peername );
    174  1.1  christos 			from->sa_in6_addr.sin6_family = AF_INET6;
    175  1.1  christos 			memcpy( &from->sa_in6_addr.sin6_addr, ppa.ip6.src_addr,
    176  1.1  christos 					sizeof(ppa.ip6.src_addr) );
    177  1.1  christos 			from->sa_in6_addr.sin6_port = ppa.ip6.src_port;
    178  1.1  christos #else
    179  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    180  1.1  christos 					"IPv6 proxied addresses disabled\n",
    181  1.1  christos 					(long)sfd );
    182  1.1  christos 			return 0;
    183  1.1  christos #endif
    184  1.1  christos 			break;
    185  1.1  christos 		}
    186  1.1  christos 
    187  1.1  christos 		break;
    188  1.1  christos 
    189  1.1  christos 	case 0x00: /* LOCAL command */
    190  1.1  christos 		Debug( LDAP_DEBUG_CONNS, "proxyp(%ld): "
    191  1.1  christos 				"local connection, ignoring proxy data\n",
    192  1.1  christos 				(long)sfd );
    193  1.1  christos 		break;
    194  1.1  christos 
    195  1.1  christos 	default:
    196  1.1  christos 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): invalid command %x\n",
    197  1.1  christos 				(long)sfd, pph.ver_cmd & 0x0F );
    198  1.1  christos 		return 0;
    199  1.1  christos 	}
    200  1.1  christos 
    201  1.1  christos 	/* Clear out any options left in proxy packet */
    202  1.1  christos 	if ( pph_len > 0 ) {
    203  1.1  christos 		if (pph_len > sizeof( proxyp_options ) ) {
    204  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    205  1.1  christos 					"options size %d too big\n",
    206  1.1  christos 					(long)sfd, pph_len );
    207  1.1  christos 			return 0;
    208  1.1  christos 		}
    209  1.1  christos 
    210  1.1  christos 		do {
    211  1.1  christos 			ret = tcp_read( SLAP_FD2SOCK (sfd), &proxyp_options, pph_len );
    212  1.1  christos 		} while ( ret == -1 && errno == EINTR );
    213  1.1  christos 
    214  1.1  christos 		if ( ret == -1 ) {
    215  1.1  christos 			char ebuf[128];
    216  1.1  christos 			int save_errno = errno;
    217  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    218  1.1  christos 					"options read failed %d (%s)\n",
    219  1.1  christos 					(long)sfd, save_errno,
    220  1.1  christos 					AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
    221  1.1  christos 			return 0;
    222  1.1  christos 		} else if ( ret != pph_len ) {
    223  1.1  christos 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
    224  1.1  christos 					"options read insufficient data, expecting %d, read %d\n",
    225  1.1  christos 					(long)sfd, pph_len, ret );
    226  1.1  christos 			return 0;
    227  1.1  christos 		}
    228  1.1  christos 	}
    229  1.1  christos 
    230  1.1  christos 	return 1;
    231  1.1  christos }
    232