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