int main(void) { int flag, status; socklen_t flaglen; struct addrinfo *ai4, *ai6; struct addrinfo hints; char addr[INET6_ADDRSTRLEN]; char *p; socket_type fd; static const char *port = "119"; static const char *ipv6_addr = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"; #ifndef HAVE_INET6 skip_all("IPv6 not supported"); #endif /* Set up the plan. */ plan(34); /* Get IPv4 and IPv6 sockaddrs to use for subsequent tests. */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; status = getaddrinfo("127.0.0.1", port, &hints, &ai4); if (status != 0) bail("getaddrinfo on 127.0.0.1 failed: %s", gai_strerror(status)); status = getaddrinfo(ipv6_addr, port, &hints, &ai6); if (status != 0) bail("getaddr on %s failed: %s", ipv6_addr, gai_strerror(status)); /* Test network_sockaddr_sprint. */ ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr), "sprint of IPv6 address"); for (p = addr; *p != '\0'; p++) if (islower((unsigned char) *p)) *p = toupper((unsigned char) *p); is_string(ipv6_addr, addr, "...with right results"); /* Test network_sockaddr_port. */ is_int(119, network_sockaddr_port(ai6->ai_addr), "sockaddr_port IPv6"); /* Test network_sockaddr_equal. */ ok(network_sockaddr_equal(ai6->ai_addr, ai6->ai_addr), "sockaddr_equal IPv6"); ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "...and not equal to IPv4"); ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...other way around"); freeaddrinfo(ai6); /* Test IPv4 mapped addresses. */ status = getaddrinfo("::ffff:7f00:1", NULL, &hints, &ai6); if (status != 0) bail("getaddr on ::ffff:7f00:1 failed: %s", gai_strerror(status)); ok(network_sockaddr_sprint(addr, sizeof(addr), ai6->ai_addr), "sprint of IPv4-mapped address"); is_string("127.0.0.1", addr, "...with right IPv4 result"); ok(network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "sockaddr_equal of IPv4-mapped address"); ok(network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...and other way around"); freeaddrinfo(ai4); status = getaddrinfo("127.0.0.2", NULL, &hints, &ai4); if (status != 0) bail("getaddrinfo on 127.0.0.2 failed: %s", gai_strerror(status)); ok(!network_sockaddr_equal(ai4->ai_addr, ai6->ai_addr), "...but not some other address"); ok(!network_sockaddr_equal(ai6->ai_addr, ai4->ai_addr), "...and the other way around"); freeaddrinfo(ai6); freeaddrinfo(ai4); /* Tests for network_addr_compare. */ is_addr_compare(1, ipv6_addr, ipv6_addr, NULL); is_addr_compare(1, ipv6_addr, ipv6_addr, "128"); is_addr_compare(1, ipv6_addr, ipv6_addr, "60"); is_addr_compare(1, "::127", "0:0::127", "128"); is_addr_compare(1, "::127", "0:0::128", "120"); is_addr_compare(0, "::127", "0:0::128", "128"); is_addr_compare(0, "::7fff", "0:0::8000", "113"); is_addr_compare(1, "::7fff", "0:0::8000", "112"); is_addr_compare(0, "::3:ffff", "::2:ffff", "120"); is_addr_compare(0, "::3:ffff", "::2:ffff", "119"); is_addr_compare(0, "ffff::1", "7fff::1", "1"); is_addr_compare(1, "ffff::1", "7fff::1", "0"); is_addr_compare(0, "fffg::1", "fffg::1", NULL); is_addr_compare(0, "ffff::1", "7fff::1", "-1"); is_addr_compare(0, "ffff::1", "ffff::1", "-1"); is_addr_compare(0, "ffff::1", "ffff::1", "129"); /* Test setting various socket options. */ fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_IP); if (fd == INVALID_SOCKET) sysbail("cannot create socket"); network_set_reuseaddr(fd); #ifdef SO_REUSEADDR flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, &flaglen), "Getting SO_REUSEADDR works"); ok(flag, "...and it is set"); #else skip_block(2, "SO_REUSEADDR not supported"); #endif network_set_v6only(fd); #ifdef IPV6_V6ONLY flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, &flaglen), "Getting IPV6_V6ONLY works"); ok(flag, "...and it is set"); #else skip_block(2, "IPV6_V6ONLY not supported"); #endif network_set_freebind(fd); #ifdef IP_FREEBIND flag = 0; flaglen = sizeof(flag); is_int(0, getsockopt(fd, IPPROTO_IP, IP_FREEBIND, &flag, &flaglen), "Getting IP_FREEBIND works"); ok(flag, "...and it is set"); #else skip_block(2, "IP_FREEBIND not supported"); #endif close(fd); return 0; }
/* * Receive request packet and verify the integrity and format of the reply. * This routine is REQUIRED to sanitize the request packet. All other program * routines can expect that the packet is safe to read once it is passed on. * * Returns a newly-allocated lbcd_request struct on success and NULL on * failure. */ static struct request * request_recv(struct lbcd_config *config, socket_type fd) { struct sockaddr_storage addr; struct sockaddr *sockaddr; socklen_t addrlen; ssize_t result; char raw[LBCD_MAXMESG]; char source[INET6_ADDRSTRLEN] = "UNKNOWN"; struct lbcd_request *packet; unsigned int protocol, id, operation, nservices, i; size_t expected; struct request *request; char *service; /* Receive the UDP packet from the wire. */ addrlen = sizeof(addr); sockaddr = (struct sockaddr *) &addr; result = recvfrom(fd, raw, sizeof(raw), 0, sockaddr, &addrlen); if (result <= 0) { syswarn("cannot receive packet"); return NULL; } /* Format the client address for logging. */ if (!network_sockaddr_sprint(source, sizeof(source), sockaddr)) syswarn("cannot convert client address to string"); /* Ensure the packet is large enough to contain the header. */ if ((size_t) result < sizeof(struct lbcd_header)) { warn("client %s: short packet received (length %lu)", source, (unsigned long) result); return NULL; } /* Extract the header fields. */ packet = (struct lbcd_request *) raw; protocol = ntohs(packet->h.version); id = ntohs(packet->h.id); operation = ntohs(packet->h.op); nservices = ntohs(packet->h.status); /* Now, ensure the request packet is exactly the correct size. */ expected = sizeof(struct lbcd_header); if (protocol == 3) { if (nservices > LBCD_MAX_SERVICES) { warn("client %s: too many services in request (%u)", source, nservices); return NULL; } expected += nservices * sizeof(lbcd_name_type); } if ((size_t) result != expected) { warn("client %s: incorrect packet size (%lu != %lu)", source, (unsigned long) result, (unsigned long) expected); return NULL; } /* The packet appears valid. Create the request struct. */ request = xcalloc(1, sizeof(struct request)); request->source = xstrdup(source); request->addrlen = addrlen; request->addr = xmalloc(addrlen); memcpy(request->addr, &addr, addrlen); request->protocol = protocol; request->id = id; request->operation = operation; request->services = vector_new(); /* Check protocol number. */ if (protocol != 2 && protocol != 3) { warn("client %s: protocol version %u unsupported", source, protocol); send_status(request, fd, LBCD_STATUS_VERSION); goto fail; } /* * Protocol version 3 takes a client-supplied list of services, with the * number of client-provided services given in the otherwise-unused status * field of the request header. */ if (request->protocol == 3) for (i = 0; i < nservices; i++) { service = xstrndup(packet->names[i], sizeof(lbcd_name_type)); if (!service_allowed(config, service)) { warn("client %s: service %s not allowed", source, service); send_status(request, fd, LBCD_STATUS_ERROR); free(service); goto fail; } vector_add(request->services, service); free(service); } return request; fail: request_free(request); return NULL; }