chiark / gitweb /
changelog: Document -t option
[chiark-utils.git] / cprogs / mcastsoundd.c
1 /*
2  * http://www.ibiblio.org/pub/Linux/docs/HOWTO/Multicast-HOWTO
3  */
4
5 #include <stdio.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <unistd.h>
10 #include <signal.h>
11
12 #include <endian.h>
13 #include <sys/types.h>
14 #include <fcntl.h>
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18
19 #include "myopt.h"
20
21 typedef unsigned char Byte;
22
23 static int ov_mode= 'r';
24 static const char *ov_requ=  "127.0.0.1";
25 static const char *ov_mcast= "239.193.27.221";
26
27 static int ov_port_requ= 4101;
28 static int ov_port_ctrl= 4101;
29 static int ov_port_data= -1;
30
31 static const struct cmdinfo cmdinfos[]= {
32   { "server",     0, &ov_mode,0,0, 's' },
33   { "player",     0, &ov_mode,0,0, 'p' },
34   { "request",    0, &ov_mode,0,0, 'r' },
35   { "mcast-addr", 1, 0,&ov_mcast },
36   { "requ-addr",  1, 0,&ov_requ },
37   { "requ-port",  1, &ov_port_requ },
38   { "ctrl-port",  1, &ov_port_ctrl },
39   { "data-port",  1, &ov_port_data },
40   0
41 };
42
43
44 static int mcast_fd, requ_fd;
45 static struct sockaddr_in requ_sa, ctrl_sa, data_sa;
46
47 static void sysfail(const char *m) { perror(m); exit(16); }
48
49 static Byte packet[1024];
50 static int packet_len;
51
52 /*---------- marshalling ----------*/
53
54 static uint64_t htonll(uint64_t v) {
55 #if LITTLE_ENDIAN
56   return (v >> 32) | (v << 32);
57 #endif
58 #if BIG_ENDIAN
59   return v;
60 #endif
61 }
62
63 #define OP_CTRL_PLAY 1
64 #define OP_CTRL_STOP 2
65 #define OP_CTRL_DATA 3
66
67 #define MAR_CTRL_PLAY                           \
68   FI8(operation)                                \
69   FI8(reserved)                                 \
70   FI8(generation)                               \
71   FI8(counter)                                  \
72   FI64(totallen)                                \
73   FI64(startts)                                 \
74   FI32(starttns)                                \
75   FI32(txrate)                                  \
76   FR(trackfn,char,256)
77   
78 #define MAR_CTRL_STOP                           \
79   FI8(operation)                                \
80   FI8(reserved)                                 \
81   FR0
82
83 #define MAR_DATA                                \
84   FI8(operation)                                \
85   FI8(reserved)                                 \
86   FI8(generation)                               \
87   FI8(counter)                                  \
88   FI64(offset)                                  \
89   FR(data,Byte,1024)
90      
91 #define FI8(f)      F(f, uint8_t,  v)
92 #define FI32(f)     F(f, uint32_t, htonl(v))
93 #define FI64(f)     F(f, uint64_t, htonll(v))
94
95 #define MARS                                    \
96   MAR(CTRL_PLAY)                                \
97   MAR(CTRL_STOP)                                \
98   MAR(DATA)
99
100 #define F(f,t,c) t f;
101 #define FR(f,t,l) t f[(l)]; int f##_l;
102 #define FR0 /* */
103 #define MAR(m) typedef struct Mar_##m { MAR_##m } Mar_##m;
104 MARS
105 #undef F
106 #undef FR
107 #undef FR0
108 #undef MAR
109
110 #define F(f,t,c) { t v= d->f; *(t*)p= c; p += sizeof(t); };
111 #define FR(f,t,l) assert(d->f##_l<=l); memcpy(p,d->f,d->f##_l); p+=d->f##_l;
112 #define FR0 /* */
113 #define MAR(m)                                  \
114   static void mar_##m(const Mar_##m *d) {       \
115     Byte *p= packet;                            \
116     MAR_##m                                     \
117     packet_len= p - packet;                     \
118     assert(packet_len < sizeof(packet));        \
119   }
120 MARS
121 #undef F
122 #undef FR
123 #undef FR0
124 #undef MAR
125
126 #define F(f,t,c) {                              \
127     t v;                                        \
128     if (lr < sizeof(t)) return -1;              \
129     v= *(const t*)p;                            \
130     p += sizeof(t);  lr -= sizeof(t);           \
131     d->f= c;                                    \
132   };
133 #define FR(f,t,l) {                             \
134     if (lr > l) return -1;                      \
135     memcpy(d->f, p, lr);                        \
136     d->f##_l= lr;                               \
137   };
138 #define FR0                                     \
139     if (lr) return -1;
140 #define MAR(m)                                  \
141   static int unmar_##m(Mar_##m *d) {            \
142     const Byte *p= packet;                      \
143     int lr= packet_len;                         \
144     MAR_##m                                     \
145     return 0;                                   \
146   }
147 MARS
148 #undef F
149 #undef FR
150 #undef FR0
151 #undef MAR
152
153 /*---------- general stuff ----------*/
154
155 static void nonblock(int fd) {
156   int r;
157   r= fcntl(fd,F_GETFL);  if (r<0) sysfail("nonblock fcntl F_GETFL");
158   r |= O_NONBLOCK;
159   r= fcntl(fd,F_SETFL,r);  if (r<0) sysfail("nonblock fcntl F_GETFL");
160 }
161
162 static void blocksignals(int how) {
163   sigset_t set;
164   int r;
165
166   sigemptyset(&set);
167   sigaddset(&set,SIGCHLD);
168   r= sigprocmask(how,&set,0);
169   if (r) sysfail("sigprocmask");
170 }
171
172 static int mksocket(int type, int proto,
173                     const struct sockaddr_in *sa, const char *what) {
174   int fd, r;
175
176   fd= socket(PF_INET, type, proto);
177   if (fd<0) sysfail("socket %s",what);
178
179   r= bind(fd, (struct sockaddr*)&mcast_sa, sizeof(*sa));
180   if (r) sysfail("bind %s",what);
181
182   return fd;
183 }
184
185 static void mkmcastrecv(const struct sockaddr_in *sa, const char *what) {
186   struct ip_mreq mreq;
187   int r;
188
189   mcast_fd= mksocket(SOCK_DGRAM, IPPROTO_UDP, sa, what);
190
191   mreq.imr_multiaddr= sa->sin_addr;
192   mreq.imr_interface.s_addr= INADDR_ANY;
193   r= setsockopt(mcast_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
194   if (r) sysfail("add mcast membership %s", what);
195 }
196
197 /*---------- player ----------*/
198
199 static void recvd_play(void) {
200   Mar_CTRL_PLAY pkt;
201   int r;
202
203   r= unmar_CTRL_PLAY(&pkt);
204   if (r) { fprintf(stderr,"bad PLAY packet\n"); return; }
205   
206 }
207
208 static void recvd_stop(void) {
209   Mar_CTRL_STOP pkt;
210   int r;
211
212   r= unmar_CTRL_STOP(&pkt);
213   if (r) { fprintf(stderr,"bad STOP packet\n"); return; }
214 }
215   
216 static void player(void) {
217   struct sockaddr_in peer_sa, old_peer_sa;
218   socklen_t peer_salen;
219   int r;
220
221   mkmcastrecv(&ctrl_sa, "ctrl");
222   
223   memset(&old_peer_sa, 0, sizeof(old_peer_sa));
224
225   for (;;) {
226     peer_salen= sizeof(peer_sa);
227     memset(&peer_sa, 0, sizeof(peer_sa));
228     
229     blocksignals(SIG_UNBLOCK);
230     packet_len= recvfrom(mcast_fd, packet, sizeof(packet),
231                          MSG_TRUNC, (struct sockaddr*)&peer_sa, &peer_salen);
232     blocksignals(SIG_BLOCK);
233
234     if (packet_len<0) {
235       if (errno==EINTR) continue;
236       perror("mcast_fd recvfrom");
237       continue;
238     }
239     if (peer_salen != sizeof(peer_sa)) {
240       fprintf(stderr,"mcast_fd recvfrom salen %ld not %ld\n",
241               (unsigned long)peer_salen, (unsigned long)sizeof(peer_sa));
242       continue;
243     }
244     if (packet_len > sizeof(packet)) {
245       fprintf(stderr,"mcast_fd recvfrom packet len %ld longer than max %ld\n",
246               (unsigned long)packet_len, (unsigned long)sizeof(packet));
247       continue;
248     }
249     if (memcmp(&old_peer_sa, &peer_sa, sizeof(old_peer_sa))) {
250       char *p= inet_ntoa(peer_sa.sin_addr);
251       fprintf(stderr,"receiving from %s:%d\n",p,ntohs(peer_sa.sin_port));
252       memcpy(&old_peer_sa, &peer_sa, sizeof(old_peer_sa));
253     }
254     if (packet_len==0) {
255       fprintf(stderr,"empty packet!\n");
256       continue;
257     }
258     switch (packet[0]) {
259     case OP_CTRL_PLAY:
260       recvd_play();
261       break;
262     case OP_CTRL_STOP:
263       recvd_stop();
264       break;
265     default:
266       fprintf(stderr,"unknown opcode %d\n",packet[0]);
267     }
268   }
269 }
270
271 /*---------- server ----------*/
272
273 void server(void) {
274   requ_fd= mksocket(SOCK_STREAM, IPPROTO_TCP, &requ_sa, "requ");
275   
276
277 /*---------- main ----------*/
278
279 static void argaddr(struct sin_addr *sa, const char *addr_name, int port) {
280   memset(sa,0,sizeof(*sa));
281   sa->sin_family= AF_INET;
282   
283   r= inet_aton(ov_mcast, &mcast_sa.sin_addr);
284   if (!r) badusage("invalid addr `%s'", addr_name);
285
286   if (port<0 || port>65536) badusage("invalid port %d",port);
287
288   sa->sin_port= htons(port);
289 }
290
291 int main(int argc, const char **argv) {
292   int r;
293
294   if (ov_port_data < 0) ov_port_data= ov_port_ctrl+1;
295   myopt(&argv, cmdinfos);
296
297   argaddr(&requ_sa, ov_requ,  ov_requ_port);
298   argaddr(&ctrl_sa, ov_mcast, ov_ctrl_port);
299   argaddr(&data_sa, ov_data,  ov_data_port);
300
301   if (argv[1] && ov_mode != 'p')
302     badusage("mode takes no non-option arguments");
303
304   switch (ov_mode) {
305   case 'p':
306     player();
307     break;
308   case 's':
309     server();
310     break;
311   case 'r':
312     if (!argv[1] || argv[2])
313       badusage("play-requester takes one non-option argument");
314     request(argv[1]);
315     break;
316   default:
317     abort();
318   }
319
320   nonblock(0);
321   mar_CTRL_PLAY(0);
322   mar_CTRL_STOP(0);
323   mar_DATA(0);
324   unmar_DATA(0);
325   return 0;
326 }