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