]> git.zerfleddert.de Git - record-dvb/blob - sap.c
6a8ef799e6a0831a6de2f5fa6be677045cfe9ca5
[record-dvb] / sap.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <sys/time.h>
8 #include <time.h>
9 #include <string.h>
10 #include <strings.h>
11 #include <unistd.h>
12 #include <sys/select.h>
13
14 #include "mcast.h"
15 #include "sap.h"
16
17 #define SAP_ADDR "224.2.127.254"
18 #define SAP_PORT 9875
19 #define SAP_MAX_SIZE 1024
20
21 #define SAP_TIMEOUT 3
22
23 #define BUFFSIZE SAP_MAX_SIZE
24
25 struct sdp_info {
26 char *service;
27 char *host;
28 char *proto;
29 char *port;
30 };
31
32 struct sdp_info *parse_sdp(char *sdp, int len)
33 {
34 char *pos, *line;
35 static struct sdp_info sdpinfo;
36
37 /* RFC 2327
38 * v=0
39 * o=- 6dca 1 IN IP4 192.168.100.17:2000
40 * s=TV Das Erste
41 * t=0 0
42 * c=IN IP4 192.168.100.17/1
43 * m=video 2000 http 33
44 * a=tool:getstream
45 * a=type:broadcast
46 */
47
48 bzero(&sdpinfo, sizeof(struct sdp_info));
49
50 pos = line = sdp;
51 while(*pos != 0 && (pos-sdp) < len) {
52 if (*pos == 0x0d)
53 *pos = 0;
54
55 if (*pos == 0x0a) {
56 *pos = 0;
57
58 if (!strncasecmp("s=", line, 2)) {
59 sdpinfo.service = line + 2;
60 } else if (!strncasecmp("c=", line, 2)) {
61 int poscnt = 0;
62
63 line += 2;
64 while (*line != 0) {
65 if (poscnt == 2 && *line == '/') {
66 *line = 0;
67 break;
68 }
69
70 if (*line == ' ') {
71 *line = 0;
72 poscnt++;
73
74 /* c=<network type> <address type> <connection address> */
75 if (poscnt == 2)
76 sdpinfo.host = line + 1;
77
78 if (poscnt > 2)
79 break;
80 }
81 line++;
82 }
83 } else if (!strncasecmp("m=", line, 2)) {
84 int poscnt = 0;
85
86 line += 2;
87 while (*line != 0) {
88 if (*line == ' ') {
89 *line = 0;
90 poscnt++;
91
92 /* m=<media> <port> <transport> <fmt list> */
93 if (poscnt == 1)
94 sdpinfo.port = line + 1;
95
96 if (poscnt == 2)
97 sdpinfo.proto = line + 1;
98
99 if (poscnt > 2)
100 break;
101 }
102 line++;
103 }
104 }
105
106 line = ++pos;
107 continue;
108 }
109 pos++;
110 }
111
112 return &sdpinfo;
113 }
114
115
116 char *get_url_from_sap(char *service)
117 {
118 struct timeval start, curr;
119 struct ip_mreq mreq;
120 unsigned char buffer[BUFFSIZE];
121 struct in_addr sapinaddr;
122 char *url = NULL;
123 int fd;
124
125 snprintf(buffer,BUFFSIZE,"udp://%s:%u", SAP_ADDR, SAP_PORT);
126
127 fd = open_mcast(buffer);
128
129 gettimeofday(&start, NULL);
130
131 do {
132 int recvd;
133 fd_set rfds;
134 struct timeval tv;
135 int retval;
136 int sap_version, sap_addrtype, sap_messagetype, sap_encrypted, sap_compressed;
137 uint8_t sender_address[16]; /* This might be IPv6! */
138 unsigned char auth_len;
139 unsigned short msgid;
140 unsigned char *sdp;
141 struct sdp_info *sdpinfo;
142
143 FD_ZERO(&rfds);
144 FD_SET(fd, &rfds);
145
146 tv.tv_sec = 0;
147 tv.tv_usec = 100000;
148
149 if ((retval = select(fd+1, &rfds, NULL, NULL, &tv)) == -1) {
150 perror("select");
151 break;
152 }
153
154 if (!retval) {
155 gettimeofday(&curr, NULL);
156 continue;
157 }
158
159 bzero(buffer, BUFFSIZE);
160 if ((recvd = recv(fd, buffer, BUFFSIZE, 0)) < 1) {
161 perror("recv");
162 break;
163 }
164
165 gettimeofday(&curr, NULL);
166
167 sap_version = (buffer[0] >> 5) & 0x7;
168 sap_addrtype = (buffer[0] >> 4) & 0x1;
169 sap_messagetype = (buffer[0] >> 2) & 0x1;
170 sap_encrypted = (buffer[0] >> 1) & 0x1;
171 sap_compressed = buffer[0] & 0x1;
172 auth_len = buffer[1];
173 msgid = buffer[2] << 8 | buffer[3];
174 memcpy(sender_address, buffer+4, (sap_addrtype?16:4));
175 sdp = buffer + 4 /* (sap_*, auth_len, msgid) */ + (sap_addrtype?16:4) + auth_len;
176
177 #ifdef DEBUG
178 printf("\n");
179 printf("SAP-Version: %d\n", sap_version);
180 printf("Adresstyp: %s\n", (sap_addrtype?"IPv6":"IPv4"));
181 printf("Messagetype: %s\n", (sap_messagetype?"Announcement":"Deletion"));
182 printf("Encrypted: %d\n", sap_encrypted);
183 printf("Compressed: %d\n", sap_compressed);
184 printf("Authentication Length: %d\n", auth_len);
185 printf("Sender: %u\n", *((in_addr_t*)sender_address));
186 printf("Message Identifier Hash: %u\n", msgid);
187 #endif
188
189 #if 0 /* Getstream gets this wrong, see rfc2974 */
190 if (sap_messagetype)
191 continue; /* We are not interested in deletions */
192 #endif
193
194 if (sap_encrypted || sap_compressed)
195 continue;
196
197 sdpinfo = parse_sdp(sdp, recvd-(sdp-buffer));
198
199 if (sdpinfo->service && sdpinfo->proto && sdpinfo->port) {
200 char hostbuf[INET6_ADDRSTRLEN];
201
202 if (!sdpinfo->host) {
203
204 if (sap_addrtype) {
205 struct in6_addr in6addr;
206
207 memcpy(in6addr.in6_u.u6_addr8, sender_address, 16);
208 if (!(sdpinfo->host = (char*)inet_ntop(AF_INET6, &in6addr, hostbuf, INET6_ADDRSTRLEN))) {
209 perror("inet_ntop");
210 continue;
211 }
212 } else {
213 struct in_addr inaddr;
214
215 inaddr.s_addr = *((in_addr_t*)sender_address);
216 if (!(sdpinfo->host = (char*)inet_ntop(AF_INET, &inaddr, hostbuf, INET6_ADDRSTRLEN))) {
217 perror("inet_ntop");
218 continue;
219 }
220 }
221 }
222
223 #ifdef DEBUG
224 printf("%s -> %s://%s:%s\n", sdpinfo->service, sdpinfo->proto, sdpinfo->host, sdpinfo->port);
225 #endif
226
227 if (strlen(service) < strlen(sdpinfo->service)) {
228 sdpinfo->service += strlen(sdpinfo->service) - strlen(service);
229 }
230
231 if (!strncasecmp(service, sdpinfo->service, strlen(service))) {
232 int len = strlen(sdpinfo->host)+strlen(sdpinfo->proto)+strlen(sdpinfo->port)+5;
233
234 if (!(url = malloc(len))) {
235 perror("malloc");
236 break;
237 }
238
239 snprintf(url, len, "%s://%s:%s", sdpinfo->proto, sdpinfo->host, sdpinfo->port);
240 url[len-1] = 0;
241 break;
242 }
243 }
244
245 } while(curr.tv_sec < start.tv_sec+SAP_TIMEOUT);
246
247 if (!(inet_aton(SAP_ADDR, &sapinaddr))) {
248 perror("inet_aton");
249 }
250
251 mreq.imr_multiaddr.s_addr = sapinaddr.s_addr;
252 mreq.imr_interface.s_addr = INADDR_ANY;
253 setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
254
255 close(fd);
256
257 return url;
258 }
Impressum, Datenschutz