1 /* $OpenBSD: getnameinfo_async.c,v 1.15 2020/12/21 09:40:35 eric Exp $ */ 2 /* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <net/if.h> 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <arpa/nameser.h> 24 #include <netdb.h> 25 26 #include <asr.h> 27 #include <errno.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #include "asr_private.h" 33 34 static int getnameinfo_async_run(struct asr_query *, struct asr_result *); 35 static int _servname(struct asr_query *); 36 static int _numerichost(struct asr_query *); 37 38 struct asr_query * 39 getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host, 40 size_t hostlen, char *serv, size_t servlen, int flags, void *asr) 41 { 42 struct asr_ctx *ac; 43 struct asr_query *as; 44 45 ac = _asr_use_resolver(asr); 46 if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL) 47 goto abort; /* errno set */ 48 as->as_run = getnameinfo_async_run; 49 50 if (sa->sa_family == AF_INET) 51 memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain)); 52 else if (sa->sa_family == AF_INET6) 53 memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6)); 54 55 as->as.ni.sa.sa.sa_len = slen; 56 as->as.ni.hostname = host; 57 as->as.ni.hostnamelen = hostlen; 58 as->as.ni.servname = serv; 59 as->as.ni.servnamelen = servlen; 60 as->as.ni.flags = flags; 61 62 _asr_ctx_unref(ac); 63 return (as); 64 65 abort: 66 if (as) 67 _asr_async_free(as); 68 _asr_ctx_unref(ac); 69 return (NULL); 70 } 71 DEF_WEAK(getnameinfo_async); 72 73 static int 74 getnameinfo_async_run(struct asr_query *as, struct asr_result *ar) 75 { 76 void *addr; 77 socklen_t addrlen; 78 int r; 79 80 next: 81 switch (as->as_state) { 82 83 case ASR_STATE_INIT: 84 85 /* Make sure the parameters are all valid. */ 86 87 if (as->as.ni.sa.sa.sa_family != AF_INET && 88 as->as.ni.sa.sa.sa_family != AF_INET6) { 89 ar->ar_gai_errno = EAI_FAMILY; 90 async_set_state(as, ASR_STATE_HALT); 91 break; 92 } 93 94 if ((as->as.ni.sa.sa.sa_family == AF_INET && 95 (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) || 96 (as->as.ni.sa.sa.sa_family == AF_INET6 && 97 (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) { 98 ar->ar_gai_errno = EAI_FAIL; 99 async_set_state(as, ASR_STATE_HALT); 100 break; 101 } 102 103 /* Set the service name first, if needed. */ 104 if (_servname(as) == -1) { 105 ar->ar_gai_errno = EAI_OVERFLOW; 106 async_set_state(as, ASR_STATE_HALT); 107 break; 108 } 109 110 if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) { 111 ar->ar_gai_errno = 0; 112 async_set_state(as, ASR_STATE_HALT); 113 break; 114 } 115 116 if (as->as.ni.flags & NI_NUMERICHOST) { 117 if (_numerichost(as) == -1) { 118 if (errno == ENOMEM) 119 ar->ar_gai_errno = EAI_MEMORY; 120 else if (errno == ENOSPC) 121 ar->ar_gai_errno = EAI_OVERFLOW; 122 else { 123 ar->ar_errno = errno; 124 ar->ar_gai_errno = EAI_SYSTEM; 125 } 126 } else 127 ar->ar_gai_errno = 0; 128 async_set_state(as, ASR_STATE_HALT); 129 break; 130 } 131 132 if (as->as.ni.sa.sa.sa_family == AF_INET) { 133 addrlen = sizeof(as->as.ni.sa.sain.sin_addr); 134 addr = &as->as.ni.sa.sain.sin_addr; 135 } else { 136 addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr); 137 addr = &as->as.ni.sa.sain6.sin6_addr; 138 } 139 140 /* 141 * Create a subquery to lookup the address. 142 */ 143 as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen, 144 as->as.ni.sa.sa.sa_family, 145 as->as_ctx); 146 if (as->as_subq == NULL) { 147 ar->ar_gai_errno = EAI_MEMORY; 148 async_set_state(as, ASR_STATE_HALT); 149 break; 150 } 151 152 async_set_state(as, ASR_STATE_SUBQUERY); 153 break; 154 155 case ASR_STATE_SUBQUERY: 156 157 if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) 158 return (ASYNC_COND); 159 160 /* 161 * Request done. 162 */ 163 as->as_subq = NULL; 164 165 if (ar->ar_hostent == NULL) { 166 if (as->as.ni.flags & NI_NAMEREQD) { 167 ar->ar_gai_errno = EAI_NONAME; 168 } else if (_numerichost(as) == -1) { 169 if (errno == ENOMEM) 170 ar->ar_gai_errno = EAI_MEMORY; 171 else if (errno == ENOSPC) 172 ar->ar_gai_errno = EAI_OVERFLOW; 173 else { 174 ar->ar_errno = errno; 175 ar->ar_gai_errno = EAI_SYSTEM; 176 } 177 } else 178 ar->ar_gai_errno = 0; 179 } else { 180 if (strlcpy(as->as.ni.hostname, 181 ar->ar_hostent->h_name, 182 as->as.ni.hostnamelen) >= as->as.ni.hostnamelen) 183 ar->ar_gai_errno = EAI_OVERFLOW; 184 else 185 ar->ar_gai_errno = 0; 186 free(ar->ar_hostent); 187 } 188 189 async_set_state(as, ASR_STATE_HALT); 190 break; 191 192 case ASR_STATE_HALT: 193 return (ASYNC_DONE); 194 195 default: 196 ar->ar_errno = EOPNOTSUPP; 197 ar->ar_gai_errno = EAI_SYSTEM; 198 async_set_state(as, ASR_STATE_HALT); 199 break; 200 } 201 goto next; 202 } 203 204 205 /* 206 * Set the service name on the result buffer is not NULL. 207 * return (-1) if the buffer is too small. 208 */ 209 static int 210 _servname(struct asr_query *as) 211 { 212 struct servent s; 213 struct servent_data sd; 214 int port, r; 215 char *buf = as->as.ni.servname; 216 size_t n, buflen = as->as.ni.servnamelen; 217 218 if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0) 219 return (0); 220 221 if (as->as.ni.sa.sa.sa_family == AF_INET) 222 port = as->as.ni.sa.sain.sin_port; 223 else 224 port = as->as.ni.sa.sain6.sin6_port; 225 226 if (!(as->as.ni.flags & NI_NUMERICSERV)) { 227 memset(&sd, 0, sizeof (sd)); 228 r = getservbyport_r(port, (as->as.ni.flags & NI_DGRAM) ? 229 "udp" : "tcp", &s, &sd); 230 if (r == 0) 231 n = strlcpy(buf, s.s_name, buflen); 232 endservent_r(&sd); 233 if (r == 0) { 234 if (n >= buflen) 235 return (-1); 236 return (0); 237 } 238 } 239 240 r = snprintf(buf, buflen, "%u", ntohs(port)); 241 if (r < 0 || r >= buflen) 242 return (-1); 243 244 return (0); 245 } 246 247 /* 248 * Write the numeric address 249 */ 250 static int 251 _numerichost(struct asr_query *as) 252 { 253 unsigned int ifidx; 254 char scope[IF_NAMESIZE + 1], *ifname; 255 void *addr; 256 char *buf = as->as.ni.hostname; 257 size_t buflen = as->as.ni.hostnamelen; 258 259 if (as->as.ni.sa.sa.sa_family == AF_INET) 260 addr = &as->as.ni.sa.sain.sin_addr; 261 else 262 addr = &as->as.ni.sa.sain6.sin6_addr; 263 264 if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL) 265 return (-1); /* errno set */ 266 267 if (as->as.ni.sa.sa.sa_family == AF_INET6 && 268 as->as.ni.sa.sain6.sin6_scope_id) { 269 270 scope[0] = SCOPE_DELIMITER; 271 scope[1] = '\0'; 272 273 ifidx = as->as.ni.sa.sain6.sin6_scope_id; 274 ifname = NULL; 275 276 if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || 277 IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || 278 IN6_IS_ADDR_MC_INTFACELOCAL(&as->as.ni.sa.sain6.sin6_addr)) 279 ifname = if_indextoname(ifidx, scope + 1); 280 281 if (ifname == NULL) 282 snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); 283 284 strlcat(buf, scope, buflen); 285 } 286 287 return (0); 288 } 289