chiark / gitweb /
Bump version to 7.0.1~iwj0
[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 <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17
18 #include "myopt.h"
19 #include "common.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 blocksignals(int how) {
156   sigset_t set;
157   int r;
158
159   sigemptyset(&set);
160   sigaddset(&set,SIGCHLD);
161   r= sigprocmask(how,&set,0);
162   if (r) sysfail("sigprocmask");
163 }
164
165 static int mksocket(int type, int proto,
166                     const struct sockaddr_in *sa, const char *what) {
167   int fd, r;
168
169   fd= socket(PF_INET, type, proto);
170   if (fd<0) sysfail("socket %s",what);
171
172   r= bind(fd, (struct sockaddr*)&mcast_sa, sizeof(*sa));
173   if (r) sysfail("bind %s",what);
174
175   return fd;
176 }
177
178 static void mkmcastrecv(const struct sockaddr_in *sa, const char *what) {
179   struct ip_mreq mreq;
180   int r;
181
182   mcast_fd= mksocket(SOCK_DGRAM, IPPROTO_UDP, sa, what);
183
184   mreq.imr_multiaddr= sa->sin_addr;
185   mreq.imr_interface.s_addr= INADDR_ANY;
186   r= setsockopt(mcast_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
187   if (r) sysfail("add mcast membership %s", what);
188 }
189
190 /*---------- player ----------*/
191
192 static void recvd_play(void) {
193   Mar_CTRL_PLAY pkt;
194   int r;
195
196   r= unmar_CTRL_PLAY(&pkt);
197   if (r) { fprintf(stderr,"bad PLAY packet\n"); return; }
198   
199 }
200
201 static void recvd_stop(void) {
202   Mar_CTRL_STOP pkt;
203   int r;
204
205   r= unmar_CTRL_STOP(&pkt);
206   if (r) { fprintf(stderr,"bad STOP packet\n"); return; }
207 }
208   
209 static void player(void) {
210   struct sockaddr_in peer_sa, old_peer_sa;
211   socklen_t peer_salen;
212   int r;
213
214   mkmcastrecv(&ctrl_sa, "ctrl");
215   
216   memset(&old_peer_sa, 0, sizeof(old_peer_sa));
217
218   for (;;) {
219     peer_salen= sizeof(peer_sa);
220     memset(&peer_sa, 0, sizeof(peer_sa));
221     
222     blocksignals(SIG_UNBLOCK);
223     packet_len= recvfrom(mcast_fd, packet, sizeof(packet),
224                          MSG_TRUNC, (struct sockaddr*)&peer_sa, &peer_salen);
225     blocksignals(SIG_BLOCK);
226
227     if (packet_len<0) {
228       if (errno==EINTR) continue;
229       perror("mcast_fd recvfrom");
230       continue;
231     }
232     if (peer_salen != sizeof(peer_sa)) {
233       fprintf(stderr,"mcast_fd recvfrom salen %ld not %ld\n",
234               (unsigned long)peer_salen, (unsigned long)sizeof(peer_sa));
235       continue;
236     }
237     if (packet_len > sizeof(packet)) {
238       fprintf(stderr,"mcast_fd recvfrom packet len %ld longer than max %ld\n",
239               (unsigned long)packet_len, (unsigned long)sizeof(packet));
240       continue;
241     }
242     if (memcmp(&old_peer_sa, &peer_sa, sizeof(old_peer_sa))) {
243       char *p= inet_ntoa(peer_sa.sin_addr);
244       fprintf(stderr,"receiving from %s:%d\n",p,ntohs(peer_sa.sin_port));
245       memcpy(&old_peer_sa, &peer_sa, sizeof(old_peer_sa));
246     }
247     if (packet_len==0) {
248       fprintf(stderr,"empty packet!\n");
249       continue;
250     }
251     switch (packet[0]) {
252     case OP_CTRL_PLAY:
253       recvd_play();
254       break;
255     case OP_CTRL_STOP:
256       recvd_stop();
257       break;
258     default:
259       fprintf(stderr,"unknown opcode %d\n",packet[0]);
260     }
261   }
262 }
263
264 /*---------- server ----------*/
265
266 void server(void) {
267   requ_fd= mksocket(SOCK_STREAM, IPPROTO_TCP, &requ_sa, "requ");
268   
269
270 /*---------- main ----------*/
271
272 static void argaddr(struct sin_addr *sa, const char *addr_name, int port) {
273   memset(sa,0,sizeof(*sa));
274   sa->sin_family= AF_INET;
275   
276   r= inet_aton(ov_mcast, &mcast_sa.sin_addr);
277   if (!r) badusage("invalid addr `%s'", addr_name);
278
279   if (port<0 || port>65536) badusage("invalid port %d",port);
280
281   sa->sin_port= htons(port);
282 }
283
284 int main(int argc, const char **argv) {
285   int r;
286
287   if (ov_port_data < 0) ov_port_data= ov_port_ctrl+1;
288   myopt(&argv, cmdinfos);
289
290   argaddr(&requ_sa, ov_requ,  ov_requ_port);
291   argaddr(&ctrl_sa, ov_mcast, ov_ctrl_port);
292   argaddr(&data_sa, ov_data,  ov_data_port);
293
294   if (argv[1] && ov_mode != 'p')
295     badusage("mode takes no non-option arguments");
296
297   switch (ov_mode) {
298   case 'p':
299     player();
300     break;
301   case 's':
302     server();
303     break;
304   case 'r':
305     if (!argv[1] || argv[2])
306       badusage("play-requester takes one non-option argument");
307     request(argv[1]);
308     break;
309   default:
310     abort();
311   }
312
313   nonblock(0);
314   mar_CTRL_PLAY(0);
315   mar_CTRL_STOP(0);
316   mar_DATA(0);
317   unmar_DATA(0);
318   return 0;
319 }