MiniDLNA update: 1.0.19.1 to 1.0.20
[tomato.git] / release / src / router / minidlna / tivo_beacon.c
blob5f111322a7e123c62f96d29663e4a4881782c359
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=%s\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 "1.0",
163 broadcast ? "broadcast" : "connected",
164 uuidvalue, friendly_name, runtime_vars.port);
165 DPRINTF(E_DEBUG, L_TIVO, "Sending TiVo beacon to %s\n", inet_ntoa(client->sin_addr));
166 sendto(fd, mesg, mesg_len, 0, (struct sockaddr*)client, len);
167 free(mesg);
171 * Parse and save a received beacon packet from another server, or from
172 * a TiVo.
174 * Returns true if this was a broadcast beacon msg
177 rcvBeaconMessage(char * beacon)
179 char * tivoConnect = NULL;
180 char * method = NULL;
181 char * identity = NULL;
182 char * machine = NULL;
183 char * platform = NULL;
184 char * services = NULL;
185 char * cp;
186 char * scp;
187 char * tokptr;
189 cp = strtok_r(beacon, "=\r\n", &tokptr);
190 while( cp != NULL )
192 scp = cp;
193 cp = strtok_r( NULL, "=\r\n", &tokptr );
194 if( strcasecmp(scp, "tivoconnect") == 0 )
195 tivoConnect = cp;
196 else if( strcasecmp(scp, "method") == 0 )
197 method = cp;
198 else if( strcasecmp(scp, "identity") == 0 )
199 identity = cp;
200 else if( strcasecmp(scp, "machine") == 0 )
201 machine = cp;
202 else if( strcasecmp(scp, "platform") == 0 )
203 platform = cp;
204 else if( strcasecmp(scp, "services") == 0 )
205 services = cp;
206 cp = strtok_r(NULL, "=\r\n", &tokptr);
209 if( tivoConnect == NULL )
210 return 0;
212 /* It's pointless to respond to our own beacon */
213 if( strcmp(identity, uuidvalue) == 0)
214 return 0;
216 #ifdef DEBUG
217 static struct aBeacon* topBeacon = NULL;
218 struct aBeacon * b;
219 time_t current;
220 int len;
221 char buf[32];
222 static time_t lastSummary = 0;
224 current = time(NULL);
225 for( b = topBeacon; b != NULL; b = b->next )
227 if( strcasecmp(machine, b->machine) == 0 ||
228 strcasecmp(identity, b->identity) == 0 )
229 break;
231 if( b == NULL )
233 b = calloc(1, sizeof(*b));
235 if( machine )
236 b->machine = strdup(machine);
237 if( identity )
238 b->identity = strdup(identity);
240 b->next = topBeacon;
241 topBeacon = b;
243 DPRINTF(E_DEBUG, L_TIVO, "Received new beacon: machine(%s) platform(%s) services(%s)\n",
244 machine ? machine : "-",
245 platform ? platform : "-",
246 services ? services : "-" );
249 b->lastSeen = current;
250 if( !lastSummary )
251 lastSummary = current;
253 if( lastSummary + 1800 < current )
254 { /* Give a summary of received server beacons every half hour or so */
255 len = 0;
256 for( b = topBeacon; b != NULL; b = b->next )
258 len += strlen(b->machine) + 32;
260 scp = malloc(len + 128);
261 strcpy( scp, "Known servers: " );
262 for( b = topBeacon; b != NULL; b = b->next )
264 strcat(scp, b->machine);
265 sprintf(buf, "(%ld)", current - b->lastSeen);
266 strcat(scp, buf);
267 if( b->next != NULL )
268 strcat(scp, ",");
270 strcat(scp, "\n");
271 DPRINTF(E_DEBUG, L_TIVO, "%s\n", scp);
272 free(scp);
273 lastSummary = current;
275 #endif
276 if( strcasecmp(method, "broadcast") == 0 )
277 return 1;
278 return 0;
281 void ProcessTiVoBeacon(int s)
283 int n;
284 char *cp;
285 struct sockaddr_in sendername;
286 socklen_t len_r;
287 char bufr[1500];
288 len_r = sizeof(struct sockaddr_in);
290 /* We only expect to see beacon msgs from TiVo's and possibly other tivo servers */
291 n = recvfrom(s, bufr, sizeof(bufr), 0,
292 (struct sockaddr *)&sendername, &len_r);
293 if( n > 0 )
294 bufr[n] = '\0';
296 /* find which subnet the client is in */
297 for(n = 0; n<n_lan_addr; n++)
299 if( (sendername.sin_addr.s_addr & lan_addr[n].mask.s_addr)
300 == (lan_addr[n].addr.s_addr & lan_addr[n].mask.s_addr))
301 break;
303 if( n == n_lan_addr )
305 DPRINTF(E_DEBUG, L_TIVO, "Ignoring TiVo beacon on other interface [%s]\n",
306 inet_ntoa(sendername.sin_addr));
307 return;
310 for( cp = bufr; *cp; cp++ )
311 /* do nothing */;
312 if( cp[-1] == '\r' || cp[-1] == '\n' )
313 *--cp = '\0';
314 if( cp[-1] == '\r' || cp[-1] == '\n' )
315 *--cp = '\0';
317 if( rcvBeaconMessage(bufr) )
318 sendBeaconMessage(s, &sendername, len_r, 0);
320 #endif // TIVO_SUPPORT