| /* |
| * librd - Rapid Development C library |
| * |
| * Copyright (c) 2012, Magnus Edenhill |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| #include "rd.h" |
| #include "rdaddr.h" |
| #include "rdrand.h" |
| |
| #ifdef _MSC_VER |
| #include <WS2tcpip.h> |
| #endif |
| |
| const char *rd_sockaddr2str (const void *addr, int flags) { |
| const rd_sockaddr_inx_t *a = (const rd_sockaddr_inx_t *)addr; |
| static RD_TLS char ret[32][INET6_ADDRSTRLEN + 16]; |
| static RD_TLS int reti = 0; |
| char portstr[64]; |
| int of = 0; |
| int niflags = NI_NUMERICSERV; |
| |
| reti = (reti + 1) % 32; |
| |
| switch (a->sinx_family) |
| { |
| case AF_INET: |
| case AF_INET6: |
| if (flags & RD_SOCKADDR2STR_F_FAMILY) |
| of += rd_snprintf(&ret[reti][of], sizeof(ret[reti])-of, "ipv%i#", |
| a->sinx_family == AF_INET ? 4 : 6); |
| |
| if ((flags & RD_SOCKADDR2STR_F_PORT) && |
| a->sinx_family == AF_INET6) |
| ret[reti][of++] = '['; |
| |
| if (!(flags & RD_SOCKADDR2STR_F_RESOLVE)) |
| niflags |= NI_NUMERICHOST; |
| |
| if (getnameinfo((const struct sockaddr *)a, |
| RD_SOCKADDR_INX_LEN(a), |
| ret[reti]+of, sizeof(ret[reti])-of, |
| (flags & RD_SOCKADDR2STR_F_PORT) ? |
| portstr : NULL, |
| (flags & RD_SOCKADDR2STR_F_PORT) ? |
| sizeof(portstr) : 0, |
| niflags)) |
| break; |
| |
| |
| if (flags & RD_SOCKADDR2STR_F_PORT) { |
| size_t len = strlen(ret[reti]); |
| rd_snprintf(ret[reti]+len, sizeof(ret[reti])-len, |
| "%s:%s", |
| a->sinx_family == AF_INET6 ? "]" : "", |
| portstr); |
| } |
| |
| return ret[reti]; |
| } |
| |
| |
| /* Error-case */ |
| rd_snprintf(ret[reti], sizeof(ret[reti]), "<unsupported:%s>", |
| rd_family2str(a->sinx_family)); |
| |
| return ret[reti]; |
| } |
| |
| |
| const char *rd_addrinfo_prepare (const char *nodesvc, |
| char **node, char **svc) { |
| static RD_TLS char snode[256]; |
| static RD_TLS char ssvc[64]; |
| const char *t; |
| const char *svct = NULL; |
| size_t nodelen = 0; |
| |
| *snode = '\0'; |
| *ssvc = '\0'; |
| |
| if (*nodesvc == '[') { |
| /* "[host]".. (enveloped node name) */ |
| if (!(t = strchr(nodesvc, ']'))) |
| return "Missing close-']'"; |
| nodesvc++; |
| nodelen = t-nodesvc; |
| svct = t+1; |
| |
| } else if (*nodesvc == ':' && *(nodesvc+1) != ':') { |
| /* ":".. (port only) */ |
| nodelen = 0; |
| svct = nodesvc; |
| } |
| |
| if ((svct = strrchr(svct ? svct : nodesvc, ':')) && (*(svct-1) != ':') && |
| *(++svct)) { |
| /* Optional ":service" definition. */ |
| if (strlen(svct) >= sizeof(ssvc)) |
| return "Service name too long"; |
| strcpy(ssvc, svct); |
| if (!nodelen) |
| nodelen = svct - nodesvc - 1; |
| |
| } else if (!nodelen) |
| nodelen = strlen(nodesvc); |
| |
| if (nodelen) { |
| /* Truncate nodename if necessary. */ |
| nodelen = RD_MIN(nodelen, sizeof(snode)-1); |
| strncpy(snode, nodesvc, nodelen); |
| snode[nodelen] = '\0'; |
| } |
| |
| *node = snode; |
| *svc = ssvc; |
| |
| return NULL; |
| } |
| |
| |
| |
| rd_sockaddr_list_t *rd_getaddrinfo (const char *nodesvc, const char *defsvc, |
| int flags, int family, |
| int socktype, int protocol, |
| const char **errstr) { |
| struct addrinfo hints = { .ai_family = family, |
| .ai_socktype = socktype, |
| .ai_protocol = protocol, |
| .ai_flags = flags }; |
| struct addrinfo *ais, *ai; |
| char *node, *svc; |
| int r; |
| int cnt = 0; |
| rd_sockaddr_list_t *rsal; |
| |
| if ((*errstr = rd_addrinfo_prepare(nodesvc, &node, &svc))) { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (*svc) |
| defsvc = svc; |
| |
| if ((r = getaddrinfo(node, defsvc, &hints, &ais))) { |
| #ifdef EAI_SYSTEM |
| if (r == EAI_SYSTEM) |
| #else |
| if (0) |
| #endif |
| *errstr = rd_strerror(errno); |
| else { |
| #ifdef _MSC_VER |
| *errstr = gai_strerrorA(r); |
| #else |
| *errstr = gai_strerror(r); |
| #endif |
| errno = EFAULT; |
| } |
| return NULL; |
| } |
| |
| /* Count number of addresses */ |
| for (ai = ais ; ai != NULL ; ai = ai->ai_next) |
| cnt++; |
| |
| if (cnt == 0) { |
| /* unlikely? */ |
| freeaddrinfo(ais); |
| errno = ENOENT; |
| *errstr = "No addresses"; |
| return NULL; |
| } |
| |
| |
| rsal = rd_calloc(1, sizeof(*rsal) + (sizeof(*rsal->rsal_addr) * cnt)); |
| |
| for (ai = ais ; ai != NULL ; ai = ai->ai_next) |
| memcpy(&rsal->rsal_addr[rsal->rsal_cnt++], |
| ai->ai_addr, ai->ai_addrlen); |
| |
| freeaddrinfo(ais); |
| |
| /* Shuffle address list for proper round-robin */ |
| if (!(flags & RD_AI_NOSHUFFLE)) |
| rd_array_shuffle(rsal->rsal_addr, rsal->rsal_cnt, |
| sizeof(*rsal->rsal_addr)); |
| |
| return rsal; |
| } |
| |
| |
| |
| void rd_sockaddr_list_destroy (rd_sockaddr_list_t *rsal) { |
| rd_free(rsal); |
| } |
| |