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);
112 memset(&ifc
, '\0', sizeof(ifc
));
113 ifc
.ifc_len
= sizeof(ifr
);
116 if(ioctl(s
, SIOCGIFCONF
, &ifc
) < 0) {
117 DPRINTF(E_ERROR
, L_TIVO
, "Error getting interface list [%s]\n", strerror(errno
));
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
]);
131 DPRINTF(E_ERROR
, L_TIVO
, "Failed to get broadcast addr on %s [%s]\n", ifr
[i
].ifr_name
, strerror(errno
));
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
));
146 * Send outgoing beacon to the specified address
147 * This will either be a specific or broadcast address
150 sendBeaconMessage(int fd
, struct sockaddr_in
* client
, int len
, int broadcast
)
155 mesg_len
= asprintf(&mesg
, "TiVoConnect=1\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
);
170 * Parse and save a received beacon packet from another server, or from
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
;
188 cp
= strtok_r(beacon
, "=\r\n", &tokptr
);
192 cp
= strtok_r( NULL
, "=\r\n", &tokptr
);
193 if( strcasecmp(scp
, "tivoconnect") == 0 )
195 else if( strcasecmp(scp
, "method") == 0 )
197 else if( strcasecmp(scp
, "identity") == 0 )
199 else if( strcasecmp(scp
, "machine") == 0 )
201 else if( strcasecmp(scp
, "platform") == 0 )
203 else if( strcasecmp(scp
, "services") == 0 )
205 cp
= strtok_r(NULL
, "=\r\n", &tokptr
);
208 if( tivoConnect
== NULL
)
212 static struct aBeacon
* topBeacon
= NULL
;
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 )
228 b
= calloc(1, sizeof(*b
));
231 b
->machine
= strdup(machine
);
233 b
->identity
= strdup(identity
);
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
;
246 lastSummary
= current
;
248 if( lastSummary
+ 1800 < current
)
249 { /* Give a summary of received server beacons every half hour or so */
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
);
262 if( b
->next
!= NULL
)
266 DPRINTF(E_DEBUG
, L_TIVO
, "%s\n", scp
);
268 lastSummary
= current
;
271 /* It's pointless to respond to a non-TiVo beacon. */
272 if( strncmp(platform
, "tcd/", 4) != 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
: "-" );
286 void ProcessTiVoBeacon(int s
)
290 struct sockaddr_in sendername
;
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
);
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
))
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
));
315 for( cp
= bufr
; *cp
; cp
++ )
317 if( cp
[-1] == '\r' || cp
[-1] == '\n' )
319 if( cp
[-1] == '\r' || cp
[-1] == '\n' )
322 if( rcvBeaconMessage(bufr
) )
323 sendBeaconMessage(s
, &sendername
, len_r
, 0);
325 #endif // TIVO_SUPPORT