Home | History | Annotate | Line # | Download | only in slapd
proxyp.c revision 1.1.1.2
      1      1.1  christos /*	$NetBSD: proxyp.c,v 1.1.1.2 2025/09/05 21:09:47 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.1.1.2  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.1.1.2 2025/09/05 21:09:47 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