/* * network.cpp * * Copyright (C) 2002 Matt Ownby * * This file is part of DAPHNE, a laserdisc arcade game emulator * * DAPHNE is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DAPHNE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include // for lousy random number generation #include #include #ifdef MAC_OSX #include #include #include #include #include #include #endif #ifdef WIN32 #include //#include //#include //#include #endif #ifdef FREEBSD #include #include #endif #ifdef LINUX #include // MATT : I'm not sure if this is good for UNIX in general so I put it here #endif #ifdef UNIX #include #include #include #include // for DNS #include #include // for write #endif #include // for crc32 calculation #include "../io/error.h" #include "../daphne.h" #include "network.h" // arbitrary port I've chosen to send incoming data #define NET_PORT 7733 // ip address to send data to // I changed this to 'stats' in case I ever want to change the IP address of the stats // server without changing the location of the web server. #define NET_IP "stats.daphne-emu.com" // which version of this simple protocol we are using (so the server can support multiple versions) static const unsigned char PROTOCOL_VERSION = 1; #define USERID_FILENAME "userid.bin" bool g_send_data_to_server = false; // whether user allows us to send data to server //////////////////// // disable sending data to server for paranoid users // (I don't know why anyone should be paranoid, this is open source for crying out loud) void net_no_server_send() { g_send_data_to_server = false; } // returns a unique (hopefully) randomly generated user id so we can track how many // different daphne users there are out there unsigned int get_user_id() { bool id_exists = false; // id already exists? unsigned int id = 0; FILE *F = NULL; F = fopen(USERID_FILENAME, "rb"); // if they already have a userid generated previously if (F) { unsigned int len = fread(&id, 1, sizeof(id), F); // make sure we read something ... if (len == sizeof(id)) { id_exists = true; } fclose(F); } // if we don't have an ID yet then generate one now if (!id_exists) { F = fopen(USERID_FILENAME, "wb"); if (F) { #ifdef UNIX struct timeval tv; gettimeofday(&tv, NULL); // get a random number (time) srand(tv.tv_sec); // seed randomizer with current usec's elapsed (hopefully super random) id = rand() ^ tv.tv_usec; // hopefully this value will be so random that it will be unique #endif #ifdef WIN32 SYSTEMTIME cur_time; FILETIME file_time; GetSystemTime(&cur_time); SystemTimeToFileTime(&cur_time, &file_time); srand(file_time.dwHighDateTime); id = rand() ^ file_time.dwLowDateTime; #endif fwrite(&id, sizeof(id), 1, F); fclose(F); } // else we couldn't create so we'll just try again next time } return id; } int g_sockfd = -1; // our socket file descriptor struct net_packet g_packet; // what we're gonna send void net_set_gamename(char *gamename) { strncpy(g_packet.gamename, gamename, sizeof(g_packet.gamename)); } void net_set_ldpname(char *ldpname) { strncpy(g_packet.ldpname, ldpname, sizeof(g_packet.ldpname)); } #ifdef WIN32 // some code I found to calculate cpu mhz _inline unsigned __int64 GetCycleCount(void) { _asm _emit 0x0F _asm _emit 0x31 } #endif // gets the cpu's mhz (rounds to nearest 50 MHz) unsigned int get_cpu_mhz() { unsigned int result = 0; unsigned int mod = 0; #ifdef LINUX #ifdef NATIVE_CPU_X86 FILE *F; double mhz; int iRes = 0; const char *s = "cat /proc/cpuinfo | grep MHz | sed -e 's/^.*: //'"; F = popen(s, "r"); if (F) { iRes = fscanf(F, "%lf", &mhz); pclose(F); } result = (unsigned int) mhz; #endif // NATIVE_CPU_X86 #ifdef NATIVE_CPU_MIPS result = 294; // assume playstation 2 for now :) #endif // NATIVE_CPU_MIPS #endif // LINUX #ifdef FREEBSD FILE *F; double mhz; char command[128]="dmesg | grep CPU | perl -e 'while (<>) {if ($_ =~ /\\D+(\\d+).+-MHz.+/) {print \"$1\n\"}}' > /tmp/result.txt"; system(command); F = fopen("/tmp/result.txt", "rb"); if (F) { fscanf(F, "%lf", &mhz); fclose(F); unlink("/tmp/result.txt"); } result = (unsigned int) mhz; #endif #ifdef WIN32 unsigned __int64 m_startcycle; unsigned __int64 m_res; m_startcycle = GetCycleCount(); Sleep(1000); m_res = GetCycleCount()-m_startcycle; result = (unsigned int)(m_res / 1000000); // convert Hz to MHz #endif #ifdef MAC_OSX long cpuSpeed = 0; Gestalt(gestaltProcClkSpeed, &cpuSpeed); result = (unsigned int)(cpuSpeed / 1000000); return result; #endif // round to nearest 50 MHz result += 25; // for rounding mod = result % 50; result -= mod; return result; } // gets the cpu's memory, rounds to nearest 64 megs of RAM unsigned int get_sys_mem() { unsigned int result = 0; unsigned int mod = 0; unsigned int mem = 0; #ifdef LINUX FILE *F; int iRes = 0; const char *s = "ls -l /proc/kcore | awk '{print $5}'"; F = popen(s, "r"); if (F) { iRes = fscanf(F, "%u", &mem); // this breaks if they have over 2 gigs of ram :) pclose(F); } #endif #ifdef FREEBSD size_t len; len = sizeof(mem); sysctlbyname("hw.physmem", &mem, &len, NULL, NULL); #endif #ifdef WIN32 MEMORYSTATUS memstat; GlobalMemoryStatus(&memstat); mem = memstat.dwTotalPhys; #endif result = (mem / (1024*1024)) + 32; // for rounding mod = result % 64; result -= mod; return result; } char *get_video_description() { static char result[NET_LONGSTRSIZE] = { "Unknown video" }; #ifdef LINUX #ifdef NATIVE_CPU_X86 FILE *F; // PCI query fix by Arnaud G. Gibert const char *s = "lspci | grep -i \"VGA compatible controller\" | awk -F ': ' '{print $2}'"; F = popen(s, "r"); if (F) { unsigned int len = fread(result, 1, 79, F); if (len > 1) result[len-1] = 0; // make sure string is null terminated pclose(F); } #endif // NATIVE_CPU_X86 #ifdef NATIVE_CPU_MIPS strcpy(result, "Playstation2"); // assume PS2 for now hehe #endif // MIPS #endif #ifdef FREEBSD //I haven't found a nice way to get graphic adaptor informations :( #endif #ifdef WIN32 typedef BOOL (WINAPI *infoproc)(PVOID, DWORD, PVOID, DWORD); infoproc pEnumDisplayDevices; HINSTANCE hInstUser32; DISPLAY_DEVICE DispDev; bool bEnumDisplayOk = true; // if it's ok to enumerate the display device (winNT may crash when doing this) // The call to EnumDisplayDevicesA may crash under WindowsNT. // Therefore we acquire the Windows version here, and if it's NT, // skip the following code. OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; // Try calling GetVersionEx using the OSVERSIONINFOEX structure. // If that fails, try using the OSVERSIONINFO structure. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) { osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); // if GetVersionEx fails under all circumstances, then we will err on the side of caution and not enumerate if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) { bEnumDisplayOk = false; } } // if it's Windows NT, then don't enumerate the display if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osvi.dwMajorVersion <= 4)) { bEnumDisplayOk = false; } // if it's ok to enumerate the display device if (bEnumDisplayOk) { hInstUser32 = LoadLibrary("user32"); if (hInstUser32) { pEnumDisplayDevices = (infoproc) GetProcAddress(hInstUser32, "EnumDisplayDevicesA"); if (pEnumDisplayDevices) { ZeroMemory(&DispDev, sizeof(DISPLAY_DEVICE)); DispDev.cb = sizeof(DISPLAY_DEVICE); if ((*pEnumDisplayDevices)(NULL, 0, &DispDev, 0)) { strncpy(result, (char *) DispDev.DeviceString, sizeof(result)-1); } } FreeLibrary(hInstUser32); } } // else we can't enumerate #endif return result; } char *get_cpu_name() { static char result[NET_LONGSTRSIZE] = { 0 }; strcpy(result, "UnknownCPU"); // default ... #ifdef NATIVE_CPU_X86 unsigned int reg_ebx, reg_ecx, reg_edx; #ifdef WIN32 _asm { xor eax, eax cpuid mov reg_ebx, ebx mov reg_ecx, ecx mov reg_edx, edx } #else asm ( "xor %%eax, %%eax\n\t" "cpuid\n\t" : "=b" (reg_ebx), "=c" (reg_ecx), "=d" (reg_edx) : /* no inputs */ : "cc", "eax" /* a is clobbered upon completion */ ); #endif result[0] = (char) ((reg_ebx) & 0xFF); result[1] = (char) ((reg_ebx >> 8) & 0xFF); result[2] = (char) ((reg_ebx >> 16) & 0xFF); result[3] = (char) (reg_ebx >> 24); result[4] = (char) ((reg_edx) & 0xFF); result[5] = (char) ((reg_edx >> 8) & 0xFF); result[6] = (char) ((reg_edx >> 16) & 0xFF); result[7] = (char) ((reg_edx >> 24) & 0xFF); result[8] = (char) ((reg_ecx) & 0xFF); result[9] = (char) ((reg_ecx >> 8) & 0xFF); result[10] = (char) ((reg_ecx >> 16) & 0xFF); result[11] = (char) ((reg_ecx >> 24) & 0xFF); #endif // NATIVE_CPU_X86 #ifdef NATIVE_CPU_MIPS strcpy(result, "MIPS R5900 V2.0"); // assume playstation 2 for now #endif // NATIVE_CPU_MIPS #ifdef NATIVE_CPU_SPARC strcpy(result, "Sparc"); #endif // NATIVE_CPU_SPARC //On Mac, we can tell what type of CPU by simply checking the ifdefs thanks to the universal binary. #ifdef MAC_OSX #ifdef __PPC__ strcpy(result, "PowerPC"); #else strcpy(result, "GenuineIntel"); #endif #endif return result; } char *get_os_description() { static char result[NET_LONGSTRSIZE] = { "Unknown OS" }; #ifdef LINUX struct utsname buf; unsigned int i = 0; int uname_result = uname(&buf); int found_first_period = 0; // if uname did not return any error ... if (uname_result == 0) { // find the second period in the linux version, so we can terminate there for (i = 0; i < (sizeof(result)-10); i++) { if (buf.release[i] == '.') { // if we haven't found the first period in the linux version yet if (!found_first_period) { found_first_period = 1; } // if we have already found the first period, then this is the second period, // so get out of the loop else { break; } } } strcpy(result, "Linux "); strncpy(&result[6], buf.release, i); result[i+6] = 0; // terminate string just in case } // end if uname worked #endif #ifdef FREEBSD size_t len; char buff[16] = { "Unknown" }; len = sizeof(buff); sysctlbyname("kern.ostype", &buff, &len, NULL, NULL); strcpy(result,buff); strcat(result," "); sysctlbyname("kern.osrelease", &buff, &len, NULL, NULL); strcat(result,buff); #endif #ifdef WIN32 OSVERSIONINFO info; memset(&info, 0, sizeof(OSVERSIONINFO)); info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&info); switch (info.dwPlatformId) { case VER_PLATFORM_WIN32_WINDOWS: switch (info.dwMinorVersion) { case 0: strcpy(result, "Windows 95"); break; case 10: strcpy(result, "Windows 98"); break; case 90: strcpy(result, "Windows ME"); break; default: strcpy(result, "Windows 95 Derivative"); break; } break; case VER_PLATFORM_WIN32_NT: switch (info.dwMinorVersion) { case 0: if (info.dwMajorVersion == 6) strcpy(result, "Vista"); else strcpy(result, "Windows NT/2000"); break; case 1: if (info.dwMajorVersion == 6) strcpy(result, "Windows 7"); if (info.dwMajorVersion == 5) strcpy(result, "Windows XP/.NET"); break; case 2: if (info.dwMajorVersion == 5) strcpy(result, "Windows XP x64"); break; default: strcpy(result, "Windows NT Derivative"); break; } break; default: strcpy(result, "Unknown Windows"); break; } #endif #ifdef SOLARIS strcpy(result, "Solaris"); #endif #ifdef MAC_OSX strcpy(result, "Mac OSX"); #endif return result; } // send stats to server void net_send_data_to_server() { struct sockaddr_in saRemote; struct hostent *info = NULL; char ip[81]; if (!g_send_data_to_server) return; // if user forbids data to be sent, don't do it #ifdef DEBUG // I don't wanna mess up the server stats with people trying to debug daphne return; #endif #ifdef WIN32 // initialize Winschlock WSADATA wsaData; WSAStartup(MAKEWORD(1,1), &wsaData ); #endif info = gethostbyname(NET_IP); // do DNS to convert address to numbers // if the DNS resolution worked if (info) { // inet_ntop(AF_INET, info->h_addr, ip, sizeof(ip)); sprintf(ip, "%u.%u.%u.%u", (unsigned char) info->h_addr_list[0][0], (unsigned char) info->h_addr_list[0][1], (unsigned char) info->h_addr_list[0][2], (unsigned char) info->h_addr_list[0][3]); g_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (g_sockfd != -1) { memset(&saRemote, 0, sizeof(saRemote)); saRemote.sin_family = AF_INET; saRemote.sin_addr.s_addr = inet_addr(ip); // inet_addr is broken and should not be used saRemote.sin_port = htons(NET_PORT); // if we are able to connect to socket successfully if (connect(g_sockfd, (struct sockaddr *) &saRemote, sizeof(saRemote)) == 0) { g_packet.user_id = get_user_id(); g_packet.os = OS_UNKNOWN; #ifdef WIN32 g_packet.os = OS_WIN32; #endif #ifdef LINUX #ifdef NATIVE_CPU_X86 g_packet.os = OS_X86_LINUX; #endif #ifdef NATIVE_CPU_MIPS g_packet.os = OS_PS2_LINUX; #endif #endif // end LINUX #ifdef FREEBSD g_packet.os = OS_X86_FREEBSD; #endif #ifdef SOLARIS #ifdef NATIVE_CPU_SPARC g_packet.os = OS_SPARC_SOLARIS; #endif #endif // end SOLARIS #ifdef MAC_OSX g_packet.os = OS_MAC_OSX; #endif // safety check if (g_packet.os == OS_UNKNOWN) { printerror("your OS is unknown in network.cpp, please fix this"); } strncpy(g_packet.os_desc, get_os_description(), sizeof(g_packet.os_desc)); g_packet.protocol = PROTOCOL_VERSION; g_packet.mhz = get_cpu_mhz(); g_packet.mem = get_sys_mem(); strncpy(g_packet.video_desc, get_video_description(), sizeof(g_packet.video_desc)); strncpy(g_packet.cpu_name, get_cpu_name(), sizeof(g_packet.cpu_name)); strncpy(g_packet.daphne_version, get_daphne_version(), sizeof(g_packet.daphne_version)); // now compute CRC32 of the rest of the packet g_packet.crc32 = crc32(0L, Z_NULL, 0); g_packet.crc32 = crc32(g_packet.crc32, (unsigned char*) &g_packet, sizeof(g_packet) - sizeof(g_packet.crc32)); send(g_sockfd, (const char *) &g_packet, sizeof(g_packet), 0); } // else connection was refused (server down) #ifdef WIN32 closesocket(g_sockfd); #else close(g_sockfd); // we're done! #endif } } // end if DNS look-up worked }