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