15c45b740Sflorian /* 25c45b740Sflorian * util/proxy_protocol.c - event notification 35c45b740Sflorian * 45c45b740Sflorian * Copyright (c) 2022, NLnet Labs. All rights reserved. 55c45b740Sflorian * 65c45b740Sflorian * This software is open source. 75c45b740Sflorian * 85c45b740Sflorian * Redistribution and use in source and binary forms, with or without 95c45b740Sflorian * modification, are permitted provided that the following conditions 105c45b740Sflorian * are met: 115c45b740Sflorian * 125c45b740Sflorian * Redistributions of source code must retain the above copyright notice, 135c45b740Sflorian * this list of conditions and the following disclaimer. 145c45b740Sflorian * 155c45b740Sflorian * Redistributions in binary form must reproduce the above copyright notice, 165c45b740Sflorian * this list of conditions and the following disclaimer in the documentation 175c45b740Sflorian * and/or other materials provided with the distribution. 185c45b740Sflorian * 195c45b740Sflorian * Neither the name of the NLNET LABS nor the names of its contributors may 205c45b740Sflorian * be used to endorse or promote products derived from this software without 215c45b740Sflorian * specific prior written permission. 225c45b740Sflorian * 235c45b740Sflorian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 245c45b740Sflorian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 255c45b740Sflorian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 265c45b740Sflorian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 275c45b740Sflorian * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 285c45b740Sflorian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 295c45b740Sflorian * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 305c45b740Sflorian * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 315c45b740Sflorian * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 325c45b740Sflorian * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 335c45b740Sflorian * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 345c45b740Sflorian */ 355c45b740Sflorian 365c45b740Sflorian /** 375c45b740Sflorian * \file 385c45b740Sflorian * 395c45b740Sflorian * This file contains PROXY protocol functions. 405c45b740Sflorian */ 415c45b740Sflorian #include "util/proxy_protocol.h" 425c45b740Sflorian 4354cc57acSflorian /** 4454cc57acSflorian * Internal struct initialized with function pointers for writing uint16 and 4554cc57acSflorian * uint32. 4654cc57acSflorian */ 4754cc57acSflorian struct proxy_protocol_data { 4854cc57acSflorian void (*write_uint16)(void* buf, uint16_t data); 4954cc57acSflorian void (*write_uint32)(void* buf, uint32_t data); 5054cc57acSflorian }; 5154cc57acSflorian struct proxy_protocol_data pp_data; 5254cc57acSflorian 5354cc57acSflorian /** 5454cc57acSflorian * Internal lookup table; could be further generic like sldns_lookup_table 5554cc57acSflorian * for all the future generic stuff. 5654cc57acSflorian */ 5754cc57acSflorian struct proxy_protocol_lookup_table { 5854cc57acSflorian int id; 5954cc57acSflorian const char *text; 6054cc57acSflorian }; 6154cc57acSflorian 6254cc57acSflorian /** 6354cc57acSflorian * Internal parsing error text; could be exposed with pp_lookup_error. 6454cc57acSflorian */ 6554cc57acSflorian static struct proxy_protocol_lookup_table pp_parse_errors_data[] = { 6654cc57acSflorian { PP_PARSE_NOERROR, "no parse error" }, 6754cc57acSflorian { PP_PARSE_SIZE, "not enough space for header" }, 6854cc57acSflorian { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" }, 6954cc57acSflorian { PP_PARSE_UNKNOWN_CMD, "unknown command" }, 7054cc57acSflorian { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" }, 7154cc57acSflorian }; 7254cc57acSflorian 7354cc57acSflorian void 7454cc57acSflorian pp_init(void (*write_uint16)(void* buf, uint16_t data), 7554cc57acSflorian void (*write_uint32)(void* buf, uint32_t data)) { 7654cc57acSflorian pp_data.write_uint16 = write_uint16; 7754cc57acSflorian pp_data.write_uint32 = write_uint32; 7854cc57acSflorian } 7954cc57acSflorian 8054cc57acSflorian const char* 8154cc57acSflorian pp_lookup_error(enum pp_parse_errors error) { 8254cc57acSflorian return pp_parse_errors_data[error].text; 8354cc57acSflorian } 8454cc57acSflorian 8554cc57acSflorian size_t 8654cc57acSflorian pp2_write_to_buf(uint8_t* buf, size_t buflen, 8754cc57acSflorian #ifdef INET6 8854cc57acSflorian struct sockaddr_storage* src, 8954cc57acSflorian #else 9054cc57acSflorian struct sockaddr_in* src, 9154cc57acSflorian #endif 925c45b740Sflorian int stream) 935c45b740Sflorian { 945c45b740Sflorian int af; 9554cc57acSflorian size_t expected_size; 965c45b740Sflorian if(!src) return 0; 975c45b740Sflorian af = (int)((struct sockaddr_in*)src)->sin_family; 9854cc57acSflorian expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36); 9954cc57acSflorian if(buflen < expected_size) { 1005c45b740Sflorian return 0; 1015c45b740Sflorian } 1025c45b740Sflorian /* sig */ 10354cc57acSflorian memcpy(buf, PP2_SIG, PP2_SIG_LEN); 10454cc57acSflorian buf += PP2_SIG_LEN; 1055c45b740Sflorian /* version and command */ 10654cc57acSflorian *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; 10754cc57acSflorian buf++; 10854cc57acSflorian switch(af) { 10954cc57acSflorian case AF_INET: 1105c45b740Sflorian /* family and protocol */ 11154cc57acSflorian *buf = (PP2_AF_INET<<4) | 11254cc57acSflorian (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 11354cc57acSflorian buf++; 1145c45b740Sflorian /* length */ 11554cc57acSflorian (*pp_data.write_uint16)(buf, 12); 11654cc57acSflorian buf += 2; 1175c45b740Sflorian /* src addr */ 11854cc57acSflorian memcpy(buf, 1195c45b740Sflorian &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); 12054cc57acSflorian buf += 4; 1215c45b740Sflorian /* dst addr */ 12254cc57acSflorian (*pp_data.write_uint32)(buf, 0); 12354cc57acSflorian buf += 4; 1245c45b740Sflorian /* src port */ 12554cc57acSflorian memcpy(buf, 1265c45b740Sflorian &((struct sockaddr_in*)src)->sin_port, 2); 12754cc57acSflorian buf += 2; 1285c45b740Sflorian /* dst addr */ 1295c45b740Sflorian /* dst port */ 13054cc57acSflorian (*pp_data.write_uint16)(buf, 12); 13154cc57acSflorian break; 13254cc57acSflorian #ifdef INET6 13354cc57acSflorian case AF_INET6: 13454cc57acSflorian /* family and protocol */ 13554cc57acSflorian *buf = (PP2_AF_INET6<<4) | 13654cc57acSflorian (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 13754cc57acSflorian buf++; 13854cc57acSflorian /* length */ 13954cc57acSflorian (*pp_data.write_uint16)(buf, 36); 14054cc57acSflorian buf += 2; 14154cc57acSflorian /* src addr */ 14254cc57acSflorian memcpy(buf, 14354cc57acSflorian &((struct sockaddr_in6*)src)->sin6_addr, 16); 14454cc57acSflorian buf += 16; 14554cc57acSflorian /* dst addr */ 14654cc57acSflorian memset(buf, 0, 16); 14754cc57acSflorian buf += 16; 14854cc57acSflorian /* src port */ 14954cc57acSflorian memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2); 15054cc57acSflorian buf += 2; 15154cc57acSflorian /* dst port */ 15254cc57acSflorian (*pp_data.write_uint16)(buf, 0); 15354cc57acSflorian break; 15454cc57acSflorian #endif /* INET6 */ 15554cc57acSflorian case AF_UNIX: 156*7037e34cSflorian ATTR_FALLTHROUGH 15754cc57acSflorian /* fallthrough */ 15854cc57acSflorian default: 15954cc57acSflorian return 0; 1605c45b740Sflorian } 16154cc57acSflorian return expected_size; 1625c45b740Sflorian } 1635c45b740Sflorian 16454cc57acSflorian int 16554cc57acSflorian pp2_read_header(uint8_t* buf, size_t buflen) 1665c45b740Sflorian { 1675c45b740Sflorian size_t size; 16854cc57acSflorian struct pp2_header* header = (struct pp2_header*)buf; 1695c45b740Sflorian /* Try to fail all the unsupported cases first. */ 17054cc57acSflorian if(buflen < PP2_HEADER_SIZE) { 17154cc57acSflorian return PP_PARSE_SIZE; 1725c45b740Sflorian } 1735c45b740Sflorian /* Check for PROXYv2 header */ 1745c45b740Sflorian if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || 1755c45b740Sflorian ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { 17654cc57acSflorian return PP_PARSE_WRONG_HEADERv2; 1775c45b740Sflorian } 1785c45b740Sflorian /* Check the length */ 1795c45b740Sflorian size = PP2_HEADER_SIZE + ntohs(header->len); 18054cc57acSflorian if(buflen < size) { 18154cc57acSflorian return PP_PARSE_SIZE; 1825c45b740Sflorian } 1835c45b740Sflorian /* Check for supported commands */ 1845c45b740Sflorian if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && 1855c45b740Sflorian (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { 18654cc57acSflorian return PP_PARSE_UNKNOWN_CMD; 1875c45b740Sflorian } 1885c45b740Sflorian /* Check for supported family and protocol */ 18954cc57acSflorian if(header->fam_prot != PP2_UNSPEC_UNSPEC && 19054cc57acSflorian header->fam_prot != PP2_INET_STREAM && 19154cc57acSflorian header->fam_prot != PP2_INET_DGRAM && 19254cc57acSflorian header->fam_prot != PP2_INET6_STREAM && 19354cc57acSflorian header->fam_prot != PP2_INET6_DGRAM && 19454cc57acSflorian header->fam_prot != PP2_UNIX_STREAM && 19554cc57acSflorian header->fam_prot != PP2_UNIX_DGRAM) { 19654cc57acSflorian return PP_PARSE_UNKNOWN_FAM_PROT; 1975c45b740Sflorian } 1985c45b740Sflorian /* We have a correct header */ 19954cc57acSflorian return PP_PARSE_NOERROR; 2005c45b740Sflorian } 201