chiark / gitweb /
499bae46aa93080d2dba8f8467d5461efec67c1d
[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 typedef unsigned char Byte;
20
21 static const char *ov_mcast= "239.193.27.221";
22 static int ov_port_ctrl= 4101;
23 static int ov_port_data= -1;
24
25 static int mcast_fd;
26 static struct sockaddr_in mcast_sa;
27
28 static void sysfail(const char *m) { perror(m); exit(16); }
29
30 static Byte packet[1024];
31 static int packet_len;
32
33 static uint64_t htonll(uint64_t v) {
34 #if LITTLE_ENDIAN
35   return (v >> 32) | (v << 32);
36 #endif
37 #if BIG_ENDIAN
38   return v;
39 #endif
40 }
41
42 #define OP_CTRL_PLAY 1
43 #define OP_CTRL_STOP 2
44 #define OP_CTRL_DATA 3
45
46 #define MAR_CTRL_PLAY                           \
47   FI8(operation)                                \
48   FI8(reserved)                                 \
49   FI8(generation)                               \
50   FI8(counter)                                  \
51   FI64(totallen)                                \
52   FI64(startts)                                 \
53   FI32(starttns)                                \
54   FI32(txrate)                                  \
55   FR(trackfn,char,256)
56   
57 #define MAR_CTRL_STOP                           \
58   FI8(operation)                                \
59   FI8(reserved)                                 \
60   FR0
61
62 #define MAR_DATA                                \
63   FI8(operation)                                \
64   FI8(reserved)                                 \
65   FI8(generation)                               \
66   FI8(counter)                                  \
67   FI64(offset)                                  \
68   FR(data,Byte,1024)
69      
70 #define FI8(f)      F(f, uint8_t,  v)
71 #define FI32(f)     F(f, uint32_t, htonl(v))
72 #define FI64(f)     F(f, uint64_t, htonll(v))
73
74 #define MARS                                    \
75   MAR(CTRL_PLAY)                                \
76   MAR(CTRL_STOP)                                \
77   MAR(DATA)
78
79 #define F(f,t,c) t f;
80 #define FR(f,t,l) t f[(l)]; int f##_l;
81 #define FR0 /* */
82 #define MAR(m) typedef struct Mar_##m { MAR_##m } Mar_##m;
83 MARS
84 #undef F
85 #undef FR
86 #undef FR0
87 #undef MAR
88
89 #define F(f,t,c) { t v= d->f; *(t*)p= c; p += sizeof(t); };
90 #define FR(f,t,l) assert(d->f##_l<=l); memcpy(p,d->f,d->f##_l); p+=d->f##_l;
91 #define FR0 /* */
92 #define MAR(m)                                  \
93   static void mar_##m(const Mar_##m *d) {       \
94     Byte *p= packet;                            \
95     MAR_##m                                     \
96     packet_len= p - packet;                     \
97     assert(packet_len < sizeof(packet));        \
98   }
99 MARS
100 #undef F
101 #undef FR
102 #undef FR0
103 #undef MAR
104
105 #define F(f,t,c) {                              \
106     t v;                                        \
107     if (lr < sizeof(t)) return -1;              \
108     v= *(const t*)p;                            \
109     p += sizeof(t);  lr -= sizeof(t);           \
110     d->f= c;                                    \
111   };
112 #define FR(f,t,l) {                             \
113     if (lr > l) return -1;                      \
114     memcpy(d->f, p, lr);                        \
115     d->f##_l= lr;                               \
116   };
117 #define FR0                                     \
118     if (lr) return -1;
119 #define MAR(m)                                  \
120   static int unmar_##m(Mar_##m *d) {            \
121     const Byte *p= packet;                      \
122     int lr= packet_len;                         \
123     MAR_##m                                     \
124     return 0;                                   \
125   }
126 MARS
127 #undef F
128 #undef FR
129 #undef FR0
130 #undef MAR
131
132 static void nonblock(int fd) {
133   int r;
134   r= fcntl(fd,F_GETFL);  if (r<0) sysfail("nonblock fcntl F_GETFL");
135   r |= O_NONBLOCK;
136   r= fcntl(fd,F_SETFL,r);  if (r<0) sysfail("nonblock fcntl F_GETFL");
137 }
138
139 static void blocksignals(int how) {
140   sigset_t set;
141   int r;
142
143   sigemptyset(&set);
144   sigaddset(&set,SIGCHLD);
145   r= sigprocmask(how,&set,0);
146   if (r) sysfail("sigprocmask");
147 }
148
149 static void recvd_play(void) {
150   Mar_CTRL_PLAY pkt;
151   int r;
152
153   r= unmar_CTRL_PLAY(&pkt);
154   if (r) { fprintf(stderr,"bad PLAY packet\n"); return; }
155 }
156
157 static void recvd_stop(void) {
158   Mar_CTRL_STOP pkt;
159   int r;
160
161   r= unmar_CTRL_STOP(&pkt);
162   if (r) { fprintf(stderr,"bad STOP packet\n"); return; }
163 }
164   
165 static void player(void) {
166   struct sockaddr_in peer_sa, old_peer_sa;
167   socklen_t peer_salen;
168   struct ip_mreq mreq;
169   int r;
170   
171   memset(&old_peer_sa, 0, sizeof(old_peer_sa));
172
173   mcast_fd= socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
174   if (mcast_fd<0) sysfail("mcast_fd socket");
175
176   r= bind(mcast_fd, (struct sockaddr*)&mcast_sa, sizeof(mcast_sa));
177   if (r) sysfail("mcast_fd bind");
178
179   mcast_sa.sin_port= htons(ov_port_ctrl);
180   
181   mreq.imr_multiaddr= mcast_sa.sin_addr;
182   mreq.imr_interface.s_addr= INADDR_ANY;
183   r= setsockopt(mcast_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
184   if (r) sysfail("mcast_fd add memb");
185
186   for (;;) {
187     peer_salen= sizeof(peer_sa);
188     memset(&peer_sa, 0, sizeof(peer_sa));
189     
190     blocksignals(SIG_UNBLOCK);
191     packet_len= recvfrom(mcast_fd, packet, sizeof(packet),
192                          MSG_TRUNC, (struct sockaddr*)&peer_sa, &peer_salen);
193     blocksignals(SIG_BLOCK);
194
195     if (packet_len<0) {
196       if (errno==EINTR) continue;
197       perror("mcast_fd recvfrom");
198       continue;
199     }
200     if (peer_salen != sizeof(peer_sa)) {
201       fprintf(stderr,"mcast_fd recvfrom salen %ld not %ld\n",
202               (unsigned long)peer_salen, (unsigned long)sizeof(peer_sa));
203       continue;
204     }
205     if (packet_len > sizeof(packet)) {
206       fprintf(stderr,"mcast_fd recvfrom packet len %ld longer than max %ld\n",
207               (unsigned long)packet_len, (unsigned long)sizeof(packet));
208       continue;
209     }
210     if (memcmp(&old_peer_sa, &peer_sa, sizeof(old_peer_sa))) {
211       char *p= inet_ntoa(peer_sa.sin_addr);
212       fprintf(stderr,"receiving from %s:%d\n",p,ntohs(peer_sa.sin_port));
213       memcpy(&old_peer_sa, &peer_sa, sizeof(old_peer_sa));
214     }
215     if (packet_len==0) {
216       fprintf(stderr,"empty packet!\n");
217       continue;
218     }
219     switch (packet[0]) {
220     case OP_CTRL_PLAY:
221       recvd_play();
222       break;
223     case OP_CTRL_STOP:
224       recvd_stop();
225       break;
226     default:
227       fprintf(stderr,"unknown opcode %d\n",packet[0]);
228     }
229   }
230 }
231
232 int main(int argc, const char *const *argv) {
233   int r;
234
235   if (ov_port_data < 0) ov_port_data= ov_port_ctrl+1;
236
237   memset(&mcast_sa,0,sizeof(mcast_sa));
238   mcast_sa.sin_family= AF_INET;
239   r= inet_aton(ov_mcast, &mcast_sa.sin_addr);  assert(r);
240
241   player();
242   nonblock(0);
243   mar_CTRL_PLAY(0);
244   mar_CTRL_STOP(0);
245   mar_DATA(0);
246   unmar_DATA(0);
247   return 0;
248 }