1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/socket.h> 31 32 #include <netdb.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 36 #include <stdio.h> 37 #include <string.h> 38 39 #include "libutil.h" 40 41 struct sockinet { 42 u_char si_len; 43 u_char si_family; 44 u_short si_port; 45 }; 46 47 int 48 realhostname(char *host, size_t hsize, const struct in_addr *ip) 49 { 50 char trimmed[MAXHOSTNAMELEN]; 51 int result; 52 struct hostent *hp; 53 54 result = HOSTNAME_INVALIDADDR; 55 hp = gethostbyaddr((const char *)ip, sizeof(*ip), AF_INET); 56 57 if (hp != NULL) { 58 strlcpy(trimmed, hp->h_name, sizeof(trimmed)); 59 trimdomain(trimmed, strlen(trimmed)); 60 if (strlen(trimmed) <= hsize) { 61 char lookup[MAXHOSTNAMELEN]; 62 63 strlcpy(lookup, hp->h_name, sizeof(lookup)); 64 hp = gethostbyname(lookup); 65 if (hp == NULL) 66 result = HOSTNAME_INVALIDNAME; 67 else for (; ; hp->h_addr_list++) { 68 if (*hp->h_addr_list == NULL) { 69 result = HOSTNAME_INCORRECTNAME; 70 break; 71 } 72 if (!memcmp(*hp->h_addr_list, ip, sizeof(*ip))) { 73 strncpy(host, trimmed, hsize); 74 return HOSTNAME_FOUND; 75 } 76 } 77 } 78 } 79 80 strncpy(host, inet_ntoa(*ip), hsize); 81 82 return result; 83 } 84 85 /* 86 * struct sockaddr has very lax alignment requirements, since all its 87 * members are char or equivalent. This is a problem when trying to 88 * dereference a struct sockaddr_in6 * that was passed in as a struct 89 * sockaddr *. Although we know (or trust) that the passed-in struct was 90 * properly aligned, the compiler doesn't, and (rightly) complains. These 91 * macros perform the cast in a way that the compiler will accept. 92 */ 93 #define SOCKADDR_IN6(p) ((struct sockaddr_in6 *)(void *)(p)) 94 #define SOCKADDR_IN(p) ((struct sockaddr_in *)(void *)(p)) 95 #define SOCKINET(p) ((struct sockinet *)(void *)(p)) 96 97 int 98 realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int addrlen) 99 { 100 int result, error; 101 char buf[NI_MAXHOST]; 102 #ifdef INET6 103 struct sockaddr_in lsin; 104 #endif 105 106 result = HOSTNAME_INVALIDADDR; 107 108 #ifdef INET6 109 /* IPv4 mapped IPv6 addr consideraton, specified in rfc2373. */ 110 if (addr->sa_family == AF_INET6 && 111 addrlen == sizeof(struct sockaddr_in6) && 112 IN6_IS_ADDR_V4MAPPED(&SOCKADDR_IN6(addr)->sin6_addr)) { 113 struct sockaddr_in6 *sin6; 114 115 sin6 = SOCKADDR_IN6(addr); 116 117 memset(&lsin, 0, sizeof(lsin)); 118 lsin.sin_len = sizeof(struct sockaddr_in); 119 lsin.sin_family = AF_INET; 120 lsin.sin_port = sin6->sin6_port; 121 memcpy(&lsin.sin_addr, &sin6->sin6_addr.s6_addr[12], 122 sizeof(struct in_addr)); 123 addr = (struct sockaddr *)&lsin; 124 addrlen = lsin.sin_len; 125 } 126 #endif 127 128 error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, 129 NI_NAMEREQD); 130 if (error == 0) { 131 struct addrinfo hints, *res, *ores; 132 struct sockaddr *sa; 133 134 memset(&hints, 0, sizeof(struct addrinfo)); 135 hints.ai_family = addr->sa_family; 136 hints.ai_flags = AI_CANONNAME | AI_PASSIVE; 137 hints.ai_socktype = SOCK_STREAM; 138 139 error = getaddrinfo(buf, NULL, &hints, &res); 140 if (error) { 141 result = HOSTNAME_INVALIDNAME; 142 goto numeric; 143 } 144 for (ores = res; ; res = res->ai_next) { 145 if (res == NULL) { 146 freeaddrinfo(ores); 147 result = HOSTNAME_INCORRECTNAME; 148 goto numeric; 149 } 150 sa = res->ai_addr; 151 if (sa == NULL) { 152 freeaddrinfo(ores); 153 result = HOSTNAME_INCORRECTNAME; 154 goto numeric; 155 } 156 if (sa->sa_len == addrlen && 157 sa->sa_family == addr->sa_family) { 158 SOCKINET(sa)->si_port = SOCKINET(addr)->si_port; 159 #ifdef INET6 160 /* 161 * XXX: sin6_socpe_id may not been 162 * filled by DNS 163 */ 164 if (sa->sa_family == AF_INET6 && 165 SOCKADDR_IN6(sa)->sin6_scope_id == 0) 166 SOCKADDR_IN6(sa)->sin6_scope_id = 167 SOCKADDR_IN6(addr)->sin6_scope_id; 168 #endif 169 if (!memcmp(sa, addr, sa->sa_len)) { 170 result = HOSTNAME_FOUND; 171 if (ores->ai_canonname == NULL) { 172 freeaddrinfo(ores); 173 goto numeric; 174 } 175 strlcpy(buf, ores->ai_canonname, 176 sizeof(buf)); 177 trimdomain(buf, hsize); 178 if (strlen(buf) > hsize && 179 addr->sa_family == AF_INET) { 180 freeaddrinfo(ores); 181 goto numeric; 182 } 183 strncpy(host, buf, hsize); 184 break; 185 } 186 } 187 } 188 freeaddrinfo(ores); 189 } else { 190 numeric: 191 if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, 192 NI_NUMERICHOST) == 0) 193 strncpy(host, buf, hsize); 194 } 195 196 return result; 197 } 198 199 200