1/* 2 * Linux/C based server for TiVo Home Media Option protocol 3 * 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" 8 * 9 * Dave Clemans, April 2003 10 * 11 * Copyright (C) 2003 Dave Clemans 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 * 27 * See the file "COPYING" for more details. 28 */ 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> 41 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> 49 50#include "tivo_beacon.h" 51#include "upnpglobalvars.h" 52#include "log.h" 53 54static struct aBeacon* topBeacon = NULL; 55 56/* OpenAndConfHTTPSocket() : 57 * setup the socket used to handle incoming HTTP connections. */ 58int 59OpenAndConfTivoBeaconSocket() 60{ 61 int s; 62 int i = 1; 63 struct sockaddr_in beacon; 64 65 if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 66 { 67 DPRINTF(E_ERROR, L_TIVO, "socket(http): %s\n", strerror(errno)); 68 return -1; 69 } 70 71 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) 72 { 73 DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno)); 74 } 75 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); 80 81 if(bind(s, (struct sockaddr *)&beacon, sizeof(struct sockaddr_in)) < 0) 82 { 83 DPRINTF(E_ERROR, L_TIVO, "bind(http): %s\n", strerror(errno)); 84 close(s); 85 return -1; 86 } 87 i = 1; 88 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0 ) 89 { 90 DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_BROADCAST): %s\n", strerror(errno)); 91 close(s); 92 return -1; 93 } 94 95 return s; 96} 97 98/* 99 * Returns the interface broadcast address to be used for beacons 100 */ 101uint32_t 102getBcastAddress(void) 103{ 104 int i, rval; 105 int s = socket(PF_INET, SOCK_STREAM, 0); 106 struct sockaddr_in sin; 107 struct sockaddr_in addr; 108 struct ifreq ifr; 109 110 for (i=1; i > 0; i++) 111 { 112 ifr.ifr_ifindex = i; 113 if( ioctl(s, SIOCGIFNAME, &ifr) < 0 ) 114 break; 115 if(ioctl(s, SIOCGIFADDR, &ifr, sizeof(struct ifreq)) < 0) 116 continue; 117 memcpy(&addr, &ifr.ifr_addr, sizeof(addr)); 118 if(strcmp(inet_ntoa(addr.sin_addr), lan_addr[0].str) == 0) 119 { 120 rval = ioctl(s, SIOCGIFBRDADDR, &ifr); 121 if( rval < 0 ) 122 { 123 close(s); 124 return INADDR_BROADCAST; 125 } 126 memcpy(&sin, &ifr.ifr_broadaddr, sizeof(sin)); 127 close(s); 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)); 130 } 131 } 132 133 return INADDR_BROADCAST; 134} 135 136/* 137 * Send outgoing beacon to the specified address 138 * This will either be a specific or broadcast address 139 */ 140void 141sendBeaconMessage(int fd, struct sockaddr_in * client, int len, int broadcast) 142{ 143 char * mesg; 144 int mesg_len; 145 146 mesg_len = asprintf(&mesg, "TiVoConnect=1\n" 147 "swversion=%s\n" 148 "method=%s\n" 149 "identity=%s\n" 150 "machine=%s\n" 151 "platform=pc/minidlna\n" 152 "services=TiVoMediaServer:%d/http\n", 153 "1.0", 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); 158 free(mesg); 159} 160 161/* 162 * Parse and save a received beacon packet from another server, or from 163 * a TiVo. 164 * 165 * Returns true if this was a broadcast beacon msg 166 */ 167int 168rcvBeaconMessage(char * beacon) 169{ 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; 177 char * cp; 178 char * scp; 179 char * tokptr; 180 struct aBeacon * b; 181 int len; 182 time_t current; 183 char buf[32]; 184 static time_t lastSummary = 0; 185 186 cp = strtok_r( beacon, "=\r\n", &tokptr ); 187 while( cp != NULL ) 188 { 189 scp = cp; 190 cp = strtok_r( NULL, "=\r\n", &tokptr ); 191 if( strcasecmp( scp, "tivoconnect" ) == 0 ) 192 tivoConnect = cp; 193 else if( strcasecmp( scp, "swversion" ) == 0 ) 194 swVersion = 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 ); 206 } 207 208 if( tivoConnect == NULL ) 209 return 0; 210 211 current = time( NULL ); 212 for( b = topBeacon; b != NULL; b = b->next ) 213 { 214 if( strcasecmp( machine, b->machine ) == 0 || 215 strcasecmp( identity, b->identity ) == 0 ) 216 { 217 break; 218 } 219 } 220 if( b == NULL ) 221 { 222 b = ( struct aBeacon* ) calloc( 1, sizeof( *b ) ); 223 b->next = NULL; 224 225 if ( machine ) 226 { 227 b->machine = ( char* ) malloc( strlen( machine ) + 1 ); 228 strcpy( b->machine, machine ); 229 } 230 if ( identity ) 231 { 232 b->identity = ( char* ) malloc( strlen( identity ) + 1 ); 233 strcpy( b->identity, identity ); 234 } 235 if ( swVersion ) 236 { 237 b->swversion = ( char* ) malloc( strlen( swVersion ) + 1 ); 238 strcpy( b->swversion, swVersion ); 239 } 240 if ( method ) 241 { 242 b->method = ( char* ) malloc( strlen( method ) + 1 ); 243 strcpy( b->method, method ); 244 } 245 if ( platform ) 246 { 247 b->platform = ( char* ) malloc( strlen( platform ) + 1 ); 248 strcpy( b->platform, platform ); 249 } 250 if ( services ) 251 { 252 b->services = ( char* ) malloc( strlen( services ) + 1 ); 253 strcpy( b->services, services ); 254 } 255 256 b->next = topBeacon; 257 topBeacon = b; 258 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 : "-" ); 263 } 264 b->lastSeen = current; 265 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 */ 270 len = 0; 271 for( b = topBeacon; b != NULL; b = b->next ) 272 { 273 len += strlen( b->machine ) + 32; 274 } 275 scp = ( char* ) malloc( len + 128 ); 276 strcpy( scp, "Known servers: " ); 277 for( b = topBeacon; b != NULL; b = b->next ) 278 { 279 strcat( scp, b->machine ); 280 sprintf( buf, "(%ld)", current - b->lastSeen ); 281 strcat( scp, buf ); 282 if( b->next != NULL ) 283 strcat( scp, "," ); 284 } 285 strcat(scp, "\n"); 286 printf("%s\n", scp); 287 free(scp); 288 lastSummary = current; 289 } 290 291 if( strcasecmp( method, "broadcast" ) == 0 ) 292 return 1; 293 return 0; 294} 295#endif // TIVO_SUPPORT 296