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