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