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