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 static struct aBeacon
* topBeacon
= NULL
;
56 /* OpenAndConfHTTPSocket() :
57 * setup the socket used to handle incoming HTTP connections. */
59 OpenAndConfTivoBeaconSocket()
63 struct sockaddr_in beacon
;
65 if( (s
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0)
67 DPRINTF(E_ERROR
, L_TIVO
, "socket(http): %s\n", strerror(errno
));
71 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
73 DPRINTF(E_WARN
, L_TIVO
, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno
));
76 memset(&beacon
, 0, sizeof(struct sockaddr_in
));
77 beacon
.sin_family
= AF_INET
;
78 beacon
.sin_port
= htons(2190);
79 beacon
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
81 if(bind(s
, (struct sockaddr
*)&beacon
, sizeof(struct sockaddr_in
)) < 0)
83 DPRINTF(E_ERROR
, L_TIVO
, "bind(http): %s\n", strerror(errno
));
88 if(setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &i
, sizeof(i
)) < 0 )
90 DPRINTF(E_WARN
, L_TIVO
, "setsockopt(http, SO_BROADCAST): %s\n", strerror(errno
));
99 * Returns the interface broadcast address to be used for beacons
102 getBcastAddress(void)
105 int s
= socket(PF_INET
, SOCK_STREAM
, 0);
106 struct sockaddr_in sin
;
107 struct sockaddr_in addr
;
110 for (i
=1; i
> 0; i
++)
113 if( ioctl(s
, SIOCGIFNAME
, &ifr
) < 0 )
115 if(ioctl(s
, SIOCGIFADDR
, &ifr
, sizeof(struct ifreq
)) < 0)
117 memcpy(&addr
, &ifr
.ifr_addr
, sizeof(addr
));
118 if(strcmp(inet_ntoa(addr
.sin_addr
), lan_addr
[0].str
) == 0)
120 rval
= ioctl(s
, SIOCGIFBRDADDR
, &ifr
);
124 return INADDR_BROADCAST
;
126 memcpy(&sin
, &ifr
.ifr_broadaddr
, sizeof(sin
));
128 DPRINTF(E_DEBUG
, L_TIVO
, "Interface: %s broadcast addr %s \n", ifr
.ifr_name
, inet_ntoa(sin
.sin_addr
));
129 return ntohl((uint32_t)(sin
.sin_addr
.s_addr
));
133 return INADDR_BROADCAST
;
137 * Send outgoing beacon to the specified address
138 * This will either be a specific or broadcast address
141 sendBeaconMessage(int fd
, struct sockaddr_in
* client
, int len
, int broadcast
)
146 mesg_len
= asprintf(&mesg
, "TiVoConnect=1\n"
151 "platform=pc/minidlna\n"
152 "services=TiVoMediaServer:%d/http\n",
154 broadcast
? "broadcast" : "connected",
155 uuidvalue
, friendly_name
, runtime_vars
.port
);
156 DPRINTF(E_DEBUG
, L_TIVO
, "Sending TiVo beacon\n");
157 sendto(fd
, mesg
, mesg_len
, 0, (struct sockaddr
*)client
, len
);
162 * Parse and save a received beacon packet from another server, or from
165 * Returns true if this was a broadcast beacon msg
168 rcvBeaconMessage(char * beacon
)
170 char * tivoConnect
= NULL
;
171 char * swVersion
= NULL
;
172 char * method
= NULL
;
173 char * identity
= NULL
;
174 char * machine
= NULL
;
175 char * platform
= NULL
;
176 char * services
= NULL
;
184 static time_t lastSummary
= 0;
186 cp
= strtok_r( beacon
, "=\r\n", &tokptr
);
190 cp
= strtok_r( NULL
, "=\r\n", &tokptr
);
191 if( strcasecmp( scp
, "tivoconnect" ) == 0 )
193 else if( strcasecmp( scp
, "swversion" ) == 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
)
211 current
= time( NULL
);
212 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
214 if( strcasecmp( machine
, b
->machine
) == 0 ||
215 strcasecmp( identity
, b
->identity
) == 0 )
222 b
= ( struct aBeacon
* ) calloc( 1, sizeof( *b
) );
227 b
->machine
= ( char* ) malloc( strlen( machine
) + 1 );
228 strcpy( b
->machine
, machine
);
232 b
->identity
= ( char* ) malloc( strlen( identity
) + 1 );
233 strcpy( b
->identity
, identity
);
237 b
->swversion
= ( char* ) malloc( strlen( swVersion
) + 1 );
238 strcpy( b
->swversion
, swVersion
);
242 b
->method
= ( char* ) malloc( strlen( method
) + 1 );
243 strcpy( b
->method
, method
);
247 b
->platform
= ( char* ) malloc( strlen( platform
) + 1 );
248 strcpy( b
->platform
, platform
);
252 b
->services
= ( char* ) malloc( strlen( services
) + 1 );
253 strcpy( b
->services
, services
);
259 printf( "Received new beacon: machine(%s) platform(%s) services(%s)\n",
260 b
->machine
? b
->machine
: "-",
261 b
->platform
? b
->platform
: "-",
262 b
->services
? b
->services
: "-" );
264 b
->lastSeen
= current
;
266 if( lastSummary
== 0 )
267 lastSummary
= current
;
268 if( lastSummary
+ 1800 < current
)
269 { /* Give a summary of received server beacons every half hour or so */
271 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
273 len
+= strlen( b
->machine
) + 32;
275 scp
= ( char* ) malloc( len
+ 128 );
276 strcpy( scp
, "Known servers: " );
277 for( b
= topBeacon
; b
!= NULL
; b
= b
->next
)
279 strcat( scp
, b
->machine
);
280 sprintf( buf
, "(%ld)", current
- b
->lastSeen
);
282 if( b
->next
!= NULL
)
288 lastSummary
= current
;
291 if( strcasecmp( method
, "broadcast" ) == 0 )
295 #endif // TIVO_SUPPORT