blob: 281e9255901435968b90f2a8a607109552b0f30c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
const char* sa_to_str(const struct sockaddr* sa, char* str, size_t strlen) {
if (sa->sa_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
return inet_ntop(AF_INET, &sin->sin_addr, str, strlen);
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
return inet_ntop(AF_INET6, &sin6->sin6_addr, str, strlen);
} else {
return NULL;
}
}
int client(const char* if_address, const char* mc_address,
const char* service) {
unsigned short port = atoi(service);
int s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
printf("socket failed (errno = %d)\n", errno);
return -1;
}
unsigned char mc_ttl = 1;
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &mc_ttl, sizeof(mc_ttl)) <
0) {
printf("setsockopt: IP_MULTICAST_TTL failed (errno = %d)\n", errno);
close(s);
return -1;
}
struct in_addr if_addr;
if_addr.s_addr = inet_addr(if_address);
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &if_addr, sizeof(if_addr)) <
0) {
printf("setsockopt: IP_MULTICAST_IF failed (errno = %d)\n", errno);
close(s);
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(mc_address);
addr.sin_port = htons(port);
char str[INET6_ADDRSTRLEN];
printf("sending to %s\n",
sa_to_str((struct sockaddr*)&addr, str, sizeof(str)));
char message[1024];
memset(message, 0, sizeof(message));
while (fgets(message, sizeof(message), stdin)) {
if (message[0] == '\n')
break;
int nsendto;
nsendto = sendto(s, message, strlen(message), 0, (struct sockaddr*)&addr,
sizeof(addr));
if (nsendto < 0) {
printf("sendto failed (%d) (errno = %d)\n", nsendto, errno);
close(s);
return -1;
}
}
close(s);
return 0;
}
int server(const char* if_address, const char* mc_address,
const char* service) {
unsigned short port = atoi(service);
int s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
printf("socket failed (errno = %d)\n", errno);
return -1;
}
int on = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
printf("setsockopt: SO_REUSEADDR failed (errno = %d)\n", errno);
close(s);
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
printf("bind failed (errno = %d)\n", errno);
close(s);
return -1;
}
struct ip_mreqn mc_reqn;
// optlen is set to either sizeof(struct ip_mreq) or sizeof(struct ip_mreqn)
// depending on how we can parse if_address.
size_t optlen = sizeof(struct ip_mreqn);
if (!inet_aton(mc_address, &(mc_reqn.imr_multiaddr))) {
printf("Invalid multicast_address: %s\n", mc_address);
return -1;
}
// First try parsing |if_address| as an integer NIC ID. If it fails then parse
// it as an IP address.
char* end;
mc_reqn.imr_ifindex = strtol(if_address, &end, 10);
if (*end != '\0') {
mc_reqn.imr_ifindex = 0;
optlen = sizeof(struct ip_mreq);
if (!inet_aton(if_address, &(mc_reqn.imr_address))) {
// If if_address isn't an IP address then try parsing it as an integer and
// pass it as NIC address in struct mc_reqn.
printf("Invalid if_address: %s\n", if_address);
return -1;
}
}
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc_reqn, optlen) != 0) {
printf("setsockopt: IP_ADD_MEMBERSHIP failed (errno = %d)\n", errno);
close(s);
return -1;
}
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc_reqn, optlen) == 0) {
printf(
"setsockopt: duplicate IP_ADD_MEMBERSHIP succeeded when it should have "
"failed\n");
}
const int kIterations = 4;
for (int i = 0; i < kIterations; i++) {
printf("waiting %s:%d...\n", mc_address, port);
char buf[128];
int nrecv;
socklen_t addrlen = sizeof(addr);
nrecv = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen);
if (nrecv < 0) {
printf("recvfrom failed (%d) (errno = %d)\n", nrecv, errno);
close(s);
return -1;
}
char str[INET6_ADDRSTRLEN];
printf("received %d bytes from %s\n", nrecv,
sa_to_str((struct sockaddr*)&addr, str, sizeof(str)));
}
if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mc_reqn, optlen) != 0) {
printf("setsockopt: IP_DROP_MEMBERSHIP failed (errno = %d)\n", errno);
close(s);
return -1;
}
if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mc_reqn, optlen) == 0) {
printf(
"setsockopt: duplicate IP_DROP_MEMBERSHIP succeeded when it should "
"have failed\n");
}
close(s);
return 0;
}
void usage(void) {
printf(
"usage: mctest server (if_address|nic_index) [multicast_address port]\n"
" mctest client if_address [multicast_address port]\n");
}
int main(int argc, char** argv) {
if (argc == 3 || argc == 5) {
char* mc_address = "232.43.211.234";
char* service = "4321";
if (argc == 5) {
mc_address = argv[3];
service = argv[4];
}
switch (argv[1][0]) {
case 'c': // client
return client(argv[2], mc_address, service);
case 's': // server
return server(argv[2], mc_address, service);
}
}
usage();
return -1;
}