1 | /* |
2 | * librd - Rapid Development C library |
3 | * |
4 | * Copyright (c) 2012, Magnus Edenhill |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions are met: |
9 | * |
10 | * 1. Redistributions of source code must retain the above copyright notice, |
11 | * this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
13 | * this list of conditions and the following disclaimer in the documentation |
14 | * and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | |
30 | #include "rd.h" |
31 | #include "rdaddr.h" |
32 | #include "rdrand.h" |
33 | |
34 | #ifdef _MSC_VER |
35 | #include <WS2tcpip.h> |
36 | #endif |
37 | |
38 | const char *rd_sockaddr2str (const void *addr, int flags) { |
39 | const rd_sockaddr_inx_t *a = (const rd_sockaddr_inx_t *)addr; |
40 | static RD_TLS char ret[32][INET6_ADDRSTRLEN + 16]; |
41 | static RD_TLS int reti = 0; |
42 | char portstr[64]; |
43 | int of = 0; |
44 | int niflags = NI_NUMERICSERV; |
45 | |
46 | reti = (reti + 1) % 32; |
47 | |
48 | switch (a->sinx_family) |
49 | { |
50 | case AF_INET: |
51 | case AF_INET6: |
52 | if (flags & RD_SOCKADDR2STR_F_FAMILY) |
53 | of += rd_snprintf(&ret[reti][of], sizeof(ret[reti])-of, "ipv%i#" , |
54 | a->sinx_family == AF_INET ? 4 : 6); |
55 | |
56 | if ((flags & RD_SOCKADDR2STR_F_PORT) && |
57 | a->sinx_family == AF_INET6) |
58 | ret[reti][of++] = '['; |
59 | |
60 | if (!(flags & RD_SOCKADDR2STR_F_RESOLVE)) |
61 | niflags |= NI_NUMERICHOST; |
62 | |
63 | if (getnameinfo((const struct sockaddr *)a, |
64 | RD_SOCKADDR_INX_LEN(a), |
65 | ret[reti]+of, sizeof(ret[reti])-of, |
66 | (flags & RD_SOCKADDR2STR_F_PORT) ? |
67 | portstr : NULL, |
68 | (flags & RD_SOCKADDR2STR_F_PORT) ? |
69 | sizeof(portstr) : 0, |
70 | niflags)) |
71 | break; |
72 | |
73 | |
74 | if (flags & RD_SOCKADDR2STR_F_PORT) { |
75 | size_t len = strlen(ret[reti]); |
76 | rd_snprintf(ret[reti]+len, sizeof(ret[reti])-len, |
77 | "%s:%s" , |
78 | a->sinx_family == AF_INET6 ? "]" : "" , |
79 | portstr); |
80 | } |
81 | |
82 | return ret[reti]; |
83 | } |
84 | |
85 | |
86 | /* Error-case */ |
87 | rd_snprintf(ret[reti], sizeof(ret[reti]), "<unsupported:%s>" , |
88 | rd_family2str(a->sinx_family)); |
89 | |
90 | return ret[reti]; |
91 | } |
92 | |
93 | |
94 | const char *rd_addrinfo_prepare (const char *nodesvc, |
95 | char **node, char **svc) { |
96 | static RD_TLS char snode[256]; |
97 | static RD_TLS char ssvc[64]; |
98 | const char *t; |
99 | const char *svct = NULL; |
100 | size_t nodelen = 0; |
101 | |
102 | *snode = '\0'; |
103 | *ssvc = '\0'; |
104 | |
105 | if (*nodesvc == '[') { |
106 | /* "[host]".. (enveloped node name) */ |
107 | if (!(t = strchr(nodesvc, ']'))) |
108 | return "Missing close-']'" ; |
109 | nodesvc++; |
110 | nodelen = t-nodesvc; |
111 | svct = t+1; |
112 | |
113 | } else if (*nodesvc == ':' && *(nodesvc+1) != ':') { |
114 | /* ":".. (port only) */ |
115 | nodelen = 0; |
116 | svct = nodesvc; |
117 | } |
118 | |
119 | if ((svct = strrchr(svct ? svct : nodesvc, ':')) && (*(svct-1) != ':') && |
120 | *(++svct)) { |
121 | /* Optional ":service" definition. */ |
122 | if (strlen(svct) >= sizeof(ssvc)) |
123 | return "Service name too long" ; |
124 | strcpy(ssvc, svct); |
125 | if (!nodelen) |
126 | nodelen = svct - nodesvc - 1; |
127 | |
128 | } else if (!nodelen) |
129 | nodelen = strlen(nodesvc); |
130 | |
131 | if (nodelen) { |
132 | /* Truncate nodename if necessary. */ |
133 | nodelen = RD_MIN(nodelen, sizeof(snode)-1); |
134 | memcpy(snode, nodesvc, nodelen); |
135 | snode[nodelen] = '\0'; |
136 | } |
137 | |
138 | *node = snode; |
139 | *svc = ssvc; |
140 | |
141 | return NULL; |
142 | } |
143 | |
144 | |
145 | |
146 | rd_sockaddr_list_t *rd_getaddrinfo (const char *nodesvc, const char *defsvc, |
147 | int flags, int family, |
148 | int socktype, int protocol, |
149 | const char **errstr) { |
150 | struct addrinfo hints = { .ai_family = family, |
151 | .ai_socktype = socktype, |
152 | .ai_protocol = protocol, |
153 | .ai_flags = flags }; |
154 | struct addrinfo *ais, *ai; |
155 | char *node, *svc; |
156 | int r; |
157 | int cnt = 0; |
158 | rd_sockaddr_list_t *rsal; |
159 | |
160 | if ((*errstr = rd_addrinfo_prepare(nodesvc, &node, &svc))) { |
161 | errno = EINVAL; |
162 | return NULL; |
163 | } |
164 | |
165 | if (*svc) |
166 | defsvc = svc; |
167 | |
168 | if ((r = getaddrinfo(node, defsvc, &hints, &ais))) { |
169 | #ifdef EAI_SYSTEM |
170 | if (r == EAI_SYSTEM) |
171 | #else |
172 | if (0) |
173 | #endif |
174 | *errstr = rd_strerror(errno); |
175 | else { |
176 | #ifdef _MSC_VER |
177 | *errstr = gai_strerrorA(r); |
178 | #else |
179 | *errstr = gai_strerror(r); |
180 | #endif |
181 | errno = EFAULT; |
182 | } |
183 | return NULL; |
184 | } |
185 | |
186 | /* Count number of addresses */ |
187 | for (ai = ais ; ai != NULL ; ai = ai->ai_next) |
188 | cnt++; |
189 | |
190 | if (cnt == 0) { |
191 | /* unlikely? */ |
192 | freeaddrinfo(ais); |
193 | errno = ENOENT; |
194 | *errstr = "No addresses" ; |
195 | return NULL; |
196 | } |
197 | |
198 | |
199 | rsal = rd_calloc(1, sizeof(*rsal) + (sizeof(*rsal->rsal_addr) * cnt)); |
200 | |
201 | for (ai = ais ; ai != NULL ; ai = ai->ai_next) |
202 | memcpy(&rsal->rsal_addr[rsal->rsal_cnt++], |
203 | ai->ai_addr, ai->ai_addrlen); |
204 | |
205 | freeaddrinfo(ais); |
206 | |
207 | /* Shuffle address list for proper round-robin */ |
208 | if (!(flags & RD_AI_NOSHUFFLE)) |
209 | rd_array_shuffle(rsal->rsal_addr, rsal->rsal_cnt, |
210 | sizeof(*rsal->rsal_addr)); |
211 | |
212 | return rsal; |
213 | } |
214 | |
215 | |
216 | |
217 | void rd_sockaddr_list_destroy (rd_sockaddr_list_t *rsal) { |
218 | rd_free(rsal); |
219 | } |
220 | |
221 | |