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/>.
36 #include <sys/ioctl.h>
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
50 #include "tivo_beacon.h"
51 #include "upnpglobalvars.h"
54 /* OpenAndConfHTTPSocket() :
55 * setup the socket used to handle incoming HTTP connections. */
57 OpenAndConfTivoBeaconSocket()
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
));
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
));
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
));
97 * Returns the interface broadcast address to be used for beacons
100 getBcastAddress(void)
104 struct sockaddr_in sin
;
105 struct sockaddr_in addr
;
106 struct ifreq ifr
[16];
109 uint32_t ret
= INADDR_BROADCAST
;
111 s
= socket(PF_INET
, SOCK_STREAM
, 0);
114 memset(&ifc
, '\0', sizeof(ifc
));
115 ifc
.ifc_len
= sizeof(ifr
);
118 if (ioctl(s
, SIOCGIFCONF
, &ifc
) < 0)
120 DPRINTF(E_ERROR
, L_TIVO
, "Error getting interface list [%s]\n", strerror(errno
));
125 count
= ifc
.ifc_len
/ sizeof(struct ifreq
);
126 for (i
= 0; i
< count
; i
++)
128 memcpy(&addr
, &ifr
[i
].ifr_addr
, sizeof(addr
));
129 if(strcmp(inet_ntoa(addr
.sin_addr
), lan_addr
[0].str
) == 0)
131 rval
= ioctl(s
, SIOCGIFBRDADDR
, &ifr
[i
]);
134 DPRINTF(E_ERROR
, L_TIVO
, "Failed to get broadcast addr on %s [%s]\n", ifr
[i
].ifr_name
, strerror(errno
));
137 memcpy(&sin
, &ifr
[i
].ifr_broadaddr
, sizeof(sin
));
138 DPRINTF(E_DEBUG
, L_TIVO
, "Interface: %s broadcast addr %s\n", ifr
[i
].ifr_name
, inet_ntoa(sin
.sin_addr
));
139 ret
= ntohl((uint32_t)(sin
.sin_addr
.s_addr
));
149 * Send outgoing beacon to the specified address
150 * This will either be a specific or broadcast address
153 sendBeaconMessage(int fd
, struct sockaddr_in
* client
, int len
, int broadcast
)
158 msg_len
= snprintf(msg
, sizeof(msg
), "TiVoConnect=1\n"
163 "platform=pc/minidlna\n"
164 "services=TiVoMediaServer:%d/http\n",
165 broadcast
? "broadcast" : "connected",
166 uuidvalue
, friendly_name
, runtime_vars
.port
);
169 DPRINTF(E_DEBUG
, L_TIVO
, "Sending TiVo beacon to %s\n", inet_ntoa(client
->sin_addr
));
170 sendto(fd
, msg
, msg_len
, 0, (struct sockaddr
*)client
, len
);
174 * Parse and save a received beacon packet from another server, or from
177 * Returns true if this was a broadcast beacon msg
180 rcvBeaconMessage(char *beacon
)
182 char *tivoConnect
= NULL
;
184 char *identity
= NULL
;
185 char *machine
= NULL
;
186 char *platform
= NULL
;
187 char *services
= NULL
;
192 cp
= strtok_r(beacon
, "=\r\n", &tokptr
);
196 cp
= strtok_r(NULL
, "=\r\n", &tokptr
);
197 if( strcasecmp(scp
, "tivoconnect") == 0 )
199 else if( strcasecmp(scp
, "method") == 0 )
201 else if( strcasecmp(scp
, "identity") == 0 )
203 else if( strcasecmp(scp
, "machine") == 0 )
205 else if( strcasecmp(scp
, "platform") == 0 )
207 else if( strcasecmp(scp
, "services") == 0 )
209 cp
= strtok_r(NULL
, "=\r\n", &tokptr
);
212 if( !tivoConnect
|| !platform
|| !method
)
216 static struct aBeacon
*topBeacon
= NULL
;
221 static time_t lastSummary
= 0;
223 current
= time(NULL
);
224 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
226 if( strcasecmp(machine
, b
->machine
) == 0 ||
227 strcasecmp(identity
, b
->identity
) == 0 )
232 b
= calloc(1, sizeof(*b
));
235 b
->machine
= strdup(machine
);
237 b
->identity
= strdup(identity
);
242 DPRINTF(E_DEBUG
, L_TIVO
, "Received new beacon: machine(%s) platform(%s) services(%s)\n",
243 machine
? machine
: "-",
244 platform
? platform
: "-",
245 services
? services
: "-" );
248 b
->lastSeen
= current
;
250 lastSummary
= current
;
252 if( lastSummary
+ 1800 < current
)
253 { /* Give a summary of received server beacons every half hour or so */
255 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
257 len
+= strlen(b
->machine
) + 32;
259 scp
= malloc(len
+ 128);
260 strcpy( scp
, "Known servers: " );
261 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
263 strcat(scp
, b
->machine
);
264 sprintf(buf
, "(%ld)", current
- b
->lastSeen
);
266 if( b
->next
!= NULL
)
270 DPRINTF(E_DEBUG
, L_TIVO
, "%s\n", scp
);
272 lastSummary
= current
;
275 /* It's pointless to respond to a non-TiVo beacon. */
276 if( strncmp(platform
, "tcd/", 4) != 0 )
279 if( strcasecmp(method
, "broadcast") == 0 )
281 DPRINTF(E_DEBUG
, L_TIVO
, "Received new beacon: machine(%s/%s) platform(%s) services(%s)\n",
282 machine
? machine
: "-",
283 identity
? identity
: "-",
284 platform
? platform
: "-",
285 services
? services
: "-" );
291 void ProcessTiVoBeacon(int s
)
295 struct sockaddr_in sendername
;
298 len_r
= sizeof(struct sockaddr_in
);
300 /* We only expect to see beacon msgs from TiVo's and possibly other tivo servers */
301 n
= recvfrom(s
, bufr
, sizeof(bufr
), 0,
302 (struct sockaddr
*)&sendername
, &len_r
);
306 /* find which subnet the client is in */
307 for(n
= 0; n
<n_lan_addr
; n
++)
309 if( (sendername
.sin_addr
.s_addr
& lan_addr
[n
].mask
.s_addr
)
310 == (lan_addr
[n
].addr
.s_addr
& lan_addr
[n
].mask
.s_addr
))
313 if( n
== n_lan_addr
)
315 DPRINTF(E_DEBUG
, L_TIVO
, "Ignoring TiVo beacon on other interface [%s]\n",
316 inet_ntoa(sendername
.sin_addr
));
320 for( cp
= bufr
; *cp
; cp
++ )
322 if( cp
[-1] == '\r' || cp
[-1] == '\n' )
324 if( cp
[-1] == '\r' || cp
[-1] == '\n' )
327 if( rcvBeaconMessage(bufr
) )
328 sendBeaconMessage(s
, &sendername
, len_r
, 0);
330 #endif // TIVO_SUPPORT