Merge branch 'Teaman-ND' into Teaman-RT
[tomato.git] / release / src / router / minidlna / tivo_beacon.c
blob80632d858532eb6084cc6052e64ac4c128f3182a
1 /*
2 * Linux/C based server for TiVo Home Media Option protocol
4 * Based on version 1.5.1 of
5 * "TiVo Connect Automatic Machine; Discovery Protocol Specification"
6 * Based on version 1.1.0 of
7 * "TiVo Home Media Option; Music and Photos Server Protocol Specification"
9 * Dave Clemans, April 2003
11 * byRequest TiVo HMO Server
12 * Copyright (C) 2003 Dave Clemans
14 * This file is based on byRequest, and is part of MiniDLNA.
16 * byRequest is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * byRequest is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with byRequest. If not, see <http://www.gnu.org/licenses/>.
29 #include "config.h"
30 #ifdef TIVO_SUPPORT
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/wait.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <time.h>
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <net/if.h>
47 #include <sys/poll.h>
48 #include <netdb.h>
50 #include "tivo_beacon.h"
51 #include "upnpglobalvars.h"
52 #include "log.h"
54 /* OpenAndConfHTTPSocket() :
55 * setup the socket used to handle incoming HTTP connections. */
56 int
57 OpenAndConfTivoBeaconSocket()
59 int s;
60 int i = 1;
61 struct sockaddr_in beacon;
63 if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
65 DPRINTF(E_ERROR, L_TIVO, "socket(http): %s\n", strerror(errno));
66 return -1;
69 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
71 DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno));
74 memset(&beacon, 0, sizeof(struct sockaddr_in));
75 beacon.sin_family = AF_INET;
76 beacon.sin_port = htons(2190);
77 beacon.sin_addr.s_addr = htonl(INADDR_ANY);
79 if(bind(s, (struct sockaddr *)&beacon, sizeof(struct sockaddr_in)) < 0)
81 DPRINTF(E_ERROR, L_TIVO, "bind(http): %s\n", strerror(errno));
82 close(s);
83 return -1;
85 i = 1;
86 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0 )
88 DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_BROADCAST): %s\n", strerror(errno));
89 close(s);
90 return -1;
93 return s;
97 * Returns the interface broadcast address to be used for beacons
99 uint32_t
100 getBcastAddress(void)
102 int i, rval;
103 int s;
104 struct sockaddr_in sin;
105 struct sockaddr_in addr;
106 struct ifreq ifr[16];
107 struct ifconf ifc;
108 int count = 0;
109 uint32_t ret = INADDR_BROADCAST;
111 s = socket(PF_INET, SOCK_STREAM, 0);
112 memset(&ifc, '\0', sizeof(ifc));
113 ifc.ifc_len = sizeof(ifr);
114 ifc.ifc_req = ifr;
116 if(ioctl(s, SIOCGIFCONF, &ifc) < 0) {
117 DPRINTF(E_ERROR, L_TIVO, "Error getting interface list [%s]\n", strerror(errno));
118 close(s);
119 return ret;
122 count = ifc.ifc_len / sizeof(struct ifreq);
123 for(i = 0; i < count; i++)
125 memcpy(&addr, &ifr[i].ifr_addr, sizeof(addr));
126 if(strcmp(inet_ntoa(addr.sin_addr), lan_addr[0].str) == 0)
128 rval = ioctl(s, SIOCGIFBRDADDR, &ifr[i]);
129 if( rval < 0 )
131 DPRINTF(E_ERROR, L_TIVO, "Failed to get broadcast addr on %s [%s]\n", ifr[i].ifr_name, strerror(errno));
132 break;
134 memcpy(&sin, &ifr[i].ifr_broadaddr, sizeof(sin));
135 DPRINTF(E_DEBUG, L_TIVO, "Interface: %s broadcast addr %s\n", ifr[i].ifr_name, inet_ntoa(sin.sin_addr));
136 ret = ntohl((uint32_t)(sin.sin_addr.s_addr));
137 break;
140 close(s);
142 return ret;
146 * Send outgoing beacon to the specified address
147 * This will either be a specific or broadcast address
149 void
150 sendBeaconMessage(int fd, struct sockaddr_in * client, int len, int broadcast)
152 char * mesg;
153 int mesg_len;
155 mesg_len = asprintf(&mesg, "TiVoConnect=1\n"
156 "swversion=1.0\n"
157 "method=%s\n"
158 "identity=%s\n"
159 "machine=%s\n"
160 "platform=pc/minidlna\n"
161 "services=TiVoMediaServer:%d/http\n",
162 broadcast ? "broadcast" : "connected",
163 uuidvalue, friendly_name, runtime_vars.port);
164 DPRINTF(E_DEBUG, L_TIVO, "Sending TiVo beacon to %s\n", inet_ntoa(client->sin_addr));
165 sendto(fd, mesg, mesg_len, 0, (struct sockaddr*)client, len);
166 free(mesg);
170 * Parse and save a received beacon packet from another server, or from
171 * a TiVo.
173 * Returns true if this was a broadcast beacon msg
176 rcvBeaconMessage(char * beacon)
178 char * tivoConnect = NULL;
179 char * method = NULL;
180 char * identity = NULL;
181 char * machine = NULL;
182 char * platform = NULL;
183 char * services = NULL;
184 char * cp;
185 char * scp;
186 char * tokptr;
188 cp = strtok_r(beacon, "=\r\n", &tokptr);
189 while( cp != NULL )
191 scp = cp;
192 cp = strtok_r( NULL, "=\r\n", &tokptr );
193 if( strcasecmp(scp, "tivoconnect") == 0 )
194 tivoConnect = cp;
195 else if( strcasecmp(scp, "method") == 0 )
196 method = cp;
197 else if( strcasecmp(scp, "identity") == 0 )
198 identity = cp;
199 else if( strcasecmp(scp, "machine") == 0 )
200 machine = cp;
201 else if( strcasecmp(scp, "platform") == 0 )
202 platform = cp;
203 else if( strcasecmp(scp, "services") == 0 )
204 services = cp;
205 cp = strtok_r(NULL, "=\r\n", &tokptr);
208 if( tivoConnect == NULL )
209 return 0;
211 #ifdef DEBUG
212 static struct aBeacon* topBeacon = NULL;
213 struct aBeacon * b;
214 time_t current;
215 int len;
216 char buf[32];
217 static time_t lastSummary = 0;
219 current = time(NULL);
220 for( b = topBeacon; b != NULL; b = b->next )
222 if( strcasecmp(machine, b->machine) == 0 ||
223 strcasecmp(identity, b->identity) == 0 )
224 break;
226 if( b == NULL )
228 b = calloc(1, sizeof(*b));
230 if( machine )
231 b->machine = strdup(machine);
232 if( identity )
233 b->identity = strdup(identity);
235 b->next = topBeacon;
236 topBeacon = b;
238 DPRINTF(E_DEBUG, L_TIVO, "Received new beacon: machine(%s) platform(%s) services(%s)\n",
239 machine ? machine : "-",
240 platform ? platform : "-",
241 services ? services : "-" );
244 b->lastSeen = current;
245 if( !lastSummary )
246 lastSummary = current;
248 if( lastSummary + 1800 < current )
249 { /* Give a summary of received server beacons every half hour or so */
250 len = 0;
251 for( b = topBeacon; b != NULL; b = b->next )
253 len += strlen(b->machine) + 32;
255 scp = malloc(len + 128);
256 strcpy( scp, "Known servers: " );
257 for( b = topBeacon; b != NULL; b = b->next )
259 strcat(scp, b->machine);
260 sprintf(buf, "(%ld)", current - b->lastSeen);
261 strcat(scp, buf);
262 if( b->next != NULL )
263 strcat(scp, ",");
265 strcat(scp, "\n");
266 DPRINTF(E_DEBUG, L_TIVO, "%s\n", scp);
267 free(scp);
268 lastSummary = current;
270 #endif
271 /* It's pointless to respond to a non-TiVo beacon. */
272 if( strncmp(platform, "tcd/", 4) != 0 )
273 return 0;
275 if( strcasecmp(method, "broadcast") == 0 )
277 DPRINTF(E_DEBUG, L_TIVO, "Received new beacon: machine(%s) platform(%s) services(%s)\n",
278 machine ? machine : "-",
279 platform ? platform : "-",
280 services ? services : "-" );
281 return 1;
283 return 0;
286 void ProcessTiVoBeacon(int s)
288 int n;
289 char *cp;
290 struct sockaddr_in sendername;
291 socklen_t len_r;
292 char bufr[1500];
293 len_r = sizeof(struct sockaddr_in);
295 /* We only expect to see beacon msgs from TiVo's and possibly other tivo servers */
296 n = recvfrom(s, bufr, sizeof(bufr), 0,
297 (struct sockaddr *)&sendername, &len_r);
298 if( n > 0 )
299 bufr[n] = '\0';
301 /* find which subnet the client is in */
302 for(n = 0; n<n_lan_addr; n++)
304 if( (sendername.sin_addr.s_addr & lan_addr[n].mask.s_addr)
305 == (lan_addr[n].addr.s_addr & lan_addr[n].mask.s_addr))
306 break;
308 if( n == n_lan_addr )
310 DPRINTF(E_DEBUG, L_TIVO, "Ignoring TiVo beacon on other interface [%s]\n",
311 inet_ntoa(sendername.sin_addr));
312 return;
315 for( cp = bufr; *cp; cp++ )
316 /* do nothing */;
317 if( cp[-1] == '\r' || cp[-1] == '\n' )
318 *--cp = '\0';
319 if( cp[-1] == '\r' || cp[-1] == '\n' )
320 *--cp = '\0';
322 if( rcvBeaconMessage(bufr) )
323 sendBeaconMessage(s, &sendername, len_r, 0);
325 #endif // TIVO_SUPPORT