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
38const 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
94const 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
146rd_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
217void rd_sockaddr_list_destroy (rd_sockaddr_list_t *rsal) {
218 rd_free(rsal);
219}
220
221