"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/tivo_beacon.c" (24 Nov 2020, 8577 Bytes) of package /linux/privat/minidlna-1.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tivo_beacon.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.1_vs_1.3.0.

    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 <poll.h>
   48 #include <netdb.h>
   49 
   50 #include "event.h"
   51 #include "tivo_beacon.h"
   52 #include "upnpglobalvars.h"
   53 #include "log.h"
   54 
   55 /* OpenAndConfHTTPSocket() :
   56  * setup the socket used to handle incoming HTTP connections. */
   57 int
   58 OpenAndConfTivoBeaconSocket()
   59 {
   60     int s;
   61     int i = 1;
   62     struct sockaddr_in beacon;
   63 
   64     if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
   65     {
   66         DPRINTF(E_ERROR, L_TIVO, "socket(http): %s\n", strerror(errno));
   67         return -1;
   68     }
   69 
   70     if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
   71     {
   72         DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno));
   73     }
   74 
   75     memset(&beacon, 0, sizeof(struct sockaddr_in));
   76     beacon.sin_family = AF_INET;
   77     beacon.sin_port = htons(2190);
   78     beacon.sin_addr.s_addr = htonl(INADDR_ANY);
   79 
   80     if(bind(s, (struct sockaddr *)&beacon, sizeof(struct sockaddr_in)) < 0)
   81     {
   82         DPRINTF(E_ERROR, L_TIVO, "bind(http): %s\n", strerror(errno));
   83         close(s);
   84         return -1;
   85     }
   86     i = 1;
   87     if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0 )
   88     {
   89         DPRINTF(E_WARN, L_TIVO, "setsockopt(http, SO_BROADCAST): %s\n", strerror(errno));
   90         close(s);
   91         return -1;
   92     }
   93 
   94     return s;
   95 }
   96 
   97 /*
   98  * Returns the interface broadcast address to be used for beacons
   99  */
  100 uint32_t
  101 getBcastAddress(void)
  102 {
  103     int i, rval;
  104     int s;
  105     struct sockaddr_in sin;
  106     struct sockaddr_in addr;
  107     struct ifreq ifr[16];
  108     struct ifconf ifc;
  109     int count = 0;
  110     uint32_t ret = INADDR_BROADCAST;
  111 
  112     s = socket(PF_INET, SOCK_STREAM, 0);
  113     if (!s)
  114         return ret;
  115     memset(&ifc, '\0', sizeof(ifc));
  116     ifc.ifc_len = sizeof(ifr);
  117     ifc.ifc_req = ifr;
  118 
  119     if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
  120     {
  121         DPRINTF(E_ERROR, L_TIVO, "Error getting interface list [%s]\n", strerror(errno));
  122         close(s);
  123         return ret;
  124     }
  125 
  126     count = ifc.ifc_len / sizeof(struct ifreq);
  127     for (i = 0; i < count; i++)
  128     {
  129         memcpy(&addr, &ifr[i].ifr_addr, sizeof(addr));
  130         if(strcmp(inet_ntoa(addr.sin_addr), lan_addr[0].str) == 0)
  131         {
  132             rval = ioctl(s, SIOCGIFBRDADDR, &ifr[i]);
  133             if( rval < 0 )
  134             {
  135                 DPRINTF(E_ERROR, L_TIVO, "Failed to get broadcast addr on %s [%s]\n", ifr[i].ifr_name, strerror(errno));
  136                 break;
  137             }
  138             memcpy(&sin, &ifr[i].ifr_broadaddr, sizeof(sin));
  139             DPRINTF(E_DEBUG, L_TIVO, "Interface: %s broadcast addr %s\n", ifr[i].ifr_name, inet_ntoa(sin.sin_addr));
  140             ret = ntohl((uint32_t)(sin.sin_addr.s_addr));
  141             break;
  142         }
  143     }
  144     close(s);
  145 
  146     return ret;
  147 }
  148 
  149 /*
  150  * Send outgoing beacon to the specified address
  151  * This will either be a specific or broadcast address
  152  */
  153 void
  154 sendBeaconMessage(int fd, struct sockaddr_in * client, int len, int broadcast)
  155 {
  156     char msg[512];
  157     int msg_len;
  158 
  159     msg_len = snprintf(msg, sizeof(msg), "TiVoConnect=1\n"
  160                                          "swversion=1.0\n"
  161                                          "method=%s\n"
  162                                          "identity=%s\n"
  163                                          "machine=%s\n"
  164                                          "platform=pc/minidlna\n"
  165                                          "services=TiVoMediaServer:%d/http\n",
  166                                          broadcast ? "broadcast" : "connected",
  167                                          uuidvalue, friendly_name, runtime_vars.port);
  168     if (msg_len < 0)
  169         return;
  170     DPRINTF(E_DEBUG, L_TIVO, "Sending TiVo beacon to %s\n", inet_ntoa(client->sin_addr));
  171     sendto(fd, msg, msg_len, 0, (struct sockaddr*)client, len);
  172 }
  173 
  174 /*
  175  * Parse and save a received beacon packet from another server, or from
  176  * a TiVo.
  177  *
  178  * Returns true if this was a broadcast beacon msg
  179  */
  180 static int
  181 rcvBeaconMessage(char *beacon)
  182 {
  183     char *tivoConnect = NULL;
  184     char *method = NULL;
  185     char *identity = NULL;
  186     char *machine = NULL;
  187     char *platform = NULL;
  188     char *services = NULL;
  189     char *cp;
  190     char *scp;
  191     char *tokptr;
  192 
  193     cp = strtok_r(beacon, "=\r\n", &tokptr);
  194     while( cp != NULL )
  195     {
  196         scp = cp;
  197         cp = strtok_r(NULL, "=\r\n", &tokptr);
  198         if( strcasecmp(scp, "tivoconnect") == 0 )
  199             tivoConnect = cp;
  200         else if( strcasecmp(scp, "method") == 0 )
  201             method = cp;
  202         else if( strcasecmp(scp, "identity") == 0 )
  203             identity = cp;
  204         else if( strcasecmp(scp, "machine") == 0 )
  205             machine = cp;
  206         else if( strcasecmp(scp, "platform") == 0 )
  207             platform = cp;
  208         else if( strcasecmp(scp, "services") == 0 )
  209             services = cp;
  210         cp = strtok_r(NULL, "=\r\n", &tokptr);
  211     }
  212 
  213     if( !tivoConnect || !platform || !method )
  214         return 0;
  215 
  216 #ifdef DEBUG
  217     static struct aBeacon *topBeacon = NULL;
  218     struct aBeacon *b;
  219     time_t current;
  220     int len;
  221     char buf[32];
  222     static time_t lastSummary = 0;
  223 
  224     current = time(NULL);
  225     for( b = topBeacon; b != NULL; b = b->next )
  226     {
  227         if( strcasecmp(machine, b->machine) == 0 ||
  228             strcasecmp(identity, b->identity) == 0 )
  229             break;
  230     }
  231     if( b == NULL )
  232     {
  233         b = calloc(1, sizeof(*b));
  234 
  235         if( machine )
  236             b->machine = strdup(machine);
  237         if( identity )
  238             b->identity = strdup(identity);
  239 
  240         b->next = topBeacon;
  241         topBeacon = b;
  242 
  243         DPRINTF(E_DEBUG, L_TIVO, "Received new beacon: machine(%s) platform(%s) services(%s)\n", 
  244                  machine ? machine : "-",
  245                  platform ? platform : "-", 
  246                  services ? services : "-" );
  247     }
  248 
  249     b->lastSeen = current;
  250     if( !lastSummary )
  251         lastSummary = current;
  252 
  253     if( lastSummary + 1800 < current )
  254     {  /* Give a summary of received server beacons every half hour or so */
  255         len = 0;
  256         for( b = topBeacon; b != NULL; b = b->next )
  257         {
  258             len += strlen(b->machine) + 32;
  259         }
  260         scp = malloc(len + 128);
  261         strcpy( scp, "Known servers: " );
  262         for( b = topBeacon; b != NULL; b = b->next )
  263         {
  264             strcat(scp, b->machine);
  265             sprintf(buf, "(%ld)", current - b->lastSeen);
  266             strcat(scp, buf);
  267             if( b->next != NULL )
  268                 strcat(scp, ",");
  269         }
  270         strcat(scp, "\n");
  271         DPRINTF(E_DEBUG, L_TIVO, "%s\n", scp);
  272         free(scp);
  273         lastSummary = current;
  274     }
  275 #endif
  276     /* It's pointless to respond to a non-TiVo beacon. */
  277     if( strncmp(platform, "tcd/", 4) != 0 )
  278         return 0;
  279 
  280     if( strcasecmp(method, "broadcast") == 0 )
  281     {
  282         DPRINTF(E_DEBUG, L_TIVO, "Received new beacon: machine(%s/%s) platform(%s) services(%s)\n", 
  283                  machine ? machine : "-",
  284                  identity ? identity : "-",
  285                  platform ? platform : "-", 
  286                  services ? services : "-" );
  287         return 1;
  288     }
  289     return 0;
  290 }
  291 
  292 void ProcessTiVoBeacon(struct event *ev)
  293 {
  294     int s, n;
  295     char *cp;
  296     struct sockaddr_in sendername;
  297     socklen_t len_r;
  298     char bufr[1500];
  299     len_r = sizeof(struct sockaddr_in);
  300 
  301     s = ev->fd;
  302     /* We only expect to see beacon msgs from TiVo's and possibly other tivo servers */
  303     n = recvfrom(s, bufr, sizeof(bufr), 0,
  304                  (struct sockaddr *)&sendername, &len_r);
  305     if( n > 0 )
  306         bufr[n] = '\0';
  307 
  308     /* find which subnet the client is in */
  309     for(n = 0; n<n_lan_addr; n++)
  310     {
  311         if( (sendername.sin_addr.s_addr & lan_addr[n].mask.s_addr)
  312            == (lan_addr[n].addr.s_addr & lan_addr[n].mask.s_addr))
  313             break;
  314     }
  315     if( n == n_lan_addr )
  316     {
  317         DPRINTF(E_DEBUG, L_TIVO, "Ignoring TiVo beacon on other interface [%s]\n",
  318             inet_ntoa(sendername.sin_addr));
  319         return;
  320     }
  321 
  322     for( cp = bufr; *cp; cp++ )
  323         /* do nothing */;
  324     if( cp[-1] == '\r' || cp[-1] == '\n' )
  325         *--cp = '\0';
  326     if( cp[-1] == '\r' || cp[-1] == '\n' )
  327         *--cp = '\0';
  328 
  329     if( rcvBeaconMessage(bufr) )
  330         sendBeaconMessage(s, &sendername, len_r, 0);
  331 }
  332 #endif // TIVO_SUPPORT