10e9b6f9fSsthen /* 20e9b6f9fSsthen * util/proxy_protocol.c - event notification 30e9b6f9fSsthen * 40e9b6f9fSsthen * Copyright (c) 2022, NLnet Labs. All rights reserved. 50e9b6f9fSsthen * 60e9b6f9fSsthen * This software is open source. 70e9b6f9fSsthen * 80e9b6f9fSsthen * Redistribution and use in source and binary forms, with or without 90e9b6f9fSsthen * modification, are permitted provided that the following conditions 100e9b6f9fSsthen * are met: 110e9b6f9fSsthen * 120e9b6f9fSsthen * Redistributions of source code must retain the above copyright notice, 130e9b6f9fSsthen * this list of conditions and the following disclaimer. 140e9b6f9fSsthen * 150e9b6f9fSsthen * Redistributions in binary form must reproduce the above copyright notice, 160e9b6f9fSsthen * this list of conditions and the following disclaimer in the documentation 170e9b6f9fSsthen * and/or other materials provided with the distribution. 180e9b6f9fSsthen * 190e9b6f9fSsthen * Neither the name of the NLNET LABS nor the names of its contributors may 200e9b6f9fSsthen * be used to endorse or promote products derived from this software without 210e9b6f9fSsthen * specific prior written permission. 220e9b6f9fSsthen * 230e9b6f9fSsthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 240e9b6f9fSsthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 250e9b6f9fSsthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 260e9b6f9fSsthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 270e9b6f9fSsthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 280e9b6f9fSsthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 290e9b6f9fSsthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 300e9b6f9fSsthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 310e9b6f9fSsthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 320e9b6f9fSsthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 330e9b6f9fSsthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 340e9b6f9fSsthen */ 350e9b6f9fSsthen 360e9b6f9fSsthen /** 370e9b6f9fSsthen * \file 380e9b6f9fSsthen * 390e9b6f9fSsthen * This file contains PROXY protocol functions. 400e9b6f9fSsthen */ 410e9b6f9fSsthen #include "util/proxy_protocol.h" 420e9b6f9fSsthen 439c7f0a49Ssthen /** 449c7f0a49Ssthen * Internal struct initialized with function pointers for writing uint16 and 459c7f0a49Ssthen * uint32. 469c7f0a49Ssthen */ 479c7f0a49Ssthen struct proxy_protocol_data { 489c7f0a49Ssthen void (*write_uint16)(void* buf, uint16_t data); 499c7f0a49Ssthen void (*write_uint32)(void* buf, uint32_t data); 509c7f0a49Ssthen }; 519c7f0a49Ssthen struct proxy_protocol_data pp_data; 529c7f0a49Ssthen 539c7f0a49Ssthen /** 549c7f0a49Ssthen * Internal lookup table; could be further generic like sldns_lookup_table 559c7f0a49Ssthen * for all the future generic stuff. 569c7f0a49Ssthen */ 579c7f0a49Ssthen struct proxy_protocol_lookup_table { 589c7f0a49Ssthen int id; 599c7f0a49Ssthen const char *text; 609c7f0a49Ssthen }; 619c7f0a49Ssthen 629c7f0a49Ssthen /** 639c7f0a49Ssthen * Internal parsing error text; could be exposed with pp_lookup_error. 649c7f0a49Ssthen */ 659c7f0a49Ssthen static struct proxy_protocol_lookup_table pp_parse_errors_data[] = { 669c7f0a49Ssthen { PP_PARSE_NOERROR, "no parse error" }, 679c7f0a49Ssthen { PP_PARSE_SIZE, "not enough space for header" }, 689c7f0a49Ssthen { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" }, 699c7f0a49Ssthen { PP_PARSE_UNKNOWN_CMD, "unknown command" }, 709c7f0a49Ssthen { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" }, 719c7f0a49Ssthen }; 729c7f0a49Ssthen 739c7f0a49Ssthen void 749c7f0a49Ssthen pp_init(void (*write_uint16)(void* buf, uint16_t data), 759c7f0a49Ssthen void (*write_uint32)(void* buf, uint32_t data)) { 769c7f0a49Ssthen pp_data.write_uint16 = write_uint16; 779c7f0a49Ssthen pp_data.write_uint32 = write_uint32; 789c7f0a49Ssthen } 799c7f0a49Ssthen 809c7f0a49Ssthen const char* 819c7f0a49Ssthen pp_lookup_error(enum pp_parse_errors error) { 829c7f0a49Ssthen return pp_parse_errors_data[error].text; 839c7f0a49Ssthen } 849c7f0a49Ssthen 859c7f0a49Ssthen size_t 869c7f0a49Ssthen pp2_write_to_buf(uint8_t* buf, size_t buflen, 879c7f0a49Ssthen #ifdef INET6 889c7f0a49Ssthen struct sockaddr_storage* src, 899c7f0a49Ssthen #else 909c7f0a49Ssthen struct sockaddr_in* src, 919c7f0a49Ssthen #endif 920e9b6f9fSsthen int stream) 930e9b6f9fSsthen { 940e9b6f9fSsthen int af; 959c7f0a49Ssthen size_t expected_size; 960e9b6f9fSsthen if(!src) return 0; 970e9b6f9fSsthen af = (int)((struct sockaddr_in*)src)->sin_family; 989c7f0a49Ssthen expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36); 999c7f0a49Ssthen if(buflen < expected_size) { 1000e9b6f9fSsthen return 0; 1010e9b6f9fSsthen } 1020e9b6f9fSsthen /* sig */ 1039c7f0a49Ssthen memcpy(buf, PP2_SIG, PP2_SIG_LEN); 1049c7f0a49Ssthen buf += PP2_SIG_LEN; 1050e9b6f9fSsthen /* version and command */ 1069c7f0a49Ssthen *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; 1079c7f0a49Ssthen buf++; 1089c7f0a49Ssthen switch(af) { 1099c7f0a49Ssthen case AF_INET: 1100e9b6f9fSsthen /* family and protocol */ 1119c7f0a49Ssthen *buf = (PP2_AF_INET<<4) | 1129c7f0a49Ssthen (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 1139c7f0a49Ssthen buf++; 1140e9b6f9fSsthen /* length */ 1159c7f0a49Ssthen (*pp_data.write_uint16)(buf, 12); 1169c7f0a49Ssthen buf += 2; 1170e9b6f9fSsthen /* src addr */ 1189c7f0a49Ssthen memcpy(buf, 1190e9b6f9fSsthen &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); 1209c7f0a49Ssthen buf += 4; 1210e9b6f9fSsthen /* dst addr */ 1229c7f0a49Ssthen (*pp_data.write_uint32)(buf, 0); 1239c7f0a49Ssthen buf += 4; 1240e9b6f9fSsthen /* src port */ 1259c7f0a49Ssthen memcpy(buf, 1260e9b6f9fSsthen &((struct sockaddr_in*)src)->sin_port, 2); 1279c7f0a49Ssthen buf += 2; 1280e9b6f9fSsthen /* dst addr */ 1290e9b6f9fSsthen /* dst port */ 1309c7f0a49Ssthen (*pp_data.write_uint16)(buf, 12); 1319c7f0a49Ssthen break; 1329c7f0a49Ssthen #ifdef INET6 1339c7f0a49Ssthen case AF_INET6: 1349c7f0a49Ssthen /* family and protocol */ 1359c7f0a49Ssthen *buf = (PP2_AF_INET6<<4) | 1369c7f0a49Ssthen (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); 1379c7f0a49Ssthen buf++; 1389c7f0a49Ssthen /* length */ 1399c7f0a49Ssthen (*pp_data.write_uint16)(buf, 36); 1409c7f0a49Ssthen buf += 2; 1419c7f0a49Ssthen /* src addr */ 1429c7f0a49Ssthen memcpy(buf, 1439c7f0a49Ssthen &((struct sockaddr_in6*)src)->sin6_addr, 16); 1449c7f0a49Ssthen buf += 16; 1459c7f0a49Ssthen /* dst addr */ 1469c7f0a49Ssthen memset(buf, 0, 16); 1479c7f0a49Ssthen buf += 16; 1489c7f0a49Ssthen /* src port */ 1499c7f0a49Ssthen memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2); 1509c7f0a49Ssthen buf += 2; 1519c7f0a49Ssthen /* dst port */ 1529c7f0a49Ssthen (*pp_data.write_uint16)(buf, 0); 1539c7f0a49Ssthen break; 1549c7f0a49Ssthen #endif /* INET6 */ 1559c7f0a49Ssthen case AF_UNIX: 156*a43524d9Ssthen ATTR_FALLTHROUGH 1579c7f0a49Ssthen /* fallthrough */ 1589c7f0a49Ssthen default: 1599c7f0a49Ssthen return 0; 1600e9b6f9fSsthen } 1619c7f0a49Ssthen return expected_size; 1620e9b6f9fSsthen } 1630e9b6f9fSsthen 1649c7f0a49Ssthen int 1659c7f0a49Ssthen pp2_read_header(uint8_t* buf, size_t buflen) 1660e9b6f9fSsthen { 1670e9b6f9fSsthen size_t size; 1689c7f0a49Ssthen struct pp2_header* header = (struct pp2_header*)buf; 1690e9b6f9fSsthen /* Try to fail all the unsupported cases first. */ 1709c7f0a49Ssthen if(buflen < PP2_HEADER_SIZE) { 1719c7f0a49Ssthen return PP_PARSE_SIZE; 1720e9b6f9fSsthen } 1730e9b6f9fSsthen /* Check for PROXYv2 header */ 1740e9b6f9fSsthen if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || 1750e9b6f9fSsthen ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { 1769c7f0a49Ssthen return PP_PARSE_WRONG_HEADERv2; 1770e9b6f9fSsthen } 1780e9b6f9fSsthen /* Check the length */ 1790e9b6f9fSsthen size = PP2_HEADER_SIZE + ntohs(header->len); 1809c7f0a49Ssthen if(buflen < size) { 1819c7f0a49Ssthen return PP_PARSE_SIZE; 1820e9b6f9fSsthen } 1830e9b6f9fSsthen /* Check for supported commands */ 1840e9b6f9fSsthen if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && 1850e9b6f9fSsthen (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { 1869c7f0a49Ssthen return PP_PARSE_UNKNOWN_CMD; 1870e9b6f9fSsthen } 1880e9b6f9fSsthen /* Check for supported family and protocol */ 1899c7f0a49Ssthen if(header->fam_prot != PP2_UNSPEC_UNSPEC && 1909c7f0a49Ssthen header->fam_prot != PP2_INET_STREAM && 1919c7f0a49Ssthen header->fam_prot != PP2_INET_DGRAM && 1929c7f0a49Ssthen header->fam_prot != PP2_INET6_STREAM && 1939c7f0a49Ssthen header->fam_prot != PP2_INET6_DGRAM && 1949c7f0a49Ssthen header->fam_prot != PP2_UNIX_STREAM && 1959c7f0a49Ssthen header->fam_prot != PP2_UNIX_DGRAM) { 1969c7f0a49Ssthen return PP_PARSE_UNKNOWN_FAM_PROT; 1970e9b6f9fSsthen } 1980e9b6f9fSsthen /* We have a correct header */ 1999c7f0a49Ssthen return PP_PARSE_NOERROR; 2000e9b6f9fSsthen } 201