Fixes to improve portability (especially to OS X, Solaris, OpenBSD).
[ircu2.10.12-pk.git] / ircd / ircd_getnameinfo.c
1 /*
2  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /*
31  * Issues to be discussed:
32  * - Thread safe-ness must be checked
33  * - RFC2553 says that we should raise error on short buffer.  X/Open says
34  *   we need to truncate the result.  We obey RFC2553 (and X/Open should be
35  *   modified).  ipngwg rough consensus seems to follow RFC2553.
36  * - What is "local" in NI_FQDN?
37  * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other.
38  * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if
39  *   sin6_scope_id is filled - standardization status?
40  *   XXX breaks backward compat for code that expects no scopeid.
41  *   beware on merge.
42  */
43
44 #include "ircd_addrinfo.h"
45 #include "ircd_string.h"
46 #include <stddef.h> /* for offsetof() */
47 #include <stdio.h> /* for snprintf() */
48 #include <string.h> /* for strlen() */
49 #include <arpa/inet.h> /* for inet_aton(), inet_pton() */
50
51 /*  $Id$ */
52
53 static const struct afd {
54   int a_af;
55   int a_addrlen;
56   socklen_t a_socklen;
57   int a_off;
58 } afdl [] = {
59 #ifdef IPV6
60     {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
61              offsetof(struct sockaddr_in6, sin6_addr)},
62 #endif
63     {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
64             offsetof(struct sockaddr_in, sin_addr)},
65     {0, 0, 0, 0},
66 };
67
68 struct sockinet
69 {
70   unsigned char si_len;
71   unsigned char si_family;
72   unsigned short si_port;
73 };
74
75 #ifdef IPV6
76 static int ip6_parsenumeric(const struct sockaddr *, const char *, char *,
77     size_t, int);
78 #endif
79
80 int
81 irc_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
82                 size_t hostlen, char *serv, size_t servlen, int flags)
83 {
84   const struct afd *afd;
85   struct servent *sp;
86   unsigned short port;
87   int family, i;
88   const char *addr;
89   uint32_t v4a;
90   char numserv[512];
91   char numaddr[512];
92
93   if (sa == NULL)
94     return EAI_FAIL;
95
96 /*      if (sa->sa_len != salen)
97                 return EAI_FAIL;
98 */
99         family = sa->sa_family;
100         for (i = 0; afdl[i].a_af; i++)
101                 if (afdl[i].a_af == family) {
102                         afd = &afdl[i];
103                         goto found;
104                 }
105         return EAI_FAMILY;
106
107  found:
108         if (salen != afd->a_socklen)
109                 return EAI_FAIL;
110
111         /* network byte order */
112         port = ((const struct sockinet *)sa)->si_port;
113         addr = (const char *)sa + afd->a_off;
114
115         if (serv == NULL || servlen == 0) {
116                 /*
117                  * do nothing in this case.
118                  * in case you are wondering if "&&" is more correct than
119                  * "||" here: rfc2553bis-03 says that serv == NULL OR
120                  * servlen == 0 means that the caller does not want the result.
121                  */
122         } else {
123                 if (flags & NI_NUMERICSERV)
124                         sp = NULL;
125                 else {
126                         sp = getservbyport(port,
127                                 (flags & NI_DGRAM) ? "udp" : "tcp");
128                 }
129                 if (sp) {
130                         if (strlen(sp->s_name) + 1 > servlen)
131                                 return EAI_MEMORY;
132                         ircd_strncpy(serv, sp->s_name, servlen);
133                 } else {
134                         snprintf(numserv, sizeof(numserv), "%u", ntohs(port));
135                         if (strlen(numserv) + 1 > servlen)
136                                 return EAI_MEMORY;
137                         ircd_strncpy(serv, numserv, servlen);
138                 }
139         }
140
141         switch (sa->sa_family) {
142         case AF_INET:
143                 v4a =
144                     ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr);
145                 if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a))
146                         flags |= NI_NUMERICHOST;
147                 v4a >>= IN_CLASSA_NSHIFT;
148                 if (v4a == 0)
149                         flags |= NI_NUMERICHOST;
150                 break;
151 #ifdef IPV6
152         case AF_INET6:
153             {
154                 const struct sockaddr_in6 *sin6;
155                 sin6 = (const struct sockaddr_in6 *)sa;
156                 switch (sin6->sin6_addr.s6_addr[0]) {
157                 case 0x00:
158                         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
159                                 ;
160                         else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
161                                 ;
162                         else
163                                 flags |= NI_NUMERICHOST;
164                         break;
165                 default:
166                         if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
167                                 flags |= NI_NUMERICHOST;
168                         }
169                         else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
170                                 flags |= NI_NUMERICHOST;
171                         break;
172                 }
173             }
174                 break;
175 #endif
176         }
177         if (host == NULL || hostlen == 0) {
178                 /*
179                  * do nothing in this case.
180                  * in case you are wondering if "&&" is more correct than
181                  * "||" here: rfc2553bis-03 says that host == NULL or
182                  * hostlen == 0 means that the caller does not want the result.
183                  */
184         } else if (flags & NI_NUMERICHOST) {
185                 size_t numaddrlen;
186
187                 /* NUMERICHOST and NAMEREQD conflicts with each other */
188                 if (flags & NI_NAMEREQD)
189                         return EAI_NONAME;
190
191                 switch(afd->a_af) {
192 #ifdef IPV6
193                 case AF_INET6:
194                 {
195                         int error;
196
197                         if ((error = ip6_parsenumeric(sa, addr, host,
198                                                       hostlen, flags)) != 0)
199                                 return(error);
200                         break;
201                 }
202 #endif
203                 default:
204                         if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr))
205                             == NULL)
206                                 return EAI_SYSTEM;
207                         numaddrlen = strlen(numaddr);
208                         if (numaddrlen + 1 > hostlen) /* don't forget terminator */
209                                 return EAI_MEMORY;
210                         ircd_strncpy(host, numaddr, hostlen);
211                         break;
212                 }
213         }
214         return(0);
215 }
216
217 #ifdef IPV6
218 static int
219 ip6_parsenumeric(const struct sockaddr *sa, const char *addr,
220                  char *host, size_t hostlen, int flags)
221 {
222   size_t numaddrlen;
223   char numaddr[512];
224
225   if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL)
226     return(EAI_SYSTEM);
227
228   numaddrlen = strlen(numaddr);
229
230   if (numaddrlen + 1 > hostlen) /* don't forget terminator */
231     return(EAI_MEMORY);
232
233   if (*numaddr == ':')
234   {
235     *host = '0';
236     ircd_strncpy(host+1, numaddr, hostlen-1);
237   }
238   else
239     ircd_strncpy(host, numaddr, hostlen);
240
241   return(0);
242 }
243 #endif