static int rmt_resolve_unix(char *name, struct sockinfo *si) { struct sockaddr_un *un; if (strlen(name) >= RMT_UNIX_ADDRSTRLEN) { return RMT_ERROR; } un = &si->addr.un; un->sun_family = AF_UNIX; rmt_memcpy(un->sun_path, name, strlen(name)); un->sun_path[strlen(name)] = '\0'; si->family = AF_UNIX; si->addrlen = sizeof(*un); /* si->addr is an alias of un */ return RMT_OK; }
int rmt_tcp_context_connect(tcp_context *tc, const char *host, int port, const struct timeval *timeout, const char *source_addr) { tc->port = port; if(tc->host != NULL) { free(tc->host); tc->host = NULL; } tc->host = rmt_strdup(host); if (timeout) { if (tc->timeout == NULL) { tc->timeout = rmt_alloc(sizeof(struct timeval)); } rmt_memcpy(tc->timeout, timeout, sizeof(struct timeval)); } else { if (tc->timeout) rmt_free(tc->timeout); tc->timeout = NULL; } if (tc->source_addr != NULL) { free(tc->source_addr); tc->source_addr = NULL; } if (source_addr) { tc->source_addr = rmt_strdup(source_addr); } if (!(tc->flags & RMT_RECONNECT)) { tc->flags |= RMT_RECONNECT; } return _rmt_tcp_context_connect(tc); }
int rmt_tcp_context_connect_old(tcp_context *tc, const char *host, int port, const struct timeval *timeout, const char *source_addr) { int ret; int s, n; char _port[6]; /* strlen("65535"); */ struct addrinfo hints, *servinfo, *bservinfo, *p, *b; int blocking = (tc->flags & RMT_BLOCK); int reuseaddr = (tc->flags & RMT_REUSEADDR); int reuses = 0; int yes = 1; tc->port = port; if(tc->host != NULL) { free(tc->host); tc->host = NULL; } tc->host = rmt_strdup(host); if (timeout) { if (tc->timeout == NULL) { tc->timeout = rmt_alloc(sizeof(struct timeval)); } rmt_memcpy(tc->timeout, timeout, sizeof(struct timeval)); } else { if (tc->timeout) rmt_free(tc->timeout); tc->timeout = NULL; } if (tc->source_addr != NULL) { free(tc->source_addr); tc->source_addr = NULL; } if (source_addr) { tc->source_addr = rmt_strdup(source_addr); } rmt_snprintf(_port, 6, "%d", port); rmt_memset(&hints,0,sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; /* Try with IPv6 if no IPv4 address was found. We do it in this order since * in a Redis client you can't afford to test if you have IPv6 connectivity * as this would add latency to every connect. Otherwise a more sensible * route could be: Use IPv6 if both addresses are available and there is IPv6 * connectivity. */ if ((ret = rmt_getaddrinfo(tc->host,_port,&hints,&servinfo)) != 0) { hints.ai_family = AF_INET6; if ((ret = rmt_getaddrinfo(tc->host,_port,&hints,&servinfo)) != 0) { log_error("ERROR: rmt_getaddrinfo error: %s", gai_strerror(ret)); return RMT_ERROR; } } for (p = servinfo; p != NULL; p = p->ai_next) { addrretry: if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) continue; tc->sd = s; if (rmt_set_nonblocking(tc->sd) < 0) { log_error("ERROR: set nonblock on socket %d on addr '%s:%d' failed: %s", s, tc->host, tc->port, strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } if (tc->source_addr) { int bound = 0; /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */ if ((ret = rmt_getaddrinfo(tc->source_addr, NULL, &hints, &bservinfo)) != 0) { log_error("ERROR: can't get %s addr: %s", tc->source_addr, gai_strerror(ret)); rmt_tcp_context_close_sd(tc); goto error; } if (reuseaddr) { n = 1; if (rmt_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, sizeof(n)) < 0) { log_error("ERROR: set SO_REUSEADDR on socket %d on addr '%s:%d' failed: %s", tc->sd, tc->host, tc->port, strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } } for (b = bservinfo; b != NULL; b = b->ai_next) { if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { bound = 1; break; } } freeaddrinfo(bservinfo); if (!bound) { log_error("ERROR: can't bind socket: %s", strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } } if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { if (errno == EHOSTUNREACH) { rmt_tcp_context_close_sd(tc); continue; } else if (errno == EINPROGRESS && !blocking) { /* This is ok. */ } else if (errno == EADDRNOTAVAIL && reuseaddr) { if (++reuses >= RMT_CONNECT_RETRIES) { log_error("ERROR: retry too many times: %s", strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } else { goto addrretry; } } else { if (rmt_tcp_context_wait_ready(tc,tc->timeout) != RMT_OK) goto error; } } if (blocking && rmt_set_blocking(tc->sd) < 0) { log_error("ERROR: set block on socket %d on addr '%s:%d' failed: %s", tc->sd, tc->host, tc->port, strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } if (rmt_setsockopt(tc->sd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { log_error("ERROR: set TCP_NODELAY on socket %d on addr '%s:%d' failed: %s", tc->sd, tc->host, tc->port, strerror(errno)); rmt_tcp_context_close_sd(tc); goto error; } tc->flags |= RMT_CONNECTED; ret = RMT_OK; goto end; } if (p == NULL) { log_error("ERROR: can't create socket: %s", strerror(errno)); goto error; } error: ret = RMT_ERROR; end: freeaddrinfo(servinfo); return ret; // Need to return RMT_OK if alright }
static int rmt_resolve_inet(char *name, int port, struct sockinfo *si) { int status; struct addrinfo *ai, *cai; /* head and current addrinfo */ struct addrinfo hints; char *node, service[RMT_UINTMAX_MAXLEN]; bool found; ASSERT(rmt_valid_port(port)); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; /* AF_INET or AF_INET6 */ hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; if (name != NULL) { node = name; } else { /* * If AI_PASSIVE flag is specified in hints.ai_flags, and node is * NULL, then the returned socket addresses will be suitable for * bind(2)ing a socket that will accept(2) connections. The returned * socket address will contain the wildcard IP address. */ node = NULL; hints.ai_flags |= AI_PASSIVE; } rmt_snprintf(service, RMT_UINTMAX_MAXLEN, "%d", port); status = getaddrinfo(node, service, &hints, &ai); if (status < 0) { log_error("ERROR: address resolution of node '%s' service '%s' failed: %s", node, service, gai_strerror(status)); return RMT_ERROR; } /* * getaddrinfo() can return a linked list of more than one addrinfo, * since we requested for both AF_INET and AF_INET6 addresses and the * host itself can be multi-homed. Since we don't care whether we are * using ipv4 or ipv6, we just use the first address from this collection * in the order in which it was returned. * * The sorting function used within getaddrinfo() is defined in RFC 3484; * the order can be tweaked for a particular system by editing * /etc/gai.conf */ for (cai = ai, found = 0; cai != NULL; cai = cai->ai_next) { si->family = cai->ai_family; si->addrlen = cai->ai_addrlen; rmt_memcpy(&si->addr, cai->ai_addr, si->addrlen); found = 1; break; } freeaddrinfo(ai); return !found ? RMT_ERROR : RMT_OK; }