summaryrefslogtreecommitdiff
path: root/xine_frontend_vdrdiscovery.c
blob: 30b9b28098efb8216a0e67cd608139538e41d073 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
 * xine_frontend_vdrdiscovery.c
 *
 * Try to found VDR with xineliboutput server from (local) network.
 *
 * See the main source file 'xineliboutput.c' for copyright information and
 * how to reach the author.
 *
 * $Id: xine_frontend_vdrdiscovery.c,v 1.2 2006-07-02 16:43:30 phintuka Exp $
 *
 */

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "xine_input_vdr_net.h"  /* default ports */

static int search_vdr_server(int *port, char *address)
{
  struct sockaddr_in sin;
  int fd_broadcast = -1;
  int dummy = 1, trycount = 0;
  struct pollfd pfd;
  char pktbuf[1024];

  *port = DEFAULT_VDR_PORT;
  strcpy(address, "vdr");

  if ((fd_broadcast = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_TCP*/)) < 0) {
    LOGERR("fd_broadcast = socket() failed");
  } else {

    if(setsockopt(fd_broadcast, SOL_SOCKET, SO_BROADCAST, 
		  &dummy, sizeof(int)) < 0)
      LOGERR("setsockopt(SO_BROADCAST) failed");
    if(setsockopt(fd_broadcast, SOL_SOCKET, SO_REUSEADDR, 
		  &dummy, sizeof(int)) < 0)
      LOGERR("setsockopt(SO_REUSEADDR) failed");

    sin.sin_family = AF_INET;
    sin.sin_port   = htons(DISCOVERY_PORT);
    sin.sin_addr.s_addr = INADDR_ANY;

    if (bind(fd_broadcast, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
      LOGERR("Can't bind fd_broadcast to %d", DISCOVERY_PORT);
    } else {  
      char *test = "VDR xineliboutput DISCOVERY 1.0\r\n"
	           "Client: 192.168.1.21:37890\r\n"
	           "\r\n";
      int testlen = strlen(test);
retry:
      sin.sin_addr.s_addr = INADDR_BROADCAST;
      if(testlen != sendto(fd_broadcast, test, testlen, 0,
			   (struct sockaddr *)&sin, sizeof(sin))) {
	LOGERR("UDP broadcast send failed (discovery)");
      } else {

	while(1) {
	  int err;
	  pfd.fd = fd_broadcast;
	  pfd.events = POLLIN;

	  errno=0;
	  while(1==(err=poll(&pfd, 1, 500))) {
	    struct sockaddr_in from;
	    socklen_t fromlen = sizeof(from);
	    memset(&from, 0, sizeof(from));
	    memset(pktbuf, 0, sizeof(pktbuf));

	    errno=0;
	    if((err=recvfrom(fd_broadcast, pktbuf, 1023, 0, 
			     (struct sockaddr *)&from, &fromlen)) > 0) {
	      char *mystring = "VDR xineliboutput DISCOVERY 1.0\r\n"
		               "Server port: ";
	      uint32_t tmp = ntohl(from.sin_addr.s_addr);

	      pktbuf[err] = 0;
	      LOGDBG("Reveived broadcast: %d bytes from %d.%d.%d.%d\n   %s", 
		     err, 
		     ((tmp>>24)&0xff), ((tmp>>16)&0xff), 
		     ((tmp>>8)&0xff), ((tmp)&0xff),
		     pktbuf);
	      if(!strncmp(mystring, pktbuf, strlen(mystring))) {
		LOGDBG("Valid discovery message");
		close(fd_broadcast);
		sprintf(address, "%d.%d.%d.%d",
			((tmp>>24)&0xff), ((tmp>>16)&0xff), 
			((tmp>>8)&0xff), ((tmp)&0xff));
		if(1==sscanf(pktbuf+strlen(mystring), "%d", port))
		return 1;
	      } else {
		LOGDBG("NOT valid discovery message");
	      }
	    } else {
	      LOGERR("broadcast recvfrom failed");
	      break;
	    }
	  } 
	  if(!err) {
	    /* timeout */
	    trycount++;
	    if(trycount < 3)
	      goto retry;
	    break;
	  }
	  LOGERR("broadcast poll error");
	  break;
	}
      }
    }    
  }

  /* failed */
  close(fd_broadcast);
  return 0;
}