8447845f |
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> |
89ec024f |
11 | #include <unistd.h> |
12 | #include <sys/select.h> |
8447845f |
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 | |
ed3679ac |
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 | { |
1e93139a |
34 | char *pos, *line; |
ed3679ac |
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 | |
1e93139a |
50 | pos = line = sdp; |
ed3679ac |
51 | while(*pos != 0 && (pos-sdp) < len) { |
52 | if (*pos == 0x0d) |
53 | *pos = 0; |
54 | |
55 | if (*pos == 0x0a) { |
56 | *pos = 0; |
57 | |
1e93139a |
58 | if (!strncasecmp("s=", line, 2)) { |
59 | sdpinfo.service = line + 2; |
60 | } else if (!strncasecmp("c=", line, 2)) { |
ed3679ac |
61 | int poscnt = 0; |
62 | |
1e93139a |
63 | line += 2; |
64 | while (*line != 0) { |
65 | if (poscnt == 2 && *line == '/') { |
66 | *line = 0; |
ed3679ac |
67 | break; |
68 | } |
69 | |
1e93139a |
70 | if (*line == ' ') { |
71 | *line = 0; |
ed3679ac |
72 | poscnt++; |
73 | |
74 | /* c=<network type> <address type> <connection address> */ |
75 | if (poscnt == 2) |
1e93139a |
76 | sdpinfo.host = line + 1; |
ed3679ac |
77 | |
78 | if (poscnt > 2) |
79 | break; |
80 | } |
1e93139a |
81 | line++; |
ed3679ac |
82 | } |
1e93139a |
83 | } else if (!strncasecmp("m=", line, 2)) { |
ed3679ac |
84 | int poscnt = 0; |
85 | |
1e93139a |
86 | line += 2; |
87 | while (*line != 0) { |
88 | if (*line == ' ') { |
89 | *line = 0; |
ed3679ac |
90 | poscnt++; |
91 | |
92 | /* m=<media> <port> <transport> <fmt list> */ |
93 | if (poscnt == 1) |
1e93139a |
94 | sdpinfo.port = line + 1; |
ed3679ac |
95 | |
96 | if (poscnt == 2) |
1e93139a |
97 | sdpinfo.proto = line + 1; |
ed3679ac |
98 | |
99 | if (poscnt > 2) |
100 | break; |
101 | } |
1e93139a |
102 | line++; |
ed3679ac |
103 | } |
104 | } |
105 | |
1e93139a |
106 | line = ++pos; |
ed3679ac |
107 | continue; |
108 | } |
109 | pos++; |
110 | } |
111 | |
112 | return &sdpinfo; |
113 | } |
114 | |
115 | |
8447845f |
116 | char *get_url_from_sap(char *service) |
117 | { |
118 | struct timeval start, curr; |
119 | struct ip_mreq mreq; |
120 | unsigned char buffer[BUFFSIZE]; |
1e3fa14a |
121 | struct in_addr sapinaddr; |
8447845f |
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; |
ed3679ac |
133 | fd_set rfds; |
134 | struct timeval tv; |
135 | int retval; |
8447845f |
136 | int sap_version, sap_addrtype, sap_messagetype, sap_encrypted, sap_compressed; |
1e3fa14a |
137 | uint8_t sender_address[16]; /* This might be IPv6! */ |
8447845f |
138 | unsigned char auth_len; |
139 | unsigned short msgid; |
ed3679ac |
140 | unsigned char *sdp; |
141 | struct sdp_info *sdpinfo; |
89ec024f |
142 | |
143 | FD_ZERO(&rfds); |
144 | FD_SET(fd, &rfds); |
145 | |
146 | tv.tv_sec = 0; |
d5b58906 |
147 | tv.tv_usec = 100000; |
89ec024f |
148 | |
149 | if ((retval = select(fd+1, &rfds, NULL, NULL, &tv)) == -1) { |
150 | perror("select"); |
1e3fa14a |
151 | break; |
89ec024f |
152 | } |
153 | |
154 | if (!retval) { |
155 | gettimeofday(&curr, NULL); |
156 | continue; |
157 | } |
8447845f |
158 | |
dacdaa20 |
159 | bzero(buffer, BUFFSIZE); |
8447845f |
160 | if ((recvd = recv(fd, buffer, BUFFSIZE, 0)) < 1) { |
161 | perror("recv"); |
1e3fa14a |
162 | break; |
8447845f |
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]; |
1e3fa14a |
174 | memcpy(sender_address, buffer+4, (sap_addrtype?16:4)); |
ed3679ac |
175 | sdp = buffer + 4 /* (sap_*, auth_len, msgid) */ + (sap_addrtype?16:4) + auth_len; |
8447845f |
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); |
1e3fa14a |
185 | printf("Sender: %u\n", *((in_addr_t*)sender_address)); |
8447845f |
186 | printf("Message Identifier Hash: %u\n", msgid); |
187 | #endif |
188 | |
8447845f |
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; |
b06a3250 |
196 | |
ed3679ac |
197 | sdpinfo = parse_sdp(sdp, recvd-(sdp-buffer)); |
8447845f |
198 | |
ed3679ac |
199 | if (sdpinfo->service && sdpinfo->proto && sdpinfo->port) { |
1e3fa14a |
200 | char hostbuf[INET6_ADDRSTRLEN]; |
201 | |
ed3679ac |
202 | if (!sdpinfo->host) { |
8447845f |
203 | |
1e3fa14a |
204 | if (sap_addrtype) { |
205 | struct in6_addr in6addr; |
206 | |
2c8bd73f |
207 | memcpy(in6addr.s6_addr, sender_address, 16); |
1e3fa14a |
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 | } |
8447845f |
221 | } |
222 | |
223 | #ifdef DEBUG |
ed3679ac |
224 | printf("%s -> %s://%s:%s\n", sdpinfo->service, sdpinfo->proto, sdpinfo->host, sdpinfo->port); |
8447845f |
225 | #endif |
226 | |
ed3679ac |
227 | if (strlen(service) < strlen(sdpinfo->service)) { |
228 | sdpinfo->service += strlen(sdpinfo->service) - strlen(service); |
8447845f |
229 | } |
230 | |
ed3679ac |
231 | if (!strncasecmp(service, sdpinfo->service, strlen(service))) { |
232 | int len = strlen(sdpinfo->host)+strlen(sdpinfo->proto)+strlen(sdpinfo->port)+5; |
8447845f |
233 | |
234 | if (!(url = malloc(len))) { |
235 | perror("malloc"); |
1e3fa14a |
236 | break; |
8447845f |
237 | } |
238 | |
ed3679ac |
239 | snprintf(url, len, "%s://%s:%s", sdpinfo->proto, sdpinfo->host, sdpinfo->port); |
8447845f |
240 | url[len-1] = 0; |
241 | break; |
242 | } |
243 | } |
244 | |
77dfd7fa |
245 | } while(curr.tv_sec < start.tv_sec+SAP_TIMEOUT || |
246 | ((curr.tv_sec == start.tv_sec+SAP_TIMEOUT) && (curr.tv_usec < start.tv_usec))); |
8447845f |
247 | |
1e3fa14a |
248 | if (!(inet_aton(SAP_ADDR, &sapinaddr))) { |
249 | perror("inet_aton"); |
250 | } |
251 | |
252 | mreq.imr_multiaddr.s_addr = sapinaddr.s_addr; |
8447845f |
253 | mreq.imr_interface.s_addr = INADDR_ANY; |
254 | setsockopt (fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); |
255 | |
90d8d87b |
256 | close(fd); |
257 | |
8447845f |
258 | return url; |
259 | } |