/* -*- c -*- * * ---------------------------------------------------------------------- * CcXstream Client Library for XBOX Media Player (Server Discovery) * ---------------------------------------------------------------------- * * Copyright (c) 2002-2003 by PuhPuh * * This code is copyrighted property of the author. It can still * be used for any non-commercial purpose following conditions: * * 1) This copyright notice is not removed. * 2) Source code follows any distribution of the software * if possible. * 3) Copyright notice above is found in the documentation * of the distributed software. * * Any express or implied warranties are disclaimed. Author is * not liable for any direct or indirect damages caused by the use * of this software. * * ---------------------------------------------------------------------- * */ #include "ccincludes.h" #include "ccbuffer.h" #include "ccxclient.h" #include "ccxencode.h" static unsigned long timeout_start(void); static int timeout_exceeded(unsigned long start_time); static unsigned long timeout_start() { #if defined (__linux__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__CYGWIN__) || defined (sun) struct timeval tv; unsigned long r; gettimeofday(&tv, NULL); r = (((unsigned long)tv.tv_sec) % 200000000U) * 10U; r += ((unsigned long)tv.tv_usec) / 100000U; return r; #elif defined (_XBOX) return ((unsigned long)(GetTickCount()) / 100U); #else time_t t; t = time(NULL); return (((unsigned long)t) % 200000000U) * 10U; #endif } static int timeout_exceeded(unsigned long start_time) { unsigned long t; t = timeout_start(); return ((t < start_time) || ((start_time + 9) < t)); } CcXstreamClientError ccx_client_discover_servers(CcXstreamServerDiscoveryCB callback, void *context) { struct sockaddr_in sa, ra; #if defined (_XBOX) || defined (WIN32) size_t sa_len, ra_len; #else socklen_t sa_len, ra_len; #endif unsigned long handle, p_len, p_handle; unsigned char *packet, ch; size_t packet_len; int found = 0, l, c; #if defined (_XBOX) || defined (WIN32) SOCKET sock; #else int sock; #endif unsigned long t0; fd_set rs; struct timeval tv; CcBufferRec buf[1], seen_buf[1]; char *p_address, *p_port, *p_version, *p_comment; memset(&sa, 0, sizeof (sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_BROADCAST); sa.sin_port = htons(CC_XSTREAM_DEFAULT_PORT); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) return CC_XSTREAM_CLIENT_COMMAND_FAILED; c = 1; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)(&c), sizeof (c)); handle = cc_xstream_client_mkpacket_server_discovery(&packet, &packet_len); sa_len = sizeof (sa); CC_DEBUG(6, ("Sending a discovery datagram.")); sendto(sock, packet, packet_len, 0, (struct sockaddr *)(&sa), sa_len); t0 = timeout_start(); cc_buffer_init(seen_buf); while (1) { if (timeout_exceeded(t0)) { cc_buffer_uninit(seen_buf); break; } FD_ZERO(&rs); FD_SET(sock, &rs); tv.tv_sec = 0; tv.tv_usec = 350000; switch (select(sock + 1, &rs, NULL, NULL, &tv)) { case -1: CC_DEBUG(5, ("Select call fails.")); #if defined (_XBOX) || defined (WIN32) closesocket(sock); #else close(sock); #endif cc_buffer_uninit(seen_buf); cc_xfree(packet); return (found > 0) ? CC_XSTREAM_CLIENT_OK : CC_XSTREAM_CLIENT_COMMAND_FAILED; /*NOTREACHED*/ case 0: /* Resend packet if we got timeout. */ CC_DEBUG(6, ("Resending the discovery datagram.")); sendto(sock, packet, packet_len, 0, (struct sockaddr *)(&sa), sa_len); break; default: memset(&ra, 0, sizeof (ra)); cc_buffer_init(buf); cc_buffer_append_space(buf, 2000); ra_len = sizeof (ra); l = recvfrom(sock, cc_buffer_ptr(buf), cc_buffer_len(buf), 0, (struct sockaddr *)(&ra), &ra_len); if (l > 0) { cc_buffer_consume_end(buf, cc_buffer_len(buf) - l); if (cc_xstream_buffer_decode_int(buf, &p_len) && (p_len == cc_buffer_len(buf)) && cc_xstream_buffer_decode_byte(buf, &ch) && ((CcXstreamPacket)ch == CC_XSTREAM_XBMSP_PACKET_SERVER_DISCOVERY_REPLY) && cc_xstream_buffer_decode_int(buf, &p_handle) && (p_handle == handle) && cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_address), NULL) && cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_port), NULL) && cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_version), NULL) && cc_xstream_buffer_decode_string(buf, (unsigned char **)(&p_comment), NULL) && (cc_buffer_len(buf) == 0)) { if (strlen(p_address) == 0) { cc_xfree(p_address); #ifdef _XBOX { unsigned char *b; b = (unsigned char *)(&(ra.sin_addr.s_addr)); p_address = cc_xmalloc(32); sprintf(p_address, "%d.%d.%d.%d", (int)(b[0]), (int)(b[1]), (int)(b[2]), (int)(b[3])); } #else p_address = cc_xstrdup(inet_ntoa(ra.sin_addr)); #endif } if (strlen(p_port) == 0) { cc_xfree(p_port); p_port = cc_xmalloc(16); #ifdef _XBOX sprintf(p_port, "%d", (int)(ntohs(ra.sin_port))); #else snprintf(p_port, 16, "%d", (int)(ntohs(ra.sin_port))); #endif } cc_buffer_append_string(buf, ">"); cc_buffer_append_string(buf, p_address); cc_buffer_append_string(buf, ":"); cc_buffer_append_string(buf, p_port); cc_buffer_append_string(buf, "<"); ch = 0; /* Terminate both buffers with '\0' */ cc_buffer_append(buf, &ch, 1); cc_buffer_append(seen_buf, &ch, 1); if (strstr((char *)cc_buffer_ptr(seen_buf), (char *)cc_buffer_ptr(buf)) == NULL) { /* Don't forget to remove the terminating '\0' */ cc_buffer_consume_end(seen_buf, 1); cc_buffer_append_string(seen_buf, (char *)cc_buffer_ptr(buf)); CC_DEBUG(6, ("New server %s", (char *)cc_buffer_ptr(buf))); if (callback != NULL) (*callback)(p_address, p_port, p_version, p_comment, context); found++; } else { /* Don't forget to remove the terminating '\0' */ cc_buffer_consume_end(seen_buf, 1); CC_DEBUG(6, ("Duplicate %s", (char *)cc_buffer_ptr(buf))); } cc_buffer_clear(buf); cc_xfree(p_address); cc_xfree(p_port); cc_xfree(p_version); cc_xfree(p_comment); } } cc_buffer_uninit(buf); break; } } #if defined (_XBOX) || defined (WIN32) closesocket(sock); #else close(sock); #endif cc_xfree(packet); return (found > 0) ? CC_XSTREAM_CLIENT_OK : CC_XSTREAM_CLIENT_SERVER_NOT_FOUND; } /* eof (ccxdiscover.c) */