singe/io/network.cpp
2019-11-11 14:53:02 -06:00

638 lines
15 KiB
C++

/*
* 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 <stdio.h>
#include <stdlib.h> // for lousy random number generation
#include <sys/types.h>
#include <string.h>
#ifdef MAC_OSX
#include <mach/host_info.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <mach/host_priv.h>
#include <mach/machine.h>
#include <carbon/carbon.h>
#endif
#ifdef WIN32
#include <windows.h>
//#include <strmif.h>
//#include <control.h>
//#include <uuids.h>
#endif
#ifdef FREEBSD
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#ifdef LINUX
#include <sys/utsname.h> // MATT : I'm not sure if this is good for UNIX in general so I put it here
#endif
#ifdef UNIX
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h> // for DNS
#include <sys/time.h>
#include <unistd.h> // for write
#endif
#include <zlib.h> // 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
}