1865f46b2SCy Schubert /* 2865f46b2SCy Schubert * util/proxy_protocol.c - event notification 3865f46b2SCy Schubert * 4865f46b2SCy Schubert * Copyright (c) 2022, NLnet Labs. All rights reserved. 5865f46b2SCy Schubert * 6865f46b2SCy Schubert * This software is open source. 7865f46b2SCy Schubert * 8865f46b2SCy Schubert * Redistribution and use in source and binary forms, with or without 9865f46b2SCy Schubert * modification, are permitted provided that the following conditions 10865f46b2SCy Schubert * are met: 11865f46b2SCy Schubert * 12865f46b2SCy Schubert * Redistributions of source code must retain the above copyright notice, 13865f46b2SCy Schubert * this list of conditions and the following disclaimer. 14865f46b2SCy Schubert * 15865f46b2SCy Schubert * Redistributions in binary form must reproduce the above copyright notice, 16865f46b2SCy Schubert * this list of conditions and the following disclaimer in the documentation 17865f46b2SCy Schubert * and/or other materials provided with the distribution. 18865f46b2SCy Schubert * 19865f46b2SCy Schubert * Neither the name of the NLNET LABS nor the names of its contributors may 20865f46b2SCy Schubert * be used to endorse or promote products derived from this software without 21865f46b2SCy Schubert * specific prior written permission. 22865f46b2SCy Schubert * 23865f46b2SCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24865f46b2SCy Schubert * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25865f46b2SCy Schubert * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26865f46b2SCy Schubert * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27865f46b2SCy Schubert * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28865f46b2SCy Schubert * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29865f46b2SCy Schubert * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30865f46b2SCy Schubert * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31865f46b2SCy Schubert * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32865f46b2SCy Schubert * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33865f46b2SCy Schubert * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34865f46b2SCy Schubert */ 35865f46b2SCy Schubert 36865f46b2SCy Schubert /** 37865f46b2SCy Schubert * \file 38865f46b2SCy Schubert * 39865f46b2SCy Schubert * This file contains PROXY protocol functions. 40865f46b2SCy Schubert */ 41865f46b2SCy Schubert #include "util/proxy_protocol.h" 42865f46b2SCy Schubert 43103ba509SCy Schubert /** 44103ba509SCy Schubert * Internal struct initialized with function pointers for writing uint16 and 45103ba509SCy Schubert * uint32. 46103ba509SCy Schubert */ 47103ba509SCy Schubert struct proxy_protocol_data { 48103ba509SCy Schubert void (*write_uint16)(void* buf, uint16_t data); 49103ba509SCy Schubert void (*write_uint32)(void* buf, uint32_t data); 50103ba509SCy Schubert }; 51103ba509SCy Schubert struct proxy_protocol_data pp_data; 52103ba509SCy Schubert 53103ba509SCy Schubert /** 54103ba509SCy Schubert * Internal lookup table; could be further generic like sldns_lookup_table 55103ba509SCy Schubert * for all the future generic stuff. 56103ba509SCy Schubert */ 57103ba509SCy Schubert struct proxy_protocol_lookup_table { 58103ba509SCy Schubert int id; 59103ba509SCy Schubert const char *text; 60103ba509SCy Schubert }; 61103ba509SCy Schubert 62103ba509SCy Schubert /** 63103ba509SCy Schubert * Internal parsing error text; could be exposed with pp_lookup_error. 64103ba509SCy Schubert */ 65103ba509SCy Schubert static struct proxy_protocol_lookup_table pp_parse_errors_data[] = { 66103ba509SCy Schubert { PP_PARSE_NOERROR, "no parse error" }, 67103ba509SCy Schubert { PP_PARSE_SIZE, "not enough space for header" }, 68103ba509SCy Schubert { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" }, 69103ba509SCy Schubert { PP_PARSE_UNKNOWN_CMD, "unknown command" }, 70103ba509SCy Schubert { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" }, 71103ba509SCy Schubert }; 72103ba509SCy Schubert 73103ba509SCy Schubert void 74103ba509SCy Schubert pp_init(void (*write_uint16)(void* buf, uint16_t data), 75103ba509SCy Schubert void (*write_uint32)(void* buf, uint32_t data)) { 76103ba509SCy Schubert pp_data.write_uint16 = write_uint16; 77103ba509SCy Schubert pp_data.write_uint32 = write_uint32; 78103ba509SCy Schubert } 79103ba509SCy Schubert 80103ba509SCy Schubert const char* 81103ba509SCy Schubert pp_lookup_error(enum pp_parse_errors error) { 82103ba509SCy Schubert return pp_parse_errors_data[error].text; 83103ba509SCy Schubert } 84103ba509SCy Schubert 85103ba509SCy Schubert size_t 86103ba509SCy Schubert pp2_write_to_buf(uint8_t* buf, size_t buflen, 87103ba509SCy Schubert #ifdef INET6 88103ba509SCy Schubert struct sockaddr_storage* src, 89103ba509SCy Schubert #else 90103ba509SCy Schubert struct sockaddr_in* src, 91103ba509SCy Schubert #endif 92865f46b2SCy Schubert int stream) 93865f46b2SCy Schubert { 94865f46b2SCy Schubert int af; 95103ba509SCy Schubert size_t expected_size; 96865f46b2SCy Schubert if(!src) return 0; 97865f46b2SCy Schubert af = (int)((struct sockaddr_in*)src)->sin_family; 98103ba509SCy Schubert expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36); 99103ba509SCy Schubert if(buflen < expected_size) { 100865f46b2SCy Schubert return 0; 101865f46b2SCy Schubert } 102865f46b2SCy Schubert /* sig */ 103103ba509SCy Schubert memcpy(buf, PP2_SIG, PP2_SIG_LEN); 104103ba509SCy Schubert buf += PP2_SIG_LEN; 105865f46b2SCy Schubert /* version and command */ 106103ba509SCy Schubert *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; 107103ba509SCy Schubert buf++; 108103ba509SCy Schubert switch(af) { 109103ba509SCy Schubert case AF_INET: 110865f46b2SCy Schubert /* family and protocol */ 111103ba509SCy Schubert *buf = (PP2_AF_INET<<4) | 112103ba509SCy Schubert (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 113103ba509SCy Schubert buf++; 114865f46b2SCy Schubert /* length */ 115103ba509SCy Schubert (*pp_data.write_uint16)(buf, 12); 116103ba509SCy Schubert buf += 2; 117865f46b2SCy Schubert /* src addr */ 118103ba509SCy Schubert memcpy(buf, 119865f46b2SCy Schubert &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); 120103ba509SCy Schubert buf += 4; 121865f46b2SCy Schubert /* dst addr */ 122103ba509SCy Schubert (*pp_data.write_uint32)(buf, 0); 123103ba509SCy Schubert buf += 4; 124865f46b2SCy Schubert /* src port */ 125103ba509SCy Schubert memcpy(buf, 126865f46b2SCy Schubert &((struct sockaddr_in*)src)->sin_port, 2); 127103ba509SCy Schubert buf += 2; 128865f46b2SCy Schubert /* dst addr */ 129865f46b2SCy Schubert /* dst port */ 130103ba509SCy Schubert (*pp_data.write_uint16)(buf, 12); 131103ba509SCy Schubert break; 132103ba509SCy Schubert #ifdef INET6 133103ba509SCy Schubert case AF_INET6: 134103ba509SCy Schubert /* family and protocol */ 135103ba509SCy Schubert *buf = (PP2_AF_INET6<<4) | 136103ba509SCy Schubert (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 137103ba509SCy Schubert buf++; 138103ba509SCy Schubert /* length */ 139103ba509SCy Schubert (*pp_data.write_uint16)(buf, 36); 140103ba509SCy Schubert buf += 2; 141103ba509SCy Schubert /* src addr */ 142103ba509SCy Schubert memcpy(buf, 143103ba509SCy Schubert &((struct sockaddr_in6*)src)->sin6_addr, 16); 144103ba509SCy Schubert buf += 16; 145103ba509SCy Schubert /* dst addr */ 146103ba509SCy Schubert memset(buf, 0, 16); 147103ba509SCy Schubert buf += 16; 148103ba509SCy Schubert /* src port */ 149103ba509SCy Schubert memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2); 150103ba509SCy Schubert buf += 2; 151103ba509SCy Schubert /* dst port */ 152103ba509SCy Schubert (*pp_data.write_uint16)(buf, 0); 153103ba509SCy Schubert break; 154103ba509SCy Schubert #endif /* INET6 */ 155103ba509SCy Schubert case AF_UNIX: 156*56850988SCy Schubert ATTR_FALLTHROUGH 157103ba509SCy Schubert /* fallthrough */ 158103ba509SCy Schubert default: 159103ba509SCy Schubert return 0; 160865f46b2SCy Schubert } 161103ba509SCy Schubert return expected_size; 162865f46b2SCy Schubert } 163865f46b2SCy Schubert 164103ba509SCy Schubert int 165103ba509SCy Schubert pp2_read_header(uint8_t* buf, size_t buflen) 166865f46b2SCy Schubert { 167865f46b2SCy Schubert size_t size; 168103ba509SCy Schubert struct pp2_header* header = (struct pp2_header*)buf; 169865f46b2SCy Schubert /* Try to fail all the unsupported cases first. */ 170103ba509SCy Schubert if(buflen < PP2_HEADER_SIZE) { 171103ba509SCy Schubert return PP_PARSE_SIZE; 172865f46b2SCy Schubert } 173865f46b2SCy Schubert /* Check for PROXYv2 header */ 174865f46b2SCy Schubert if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || 175865f46b2SCy Schubert ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { 176103ba509SCy Schubert return PP_PARSE_WRONG_HEADERv2; 177865f46b2SCy Schubert } 178865f46b2SCy Schubert /* Check the length */ 179865f46b2SCy Schubert size = PP2_HEADER_SIZE + ntohs(header->len); 180103ba509SCy Schubert if(buflen < size) { 181103ba509SCy Schubert return PP_PARSE_SIZE; 182865f46b2SCy Schubert } 183865f46b2SCy Schubert /* Check for supported commands */ 184865f46b2SCy Schubert if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && 185865f46b2SCy Schubert (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { 186103ba509SCy Schubert return PP_PARSE_UNKNOWN_CMD; 187865f46b2SCy Schubert } 188865f46b2SCy Schubert /* Check for supported family and protocol */ 189103ba509SCy Schubert if(header->fam_prot != PP2_UNSPEC_UNSPEC && 190103ba509SCy Schubert header->fam_prot != PP2_INET_STREAM && 191103ba509SCy Schubert header->fam_prot != PP2_INET_DGRAM && 192103ba509SCy Schubert header->fam_prot != PP2_INET6_STREAM && 193103ba509SCy Schubert header->fam_prot != PP2_INET6_DGRAM && 194103ba509SCy Schubert header->fam_prot != PP2_UNIX_STREAM && 195103ba509SCy Schubert header->fam_prot != PP2_UNIX_DGRAM) { 196103ba509SCy Schubert return PP_PARSE_UNKNOWN_FAM_PROT; 197865f46b2SCy Schubert } 198865f46b2SCy Schubert /* We have a correct header */ 199103ba509SCy Schubert return PP_PARSE_NOERROR; 200865f46b2SCy Schubert } 201