1 /*   -*- c -*-
2  *
3  *  ----------------------------------------------------------------------
4  *  CcXstream Client Library for XBOX Media Player (Server Discovery)
5  *  ----------------------------------------------------------------------
6  *
7  *  Copyright (c) 2002-2003 by PuhPuh
8  *
9  *  This code is copyrighted property of the author.  It can still
10  *  be used for any non-commercial purpose following conditions:
11  *
12  *      1) This copyright notice is not removed.
13  *      2) Source code follows any distribution of the software
14  *         if possible.
15  *      3) Copyright notice above is found in the documentation
16  *         of the distributed software.
17  *
18  *  Any express or implied warranties are disclaimed.  Author is
19  *  not liable for any direct or indirect damages caused by the use
20  *  of this software.
21  *
22  *  ----------------------------------------------------------------------
23  *
24  */
25 
26 #include "ccincludes.h"
27 #include "ccbuffer.h"
28 #include "ccxclient.h"
29 #include "ccxencode.h"
30 
31 static unsigned long timeout_start(void);
32 static int timeout_exceeded(unsigned long start_time);
33 
timeout_start()34 static unsigned long timeout_start()
35 {
36 #if defined (__linux__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__CYGWIN__) || defined (sun)
37   struct timeval tv;
38   unsigned long r;
39 
40   gettimeofday(&tv, NULL);
41   r = (((unsigned long)tv.tv_sec) % 200000000U) * 10U;
42   r += ((unsigned long)tv.tv_usec) / 100000U;
43   return r;
44 #elif defined (_XBOX)
45   return ((unsigned long)(GetTickCount()) / 100U);
46 #else
47   time_t t;
48 
49   t = time(NULL);
50   return (((unsigned long)t) % 200000000U) * 10U;
51 #endif
52 }
53 
timeout_exceeded(unsigned long start_time)54 static int timeout_exceeded(unsigned long start_time)
55 {
56   unsigned long t;
57 
58   t = timeout_start();
59   return ((t < start_time) || ((start_time + 9) < t));
60 }
61 
ccx_client_discover_servers(CcXstreamServerDiscoveryCB callback,void * context)62 CcXstreamClientError ccx_client_discover_servers(CcXstreamServerDiscoveryCB callback, void *context)
63 {
64   struct sockaddr_in sa, ra;
65 #if defined (_XBOX) || defined (WIN32)
66   size_t sa_len, ra_len;
67 #else
68   socklen_t sa_len, ra_len;
69 #endif
70   unsigned long handle, p_len, p_handle;
71   unsigned char *packet, ch;
72   size_t packet_len;
73   int found = 0, l, c;
74 #if defined (_XBOX) || defined (WIN32)
75   SOCKET sock;
76 #else
77   int sock;
78 #endif
79   unsigned long t0;
80   fd_set rs;
81   struct timeval tv;
82   CcBufferRec buf[1], seen_buf[1];
83   char *p_address, *p_port, *p_version, *p_comment;
84 
85   memset(&sa, 0, sizeof (sa));
86   sa.sin_family = AF_INET;
87   sa.sin_addr.s_addr = htonl(INADDR_BROADCAST);
88   sa.sin_port = htons(CC_XSTREAM_DEFAULT_PORT);
89   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
90   if (sock < 0)
91     return CC_XSTREAM_CLIENT_COMMAND_FAILED;
92   c = 1;
93   setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)(&c), sizeof (c));
94   handle = cc_xstream_client_mkpacket_server_discovery(&packet, &packet_len);
95   sa_len = sizeof (sa);
96   CC_DEBUG(6, ("Sending a discovery datagram."));
97   sendto(sock,  packet, packet_len, 0, (struct sockaddr *)(&sa), sa_len);
98   t0 = timeout_start();
99   cc_buffer_init(seen_buf);
100   while (1)
101     {
102       if (timeout_exceeded(t0))
103 	{
104 	  cc_buffer_uninit(seen_buf);
105 	  break;
106 	}
107       FD_ZERO(&rs);
108       FD_SET(sock, &rs);
109       tv.tv_sec = 0;
110       tv.tv_usec = 350000;
111       switch (select(sock + 1, &rs, NULL, NULL, &tv))
112 	{
113 	case -1:
114 	  CC_DEBUG(5, ("Select call fails."));
115 #if defined (_XBOX) || defined (WIN32)
116 	  closesocket(sock);
117 #else
118 	  close(sock);
119 #endif
120 	  cc_buffer_uninit(seen_buf);
121 	  cc_xfree(packet);
122 	  return (found > 0) ? CC_XSTREAM_CLIENT_OK : CC_XSTREAM_CLIENT_COMMAND_FAILED;
123 	  /*NOTREACHED*/
124 
125 	case 0:
126 	  /* Resend packet if we got timeout. */
127 	  CC_DEBUG(6, ("Resending the discovery datagram."));
128 	  sendto(sock,  packet, packet_len, 0, (struct sockaddr *)(&sa), sa_len);
129 	  break;
130 
131 	default:
132 	  memset(&ra, 0, sizeof (ra));
133 	  cc_buffer_init(buf);
134 	  cc_buffer_append_space(buf, 2000);
135 	  ra_len = sizeof (ra);
136 	  l = recvfrom(sock, cc_buffer_ptr(buf), cc_buffer_len(buf), 0, (struct sockaddr *)(&ra), &ra_len);
137 	  if (l > 0)
138 	    {
139 	      cc_buffer_consume_end(buf, cc_buffer_len(buf) - l);
140 	      if (cc_xstream_buffer_decode_int(buf, &p_len) &&
141 		  (p_len == cc_buffer_len(buf)) &&
142 		  cc_xstream_buffer_decode_byte(buf, &ch) &&
143 		  ((CcXstreamPacket)ch == CC_XSTREAM_XBMSP_PACKET_SERVER_DISCOVERY_REPLY) &&
144 		  cc_xstream_buffer_decode_int(buf, &p_handle) &&
145 		  (p_handle == handle) &&
146 		  cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_address), NULL) &&
147 		  cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_port), NULL) &&
148 		  cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_version), NULL) &&
149 		  cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_comment), NULL) &&
150 		  (cc_buffer_len(buf) == 0))
151 		{
152 		  if (strlen(p_address) == 0)
153 		    {
154 		      cc_xfree(p_address);
155 #ifdef _XBOX
156 		      {
157 			unsigned char *b;
158 
159 			b = (unsigned char *)(&(ra.sin_addr.s_addr));
160 			p_address = cc_xmalloc(32);
161 			sprintf(p_address, "%d.%d.%d.%d", (int)(b[0]), (int)(b[1]), (int)(b[2]), (int)(b[3]));
162 		      }
163 #else
164 		      p_address = cc_xstrdup(inet_ntoa(ra.sin_addr));
165 #endif
166 		    }
167 		  if (strlen(p_port) == 0)
168 		    {
169 		      cc_xfree(p_port);
170 		      p_port = cc_xmalloc(16);
171 #ifdef _XBOX
172 		      sprintf(p_port, "%d", (int)(ntohs(ra.sin_port)));
173 #else
174 		      snprintf(p_port, 16, "%d", (int)(ntohs(ra.sin_port)));
175 #endif
176 		    }
177 		  cc_buffer_append_string(buf, ">");
178 		  cc_buffer_append_string(buf, p_address);
179 		  cc_buffer_append_string(buf, ":");
180 		  cc_buffer_append_string(buf, p_port);
181 		  cc_buffer_append_string(buf, "<");
182 		  ch = 0;
183 		  /* Terminate both buffers with '\0' */
184 		  cc_buffer_append(buf, &ch, 1);
185 		  cc_buffer_append(seen_buf, &ch, 1);
186 		  if (strstr((char *)cc_buffer_ptr(seen_buf), (char *)cc_buffer_ptr(buf)) == NULL)
187 		    {
188 		      /* Don't forget to remove the terminating '\0' */
189 		      cc_buffer_consume_end(seen_buf, 1);
190 		      cc_buffer_append_string(seen_buf, (char *)cc_buffer_ptr(buf));
191 		      CC_DEBUG(6, ("New server %s", (char *)cc_buffer_ptr(buf)));
192 		      if (callback != NULL)
193 			(*callback)(p_address, p_port, p_version, p_comment, context);
194 		      found++;
195 		    }
196 		  else
197 		    {
198 		      /* Don't forget to remove the terminating '\0' */
199 		      cc_buffer_consume_end(seen_buf, 1);
200 		      CC_DEBUG(6, ("Duplicate  %s", (char *)cc_buffer_ptr(buf)));
201 		    }
202 		  cc_buffer_clear(buf);
203 		  cc_xfree(p_address);
204 		  cc_xfree(p_port);
205 		  cc_xfree(p_version);
206 		  cc_xfree(p_comment);
207 		}
208 	    }
209 	  cc_buffer_uninit(buf);
210 	  break;
211 	}
212     }
213 #if defined (_XBOX) || defined (WIN32)
214   closesocket(sock);
215 #else
216   close(sock);
217 #endif
218   cc_xfree(packet);
219 
220   return (found > 0) ? CC_XSTREAM_CLIENT_OK : CC_XSTREAM_CLIENT_SERVER_NOT_FOUND;
221 }
222 
223 /* eof (ccxdiscover.c) */
224