source: titan/minidlna-1.0.22/tivo_beacon.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

File size: 8.2 KB
Line 
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 * byRequest TiVo HMO Server
12 * Copyright (C) 2003  Dave Clemans
13 *
14 * This file is based on byRequest, and is part of MiniDLNA.
15 *
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.
20 *
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.
25 *
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/>.
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
54/* OpenAndConfHTTPSocket() :
55 * setup the socket used to handle incoming HTTP connections. */
56int
57OpenAndConfTivoBeaconSocket()
58{
59        int s;
60        int i = 1;
61        struct sockaddr_in beacon;
62
63        if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
64        {
65                DPRINTF(E_ERROR, L_TIVO, "socket(http): %s\n", strerror(errno));
66                return -1;
67        }
68
69        if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
70        {
71                DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno));
72        }
73
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);
78
79        if(bind(s, (struct sockaddr *)&beacon, sizeof(struct sockaddr_in)) < 0)
80        {
81                DPRINTF(E_ERROR, L_TIVO, "bind(http): %s\n", strerror(errno));
82                close(s);
83                return -1;
84        }
85        i = 1;
86        if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0 )
87        {
88                DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_BROADCAST): %s\n", strerror(errno));
89                close(s);
90                return -1;
91        }
92
93        return s;
94}
95
96/*
97 * Returns the interface broadcast address to be used for beacons
98 */
99uint32_t
100getBcastAddress(void)
101{
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;
110
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;
115
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;
120        }
121
122        count = ifc.ifc_len / sizeof(struct ifreq);
123        for(i = 0; i < count; i++)
124        {
125                memcpy(&addr, &ifr[i].ifr_addr, sizeof(addr));
126                if(strcmp(inet_ntoa(addr.sin_addr), lan_addr[0].str) == 0)
127                {
128                        rval = ioctl(s, SIOCGIFBRDADDR, &ifr[i]);
129                        if( rval < 0 )
130                        {
131                                DPRINTF(E_ERROR, L_TIVO, "Failed to get broadcast addr on %s [%s]\n", ifr[i].ifr_name, strerror(errno));
132                                break;
133                        }
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;
138                }
139        }
140        close(s);
141
142        return ret;
143}
144
145/*
146 * Send outgoing beacon to the specified address
147 * This will either be a specific or broadcast address
148 */
149void
150sendBeaconMessage(int fd, struct sockaddr_in * client, int len, int broadcast)
151{
152        char * mesg;
153        int mesg_len;
154
155        mesg_len = asprintf(&mesg, "TiVoConnect=1\n"
156                                   "swversion=1.0\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                                   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);
166        free(mesg);
167}
168
169/*
170 * Parse and save a received beacon packet from another server, or from
171 * a TiVo.
172 *
173 * Returns true if this was a broadcast beacon msg
174 */
175int
176rcvBeaconMessage(char * beacon)
177{
178        char * tivoConnect = NULL;
179        char * method = NULL;
180        char * identity = NULL;
181        char * machine = NULL;
182        char * platform = NULL;
183        char * services = NULL;
184        char * cp;
185        char * scp;
186        char * tokptr;
187
188        cp = strtok_r(beacon, "=\r\n", &tokptr);
189        while( cp != NULL )
190        {
191                scp = cp;
192                cp = strtok_r( NULL, "=\r\n", &tokptr );
193                if( strcasecmp(scp, "tivoconnect") == 0 )
194                        tivoConnect = 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#ifdef DEBUG
212        static struct aBeacon* topBeacon = NULL;
213        struct aBeacon * b;
214        time_t current;
215        int len;
216        char buf[32];
217        static time_t lastSummary = 0;
218
219        current = time(NULL);
220        for( b = topBeacon; b != NULL; b = b->next )
221        {
222                if( strcasecmp(machine, b->machine) == 0 ||
223                    strcasecmp(identity, b->identity) == 0 )
224                        break;
225        }
226        if( b == NULL )
227        {
228                b = calloc(1, sizeof(*b));
229
230                if( machine )
231                        b->machine = strdup(machine);
232                if( identity )
233                        b->identity = strdup(identity);
234
235                b->next = topBeacon;
236                topBeacon = b;
237
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 : "-" );
242        }
243
244        b->lastSeen = current;
245        if( !lastSummary )
246                lastSummary = current;
247
248        if( lastSummary + 1800 < current )
249        {  /* Give a summary of received server beacons every half hour or so */
250                len = 0;
251                for( b = topBeacon; b != NULL; b = b->next )
252                {
253                        len += strlen(b->machine) + 32;
254                }
255                scp = malloc(len + 128);
256                strcpy( scp, "Known servers: " );
257                for( b = topBeacon; b != NULL; b = b->next )
258                {
259                        strcat(scp, b->machine);
260                        sprintf(buf, "(%ld)", current - b->lastSeen);
261                        strcat(scp, buf);
262                        if( b->next != NULL )
263                                strcat(scp, ",");
264                }
265                strcat(scp, "\n");
266                DPRINTF(E_DEBUG, L_TIVO, "%s\n", scp);
267                free(scp);
268                lastSummary = current;
269        }
270#endif
271        /* It's pointless to respond to a non-TiVo beacon. */
272        if( strncmp(platform, "tcd/", 4) != 0 )
273                return 0;
274
275        if( strcasecmp(method, "broadcast") == 0 )
276        {
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 : "-" );
281                return 1;
282        }
283        return 0;
284}
285
286void ProcessTiVoBeacon(int s)
287{
288        int n;
289        char *cp;
290        struct sockaddr_in sendername;
291        socklen_t len_r;
292        char bufr[1500];
293        len_r = sizeof(struct sockaddr_in);
294
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);
298        if( n > 0 )
299                bufr[n] = '\0';
300
301        /* find which subnet the client is in */
302        for(n = 0; n<n_lan_addr; n++)
303        {
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))
306                        break;
307        }
308        if( n == n_lan_addr )
309        {
310                DPRINTF(E_DEBUG, L_TIVO, "Ignoring TiVo beacon on other interface [%s]\n",
311                        inet_ntoa(sendername.sin_addr));
312                return;
313        }
314
315        for( cp = bufr; *cp; cp++ )
316                /* do nothing */;
317        if( cp[-1] == '\r' || cp[-1] == '\n' )
318                *--cp = '\0';
319        if( cp[-1] == '\r' || cp[-1] == '\n' )
320                *--cp = '\0';
321
322        if( rcvBeaconMessage(bufr) )
323                sendBeaconMessage(s, &sendername, len_r, 0);
324}
325#endif // TIVO_SUPPORT
Note: See TracBrowser for help on using the repository browser.