Reuse multicast socket address.
[mctap.git] / src / mctap.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <errno.h>
9
10 #include "utils.h"
11
12 static void usage(FILE *out)
13 {
14     fprintf(out, "usage: mctap [-h] MCASTGROUP PORT\n");
15 }
16
17 static char *formataddress(struct sockaddr *arg, socklen_t arglen)
18 {
19     struct sockaddr_in *ipv4;
20     struct sockaddr_in6 *ipv6;
21     static char *ret = NULL;
22     char buf[1024];
23     
24     if(ret != NULL)
25         free(ret);
26     ret = NULL;
27     switch(arg->sa_family)
28     {
29     case AF_UNIX:
30         ret = strdup("Unix socket");
31         break;
32     case AF_INET:
33         ipv4 = (struct sockaddr_in *)arg;
34         if(inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)) == NULL)
35             return(NULL);
36         ret = sprintf2("%s:%i", buf, (int)ntohs(ipv4->sin_port));
37         break;
38     case AF_INET6:
39         ipv6 = (struct sockaddr_in6 *)arg;
40         if(inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)) == NULL)
41             return(NULL);
42         ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port));
43         break;
44     default:
45         errno = EPFNOSUPPORT;
46         break;
47     }
48     return(ret);
49 }
50
51 static int mkmcastsk4(struct in_addr group, int port)
52 {
53     int fd;
54     int soval;
55     struct sockaddr_in nm;
56     struct ip_mreqn mreq;
57     
58     fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
59     soval = 1;
60     if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &soval, sizeof(soval)))
61         return(-1);
62     memset(&nm, 0, sizeof(nm));
63     nm.sin_family = AF_INET;
64     nm.sin_port = htons(port);
65     if(bind(fd, (struct sockaddr *)&nm, sizeof(nm)))
66         return(-1);
67     memset(&mreq, 0, sizeof(mreq));
68     mreq.imr_multiaddr = group;
69     if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)))
70         return(-1);
71     return(fd);
72 }
73
74 static void test(int fd)
75 {
76     char buf[65536];
77     int ret;
78     struct sockaddr_storage nm;
79     socklen_t nmlen;
80     
81     while(1) {
82         nmlen = sizeof(nm);
83         ret = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&nm, &nmlen);
84         if(ret < 0) {
85             fprintf(stderr, "mctap: recvfrom: %s\n", strerror(ret));
86             exit(1);
87         }
88         printf("%s %i:\n", formataddress((struct sockaddr *)&nm, nmlen), ret);
89     }
90 }
91
92 int main(int argc, char **argv)
93 {
94     int c;
95     int sock;
96     struct in_addr group;
97     int port;
98     
99     while((c = getopt(argc, argv, "h")) >= 0) {
100         switch(c) {
101         case 'h':
102             usage(stdout);
103             return(0);
104         default:
105             usage(stderr);
106             exit(1);
107         }
108     }
109     if(argc - optind < 2) {
110         usage(stderr);
111         exit(1);
112     }
113     if(!inet_aton(argv[optind], &group)) {
114         fprintf(stderr, "mctap: invalid group address: %s\n", argv[optind]);
115         exit(1);
116     }
117     port = atoi(argv[optind + 1]);
118     if((sock = mkmcastsk4(group, port)) < 0) {
119         fprintf(stderr, "mctap: could not create multicast socket: %s\n", strerror(errno));
120         exit(1);
121     }
122     
123     test(sock);
124     return(0);
125 }