1 /* $OpenBSD: getnameinfo_async.c,v 1.11 2015/09/14 11:52:49 guenther 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 <err.h> 28 #include <errno.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "asr_private.h" 34 35 static int getnameinfo_async_run(struct asr_query *, struct asr_result *); 36 static int _servname(struct asr_query *); 37 static int _numerichost(struct asr_query *); 38 39 struct asr_query * 40 getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host, 41 size_t hostlen, char *serv, size_t servlen, int flags, void *asr) 42 { 43 struct asr_ctx *ac; 44 struct asr_query *as; 45 46 ac = _asr_use_resolver(asr); 47 if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL) 48 goto abort; /* errno set */ 49 as->as_run = getnameinfo_async_run; 50 51 if (sa->sa_family == AF_INET) 52 memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain)); 53 else if (sa->sa_family == AF_INET6) 54 memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6)); 55 56 as->as.ni.sa.sa.sa_len = slen; 57 as->as.ni.hostname = host; 58 as->as.ni.hostnamelen = hostlen; 59 as->as.ni.servname = serv; 60 as->as.ni.servnamelen = servlen; 61 as->as.ni.flags = flags; 62 63 _asr_ctx_unref(ac); 64 return (as); 65 66 abort: 67 if (as) 68 _asr_async_free(as); 69 _asr_ctx_unref(ac); 70 return (NULL); 71 } 72 DEF_WEAK(getnameinfo_async); 73 74 static int 75 getnameinfo_async_run(struct asr_query *as, struct asr_result *ar) 76 { 77 void *addr; 78 socklen_t addrlen; 79 int r; 80 81 next: 82 switch (as->as_state) { 83 84 case ASR_STATE_INIT: 85 86 /* Make sure the parameters are all valid. */ 87 88 if (as->as.ni.sa.sa.sa_family != AF_INET && 89 as->as.ni.sa.sa.sa_family != AF_INET6) { 90 ar->ar_gai_errno = EAI_FAMILY; 91 async_set_state(as, ASR_STATE_HALT); 92 break; 93 } 94 95 if ((as->as.ni.sa.sa.sa_family == AF_INET && 96 (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) || 97 (as->as.ni.sa.sa.sa_family == AF_INET6 && 98 (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) { 99 ar->ar_gai_errno = EAI_FAIL; 100 async_set_state(as, ASR_STATE_HALT); 101 break; 102 } 103 104 /* Set the service name first, if needed. */ 105 if (_servname(as) == -1) { 106 ar->ar_gai_errno = EAI_OVERFLOW; 107 async_set_state(as, ASR_STATE_HALT); 108 break; 109 } 110 111 if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) { 112 ar->ar_gai_errno = 0; 113 async_set_state(as, ASR_STATE_HALT); 114 break; 115 } 116 117 if (as->as.ni.flags & NI_NUMERICHOST) { 118 if (_numerichost(as) == -1) { 119 if (errno == ENOMEM) 120 ar->ar_gai_errno = EAI_MEMORY; 121 else if (errno == ENOSPC) 122 ar->ar_gai_errno = EAI_OVERFLOW; 123 else { 124 ar->ar_errno = errno; 125 ar->ar_gai_errno = EAI_SYSTEM; 126 } 127 } else 128 ar->ar_gai_errno = 0; 129 async_set_state(as, ASR_STATE_HALT); 130 break; 131 } 132 133 if (as->as.ni.sa.sa.sa_family == AF_INET) { 134 addrlen = sizeof(as->as.ni.sa.sain.sin_addr); 135 addr = &as->as.ni.sa.sain.sin_addr; 136 } else { 137 addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr); 138 addr = &as->as.ni.sa.sain6.sin6_addr; 139 } 140 141 /* 142 * Create a subquery to lookup the address. 143 */ 144 as->as.ni.subq = _gethostbyaddr_async_ctx(addr, addrlen, 145 as->as.ni.sa.sa.sa_family, 146 as->as_ctx); 147 if (as->as.ni.subq == NULL) { 148 ar->ar_gai_errno = EAI_MEMORY; 149 async_set_state(as, ASR_STATE_HALT); 150 break; 151 } 152 153 async_set_state(as, ASR_STATE_SUBQUERY); 154 break; 155 156 case ASR_STATE_SUBQUERY: 157 158 if ((r = asr_run(as->as.ni.subq, ar)) == ASYNC_COND) 159 return (ASYNC_COND); 160 161 /* 162 * Request done. 163 */ 164 as->as.ni.subq = NULL; 165 166 if (ar->ar_hostent == NULL) { 167 if (as->as.ni.flags & NI_NAMEREQD) { 168 ar->ar_gai_errno = EAI_NONAME; 169 } else if (_numerichost(as) == -1) { 170 if (errno == ENOMEM) 171 ar->ar_gai_errno = EAI_MEMORY; 172 else if (errno == ENOSPC) 173 ar->ar_gai_errno = EAI_OVERFLOW; 174 else { 175 ar->ar_errno = errno; 176 ar->ar_gai_errno = EAI_SYSTEM; 177 } 178 } else 179 ar->ar_gai_errno = 0; 180 } else { 181 if (strlcpy(as->as.ni.hostname, 182 ar->ar_hostent->h_name, 183 as->as.ni.hostnamelen) >= as->as.ni.hostnamelen) 184 ar->ar_gai_errno = EAI_OVERFLOW; 185 else 186 ar->ar_gai_errno = 0; 187 free(ar->ar_hostent); 188 } 189 190 async_set_state(as, ASR_STATE_HALT); 191 break; 192 193 case ASR_STATE_HALT: 194 return (ASYNC_DONE); 195 196 default: 197 ar->ar_errno = EOPNOTSUPP; 198 ar->ar_gai_errno = EAI_SYSTEM; 199 async_set_state(as, ASR_STATE_HALT); 200 break; 201 } 202 goto next; 203 } 204 205 206 /* 207 * Set the service name on the result buffer is not NULL. 208 * return (-1) if the buffer is too small. 209 */ 210 static int 211 _servname(struct asr_query *as) 212 { 213 struct servent s; 214 struct servent_data sd; 215 int port, r; 216 char *buf = as->as.ni.servname; 217 size_t buflen = as->as.ni.servnamelen; 218 219 if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0) 220 return (0); 221 222 if (as->as.ni.sa.sa.sa_family == AF_INET) 223 port = as->as.ni.sa.sain.sin_port; 224 else 225 port = as->as.ni.sa.sain6.sin6_port; 226 227 if (!(as->as.ni.flags & NI_NUMERICSERV)) { 228 memset(&sd, 0, sizeof (sd)); 229 if (getservbyport_r(port, 230 (as->as.ni.flags & NI_DGRAM) ? "udp" : "tcp", 231 &s, &sd) != -1) { 232 r = strlcpy(buf, s.s_name, buflen) >= buflen; 233 endservent_r(&sd); 234 return (r ? -1 : 0); 235 } 236 } 237 238 r = snprintf(buf, buflen, "%u", ntohs(port)); 239 if (r == -1 || r >= (int)buflen) 240 return (-1); 241 242 return (0); 243 } 244 245 /* 246 * Write the numeric address 247 */ 248 static int 249 _numerichost(struct asr_query *as) 250 { 251 unsigned int ifidx; 252 char scope[IF_NAMESIZE + 1], *ifname; 253 void *addr; 254 char *buf = as->as.ni.hostname; 255 size_t buflen = as->as.ni.hostnamelen; 256 257 if (as->as.ni.sa.sa.sa_family == AF_INET) 258 addr = &as->as.ni.sa.sain.sin_addr; 259 else 260 addr = &as->as.ni.sa.sain6.sin6_addr; 261 262 if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL) 263 return (-1); /* errno set */ 264 265 if (as->as.ni.sa.sa.sa_family == AF_INET6 && 266 as->as.ni.sa.sain6.sin6_scope_id) { 267 268 scope[0] = SCOPE_DELIMITER; 269 scope[1] = '\0'; 270 271 ifidx = as->as.ni.sa.sain6.sin6_scope_id; 272 ifname = NULL; 273 274 if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || 275 IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || 276 IN6_IS_ADDR_MC_INTFACELOCAL(&as->as.ni.sa.sain6.sin6_addr)) 277 ifname = if_indextoname(ifidx, scope + 1); 278 279 if (ifname == NULL) 280 snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); 281 282 strlcat(buf, scope, buflen); 283 } 284 285 return (0); 286 } 287