static int vr_resolve_inet(sds name, int port, struct sockinfo *si) { int status; struct addrinfo *ai, *cai; /* head and current addrinfo */ struct addrinfo hints; char *node, service[VR_UINTMAX_MAXLEN]; bool found; ASSERT(vr_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 = (char *)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; } dsnprintf(service, VR_UINTMAX_MAXLEN, "%d", port); /* * getaddrinfo() returns zero on success or one of the error codes listed * in gai_strerror(3) if an error occurs */ status = getaddrinfo(node, service, &hints, &ai); if (status != 0) { log_error("address resolution of node '%s' service '%s' failed: %s", node, service, gai_strerror(status)); return -1; } /* * 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 = false; cai != NULL; cai = cai->ai_next) { si->family = cai->ai_family; si->addrlen = cai->ai_addrlen; vr_memcpy(&si->addr, cai->ai_addr, si->addrlen); found = true; break; } freeaddrinfo(ai); return !found ? -1 : 0; }
vr_listen * vr_listen_create(sds listen_str) { rstatus_t status; vr_listen *vlisten; uint8_t *p, *name; uint32_t namelen; if (listen_str == NULL) { return NULL; } vlisten = vr_alloc(sizeof(struct vr_listen)); if (vlisten == NULL) { return NULL; } vlisten->name = NULL; vlisten->port = 0; memset(&vlisten->info, 0, sizeof(vlisten->info)); vlisten->sd = -1; if (listen_str == '/') { uint8_t *q, *start, *perm; uint32_t permlen; /* parse "socket_path permissions" from the end */ p = listen_str + sdslen(listen_str) - 1; start = listen_str; q = vr_strrchr(p, start, ' '); if (q == NULL) { /* no permissions field, so use defaults */ name = listen_str; namelen = sdslen(listen_str); } else { perm = q + 1; permlen = (uint32_t)(p - perm + 1); p = q - 1; name = start; namelen = (uint32_t)(p - start + 1); errno = 0; vlisten->perm = (mode_t)strtol((char *)perm, NULL, 8); if (errno || vlisten->perm > 0777) { log_error("config file has an invalid file permission in \"socket_path permission\" format string"); vr_listen_destroy(vlisten); return NULL; } } } else { uint8_t *q, *start, *port; uint32_t portlen; /* parse "hostname:port" from the end */ p = listen_str + sdslen(listen_str) - 1; start = listen_str; q = vr_strrchr(p, start, ':'); if (q == NULL) { log_error("config file has an invalid \"hostname:port\" format string"); vr_listen_destroy(vlisten); return NULL; } port = q + 1; portlen = (uint32_t)(p - port + 1); p = q - 1; name = start; namelen = (uint32_t)(p - start + 1); vlisten->port = vr_atoi(port, portlen); if (vlisten->port < 0 || !vr_valid_port(vlisten->port)) { log_error("config file has an invalid port in \"hostname:port\" format string"); vr_listen_destroy(vlisten); return NULL; } } vlisten->name = sdsnewlen(name, namelen); if (vlisten->name == NULL) { log_error("create a sds string failed: out of memory."); vr_listen_destroy(vlisten); return NULL; } status = vr_resolve(vlisten->name, vlisten->port, &vlisten->info); if (status != VR_OK) { vr_listen_destroy(vlisten); return NULL; } return vlisten; }