commit a3eeb02f32226d0083d266ce06e7cc5f60637107 Author: Ronald Kinard Date: Sun Feb 22 23:36:26 2015 -0600 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f45745 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/client +/server +/server.log +/*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7e66ab2 --- /dev/null +++ b/Makefile @@ -0,0 +1,140 @@ +# Copyright (C) 2000 by DooM Legacy Team. +# +# This program 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. +# +# This program 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. +# +# DESCRIPTION: +# Makefile for DooM Legacy Master Server +# Created on 06/23/2000 by Thierry Van Elsuwe +# e-mail: hurdler@newdoom.com +# +#----------------------------------------------------------------------------- + +PORT=28900 +#PORT=7896 + +#*******************************************************************# + +CCFILES=common.cpp ipcs.cpp crypt.cpp md5.cpp +Main_CCFILES=server.cpp #client.cpp + +#******************************************************************# + +MYSQL_CONFIG?= mysql_config +MYSQL_CFLAGS?=$(shell $(MYSQL_CONFIG) --cflags) +MYSQL_LIBS?=$(shell $(MYSQL_CONFIG) --libs) + +CXXFLAGS+=-Wall -O3 -Wextra +ifdef MINGW +LIBS+=-lws2_32 +else +LIBS+=-lm +endif + +CXXFLAGS+=$(MYSQL_CFLAGS) +LIBS+=$(MYSQL_LIBS) + +ifdef DEBUG +CXXFLAGS+=-g -D__DEBUG__ +endif + +SRCS=$(CCFILES) $(Main_CCFILES) +OFILES=$(CCFILES:.cpp=.o) +Main_OFILES=$(Main_CCFILES:.cpp=.o) +EXEFILE=$(Main_CCFILES:.cpp=) + +##################################################################### + +.SUFFIXES: .cpp .h + +default: init $(EXEFILE) end + +init: + @echo + @echo "Compiling options:" + @echo " CXX : $(CXX)" + @echo " LDFLAGS : $(LDFLAGS)" + @echo " LIBS : $(LIBS)" + @echo " CXXFLAGS: $(CXXFLAGS)" + @echo + @echo "Files:" + @echo " SRCS : $(SRCS)" + @echo " OFILES : $(OFILES) $(Main_OFILES)" + @echo " EXEFILE : $(EXEFILE)" + @echo + +end: + @echo + @echo "*** Makefile ended successfully ***" + @echo + +##################################################################### + +$(EXEFILE): $(OFILES) $(Main_OFILES) + @echo Linking ... + @$(CXX) $(LDFLAGS) $(OFILES) $@.o -o $@ $(LIBS) +ifndef CYGWIN +ifndef MINGW + @chmod 755 $@ +endif +endif + @echo File $@ has been created + +##################################################################### + +.cpp.o: + @echo Compiling $< -\> $@ + @$(CXX) $(CXXFLAGS) $(INCS) -c $< -o $@ + +##################################################################### + +debug: debug_ +debug_: + @make "CXXFLAGS = $(CXXFLAGS) -D__DEBUG__" + @echo + +clean: clean_ +clean_: + @echo + @echo Removing obejcts files, executable files and eventually core file... + @rm -f $(OFILES) $(Main_OFILES) $(EXEFILE) core + @echo + +realclean: clean + @echo Removing backup files... + @rm -f *~ *.bak + @echo + +depend: dep +dep: + @echo + @echo Make dependencies... + @makedepend -- $(CXXFLAGS) -- $(Main_CCFILES) $(CCFILES) 2> /dev/null + @rm -f Makefile.bak + @echo + +BAKFILE = ../SRB2MasterServer +backup: bak +bak: clean + @echo Copy files to $(BAKFILE).tgz... + @tar cvf $(BAKFILE).tar * > /dev/null + @gzip $(BAKFILE).tar + @mv $(BAKFILE).tar.gz $(BAKFILE).tgz + @echo Removing backup files... + @rm -f *~ *.bak + @echo + +#mrproper: dep clean default +mrproper: clean default + +install: mrproper + @echo "Launching the server, port $(PORT)..." + @./server $(PORT) + @echo diff --git a/Master.dsp b/Master.dsp new file mode 100644 index 0000000..98d2008 --- /dev/null +++ b/Master.dsp @@ -0,0 +1,230 @@ +# Microsoft Developer Studio Project File - Name="MasterServer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=MasterServer - Win32 Client Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Master.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Master.mak" CFG="MasterServer - Win32 Client Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "MasterServer - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "MasterServer - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "MasterServer - Win32 Client Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "MasterServer - Win32 Client Release" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "MasterServer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../bin/VC/Release/MasterServer" +# PROP Intermediate_Dir "../../objs/VC/Release/MasterServer" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W4 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /D "_POSIX_" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/VC/Release/MasterServer.exe" + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../bin/VC/Debug/MasterServer" +# PROP Intermediate_Dir "../../objs/VC/Debug/MasterServer" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "_DEBUG" /D "_WIN32" /D "__DEBUG__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /D "_POSIX_" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/VC/Debug/MasterServer.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "MasterServer___Win32_Client_Debug" +# PROP BASE Intermediate_Dir "MasterServer___Win32_Client_Debug" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../bin/VC/Debug/MasterClient" +# PROP Intermediate_Dir "../../objs/VC/Debug/MasterClient" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W4 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "__STDC__" /YX /FD /GZ /c +# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "_DEBUG" /D "_WIN32" /D "__DEBUG__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /D "_POSIX_" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/VC/Debug/MasterServer.exe" /pdbtype:sept +# ADD LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/VC/Debug/MasterClient.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "MasterServer___Win32_Client_Release" +# PROP BASE Intermediate_Dir "MasterServer___Win32_Client_Release" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../bin/VC/Release/MasterClient" +# PROP Intermediate_Dir "../../objs/VC/Release/MasterClient" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W4 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__STDC__" /D "_POSIX_" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/VC/Release/MasterServer.exe" +# ADD LINK32 kernel32.lib wsock32.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/VC/Release/MasterClient.exe" + +!ENDIF + +# Begin Target + +# Name "MasterServer - Win32 Release" +# Name "MasterServer - Win32 Debug" +# Name "MasterServer - Win32 Client Debug" +# Name "MasterServer - Win32 Client Release" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\client.cpp + +!IF "$(CFG)" == "MasterServer - Win32 Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Debug" + +# PROP BASE Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Release" + +# PROP BASE Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\common.cpp +# End Source File +# Begin Source File + +SOURCE=.\crypt.cpp +# End Source File +# Begin Source File + +SOURCE=.\ipcs.cpp +# End Source File +# Begin Source File + +SOURCE=.\server.cpp + +!IF "$(CFG)" == "MasterServer - Win32 Release" + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Debug" + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "MasterServer - Win32 Client Release" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\srvlist.cpp +# End Source File +# Begin Source File + +SOURCE=.\stats.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\common.h +# End Source File +# Begin Source File + +SOURCE=.\ipcs.h +# End Source File +# Begin Source File + +SOURCE=.\srvlist.h +# End Source File +# Begin Source File + +SOURCE=.\stats.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Master.sln b/Master.sln new file mode 100644 index 0000000..1dd7f8c --- /dev/null +++ b/Master.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MasterServer", "Master.vcproj", "{6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Client Debug|Win32 = Client Debug|Win32 + Client Debug|x64 = Client Debug|x64 + Client Release|Win32 = Client Release|Win32 + Client Release|x64 = Client Release|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Debug|Win32.ActiveCfg = Client Debug|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Debug|Win32.Build.0 = Client Debug|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Debug|x64.ActiveCfg = Client Debug|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Debug|x64.Build.0 = Client Debug|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Release|Win32.ActiveCfg = Client Release|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Release|Win32.Build.0 = Client Release|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Release|x64.ActiveCfg = Client Release|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Client Release|x64.Build.0 = Client Release|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Debug|Win32.ActiveCfg = Debug|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Debug|Win32.Build.0 = Debug|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Debug|x64.ActiveCfg = Debug|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Debug|x64.Build.0 = Debug|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Release|Win32.ActiveCfg = Release|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Release|Win32.Build.0 = Release|Win32 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Release|x64.ActiveCfg = Release|x64 + {6AEBAD84-BCB1-4302-A453-7AAD1DF006DA}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Master.vcproj b/Master.vcproj new file mode 100644 index 0000000..4c73414 --- /dev/null +++ b/Master.vcproj @@ -0,0 +1,1063 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MasterClient.dev b/MasterClient.dev new file mode 100644 index 0000000..035bb0b --- /dev/null +++ b/MasterClient.dev @@ -0,0 +1,159 @@ +[Project] +FileName=MasterClient.dev +Name=MasterClient +UnitCount=7 +Type=1 +Ver=1 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler=-Wall_@@_ +CppCompiler=-Wall_@@_ +Linker=-lwsock32_@@_ +IsCpp=1 +Icon= +ExeOutput=..\..\bin +ObjectOutput=..\..\objs\MasterClient +OverrideOutput=0 +OverrideOutputName=MasterServer.exe +HostApplication= +Folders=Client,Common +CommandLine= +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000010001001001010 + +[Unit1] +FileName=common.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=ipcs.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=2 +OverrideBuildCmd=0 +BuildCmd= + +[Unit7] +FileName=Makefile +CompileCpp=1 +Folder= +Compile=0 +Link=0 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=stats.h +Folder=Server +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= +CompileCpp=1 + +[Unit9] +FileName=common.h +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=Makefile +CompileCpp=1 +Folder= +Compile=0 +Link=0 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit12] +FileName=srvlist.h +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + +[Unit3] +FileName=crypt.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=4 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=client.cpp +CompileCpp=1 +Folder=Client +Compile=1 +Link=1 +Priority=7 +OverrideBuildCmd=0 +BuildCmd=$(CPP) -c client.cpp -o ../../../objs/MasterServer/client.o $(CXXFLAGS) + +[Unit5] +FileName=common.h +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=ipcs.h +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/MasterServer.dev b/MasterServer.dev new file mode 100644 index 0000000..bc089cc --- /dev/null +++ b/MasterServer.dev @@ -0,0 +1,169 @@ +[Project] +FileName=MasterServer.dev +Name=MasterServer +UnitCount=11 +Type=1 +Ver=1 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler=-Wall_@@_-DDEBUG_@@_ +CppCompiler=-Wall_@@_-DDEBUG_@@_ +Linker=-lwsock32_@@_-lmysql_@@_ +IsCpp=1 +Icon= +ExeOutput=..\..\bin +ObjectOutput=..\..\objs\MasterServer +OverrideOutput=0 +OverrideOutputName=MasterServer.exe +HostApplication= +Folders=Common,Server +CommandLine=28900 +UseCustomMakefile=0 +CustomMakefile= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=0 +CompilerSettings=0000000011001001001010 + +[Unit1] +FileName=common.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1 +OverrideBuildCmd=0 +BuildCmd= + +[Unit2] +FileName=ipcs.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=2 +OverrideBuildCmd=0 +BuildCmd= + +[Unit3] +FileName=srvlist.cpp +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=3 +OverrideBuildCmd=0 +BuildCmd= + +[Unit4] +FileName=crypt.cpp +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=4 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=stats.cpp +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=5 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=server.cpp +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=6 +OverrideBuildCmd=0 +BuildCmd= + +[Unit8] +FileName=common.h +Folder=Common +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= +CompileCpp=1 + +[Unit9] +FileName=ipcs.h +CompileCpp=1 +Folder=Common +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit10] +FileName=Makefile +CompileCpp=1 +Folder= +Compile=0 +Link=0 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit11] +FileName=srvlist.h +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[VersionInfo] +Major=0 +Minor=1 +Release=1 +Build=1 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 + +[Unit7] +FileName=stats.h +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit13] +FileName=LIVE_MySQL.h +CompileCpp=1 +Folder=Server +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6895011 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# SRB2 Master Server + +This has been separated from the core SRB2 repo for cleanliness. It needs to be updated with documentation! + +# License + +Copyright (C) Sonic Team Junior, 2015 + +This code is made available under the terms of the GNU General Public License, version 2. You can see that license [here](http://www.gnu.org/licenses/gpl-2.0.html). \ No newline at end of file diff --git a/client.cpp b/client.cpp new file mode 100644 index 0000000..8eba3f1 --- /dev/null +++ b/client.cpp @@ -0,0 +1,133 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#include +#include +#include "ipcs.h" +#include "common.h" + +//============================================================================= + +static CClientSocket client_socket; +//static msg_t msg; +FILE *logfile; + +//============================================================================= + +static msg_t *getData(int argc, char *argv[]) +{ + static msg_t msg; + msg_server_t *info = (msg_server_t *)msg.buffer; + +#if defined (_WIN32) + strcpy(info->header.buffer, ""); +#else + strcpy(info->header.buffer, getpass("Enter password: ")); +#endif + + // default values if one of the param is not entered + strcpy(info->ip, "88.86.106.169"); + strcpy(info->port, "5029"); + strcpy(info->name, "Cueball's Dedicated Server"); + strcpy(info->version, "1.09.4"); + msg.type = ADD_PSERVER_MSG; + msg.length = sizeof (msg_server_t); + + for (int i = 0; i < argc; i++) + { + // first: extra parameters (must be after all -param) + if (!strcasecmp(argv[i], "get")) + { + msg.type = GET_SERVER_MSG; + msg.length = 0; + } + else if (!strcasecmp(argv[i], "log")) + { + msg.type = GET_LOGFILE_MSG; + msg.length = sizeof (msg_server_t); + } + else if (!strcasecmp(argv[i], "erase")) + { + msg.type = ERASE_LOGFILE_MSG; + msg.length = sizeof (msg_server_t); + } + else if (!strcasecmp(argv[i], "remove")) + { + msg.type = REMOVE_PSERVER_MSG; + msg.length = sizeof (msg_server_t); + } + + if (i >= argc-1) + continue; + + // second: parameter requiring extra parameters + if (strcasecmp(argv[i], "-ip") == 0) + strcpy(info->ip, argv[i+1]); + else if (strcasecmp(argv[i], "-port") == 0) + strcpy(info->port, argv[i+1]); + else if (strcasecmp(argv[i], "-hostname") == 0) + strcpy(info->name, argv[i+1]); + else if (strcasecmp(argv[i], "-version") == 0) + strcpy(info->version, argv[i+1]); + else if (strcasecmp(argv[i], "-pass") == 0) + strcpy(info->header.buffer, argv[i+1]); + } + return &msg; +} + +int main(int argc, char *argv[]) +{ + if (argc == 2) + { + printf("encrypt: %s\n", pCrypt(argv[1], "04")); + return 0; + } + else if (argc > 2) + { + if (client_socket.connect(argv[1], argv[2]) < 0) + { + conPrintf(RED, "Connect failed.\n"); + return -1; + } + + msg_t *msg = getData(argc, argv); + + if (client_socket.write(msg) < 0) + { + dbgPrintf(RED, "Write failed.\n"); + return -1; + } + + switch (ntohl(msg->type)) + { + case GET_SERVER_MSG: + case GET_LOGFILE_MSG: + while (client_socket.read(msg) >= 0) + { + if (ntohl(msg->length) == 0) + break; + printf("%s", msg->buffer); + } + break; + + case REMOVE_PSERVER_MSG: + default: + break; + } + } + return 0; +} diff --git a/common.cpp b/common.cpp new file mode 100644 index 0000000..b9795bd --- /dev/null +++ b/common.cpp @@ -0,0 +1,207 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "common.h" + +// ================================== GLOBALS ================================= + +static const char *fatal_error_msg[NUM_FATAL_ERROR] = +{ + "Error: signal()", + "Error: select()", + "Error: read()", + "Error: write()", +}; + +// used by xxxPrintf() functions as temporary variable +static char str[1024] =""; +static va_list arglist; +#ifdef _WIN32 +static size_t len = 0; +static HANDLE co = INVALID_HANDLE_VALUE; +static DWORD bytesWritten = 0; +static CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; +#endif + +// ================================= FUNCTIONS ================================ + +/* + * clearScreen(): + */ +void clearScreen() +{ +#ifdef _WIN32 +#else + printf("\033[0m \033[2J \033[0;0H"); +#endif +} + +/* + * fatalError(): + */ +void fatalError(fatal_error_t num) +{ + clearScreen(); + printf("\n"); + perror(fatal_error_msg[num]); + printf("\n"); + exit(-1); +} + + +/* + * dbgPrintf(): + */ +#ifdef _WIN32 +void dbgPrintf(DWORDLONG col, const char *lpFmt, ...) +#else +void dbgPrintf(const char *col, const char *lpFmt, ...) +#endif +{ +#if defined (__DEBUG__) + va_start(arglist, lpFmt); + vsnprintf(str, sizeof str, lpFmt, arglist); + va_end(arglist); + +#ifdef _WIN32 + len = strlen(str); + co = GetStdHandle(STD_OUTPUT_HANDLE); + + if (co == INVALID_HANDLE_VALUE) + return; + + if (col == DEFCOL) + { + if (!GetConsoleScreenBufferInfo(co, &ConsoleScreenBufferInfo)) + ConsoleScreenBufferInfo.wAttributes = + FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; + + SetConsoleTextAttribute(co, (WORD)col); + } + + if (GetFileType(co) == FILE_TYPE_CHAR) + WriteConsoleA(co, str, (DWORD)len, &bytesWritten, NULL); + else + WriteFile(co, str, (DWORD)len, &bytesWritten, NULL); + + if (col != DEFCOL) + SetConsoleTextAttribute(co, + ConsoleScreenBufferInfo.wAttributes); +#else + printf("%s%s", col, str); + fflush(stdout); +#endif +#else + (void)col; + (void)lpFmt; +#endif +} + +/* + * conPrintf() + */ +#ifdef _WIN32 +void conPrintf(DWORDLONG col, const char *lpFmt, ...) +#else +void conPrintf(const char *col, const char *lpFmt, ...) +#endif +{ + va_start(arglist, lpFmt); + vsnprintf(str, sizeof str, lpFmt, arglist); + va_end(arglist); + +#ifdef _WIN32 + len = strlen(str); + co = GetStdHandle(STD_OUTPUT_HANDLE); + + if (col == DEFCOL) + { + if (!GetConsoleScreenBufferInfo(co, &ConsoleScreenBufferInfo)) + ConsoleScreenBufferInfo.wAttributes = + FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; + + SetConsoleTextAttribute(co, (WORD)col); + } + + if (co == INVALID_HANDLE_VALUE) + return; + + if (GetFileType(co) == FILE_TYPE_CHAR) + WriteConsoleA(co, str, (DWORD)len, &bytesWritten, NULL); + else + WriteFile(co, str, (DWORD)len, &bytesWritten, NULL); + + if (col != DEFCOL) + SetConsoleTextAttribute(co, + ConsoleScreenBufferInfo.wAttributes); +#else + printf("%s%s", col, str); + fflush(stdout); +#endif +} + +/* + * logPrintf(): + */ +void logPrintf(FILE *f, const char *lpFmt, ...) +{ + char *ct; + time_t t; + + va_start(arglist, lpFmt); + vsnprintf(str, sizeof str, lpFmt, arglist); + va_end(arglist); + + t = time(NULL); + ct = ctime(&t); + ct[strlen(ct)-1] = '\0'; + fprintf(f, "%s: %s", ct, str); + fflush(f); +#if defined (__DEBUG__) +#ifdef _WIN32 + len = strlen(str); + printf("%s", str); +#else + printf("%s%s", DEFCOL, str); +#endif + fflush(stdout); +#endif +} + +/* + * openFile(): + */ +FILE *openFile(const char *filename) +{ + return fopen(filename, "a+t"); +} + +void strrand(char *s, const int len) { + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + for (int i = 0; i < len; ++i) { + s[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; + } + + s[len] = 0; +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..cb93557 --- /dev/null +++ b/common.h @@ -0,0 +1,151 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#ifdef __GNUC__ +#include +#include + +#define UINT8 uint8_t +#define SINT8 int8_t + +#define UINT16 uint16_t +#define INT16 int16_t + +#define INT32 int32_t +#define UINT32 uint32_t +#define INT64 int64_t +#define UINT64 uint64_t +#define ATTRPACK __attribute__ ((packed)) +#elif defined ( _MSC_VER) +#include +#define UINT8 unsigned __int8 +#define SINT8 signed __int8 + +#define UINT16 unsigned __int16 +#define INT16 __int16 + +#define INT32 __int32 +#define UINT32 unsigned __int32 + +#define INT64 __int64 +#define UINT64 unsigned __int64 + +typedef long ssize_t; +#endif + +#ifndef ATTRPACK +#define ATTRPACK +#endif + +#include +#include +#include +#include + +#if defined (_WIN32) || defined (__OS2__) +#ifdef __GNUC__ +#define strcasecmp stricmp +#elif defined (_MSC_VER) +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strcasecmp _stricmp +#pragma warning(disable : 4244) +#endif +#endif + +// ================================ DEFINITIONS =============================== + + +#ifdef _WIN32 +#ifdef __GNUC__ +#include +#include +#include +#include +#endif +#define DEFCOL 0xD0E0F0C0 // codes couleurs ANSI +#define BLACK 0 +#define RED FOREGROUND_RED +#define GREEN FOREGROUND_GREEN +#define BROWN FOREGROUND_GREEN|FOREGROUND_RED +#define BLUE FOREGROUND_BLUE +#define PURPLE FOREGROUND_RED|FOREGROUND_BLUE +#define CYAN FOREGROUND_BLUE|FOREGROUND_GREEN +#define LIGHTGRAY FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED +#define DARKGRAY FOREGROUND_INTENSITY +#define LIGHTRED FOREGROUND_RED|FOREGROUND_INTENSITY +#define LIGHTGREEN FOREGROUND_GREEN|FOREGROUND_INTENSITY +#define YELLOW FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY +#define LIGHTBLUE FOREGROUND_BLUE|FOREGROUND_INTENSITY +#define MAGENTA FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY +#define LIGHTCYAN FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY +#define WHITE FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY + +#else + +#define DEFCOL "\033[0m" // codes couleurs ANSI +#define BLACK "\033[0;30m" +#define RED "\033[0;31m" +#define GREEN "\033[0;32m" +#define BROWN "\033[0;33m" +#define BLUE "\033[0;34m" +#define PURPLE "\033[0;35m" +#define CYAN "\033[0;36m" +#define LIGHTGRAY "\033[0;37m" +#define DARKGRAY "\033[1;30m" +#define LIGHTRED "\033[1;31m" +#define LIGHTGREEN "\033[1;32m" +#define YELLOW "\033[1;33m" +#define LIGHTBLUE "\033[1;34m" +#define MAGENTA "\033[1;35m" +#define LIGHTCYAN "\033[1;36m" +#define WHITE "\033[1;37m" + +#endif + +typedef enum +{ + FE_SIGNAL_ERR, + FE_SELECT_ERR, + FE_READ_ERR, + FE_WRITE_ERR, + NUM_FATAL_ERROR +} fatal_error_t; + +// ================================== PROTOS ================================== + +void clearScreen(); +void fatalError(fatal_error_t); +void logPrintf(FILE *, const char *, ...); +#ifdef _WIN32 +void dbgPrintf(DWORDLONG col, const char *lpFmt, ...); +void conPrintf(DWORDLONG col, const char *lpFmt, ...); +#else +void dbgPrintf(const char *col, const char *lpFmt, ...); +void conPrintf(const char *col, const char *lpFmt, ...); +#endif +FILE *openFile(const char *filename); +const char *pCrypt(const char *pw, const char *salt); + +// ================================== STRINGS ================================= +void strrand(char *s, const int len); + +// ================================== EXTERNS ================================= +#endif diff --git a/crypt.cpp b/crypt.cpp new file mode 100644 index 0000000..4f15d3c --- /dev/null +++ b/crypt.cpp @@ -0,0 +1,278 @@ +#include "common.h" + +static SINT8 IP[] = +{ + 58,50,42,34,26,18,10, 2, + 60,52,44,36,28,20,12, 4, + 62,54,46,38,30,22,14, 6, + 64,56,48,40,32,24,16, 8, + 57,49,41,33,25,17, 9, 1, + 59,51,43,35,27,19,11, 3, + 61,53,45,37,29,21,13, 5, + 63,55,47,39,31,23,15, 7, +}; +static SINT8 FP[] = +{ + 40, 8,48,16,56,24,64,32, + 39, 7,47,15,55,23,63,31, + 38, 6,46,14,54,22,62,30, + 37, 5,45,13,53,21,61,29, + 36, 4,44,12,52,20,60,28, + 35, 3,43,11,51,19,59,27, + 34, 2,42,10,50,18,58,26, + 33, 1,41, 9,49,17,57,25, +}; +static SINT8 PC1_C[] = +{ + 57,49,41,33,25,17, 9, + 1,58,50,42,34,26,18, + 10, 2,59,51,43,35,27, + 19,11, 3,60,52,44,36, +}; +static SINT8 PC1_D[] = +{ + 63,55,47,39,31,23,15, + 7,62,54,46,38,30,22, + 14, 6,61,53,45,37,29, + 21,13, 5,28,20,12, 4, +}; +static SINT8 shifts[] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; +static SINT8 PC2_C[] = +{ + 14,17,11,24, 1, 5, + 3,28,15, 6,21,10, + 23,19,12, 4,26, 8, + 16, 7,27,20,13, 2, +}; +static SINT8 PC2_D[] = +{ + 41,52,31,37,47,55, + 30,40,51,45,33,48, + 44,49,39,56,34,53, + 46,42,50,36,29,32, +}; +static SINT8 C[28]; +static SINT8 D[28]; +static SINT8 KS[16][48]; +static SINT8 E[48]; +static SINT8 e2[] = +{ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9,10,11,12,13, + 12,13,14,15,16,17, + 16,17,18,19,20,21, + 20,21,22,23,24,25, + 24,25,26,27,28,29, + 28,29,30,31,32, 1, +}; +static SINT8 S[8][64] = +{ + { + 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, + }, + { + 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, + }, + { + 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, + }, + { + 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, + }, + { + 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, + }, + { + 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, + }, + { + 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, + }, + { + 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, + } +}; +static SINT8 P[] = +{ + 16, 7,20,21, + 29,12,28,17, + 1,15,23,26, + 5,18,31,10, + 2, 8,24,14, + 32,27, 3, 9, + 19,13,30, 6, + 22,11, 4,25, +}; +static SINT8 L[64], *R = L + 32; +static SINT8 tempL[32]; +static SINT8 f[32]; +static SINT8 preS[48]; + +//======================================================================= + +static void setKey(const SINT8 *key) +{ + register INT32 i, j, k; + INT32 t; + + for (i = 0; i < 28; i++) + { + C[i] = key[PC1_C[i]-1]; + D[i] = key[PC1_D[i]-1]; + } + for (i = 0; i < 16; i++) + { + for (k = 0; k < shifts[i]; k++) + { + t = C[0]; + for (j = 0; j < 28-1; j++) + C[j] = C[j+1]; + C[27] = t; + t = D[0]; + for (j = 0; j < 28-1; j++) + D[j] = D[j+1]; + D[27] = t; + } + for (j = 0; j < 24; j++) + { + KS[i][j] = C[PC2_C[j]-1]; + KS[i][j+24] = D[PC2_D[j]-28-1]; + } + } + for (i = 0; i < 48; i++) + E[i] = e2[i]; +} + +void enCrypt(SINT8 *block) +{ + INT32 i, ii; + register INT32 t, j, k; + + for (j = 0; j < 64; j++) + L[j] = block[IP[j]-1]; + for (ii = 0; ii < 16; ii++) + { + i = ii; + for (j = 0; j < 32; j++) + tempL[j] = R[j]; + for (j = 0; j < 48; j++) + preS[j] = R[E[j]-1] ^ KS[i][j]; + for (j = 0; j < 8; j++) + { + t = 6*j; + k = S[j][(preS[t+0]<<5)+ + (preS[t+1]<<3)+ + (preS[t+2]<<2)+ + (preS[t+3]<<1)+ + (preS[t+4]<<0)+ + (preS[t+5]<<4)]; + t = 4*j; + f[t+0] = (k>>3)&01; + f[t+1] = (k>>2)&01; + f[t+2] = (k>>1)&01; + f[t+3] = (k>>0)&01; + } + for (j = 0; j < 32; j++) + R[j] = L[j] ^ f[P[j]-1]; + for (j = 0; j < 32; j++) + L[j] = tempL[j]; + } + for (j = 0; j < 32; j++) + { + t = L[j]; + L[j] = R[j]; + R[j] = t; + } + for (j = 0; j < 64; j++) + block[j] = L[FP[j]-1]; +} + +const char *pCrypt(const char *pw, const char *salt) +{ + register INT32 i, j, c; + INT32 temp; + static SINT8 block[66]; + static char iobuf[16]; + + for (i = 0; i < 66; i++) + block[i] = 0; + for (i = 0; (c = *pw) != '\0' && i < 64; pw++) + { + for (j = 0; j < 7; j++, i++) + block[i] = (c>>(6-j)) & 01; + i++; + } + + setKey(block); + + for (i = 0; i < 66; i++) + block[i] = 0; + + for (i = 0; i < 2; i++) + { + c = *salt++; + iobuf[i] = c; + if (c > 'Z') + c -= 6; + if (c > '9') + c -= 7; + c -= '.'; + for (j = 0; j < 6; j++) + { + if ((c>>j) & 01) + { + temp = E[6*i+j]; + E[6*i+j] = E[6*i+j+24]; + E[6*i+j+24] = temp; + } + } + } + + for (i = 0; i < 25; i++) + enCrypt(block); + + for (i = 0; i < 11; i++) + { + c = 0; + for (j = 0; j < 6; j++) + { + c <<= 1; + c |= block[6*i+j]; + } + c += '.'; + if (c > '9') + c += 7; + if (c > 'Z') + c += 6; + iobuf[i+2] = c; + } + iobuf[i+2] = 0; + if (iobuf[1] == 0) + iobuf[1] = iobuf[0]; + return(iobuf); +} diff --git a/ipcs.cpp b/ipcs.cpp new file mode 100644 index 0000000..4c3c6de --- /dev/null +++ b/ipcs.cpp @@ -0,0 +1,595 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifdef __GNUC__ +#include +#endif + +#ifdef _WIN32 +//#include +#else +#include // socket(),... +#include // socket(),... +#endif +#include // atoi(),... +#include // signal(),... +#ifndef _WIN32 +#include // gethostbyname(),... +#endif +#ifdef _WIN32 +#include +#else +#include // timeval,... (TIMEOUT) +#endif +#include // memset(),... +#include "ipcs.h" +#include "common.h" + +typedef void Sigfunc(int); + +#ifndef _WIN32 +static void sigHandler(int signr); +static Sigfunc *mySignal(int signo, Sigfunc *func); +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4127) +#endif +//============================================================================= + +#if defined (_WIN32) || defined (__OS2__) +// it seems windows doesn't define that... maybe some other OS? OS/2 +static int inet_aton(const char *hostname, struct in_addr *addr) +{ + return ((addr->s_addr=inet_addr(hostname)) != INADDR_NONE); +} +#endif + +/* +** CSocket() +*/ +CSocket::CSocket() +{ + dbgPrintf(DEFCOL, "Initializing socket... "); +#ifdef _WIN32 +//#warning SIGPIPE needed + WSADATA winsockdata; + if (WSAStartup(MAKEWORD(1, 1), &winsockdata)) + conPrintf(RED,"No TCP/IP driver detected"); +#else + signal(SIGPIPE, sigHandler); +#endif + FD_ZERO(&rset); + memset(&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + dbgPrintf(DEFCOL, "DONE1.\n"); +} + +/* +** ~CSocket() +*/ +CSocket::~CSocket() +{ + dbgPrintf(DEFCOL, "Freeing socket... "); +#ifdef _WIN32 + WSACleanup(); +#endif + dbgPrintf(DEFCOL, "DONE2.\n"); +} + +/* +** getIP() +*/ +int CSocket::getIP(const char *ip_addr) +{ + struct hostent *host_ent; + + dbgPrintf(DEFCOL, "get IP for %s... ", ip_addr); + if (!inet_aton(ip_addr, &addr.sin_addr)) + { + host_ent = gethostbyname(ip_addr); + if (host_ent == NULL) + return GETHOSTBYNAME_ERROR; + memcpy(&addr.sin_addr, host_ent->h_addr_list[0], + sizeof (struct in_addr)); + } + dbgPrintf(DEFCOL, "got %s. ", inet_ntoa(addr.sin_addr)); + dbgPrintf(DEFCOL, "DONE3.\n"); + return 0; +} + +/* +** CClientSocket() +*/ +CClientSocket::CClientSocket() +{ + dbgPrintf(DEFCOL, "Initializing client socket... "); + socket_fd = (SOCKET)-1; + dbgPrintf(DEFCOL, "DONE4.\n"); +} + +/* +** ~CClientSocket() +*/ +CClientSocket::~CClientSocket() +{ + dbgPrintf(DEFCOL, "Freeing client socket... "); + if (socket_fd != (SOCKET)-1) + close(socket_fd); + dbgPrintf(DEFCOL, "DONE5.\n"); +} + +/* +** connect() +*/ +int CClientSocket::connect(const char *ip_addr, const char *str_port) +{ + dbgPrintf(DEFCOL, "Connect to %s %s...\n", ip_addr, str_port); + + // put that in the constructor? + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == (SOCKET)-1) + return SOCKET_ERROR; + + if (getIP(ip_addr) == GETHOSTBYNAME_ERROR) + return GETHOSTBYNAME_ERROR; + addr.sin_port = htons(atoi(str_port)); + + if (::connect(socket_fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) + return CONNECT_ERROR; + + FD_SET(socket_fd, &rset); + + dbgPrintf(DEFCOL, "DONE6.\n"); + + return 0; +} + +/* + * write() + */ +int CClientSocket::write(msg_t *msg) +{ + int len; + + if (msg->length < 0) + msg->length = (INT32)strlen(msg->buffer); + if (msg->length > PACKET_SIZE) + return WRITE_ERROR; // too big + len = msg->length + HEADER_SIZE; + + msg->id = htonl(msg->id); + msg->type = htonl(msg->type); + msg->length = htonl(msg->length); + msg->room = htonl(msg->room); + + dbgPrintf(DEFCOL, "Write a message %d (%s)... ", len, msg->buffer); + + if (send(socket_fd, (const char *)msg, len, 0) != len) + return WRITE_ERROR; + + dbgPrintf(DEFCOL, "DONE7.\n"); + + return 0; +} + +/* + * read() + */ +int CClientSocket::read(msg_t *msg) +{ + struct timeval timeout; + fd_set tset; + + dbgPrintf(DEFCOL, "Waiting a message... "); + + timeout.tv_sec = 60, timeout.tv_usec = 0; + memcpy(&tset, &rset, sizeof (tset)); + if ((select(255, &tset, NULL, NULL, &timeout)) <= 0) + return TIMEOUT_ERROR; + + if (!FD_ISSET(socket_fd, &tset)) + return READ_ERROR; + + dbgPrintf(DEFCOL, "Reading a message.\n"); + + if (recv(socket_fd, (char *)msg, HEADER_SIZE, 0) != HEADER_SIZE) + return READ_ERROR; + + msg->id = ntohl(msg->id); + msg->type = ntohl(msg->type); + msg->length = ntohl(msg->length); + msg->room = ntohl(msg->room); + + if (!msg->length) // work around a bug in Windows 2000 + return 0; + + if (msg->length > PACKET_SIZE) + return READ_ERROR; // packet too big + + if (recv(socket_fd, msg->buffer, msg->length, 0) != msg->length) + return READ_ERROR; + + dbgPrintf(DEFCOL, "DONE8.\n"); + + return 0; +} + +/* +** CServerSocket() +*/ +CServerSocket::CServerSocket() +{ + size_t id; + dbgPrintf(DEFCOL, "Initializing server socket... "); + num_clients = 0; + accept_fd = udp_fd = (SOCKET)-1; + for (id = 0; id < MAX_CLIENT; id++) + client_fd[id] = (SOCKET)-1; + udp_addr.sin_family = AF_INET; + dbgPrintf(DEFCOL, "DONE9.\n"); +} + +/* +** ~CServerSocket() +*/ +CServerSocket::~CServerSocket() +{ + size_t id; + dbgPrintf(DEFCOL, "Freeing server socket... "); + if (udp_fd != (SOCKET)-1) + close(udp_fd); + if (accept_fd != (SOCKET)-1) + close(accept_fd); + for (id = 0; id < num_clients || id < MAX_CLIENT; id++) + if (client_fd[id] != (SOCKET)-1) + close(client_fd[id]); + dbgPrintf(DEFCOL, "DONE10.\n"); +} + +/* +** listen() +*/ +int CServerSocket::listen(const char *str_port) +{ + dbgPrintf(DEFCOL, "Listen on %s... ", str_port); + // Init TCP socket + if ((accept_fd = socket(AF_INET, SOCK_STREAM, 0)) == (SOCKET)-1) + return SOCKET_ERROR; + int one = 1; setsockopt(accept_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int)); + + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(atoi(str_port)); + + if (bind(accept_fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) + return BIND_ERROR; + + if (::listen(accept_fd, 5) < 0) + return LISTEN_ERROR; + + FD_SET(accept_fd, &rset); + + // Init UDP socket + if ((udp_fd = socket(AF_INET, SOCK_DGRAM, 0)) == (SOCKET)-1) + return SOCKET_ERROR; + + udp_addr.sin_addr.s_addr = htonl(INADDR_ANY); + udp_addr.sin_port = htons(atoi(str_port)+1); + + if (bind(udp_fd, (struct sockaddr *) &udp_addr, sizeof (udp_addr)) < 0) + return BIND_ERROR; + + FD_SET(udp_fd, &rset); + + dbgPrintf(DEFCOL, "DONE11.\n"); + + return 0; +} + +/* + * deleteClient(): + */ +int CServerSocket::deleteClient(size_t id) +{ + dbgPrintf(DEFCOL, "Deleting (client %u) of %u... ", id+1, num_clients); + FD_CLR(client_fd[id], &rset); + close(client_fd[id]); + if (num_clients != 0) + num_clients--; + client_fd[id] = client_fd[num_clients]; // move the top socket into this now empty space + memmove(&client_addr[id], &client_addr[num_clients], sizeof (client_addr[num_clients])); + client_fd[num_clients] = (SOCKET)-1; // now empty the top socket + memset(&client_addr[num_clients], 0 , sizeof (client_addr[num_clients])); + + dbgPrintf(DEFCOL, "DONE12.\n"); + + return 0; +} + +/* + * getUdpIP() + */ +const char *CServerSocket::getUdpIP(void) +{ + return inet_ntoa(udp_in_addr.sin_addr); +} + +/* + * getUdpPort() + */ +const char *CServerSocket::getUdpPort(bool offset) +{ + static char buffer[8]; + UINT16 port = htons(udp_in_addr.sin_port); + if (offset) + port--; + snprintf(buffer, sizeof buffer, "%d", port); + return buffer; +} + +/* + * read(): wait message on a socket, if it's an accept, call the accept method, + * otherwise read the message and update msg->id to the right value + * (client cannot put valid entry in msg->id) + */ +int CServerSocket::read(msg_t *msg) +{ + struct timeval timeout; + fd_set tset; + UINT32 id; + socklen_t lg = sizeof (addr); + + timeout.tv_sec = 20, timeout.tv_usec = 0; + memcpy(&tset, &rset, sizeof (tset)); + if ((select(255, &tset, NULL, NULL, &timeout)) <= 0) + { + msg->type = TIMEOUT_MSG; + return TIMEOUT_ERROR; + } + + if (FD_ISSET(udp_fd, &tset)) + { + msg->type = UDP_RECV_MSG; // This is a UDP packet + msg->length = recvfrom(udp_fd, msg->buffer, PACKET_SIZE, 0, (struct sockaddr *)&udp_in_addr, &lg); + return 0; + } + + if (FD_ISSET(accept_fd, &tset)) + { + msg->type = ACCEPT_MSG; + return this->accept(); + } + + id = 0; + while (!FD_ISSET(client_fd[id], &tset) && id < MAX_CLIENT) + id++; + + // paranoia + if ( (id >= num_clients) || (id >= MAX_CLIENT) ) + { + dbgPrintf(DEFCOL, "This should never happen\n"); + return READ_ERROR; + } + + dbgPrintf(DEFCOL, "Reading a message from (socket %u) for (client %u)... ", client_fd[id], id+1); + + if (recv(client_fd[id], (char *)msg, HEADER_SIZE, 0) != HEADER_SIZE) + { + deleteClient(id); // the connection is not good with the client, kick him + return READ_ERROR; + } + + //msg->id = ntohl(msg->id); // see comment above + msg->type = ntohl(msg->type); + msg->length = ntohl(msg->length); + msg->room = ntohl(msg->room); + + if ((0 < msg->length) && (msg->length < PACKET_SIZE)) + { + timeout.tv_sec = 0, timeout.tv_usec = 0; // the info should be already in the socket, so don't wait + memcpy(&tset, &rset, sizeof (tset)); + if ((select(255, &tset, NULL, NULL, &timeout)) <= 0) + { + deleteClient(id); + return READ_ERROR; + } + + if (!FD_ISSET(client_fd[id], &tset)) + { + deleteClient(id); + return READ_ERROR; + } + if (recv(client_fd[id], msg->buffer, msg->length, 0) != msg->length) + { + deleteClient(id); + return READ_ERROR; + } + } + + msg->id = id; + + dbgPrintf(DEFCOL, "DONE13.\n"); + + return 0; +} + +/* + * write(): + */ +int CServerSocket::write(msg_t *msg) +{ + struct timeval timeout; + fd_set tset; + int len; + + dbgPrintf(DEFCOL, "Write a message... "); + FD_ZERO(&tset); + /* + * Be sure the msg->id is still valid; we cannot do + * a delete between a read and a write. Should we + * use a readpending set in read method, so we know + * that we are still waiting for writing? For now, + * it's the responsibility of server code. + */ + FD_SET(client_fd[msg->id], &tset); + timeout.tv_sec = 0, timeout.tv_usec = 0; + if ((select(255, NULL, &tset, NULL, &timeout)) <= 0) + { + deleteClient(msg->id); // this shouldn't happen if the net connection is good + return TIMEOUT_ERROR; + } + if (msg->length < 0) + msg->length = (INT32)strlen(msg->buffer); + if (msg->length > PACKET_SIZE) + return WRITE_ERROR; // too big + len = msg->length+HEADER_SIZE; + //msg->id = htonl(msg->id); // same comment as for read + msg->type = htonl(msg->type); + msg->length = htonl(msg->length); + if (send(client_fd[msg->id], (const char *)msg, len, 0) != len) + { + deleteClient(msg->id); + return WRITE_ERROR; + } + + dbgPrintf(DEFCOL, "DONE14.\n"); + + return 0; +} + +/* + * writeUDP() + */ +int CServerSocket::writeUDP(const char *data, size_t length, const char *ip, UINT16 port) +{ + sockaddr_in sin; + struct timeval timeout; + fd_set tset; + + FD_ZERO(&tset); + FD_SET(udp_fd, &tset); + + if ((select(255, NULL, &tset, NULL, &timeout)) <= 0) + { + return TIMEOUT_ERROR; + } + + sin.sin_family = AF_INET; + inet_aton(ip, &sin.sin_addr); + sin.sin_port = htons(port); + + sendto(udp_fd, data, (int)length, 0, (sockaddr*)&sin, sizeof(sin)); + + return 0; +} + + +/* + * accept() + */ +int CServerSocket::accept() +{ + socklen_t lg = sizeof (addr); + + dbgPrintf(DEFCOL, "Accepting on (socket %u)... ", accept_fd); + + if ((client_fd[num_clients] = ::accept(accept_fd, (struct sockaddr *) &addr, &lg)) == (SOCKET)-1) + { + //client_fd[num_clients] = (SOCKET)-1; // we haven't accepted the client, so set it at default value + return ACCEPT_ERROR; + } + + if (num_clients < MAX_CLIENT-1) + { + extern FILE *sockfile; + + logPrintf(sockfile, "Accepting on socket %u for client %u (%s:%i)\n", + client_fd[num_clients], num_clients+1, inet_ntoa(addr.sin_addr), htons(addr.sin_port)); + memcpy(&client_addr[num_clients], &addr, sizeof (addr)); + FD_SET(client_fd[num_clients], &rset); + num_clients++; + } + else // we have reached the maximum number of clients + { + // close the connection (meaning "try again" for the client) + close(client_fd[num_clients]); + client_fd[num_clients] = (SOCKET)-1; + return ACCEPT_ERROR; + } + + //dbgPrintf(DEFCOL, "DONE15.\n"); + + return 0; +} + +/* + * getClientIP() + */ +const char *CServerSocket::getClientIP(size_t id) +{ + dbgPrintf(DEFCOL, "getClientIP: %u\n", id); + return inet_ntoa(client_addr[id].sin_addr); +} + +/* + * getClientPort() + */ +const char *CServerSocket::getClientPort(size_t id) +{ + static char buffer[8]; + + dbgPrintf(DEFCOL, "getClientPort: %u\n", id); + UINT16 port = htons(client_addr[id].sin_port); + snprintf(buffer, sizeof buffer, "%d", port); + return buffer; +} + +/* + * mySignal() + */ +#ifndef _WIN32 +static Sigfunc *mySignal(int signo, Sigfunc *func) +{ + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); +#if defined (SA_RESTART) + act.sa_flags = SA_RESTART; +#else + act.sa_flags = 0; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +} + +/* + * sigHandler() + */ +static void sigHandler(int signr) +{ + mySignal(signr, sigHandler); + switch (signr) + { + case SIGPIPE: + dbgPrintf(DEFCOL, "Catch SIG_PIPE\n"); + /// TODO be sure the socket is closed if the client + /// didn't quit properly + break; + } +} +#endif diff --git a/ipcs.h b/ipcs.h new file mode 100644 index 0000000..b9dc52f --- /dev/null +++ b/ipcs.h @@ -0,0 +1,281 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifndef _IPCS_H_ +#define _IPCS_H_ + +#include "common.h" + +#if defined (_WIN32) || defined ( __OS2__) +#include +#include +typedef int socklen_t; +#if defined (__OS2__) +#include +#endif +#endif +#ifdef _WIN32 +#include +#define close closesocket +#else +#include // inet_addr(),... +#endif + +#ifndef SOCKET +#define SOCKET u_int +#endif + +// ================================ DEFINITIONS =============================== + +#define PACKET_SIZE 1024 +#define MAX_CLIENT 512 + +#ifndef _WIN32 +#define NO_ERROR 0 +#define SOCKET_ERROR -201 +#endif +#define BIND_ERROR -202 +#define CONNECT_ERROR -203 +#define LISTEN_ERROR -204 +#define ACCEPT_ERROR -205 +#define WRITE_ERROR -210 +#define READ_ERROR -211 +#define CLOSE_ERROR -212 +#define GETHOSTBYNAME_ERROR -220 +#define SELECT_ERROR -230 +#define TIMEOUT_ERROR -231 +#define MALLOC_ERROR -301 + +#define INVALID_MSG -1 +#define ACCEPT_MSG 100 +#define ADD_SERVER_MSG 101 +#define ADD_CLIENT_MSG 102 +#define REMOVE_SERVER_MSG 103 +#define ADD_SERVERv2_MSG 104 +#define GET_SERVER_MSG 200 +#define SEND_SERVER_MSG 201 +#define GET_LOGFILE_MSG 202 +#define SEND_FILE_MSG 203 +#define ERASE_LOGFILE_MSG 204 +#define GET_SHORT_SERVER_MSG 205 +#define SEND_SHORT_SERVER_MSG 206 +#define ASK_SERVER_MSG 206 +#define ANSWER_ASK_SERVER_MSG 207 +#define GET_MOTD_MSG 208 +#define SEND_MOTD_MSG 209 +#define GET_ROOMS_MSG 210 +#define SEND_ROOMS_MSG 211 +#define GET_ROOMS_HOST_MSG 212 +#define GET_VERSION_MSG 213 +#define SEND_VERSION_MSG 214 +#define GET_BANNED_MSG 215 +#define PING_SERVER_MSG 216 + +#define UDP_RECV_MSG 300 +#define TIMEOUT_MSG 301 +#define HTTP_REQUEST_MSG 875770417 // "4321" +#define SEND_HTTP_REQUEST_MSG 875770418 // "4322" +#define TEXT_REQUEST_MSG 825373494 // "1236" +#define SEND_TEXT_REQUEST_MSG 825373495 // "1237" +#define RSS92_REQUEST_MSG 825373496 // "1238" +#define SEND_RSS92_REQUEST_MSG 825373497 // "1239" +#define RSS10_REQUEST_MSG 825373744 // "1240" +#define SEND_RSS10_REQUEST_MSG 825373745 // "1241" +#define ADD_PSERVER_MSG 0xabacab81 // this number just need to be different than the others +#define REMOVE_PSERVER_MSG 0xabacab82 + +// Sent FROM Client +#define LIVE_AUTH_USER 600 +#define LIVE_AUTH_KEY 601 +#define LIVE_GET_USER 602 +#define LIVE_UPDATE_LOCATION 603 +#define LIVE_UPDATE_PUBLIC_KEY 604 +#define LIVE_AUTH_PUBLIC_KEY 605 + +// Sent TO Client +#define LIVE_INVALID_KEY 800 +#define LIVE_INVALID_USER 801 +#define LIVE_AUTHORISED_KEY 802 +#define LIVE_SEND_USER 803 +#define LIVE_VALIDATED_USER 804 + +// Location Types +#define LIVE_LOCATION_SP 100 +#define LIVE_LOCATION_MENU 101 +#define LIVE_LOCATION_MP_JOIN 102 +#define LIVE_LOCATION_MP_HOST 103 +#define LIVE_LOCATION_MP_LOCAL 104 +#define LIVE_LOCATION_MP_PRIVATE 105 + +#define HEADER_SIZE ((UINT32)sizeof (UINT32)*4) + +#define HEADER_MSG_POS 0 +#define IP_MSG_POS 16 +#define PORT_MSG_POS 32 +#define HOSTNAME_MSG_POS 40 + +#if defined(_MSC_VER) +#pragma pack(1) +#endif + +// Keep this structure 8 bytes aligned (current size is 80) +typedef struct +{ + char header[16]; // information such as password + char ip[16]; + char port[8]; + char name[32]; + INT32 room; + char key[32]; // Secret key for linking dedicated servers to accounts + char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31) +} ATTRPACK msg_server_t; + +typedef struct +{ + char header[16]; + UINT32 id; + char name[32]; + char motd[256]; +} ATTRPACK msg_rooms_t; + +typedef struct +{ + char header[16]; + char ipstart[16]; + char ipend[16]; + char endstamp[32]; + char reason[256]; + UINT8 hostonly; +} ATTRPACK msg_ban_t; + +typedef struct +{ + char header[16]; + INT32 id; + char username[100]; + char password[32]; +} ATTRPACK msg_live_auth_t; + +typedef struct +{ + char header[16]; + INT32 uid; + char username[100]; + INT32 location_type; + char location_ip[32]; + INT32 location_port; + INT32 lastseen_type; + char lastseen_data1[256]; + char lastseen_data2[256]; + char lastseen_data3[256]; +} ATTRPACK msg_live_user_t; + +typedef struct +{ + char header[16]; + UINT8 location_type; + char location_ip[32]; + INT32 location_port; + char location_data1[256]; + char location_data2[256]; + char location_data3[256]; +} ATTRPACK msg_live_updatelocation_t; + +typedef struct +{ + char header[16]; + char publickey[256]; + char username[256]; +} ATTRPACK msg_live_validateuser_t; + +typedef struct +{ + char header[16]; + char username[256]; + UINT8 keytype; + char keydata[256]; +} ATTRPACK msg_live_update_key_t; + + +typedef struct +{ + UINT32 id; + INT32 type; + INT32 room; + INT32 length; + char buffer[PACKET_SIZE]; +} ATTRPACK msg_t; + +#if defined(_MSC_VER) +#pragma pack() +#endif + +class CSocket +{ +protected: + sockaddr_in addr; + msg_t msg; + fd_set rset; +public: + int getIP(const char *); + CSocket(); + ~CSocket(); +}; + +class CServerSocket : public CSocket +{ +private: + sockaddr_in udp_addr; + sockaddr_in udp_in_addr; + SOCKET udp_fd; + SOCKET accept_fd; + size_t num_clients; + SOCKET client_fd[MAX_CLIENT]; + sockaddr_in client_addr[MAX_CLIENT]; + +public: + int deleteClient(size_t id); + int listen(const char *str_port); + int accept(void); + int read(msg_t *msg); + const char *getUdpIP(void); + const char *getUdpPort(bool); + int write(msg_t *msg); + int writeUDP(const char *data, size_t length, const char *ip, UINT16 port); + const char *getClientIP(size_t id); + const char *getClientPort(size_t id); + CServerSocket(void); + ~CServerSocket(void); +}; + +class CClientSocket : public CSocket +{ +private: + SOCKET socket_fd; +public: + int connect(const char *ip_addr, const char *str_port); + int read(msg_t *msg); + int write(msg_t *msg); + CClientSocket(void); + ~CClientSocket(void); +}; + +// ================================== PROTOS ================================== + +// ================================== EXTERNS ================================= + +#endif diff --git a/masterserver.sh b/masterserver.sh new file mode 100644 index 0000000..9b1adb1 --- /dev/null +++ b/masterserver.sh @@ -0,0 +1,48 @@ +#!/bin/sh -e +# +# SRB2 MasterServer - start up the masterserver +# + +# Get LSB functions +. /lib/lsb/init-functions +. /etc/default/rcS + +SRB2MS=/usr/local/bin/masterserver +SRB2MS_PORT=28900 + +# Check that the package is still installed +[ -x $SRB2MS ] || exit 0; + +case "$1" in + start) + log_begin_msg "Starting SRB2MS..." + umask 002 + if start-stop-daemon --start \ + --exec $SRB2MS \ + -- $SRB2MS_PORT; then + log_end_msg 0 + else + log_end_msg $? + fi + ;; + + stop) + log_begin_msg "Stopping SRB2MS..." + if start-stop-daemon --stop --exec $SRB2MS; then + log_end_msg 0 + else + log_end_msg $? + fi + ;; + + restart|force-reload) + "$0" stop && "$0" start + ;; + + *) + e cho "Usage: /etc/init.d/masterserver {start|stop|restart|force-reload}" + exit 1 + ;; +esac + +exit 0 diff --git a/md5.cpp b/md5.cpp new file mode 100644 index 0000000..4f4ff22 --- /dev/null +++ b/md5.cpp @@ -0,0 +1,363 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implemantion of RFC 1321 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +/* interface header */ +#include "md5.h" + +/* system implementation headers */ +#include +#include + + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/////////////////////////////////////////////// + +// F, G, H and I are basic MD5 functions. +inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { + return (x&y) | (~x&z); +} + +inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { + return (x&z) | (y&~z); +} + +inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { + return x^y^z; +} + +inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { + return y ^ (x | ~z); +} + +// rotate_left rotates x left n bits. +inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { + return (x << n) | (x >> (32-n)); +} + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. +inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; +} + +inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + G(b,c,d) + x + ac, s) + b; +} + +inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + H(b,c,d) + x + ac, s) + b; +} + +inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { + a = rotate_left(a + I(b,c,d) + x + ac, s) + b; +} + +////////////////////////////////////////////// + +// default ctor, just initailize +MD5::MD5() +{ + init(); +} + +////////////////////////////////////////////// + +// nifty shortcut ctor, compute MD5 for string and finalize it right away +MD5::MD5(const std::string &text) +{ + init(); + update(text.c_str(), text.length()); + finalize(); +} + +////////////////////////////// + +void MD5::init() +{ + finalized=false; + + count[0] = 0; + count[1] = 0; + + // load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; +} + +////////////////////////////// + +// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. +void MD5::decode(uint4 output[], const uint1 input[], size_type len) +{ + for (unsigned int i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | + (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); +} + +////////////////////////////// + +// encodes input (uint4) into output (unsigned char). Assumes len is +// a multiple of 4. +void MD5::encode(uint1 output[], const uint4 input[], size_type len) +{ + for (size_type i = 0, j = 0; j < len; i++, j += 4) { + output[j] = input[i] & 0xff; + output[j+1] = (input[i] >> 8) & 0xff; + output[j+2] = (input[i] >> 16) & 0xff; + output[j+3] = (input[i] >> 24) & 0xff; + } +} + +////////////////////////////// + +// apply MD5 algo on a block +void MD5::transform(const uint1 block[blocksize]) +{ + uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + decode (x, block, blocksize); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + // Zeroize sensitive information. + memset(x, 0, sizeof x); +} + +////////////////////////////// + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block +void MD5::update(const unsigned char input[], size_type length) +{ + // compute number of bytes mod 64 + size_type index = count[0] / 8 % blocksize; + + // Update number of bits + if ((count[0] += (length << 3)) < (length << 3)) + count[1]++; + count[1] += (length >> 29); + + // number of bytes we need to fill in buffer + size_type firstpart = 64 - index; + + size_type i; + + // transform as many times as possible. + if (length >= firstpart) + { + // fill buffer first, transform + memcpy(&buffer[index], input, firstpart); + transform(buffer); + + // transform chunks of blocksize (64 bytes) + for (i = firstpart; i + blocksize <= length; i += blocksize) + transform(&input[i]); + + index = 0; + } + else + i = 0; + + // buffer remaining input + memcpy(&buffer[index], &input[i], length-i); +} + +////////////////////////////// + +// for convenience provide a verson with signed char +void MD5::update(const char input[], size_type length) +{ + update((const unsigned char*)input, length); +} + +////////////////////////////// + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. +MD5& MD5::finalize() +{ + static unsigned char padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + if (!finalized) { + // Save number of bits + unsigned char bits[8]; + encode(bits, count, 8); + + // pad out to 56 mod 64. + size_type index = count[0] / 8 % 64; + size_type padLen = (index < 56) ? (56 - index) : (120 - index); + update(padding, padLen); + + // Append length (before padding) + update(bits, 8); + + // Store state in digest + encode(digest, state, 16); + + // Zeroize sensitive information. + memset(buffer, 0, sizeof buffer); + memset(count, 0, sizeof count); + + finalized=true; + } + + return *this; +} + +////////////////////////////// + +// return hex representation of digest as string +std::string MD5::hexdigest() const +{ + if (!finalized) + return ""; + + char buf[33]; + for (int i=0; i<16; i++) + sprintf(buf+i*2, "%02x", digest[i]); + buf[32]=0; + + return std::string(buf); +} + +////////////////////////////// + +std::ostream& operator<<(std::ostream& out, MD5 md5) +{ + return out << md5.hexdigest(); +} + +////////////////////////////// + +std::string md5(const std::string str) +{ + MD5 md5 = MD5(str); + + return md5.hexdigest(); +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..55a90c4 --- /dev/null +++ b/md5.h @@ -0,0 +1,93 @@ +/* MD5 + converted to C++ class by Frank Thilo (thilo@unix-ag.org) + for bzflag (http://www.bzflag.org) + + based on: + + md5.h and md5.c + reference implementation of RFC 1321 + + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +*/ + +#ifndef BZF_MD5_H +#define BZF_MD5_H + +#include +#include + + +// a small class for calculating MD5 hashes of strings or byte arrays +// it is not meant to be fast or secure +// +// usage: 1) feed it blocks of uchars with update() +// 2) finalize() +// 3) get hexdigest() string +// or +// MD5(std::string).hexdigest() +// +// assumes that char is 8 bit and int is 32 bit +class MD5 +{ +public: + typedef unsigned int size_type; // must be 32bit + + MD5(); + MD5(const std::string& text); + void update(const unsigned char *buf, size_type length); + void update(const char *buf, size_type length); + MD5& finalize(); + std::string hexdigest() const; + friend std::ostream& operator<<(std::ostream&, MD5 md5); + +private: + void init(); + typedef unsigned char uint1; // 8bit + typedef unsigned int uint4; // 32bit + enum {blocksize = 64}; // VC6 won't eat a const static int here + + void transform(const uint1 block[blocksize]); + static void decode(uint4 output[], const uint1 input[], size_type len); + static void encode(uint1 output[], const uint4 input[], size_type len); + + bool finalized; + uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk + uint4 count[2]; // 64bit counter for number of bits (lo, hi) + uint4 state[4]; // digest so far + uint1 digest[16]; // the result + + // low level logic operations + static inline uint4 F(uint4 x, uint4 y, uint4 z); + static inline uint4 G(uint4 x, uint4 y, uint4 z); + static inline uint4 H(uint4 x, uint4 y, uint4 z); + static inline uint4 I(uint4 x, uint4 y, uint4 z); + static inline uint4 rotate_left(uint4 x, int n); + static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); + static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); +}; + +std::string md5(const std::string str); + +#endif diff --git a/server.cpp b/server.cpp new file mode 100644 index 0000000..b7ed0d6 --- /dev/null +++ b/server.cpp @@ -0,0 +1,1037 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- +#ifdef __GNUC__ +#include +#include +#endif +#include +#include +#include +#include "ipcs.h" +#include "common.h" +#include "mysql.h" +#include "md5.h" +#include +//#include + +//============================================================================= + +#ifdef __GNUC__ +#define ATTRPACK __attribute__ ((packed)) +#else +#define ATTRPACK +#endif + +#define PT_ASKINFOVIAMS 15 + +static CServerSocket server_socket; + +FILE *logfile; +FILE *errorfile; +FILE *mysqlfile; +FILE *sockfile; +FILE *pidfile; + +#if defined(_MSC_VER) +#pragma pack(1) +#endif + +typedef struct +{ + char ip[16]; // Big enough to hold a full address. + UINT16 port; + UINT8 padding1[2]; + UINT32 time; +} ATTRPACK ms_holepunch_packet_t; + +typedef struct +{ + char clientaddr[22]; + UINT8 padding1[2]; + UINT32 time; +} ATTRPACK msaskinfo_pak; + +// +// SRB2 network packet data. +// +typedef struct +{ + UINT32 checksum; + UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack + UINT8 ackreturn; // the return of the ack number + + UINT8 packettype; + UINT8 reserved; // padding + + msaskinfo_pak msaskinfo; +} ATTRPACK doomdata_t; + +#if defined(_MSC_VER) +#pragma pack() +#endif + +//============================================================================= + +#define HOSTNAME "loopback" +#define USER "srb2_ms" +#define PASSWORD "gLRDRb7WgLRDRb7W" +#define DATABASE "srb2_ms" + +// MySQL Stuff :D + + const char *server = HOSTNAME; + const char *user = USER; + const char *password = PASSWORD; + const char *database = DATABASE; + time_t lastupdate; + + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + int mysqlconnected = 0; +/* +char *str_replace(char * t1, char * t2, char * t6) +{ + char*t4; + char*t5=new(0); + + while(strstr(t6,t1)){ + t4=strstr(t6,t1); + strncpy(t5+strlen(t5),t6,t4-t6); + strcat(t5,t2); + t4+=strlen(t1); + t6=t4; + } + return strcat(t5,t4); +} +*/ +/* +// Cue was here +char *str_quakeformat(char *msg) +{ + char *quakemsg , *quakemsg1, *quakemsg2; + char *quakemsg3, *quakemsg4, *quakemsg5; + char *quakemsg6, *quakemsg7, *quakemsg8; + + quakemsg1 = new(sizeof(char) * (strlen(msg) + 1)); + strcpy(quakemsg1, msg); + quakemsg2 = str_replace("^1", "\x81",quakemsg1); + quakemsg3 = str_replace("^2", "\x82",quakemsg2); + quakemsg4 = str_replace("^3", "\x83",quakemsg3); + quakemsg5 = str_replace("^4", "\x84",quakemsg4); + quakemsg6 = str_replace("^5", "\x85",quakemsg5); + quakemsg7 = str_replace("^6", "\x86",quakemsg6); + quakemsg8 = str_replace("^7", "\x87",quakemsg7); + quakemsg = str_replace("^0", "\x80",quakemsg8); + delete(quakemsg1); + delete(quakemsg2); + delete(quakemsg3); + delete(quakemsg4); + delete(quakemsg5); + delete(quakemsg6); + delete(quakemsg7); + delete(quakemsg8); + + return quakemsg; +} + */ +void MySQL_Conn(bool force) { + //if(mysql_ping(conn)) + //mysqlconnected = 0; + logPrintf(mysqlfile, "Trying to connect to MySQL...\n"); + if(mysqlconnected != 1 || force) { + if(force) + mysql_close(conn); + const char *server = HOSTNAME; + const char *user = USER; + const char *password = PASSWORD; + const char *database = DATABASE; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 8219, NULL, 0)) { + logPrintf(errorfile, "%s\n", mysql_error(conn)); + } + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0)) { + logPrintf(errorfile, "%s\n", mysql_error(conn)); + mysqlconnected = 0; + } else { + logPrintf(mysqlfile, "Connection to MySQL Database successful!\n"); + mysqlconnected = 1; + } + } +} + +static int MySQL_CheckBan(const char *ip, UINT32 id, bool sendinfo, bool type) { + msg_t msg; + int writecode; + MySQL_Conn(false); + msg.id = id; + char banqueryp1[500] = "SELECT bid,INET_NTOA(ipstart),INET_NTOA(ipend),DATE_FORMAT(FROM_UNIXTIME(full_endtime),'%%a %%b %%e %%Y at %%k:%%i (CDT)'),reason,hostonly,permanent FROM ms_bans WHERE INET_ATON('%s') BETWEEN `ipstart` AND `ipend` AND (`full_endtime` > %ld OR `permanent` = '1') LIMIT 1"; +// char exqueryp1[500] = "SELECT * FROM ms_exceptions WHERE `ip` = '%s' AND `bid` = '%s' LIMIT 1"; +// char exquery[500]; + char banquery[500]; +// char bid[5]; + + time_t current_time = time (NULL); + sprintf(banquery, banqueryp1, ip, current_time); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", banquery); + if(mysql_query(conn, banquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + return false; + } else { + res = mysql_store_result(conn); + if(mysql_num_rows(res) >= 1) { + logPrintf(logfile, "We got a ban coming on!\n"); + row = mysql_fetch_row(res); + if(!type && strcmp(row[5],"1") == 0) + return false; + if(sendinfo) + { + logPrintf(logfile, "And we're meant to tell them some more!\n"); + msg_ban_t *info = (msg_ban_t *) msg.buffer; + info->header[0] = '\0'; + + logPrintf(logfile, "Got Ban: %s - %s, ends %s, for %s\n", row[1], row[2], row[3], row[4]); + logPrintf(logfile, "COPY: ipstart\n"); + strcpy(info->ipstart, row[1]); + logPrintf(logfile, "COPY: ipend\n"); + strcpy(info->ipend, row[2]); + logPrintf(logfile, "COPY: endtime\n"); + if(strcmp(row[6], "0") == 0) + { + logPrintf(logfile, "COPY: It's not permanent.\n"); + strcpy(info->endstamp, row[3]); + } + else + { + logPrintf(logfile, "COPY: It's permanent.\n"); + strcpy(info->endstamp, "Never"); + } + logPrintf(logfile, "COPY: reason\n"); + strcpy(info->reason, row[4]); + logPrintf(logfile, "COPY: hostonly\n"); + if(strcmp(row[5],"0") == 0) + info->hostonly = false; + else + info->hostonly = true; + + if(info->hostonly) + logPrintf(logfile, "BAN HAMMER: %s - %s || Reason: %s || Endstamp: %s || HOSTONLY\n", info->ipstart, info->ipend, info->reason, info->endstamp); + else + logPrintf(logfile, "BAN HAMMER: %s - %s || Reason: %s || Endstamp: %s || NOTHOSTONLY\n", info->ipstart, info->ipend, info->reason, info->endstamp); + mysql_free_result(res); + msg.type = GET_BANNED_MSG; + msg.length = sizeof (msg_ban_t); + msg.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + return true; + } + } + return true; + } else { + mysql_free_result(res); + return false; + } + } +} + +int MySQL_CheckRoom(UINT32 room) +{ + MySQL_Conn(false); + char checkqueryp1[500] = "SELECT private FROM `ms_rooms` WHERE `room_id` = '%d' AND `private` = '1' LIMIT 1"; + char checkquery[500]; + sprintf(checkquery, checkqueryp1, room); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", checkquery); + if(mysql_query(conn, checkquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + return false; + } else { + res = mysql_store_result(conn); + if(mysql_num_rows(res) < 1) + return true; + else + return false; + } +} + +void MySQL_AddServer(const char *ip, const char *port, const char *name, const char *version, UINT32 room, bool firstadd, const char *key) { + char escapedName[255]; + char escapedPort[10]; + char escapedVersion[10]; + char escapedKey[32]; + char insertquery[5000]; + char checkquery[500]; + char updatequery[5000]; + char queryp1[5000] = "INSERT INTO `ms_servers` (`name`,`ip`,`port`,`version`,`timestamp`,`room`,`key`) VALUES ('%s','%s','%s','%s','%ld','%d','%s')"; + char checkqueryp1[500] = "SELECT room_override FROM `ms_servers` WHERE `ip` = '%s'"; + char updatequeryp1[5000]; + if(firstadd) + { + logPrintf(logfile, "First add.\n"); + strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `delisted` = '0', `key` = '%s' WHERE `ip` = '%s'"); + } + else + { + logPrintf(logfile, "Update ping.\n"); + strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `key` = '%s' WHERE `ip` = '%s' AND `delisted` = '0'"); + } + MySQL_Conn(false); + mysql_real_escape_string(conn, escapedName, name, (unsigned long)strlen(name)); + mysql_real_escape_string(conn, escapedPort, port, (unsigned long)strlen(port)); + mysql_real_escape_string(conn, escapedVersion, version, (unsigned long)strlen(version)); + mysql_real_escape_string(conn, escapedKey, key, (unsigned long)strlen(key)); + if(!MySQL_CheckBan(ip,0,false,1)) { + if(!MySQL_CheckRoom(room)) + { + logPrintf(errorfile, "IP %s tried to use the private room %d! THIS SHOULD NOT HAPPEN\n", ip, room); + return; + } + sprintf(checkquery, checkqueryp1, ip); + time_t timestamp; + timestamp = time (NULL); + logPrintf(logfile, "Checking for existing servers in table with the same IP...\n"); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", checkquery); + if(mysql_query(conn, checkquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + res = mysql_store_result(conn); + logPrintf(logfile, "Found %d rows...\n", mysql_num_rows(res)); + if(mysql_num_rows(res) < 1) { + mysql_free_result(res); + logPrintf(logfile, "Adding the temporary server: %s:%s || Name: %s || Version: %s || Time: %ld || Room: %d || Key: %s\n", ip, port, name, version, timestamp, room, key); + sprintf(insertquery, queryp1, escapedName, ip, escapedPort, escapedVersion, timestamp, room, escapedKey); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", insertquery); + if(mysql_query(conn, insertquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + logPrintf(logfile, "Server added successfully!\n"); + } + } else { + row = mysql_fetch_row(res); + if(atoi(row[0]) != 0) + room = atoi(row[0]); + mysql_free_result(res); + logPrintf(logfile, "Server's IP already exists, so let's just update it instead...\n"); + logPrintf(logfile, "Updating Server Data for %s\n", ip); + sprintf(updatequery, updatequeryp1, escapedName, escapedPort, escapedVersion, timestamp, room, escapedKey, ip); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", updatequery); + if(mysql_query(conn, updatequery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + logPrintf(logfile, "Server status for Server: %s:%s || Name: %s || Version: %s || Time: %ld || Room: %d || Key: %s set successfully.\n", ip, port, name, version, timestamp, room, key); + } + } + } + } else { + logPrintf(logfile, "IP %s is banned so do nothing.\n", ip); + } +} + +void MySQL_ListServers(UINT32 id, UINT32 type, const char *ip, UINT32 room) { + msg_t msg; + int writecode; + MySQL_Conn(false); + msg.id = id; + msg.type = type; + char servquery[1000]; + if(room == 0) + sprintf(servquery, "SELECT ip, port, name, version FROM ( SELECT * FROM `ms_servers` WHERE `upnow` = '1' ORDER BY `sid` ASC) as t2 ORDER BY `sticky` DESC"); + else + { + char servqueryp1[1000] = "SELECT ip, port, name, version FROM ( SELECT * FROM `ms_servers` WHERE `upnow` = '1' AND `room` = '%d' ORDER BY `sid` ASC) as t2 ORDER BY `sticky` DESC"; + sprintf(servquery, servqueryp1, room); + } + if(!MySQL_CheckBan(ip,0,false,0)) { + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", servquery); + if(mysql_query(conn, servquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + res = mysql_store_result(conn); + logPrintf(logfile, "Found %d servers...\n", mysql_num_rows(res)); + while ((row = mysql_fetch_row(res)) != NULL) + { + msg_server_t *info = (msg_server_t *) msg.buffer; + + info->header[0] = '\0'; // nothing interresting in it (for now) + strcpy(info->ip, row[0]); + strcpy(info->port, row[1]); + strcpy(info->name, row[2]); + strcpy(info->version, row[3]); + + msg.length = sizeof (msg_server_t); + msg.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + return; + } + } + mysql_free_result(res); + } + } else { + logPrintf(logfile, "IP %s is banned so do nothing.\n", ip); + } +} + +void MySQL_ListRooms(UINT32 id, UINT32 type, const char *ip, bool sendtype) { + msg_t msg; + int writecode; + //(void)ip; + MySQL_Conn(false); + msg.id = id; + msg.type = type; + char roomquery[1000]; + logPrintf(logfile, "Check their ban status first... (ID %d)\n",id); + if(MySQL_CheckBan(ip,id,true,sendtype)) + return; + + //Hmph! I suppose it'll be up to me to handle this. ~Inuyasha + if (sendtype) + { + char overridequery[500]; + + sprintf(overridequery, "SELECT room_override FROM `ms_servers` WHERE `ip` = '%s' AND `room_override` > 0", ip); + logPrintf(mysqlfile, "Checking for room overrides! Executing MySQL Query: %s\n", overridequery); + if (mysql_query(conn, overridequery)) + { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + return; + } + + res = mysql_store_result(conn); + if (mysql_num_rows(res) >= 1) + { + int overriddenRoom; + + row = mysql_fetch_row(res); + overriddenRoom = atoi(row[0]); + mysql_free_result(res); + + sprintf(roomquery, "SELECT room_id, title, motd FROM `ms_rooms` WHERE `room_id` = '%d' LIMIT 1", overriddenRoom); + if (mysql_query(conn, roomquery)) + { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + return; + } + res = mysql_store_result(conn); + if (mysql_num_rows(res) >= 1) + { + msg_rooms_t *info = (msg_rooms_t *) msg.buffer; + char roommotd[300]; + + row = mysql_fetch_row(res); + + info->header[0] = '\0'; // nothing interesting in it (for now) + strcpy(info->name, row[1]); + + // Just in case, let's make sure we don't overflow. + sprintf(roommotd, "\x85Your server has had a room override applied to it, so this is the only room you may host in.\x80\n\n%s", row[2]); + strncpy(info->motd, roommotd, 255); + info->motd[255] = '\0'; + + info->id = atoi(row[0]); + + logPrintf(logfile, "Sending Room %s with ID %d and MOTD %s\n", info->name, info->id, info->motd); + + msg.length = sizeof (msg_rooms_t); + msg.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + } + mysql_free_result(res); + return; + } + else + { + logPrintf(errorfile, "Someone's room override isn't set correctly! Room %d doesn't exist!", overriddenRoom); + mysql_free_result(res); + } + } + } + + if(sendtype) + sprintf(roomquery, "SELECT room_id, title, motd FROM `ms_rooms` WHERE `private` = '0' AND `visible` = '1' ORDER BY `order` ASC"); + else + sprintf(roomquery, "SELECT room_id, title, motd FROM `ms_rooms` WHERE `visible` = '1' ORDER BY `order` ASC"); + if(mysql_query(conn, roomquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else + { + res = mysql_store_result(conn); + logPrintf(logfile, "Found %d rooms...\n", mysql_num_rows(res)); + while ((row = mysql_fetch_row(res)) != NULL) + { + msg_rooms_t *info = (msg_rooms_t *) msg.buffer; + + info->header[0] = '\0'; // nothing interresting in it (for now) + strcpy(info->name, row[1]); + strcpy(info->motd, row[2]); + info->id = atoi(row[0]); + + logPrintf(logfile, "Sending Room %s with ID %d and MOTD %s\n", info->name, info->id, info->motd); + + msg.length = sizeof (msg_rooms_t); + msg.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + return; + } + } + mysql_free_result(res); + } +} + +void MySQL_CheckVersion(UINT32 id, UINT32 type, const char *ip, UINT32 modid, const char *modversion) { + msg_t msg; + int writecode; + (void)ip; + MySQL_Conn(false); + msg.id = id; + msg.type = type; + char versionquery[1000]; + sprintf(versionquery, "SELECT * FROM `ms_versions` WHERE `mod_id` = '%d' AND `mod_version` > %s LIMIT 1",modid, modversion); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", versionquery); + if(mysql_query(conn, versionquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else + { + res = mysql_store_result(conn); + if(mysql_num_rows(res) < 1) + { + logPrintf(logfile, "Buffer to NULL\n"); + strcpy(msg.buffer,"NULL"); + } + else + { + row = mysql_fetch_row(res); + logPrintf(logfile, "Version found: %s || Codebase: %s || Version: %s || Version String: %s || Mod ID: %s\n", row[4], row[3], row[1], row[2], row[0]); + strcpy(msg.buffer,row[2]); + } + msg.length = sizeof msg.buffer; + msg.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + return; + } + mysql_free_result(res); + } +} + +void MySQL_ListServServers(UINT32 id, UINT32 type, const char *ip) { + msg_t msg; + int writecode; + static char str[1024]; + //char banqueryp1[500] = "SELECT reason,name,FROM_UNIXTIME(endtime) FROM ms_bans WHERE INET_ATON('%s') BETWEEN `ipstart` AND `ipend` AND `endtime` < %ld LIMIT 1"; + //char banquery[500]; + //time_t current_time = time (NULL); + char servquery[1000] = "SELECT ip, port, name, version, permanent FROM ( SELECT * FROM `ms_servers` WHERE `upnow` = '1' OR `permanent` = '1' ORDER BY `sid` ASC) as ms_servers ORDER BY `sticky` DESC"; + MySQL_Conn(false); + if(!MySQL_CheckBan(ip,0,false,0)) { + msg.id = id; + msg.type = type; + msg.room = 0; + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", servquery); + if(mysql_query(conn, servquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + res = mysql_store_result(conn); + logPrintf(logfile, "Found %d servers...\n", mysql_num_rows(res)); + while ((row = mysql_fetch_row(res)) != NULL) + { + if(strcmp(row[4], "0") == 1) + snprintf(str, sizeof str, "IP\t\t: %s\nPort\t\t: %s\nHostname\t: %s\nVersion\t\t: %s\nPermanent\t: %s\n", row[0], row[1], row[2], row[3], "Yes"); + else + snprintf(str, sizeof str, "IP\t\t: %s\nPort\t\t: %s\nHostname\t: %s\nVersion\t\t: %s\nPermanent\t: %s\n", row[0], row[1], row[2], row[3], "No"); + + msg.length = (INT32)(strlen(str)+1); // send also the '\0' + strcpy(msg.buffer, str); + dbgPrintf(CYAN, "Writing: (%d)\n%s\n", msg.length, msg.buffer); + writecode = server_socket.write(&msg); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d deleted\n", writecode, id); + return; + } + } + mysql_free_result(res); + } + } else { + logPrintf(logfile, "IP %s is banned so do nothing! Let them find out for themselves.\n", ip); + } +} + +void MySQL_RemoveServer(char *ip, char *port, char *name, char *version) { + char escapedName[255]; + char updatequery[5000]; + char updatequeryp1[5000] = "UPDATE `ms_servers` SET upnow = '0' WHERE `ip` = '%s' AND `permanent` = '0'"; + MySQL_Conn(false); + mysql_real_escape_string(conn, escapedName, name, (unsigned long)strlen(name)); + sprintf(updatequery, updatequeryp1, ip); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", updatequery); + if(mysql_query(conn, updatequery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + logPrintf(logfile, "Server: %s:%s || Name: %s || Version: %s removed successfully.\n", ip, port, name, version); + } +} + +/* LIVE FUNCTIONS */ +void LIVE_AuthUser(UINT32 id, char *buffer) +{ + msg_live_auth_t *auth; + char escapedName[255]; + char saltedPassword[62]; + char hashedPassword[34]; + char namequery[1000]; + char namequeryp1[1000] = "SELECT username,userid,salt,password FROM `user` WHERE `username` = '%s' LIMIT 1"; + char userquery[1000]; + char userqueryp1[1000] = "SELECT userid,username,live_authkey,live_publickey FROM `user` WHERE `userid` = '%s' LIMIT 1"; + msg_t msg2; + char ip[32]; + int writecode; + msg2.type = LIVE_INVALID_USER; + + auth = (msg_live_auth_t *)buffer; + + logPrintf(logfile, "Got User %s Pass %s\n", auth->username, auth->password); + // retrieve the true ip of the server + strcpy(ip, server_socket.getClientIP(id)); + MySQL_Conn(false); + msg2.id = id; + logPrintf(logfile, "Check their ban status first... (ID %d)\n",id); + if(MySQL_CheckBan(ip,0,false,0)) + return; + mysql_real_escape_string(conn, escapedName, auth->username, (unsigned long)strlen(auth->username)); + sprintf(namequery, namequeryp1, escapedName); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", namequery); + if(mysql_query(conn, namequery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + res = mysql_store_result(conn); + if(mysql_num_rows(res) < 1) + { + logPrintf(logfile, "Invalid user: %s\n", auth->username); + msg2.type = LIVE_INVALID_USER; + } + else + { + row = mysql_fetch_row(res); + sprintf(saltedPassword, "%s%s", auth->password, row[2]); + logPrintf(logfile, "Pre-hash: %s (Salt is %s)\n", saltedPassword, row[2]); + sprintf(hashedPassword, md5(saltedPassword).c_str()); + logPrintf(logfile, "Got Hashed Password: %s\n", hashedPassword); + if(strcmp(hashedPassword, row[3]) != 0) + { + msg2.type = LIVE_INVALID_USER; + logPrintf(logfile, "INVALID USER!\n"); + } else { + msg2.type = LIVE_SEND_USER; + sprintf(userquery, userqueryp1, row[1]); + mysql_free_result(res); + logPrintf(mysqlfile, "Executing MySQL Query: %s\n", userquery); + if(mysql_query(conn, userquery)) { + logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn)); + MySQL_Conn(true); + } else { + res = mysql_store_result(conn); + if(mysql_num_rows(res) < 1) + { + logPrintf(errorfile, "User Information Error, invalid USER ID!\n"); + msg2.type = LIVE_INVALID_USER; + } else { + row = mysql_fetch_row(res); + msg_live_user_t *user = (msg_live_user_t *) msg2.buffer; + + user->header[0] = '\0'; // nothing interresting in it (for now) + user->uid = atoi(row[0]); + strcpy(user->username, row[1]); + logPrintf(logfile, "Found user: %s, id %s\n", row[1], row[0]); + logPrintf(logfile, "Sending User %s with ID %d\n", user->username, user->uid); + + msg2.length = sizeof (msg_live_user_t); + } + } + } + } + } + msg2.length = sizeof msg2.buffer; + msg2.room = 0; + logPrintf(logfile, "Sending message?\n"); + writecode = server_socket.write(&msg2); + logPrintf(logfile, "Message sent! :D\n"); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d " + "deleted\n", writecode, id); + return; + } + mysql_free_result(res); +} + +/* +** sendServersInformations() +*/ +static void sendServersInformations(UINT32 id, UINT32 room) +{ + msg_t msg; + int writecode; + char ip[16]; + strcpy(ip, server_socket.getClientIP(id)); + (void)room; + logPrintf(logfile, "Sending servers informations\n"); + msg.id = id; + msg.type = SEND_SERVER_MSG; + msg.room = 0; + MySQL_ListServServers(id, SEND_SERVER_MSG, ip); // Awesome new MySQL Code! + msg.length = 0; + //dbgPrintf(CYAN, "Writing: (%d) %s\n", msg.length, ""); + writecode = server_socket.write(&msg); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d deleted\n", + writecode, id); + } +} + +/* +** sendShortServersInformations() +*/ +static void sendShortServersInformations(UINT32 id, UINT32 room) +{ + msg_t msg; + int writecode; + char ip[16]; + strcpy(ip, server_socket.getClientIP(id)); + + logPrintf(logfile, "Sending short servers informations\n"); + msg.id = id; + msg.type = SEND_SHORT_SERVER_MSG; + MySQL_ListServers(id, SEND_SHORT_SERVER_MSG, ip, room); // New code that's full of win! + logPrintf(logfile, "Packet Room: %d\n", room); + msg.length = 0; + writecode = server_socket.write(&msg); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d deleted\n", + writecode, id); + } +} + +/* +** sendRoomInformations() +*/ +static void sendRoomInformations(UINT32 id, UINT32 room, bool type) +{ + msg_t msg; + int writecode; + char ip[16]; + strcpy(ip, server_socket.getClientIP(id)); + (void)room; + logPrintf(logfile, "Sending room information\n"); + msg.id = id; + msg.type = SEND_ROOMS_MSG; + msg.room = 0; + MySQL_ListRooms(id, SEND_ROOMS_MSG, ip, type); // New code that's full of win! + msg.length = 0; + writecode = server_socket.write(&msg); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d deleted\n", + writecode, id); + } +} + +/* +** sendVersionInformations() +*/ +static void sendVersionInformations(UINT32 id, UINT32 modid, char *modversion) +{ + msg_t msg; + int writecode; + char ip[16]; + strcpy(ip, server_socket.getClientIP(id)); + + logPrintf(logfile, "Sending version information\n"); + msg.id = id; + msg.type = SEND_VERSION_MSG; + msg.room = 0; + MySQL_CheckVersion(id, SEND_VERSION_MSG, ip, modid, modversion); // New code that's full of win! + msg.length = sizeof msg.buffer; + writecode = server_socket.write(&msg); + if (writecode < 0) + { + dbgPrintf(LIGHTRED, "Write error... %d client %d deleted\n", + writecode, id); + } +} + +/* +** addServer() +*/ +static void addServer(int id, char *buffer, bool firstadd) +{ + msg_server_t *info; + char oldversion = 0; + + //TODO: Be sure there is no flood from a given IP: + // If a host need more than 2 servers, then it should be registrated + // manually + + info = (msg_server_t *)buffer; + + // I want to be sure the informations are correct, of course! + info->port[sizeof (info->port)-1] = '\0'; + info->name[sizeof (info->name)-1] = '\0'; + info->version[sizeof (info->version)-1] = '\0'; + // retrieve the true ip of the server + strcpy(info->ip, server_socket.getClientIP(id)); + //strcpy(info->port, server_socket.getClientPort(id)); + + if (info->version[0] == '1' && + info->version[1] == '.' && + info->version[2] == '0' && + info->version[3] == '9' && + info->version[4] == '.') + { + if ((info->version[5] == '2') || (info->version[5] == '3')) + { + oldversion = 1; + } + } + + if (info->version[0] == '1' && + info->version[1] == '.' && + info->version[2] == '6' && + info->version[3] == '9' && + info->version[4] == '.' && + info->version[5] == '6') + { + oldversion = 1; + } + + if (!oldversion) + { + MySQL_AddServer(info->ip, info->port, info->name, info->version, info->room, firstadd, info->key); + } + else + { + logPrintf(logfile, "Not adding the temporary server: %s %s %s %s\n", info->ip, info->port, info->name, info->version); + } +} + +/* +** removeServer() +*/ +static void removeServer(int id, char *buffer) +{ + msg_server_t *info; + + info = (msg_server_t *)buffer; + + // I want to be sure the informations are correct, of course! + info->port[sizeof (info->port)-1] = '\0'; + info->name[sizeof (info->name)-1] = '\0'; + info->version[sizeof (info->version)-1] = '\0'; + + // retrieve the true ip of the server + strcpy(info->ip, server_socket.getClientIP(id)); + + logPrintf(logfile, "Removing the temporary server: %s %s %s %s\n", info->ip, info->port, info->name, info->version); + MySQL_RemoveServer(info->ip, info->port, info->name, info->version); // New and win. +} + +/* +** analyseUDP() +*/ +static int analyseUDP(size_t size, const char *buffer) +{ + // this would be the part of reading the PT_SERVERINFO packet, + // but i'm not about to backport that sloppy code + (void)size; + (void)buffer; + return INVALID_MSG; +} + +/* +** UDPMessage() +*/ +static int UDPMessage(size_t size, const char *buffer) +{ + if (size == 4) + { + return 0; + } + else if (size == sizeof(ms_holepunch_packet_t)) + { + return 0; + } + else if (size > 58) + { + dbgPrintf(LIGHTGREEN, "Got a UDP %d byte long message\n", size); + return analyseUDP(size, buffer); + } + else + return INVALID_MSG; +} + +/* +** analyseMessage() +*/ +static int analyseMessage(msg_t *msg) +{ + switch (msg->type) + { + case UDP_RECV_MSG: + return UDPMessage(msg->length, msg->buffer); + case ACCEPT_MSG: + break; + case ADD_SERVER_MSG: + addServer(msg->id, msg->buffer, true); + break; + case PING_SERVER_MSG: + addServer(msg->id, msg->buffer, false); + break; + case REMOVE_SERVER_MSG: + removeServer(msg->id, msg->buffer); + break; + case GET_SERVER_MSG: + sendServersInformations(msg->id, msg->room); + break; + case GET_SHORT_SERVER_MSG: + sendShortServersInformations(msg->id, msg->room); + break; + case GET_ROOMS_MSG: + sendRoomInformations(msg->id, msg->room, 0); + break; + case GET_ROOMS_HOST_MSG: + sendRoomInformations(msg->id, msg->room, 1); + break; + case GET_VERSION_MSG: + sendVersionInformations(msg->id, msg->room, msg->buffer); + break; + case LIVE_AUTH_USER: + LIVE_AuthUser(msg->id, msg->buffer); + break; + default: + return INVALID_MSG; + } + return 0; +} + +/* +** main() +*/ +int main(int argc, char *argv[]) +{ + msg_t msg; +#ifdef __unix__ + pid_t pid; + char pidstr[12]; +#endif + + if (argc <= 1) + { + fprintf(stderr, "usage: %s port\n", argv[0]); + exit(1); + } + + if (server_socket.listen(argv[1]) < 0) + { + fprintf(stderr, "Error while initializing the server\n"); + exit(2); + } + + logfile = openFile("server.log"); + errorfile = openFile("error.log"); + mysqlfile = openFile("mysql.log"); + sockfile = openFile("sockets.log"); + MySQL_Conn(false); +#if !defined (DEBUG) && defined (__unix__) + pid = fork(); + switch (pid) + { + case 0: break; // child + case -1: printf("Error while launching the server in background\n"); return -1; + default: + pidfile = fopen("msd.pid", "w+"); + sprintf(pidstr, "%d", pid); + fputs(pidstr, pidfile); + fclose(pidfile); + return 0; // parent: keep child in background + } +#endif + srand((unsigned)time(NULL)); // Alam: GUIDs + for (;;) + { + memset(&msg, 0, sizeof (msg)); // remove previous message + if (!server_socket.read(&msg)) + { + // valid message: header message seems ok + analyseMessage(&msg); + //servers_list.show(); // for debug purpose + } + } + +#ifndef _MSC_VER + /* NOTREACHED */ + return 0; +#endif +} diff --git a/srvlist.cpp b/srvlist.cpp new file mode 100644 index 0000000..c9a6fee --- /dev/null +++ b/srvlist.cpp @@ -0,0 +1,405 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifdef __GNUC__ +#include +#endif +#include +#include "common.h" +#include "srvlist.h" + +//============================================================================= + +/* +** +*/ +CList::CList() +{ + list = NULL; + current = NULL; +} + +/* +** +*/ +CList::~CList() +{ + CItem *p; + + while (list) + { + p = list; + list = list->next; + delete(p); + } +} + +/* +** +*/ +int CList::insert(CItem *item) +{ + item->next = list; + list = item; + return 0; +} + +/* +** +*/ +int CList::remove(CItem *item) +{ + CItem *position, *q; + + q = NULL; + position = list; + + while (position && (position != item)) + { + q = position; + position = position->next; + } + if (position) + { + if (q) + q->next = position->next; + else + list = position->next; + delete position; + + return 1; + } + + return 0; +} + +/* +** +*/ +CItem *CList::getFirst() +{ + current = list; + return current; +} + +/* +** +*/ +CItem *CList::getNext() +{ + if (current) + current = current->next; + return current; +} + +/* +** +*/ +void CList::show() +{ + CItem *p; + + p = list; + while (p) + { + p->print(); + p = p->next; + } +} + +//============================================================================= + +/* +** +*/ +CItem::CItem() +{ + next = NULL; +} + +//============================================================================= + +/* +** +*/ +CInetAddr::CInetAddr(const char *ip, const char *port) +{ + strcpy(this->ip, ip); + strcpy(this->port, port); + PortNotChanged = true; +} + +/* +** +*/ +const char *CInetAddr::getIP() +{ + return ip; +} + +/* +** +*/ +const char *CInetAddr::getPort() +{ + return port; +} + +/* +** +*/ +bool CInetAddr::setPort(const char *port) +{ + if (PortNotChanged) + { + strcpy(this->port, port); + PortNotChanged = false; + } + return !PortNotChanged; +} + +//============================================================================= + +/* +** +*/ +CPlayerItem::CPlayerItem(const char *ip, const char *port, + const char *nickname) : CInetAddr(ip, port) +{ + strcpy(this->nickname, nickname); +} + +/* +** +*/ +void CPlayerItem::print() +{ + dbgPrintf(GREEN, "\tIP\t\t: %s\n\tPort\t\t: %s\n\tNickname\t: %s\n", + ip, port, nickname); +} + +/* +** +*/ +char *CPlayerItem::getString() +{ + static char tmpbuf[1024]; + + snprintf(tmpbuf, sizeof tmpbuf, + "\tIP\t\t: %s\n\tPort\t\t: %s\n\tNickname\t: %s\n", + ip, port, nickname); + return tmpbuf; +} + +//============================================================================= + +/* +** +*/ +CServerItem::CServerItem(const char *ip, const char *port, const char *hostname, const char *version, ServerType type) : CInetAddr(ip, port) +{ + time_t timenow = time(NULL); + const tm *timeGMT = gmtime(&timenow); + // check name of server here + strcpy(this->hostname, hostname); + strcpy(this->version, version); + this->type = type; + strftime(reg_time, REG_TIME_SIZE+1, "%Y-%m-%dT%H:%MZ",timeGMT); + { + int i; + memset(guid,'\0',GUID_SIZE); + strcpy(&guid[0], ip); + strcpy(&guid[15], port); // GenUID + for (i = 0; i <= GUID_SIZE-1; i++) + { + if (guid[i] == '\0' || guid[i] == '.') + guid[i] = '0' + (rand()/(RAND_MAX/15)); + if (guid[i] > '9') + guid[i] += 'A'-'9'; + } + guid[GUID_SIZE] = '\0'; + } + + HeartBeat = time(NULL); +} + +/* +** +*/ +void CServerItem::print() +{ + dbgPrintf(GREEN, "IP\t\t: %s\nPort\t\t: %s\nHostname\t: %s\nVersion\t: %s\nPermanent\t: %s\n", + ip, port, hostname, version, (type == ST_PERMANENT) ? "Yes" : "No"); +} + +/* +** +*/ +const char *CServerItem::getString() +{ + static char tmpbuf[1024]; + + snprintf(tmpbuf, sizeof tmpbuf, + "IP\t\t: %s\nPort\t\t: %s\nHostname\t: %s\nVersion\t\t: %s\nPermanent\t: %s\n", + ip, port, hostname, version, (type==ST_PERMANENT) ? "Yes" : "No"); + return tmpbuf; +} + +/* +** +*/ +const char *CServerItem::getName() +{ + return hostname; +} + +/* +** +*/ +const char *CServerItem::getVersion() +{ + return version; +} + +/* +** +*/ +const char *CServerItem::getGuid() +{ + return guid; +} + +/* +** +*/ +const char *CServerItem::getRegtime() +{ + return reg_time; +} + +/* +** +*/ +//============================================================================= + +/* +** +*/ +void CServerList::insertPlayer(CServerItem *server, CPlayerItem *player) +{ + server->players_list.insert(player); +} + +/* +** +*/ +void CServerList::removePlayer(CServerItem *server, CPlayerItem *player) +{ + server->players_list.remove(player); +} + +/* +** +*/ +int CServerList::insert(CServerItem *server) +{ + CList::insert((CItem *)server); + return 0; +} + +/* +** +*/ +int CServerList::insert(const char *ip, const char *port, + const char *hostname, const char *version, ServerType type) +{ + CServerItem *server; + + server = new CServerItem(ip, port, hostname, version, type); + CList::insert(server); + return 0; +} + +/* +** +*/ +int CServerList::remove(CServerItem *server) +{ + return CList::remove((CItem *)server); +} + +/* +** +*/ +int CServerList::remove(const char *ip, const char *port, + const char *hostname, const char *version, ServerType type) +{ + // TODO + CServerItem *position, *q; + bool match; + + (void)hostname; + (void)port; + + match = false; + position = (CServerItem *)list; + q = NULL; + + while (position && !match) + { + if (strcmp(position->ip, ip) == 0 + && strcmp(position->version, version) == 0 + && strcmp(position->port, port) == 0 + && position->type == type) + { + match = true; + } + else + { + q = position; + position = (CServerItem *)position->next; + } + } + if (position && match) + { + if (q) + q->next = position->next; + else + list = position->next; + delete position; + + return 1; + } + + return 0; +} + +/* +** +*/ +void CServerList::show() +{ + CServerItem *p; + + p = (CServerItem *)list; + while (p) + { + p->print(); + p->players_list.show(); + p = (CServerItem *)p->next; + } +} diff --git a/srvlist.h b/srvlist.h new file mode 100644 index 0000000..aa582ff --- /dev/null +++ b/srvlist.h @@ -0,0 +1,136 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifndef SRVLIST_H +#define SRVLIST_H + +#include + +enum ServerType +{ + ST_PERMANENT = 0, + ST_TEMPORARY, +}; + +//============================================================================= + +class CItem +{ + friend class CList; +protected: + CItem *next; +public: + CItem(); + virtual ~CItem() {}; // destructor must exist and be virtual + virtual void print() = 0; +}; + +//============================================================================= + +class CList +{ +protected: + CItem *list; + CItem *current; // the current item we are pointing on (for getfirst/getnext) +public: + CList(); + virtual ~CList(); + int insert(CItem *); + int remove(CItem *); + CItem *getFirst(); + CItem *getNext(); + void show(); +}; + +//============================================================================= + +class CInetAddr +{ +protected: + char ip[16]; + char port[8]; + bool PortNotChanged; +public: + CInetAddr(const char *ip, const char *port); + const char *getIP(); + bool setPort(const char *port); + const char *getPort(); +}; + +//============================================================================= + +class CPlayerItem : public CItem, public CInetAddr +{ +friend class CPlayerList; +private: + char nickname[32]; +public: + CPlayerItem(const char *ip, const char *port, const char *nickname); + void print(); + char *getString(); +}; + +//============================================================================= + +class CPlayerList : public CList +{ +}; + +//============================================================================= + +#define REG_TIME_SIZE 24 //1970-00-00T00:00:00.0+00:00 +#define GUID_SIZE 24-1 // format is 12700000000105000 + +class CServerItem : public CItem, public CInetAddr +{ + friend class CServerList; + +private: + char hostname[32]; + char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31) + CPlayerList players_list; + char reg_time[REG_TIME_SIZE]; + char guid[GUID_SIZE+1]; + +public: + ServerType type; + time_t HeartBeat; + + CServerItem(const char *ip, const char *port, const char *hostname, const char *version, ServerType type); + void print(); + const char *getString(); + const char *getName(); + const char *getVersion(); + const char *getRegtime(); + const char *getGuid(); +}; + +//============================================================================= + +class CServerList : public CList +{ +public: + void insertPlayer(CServerItem *server, CPlayerItem *player); + void removePlayer(CServerItem *server, CPlayerItem *player); + int insert(const char *ip, const char *port, const char *hostname, const char *version, ServerType type); + int insert(CServerItem *server); + int remove(const char *ip, const char *port, const char *hostname, const char *version, ServerType type); + int remove(CServerItem *server); + void show(); +}; + +#endif diff --git a/stats.cpp b/stats.cpp new file mode 100644 index 0000000..741d826 --- /dev/null +++ b/stats.cpp @@ -0,0 +1,147 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifdef __GNUC__ +#include +#endif +#include // strcat(),... +#include "common.h" +#include "ipcs.h" +#include "stats.h" + +//============================================================================= + +/* +** CServerStats() +*/ +CServerStats::CServerStats() +{ + uptime = time(NULL); + num_connections = 0; + num_http_con = 0; + num_servers = 0; + snprintf(motd, sizeof motd, "%s%s", "Welcome to the SRB2 Master Server!
", SERVERMOTDTEXT); + num_add = 0; + num_removal = 0; + num_retrieval = 0; + num_autoremoval = 0; + num_badconnection = 0; + for (int i = 0; i < NUMLASTSERVERS; i++) + { + strcpy(last_server[i].ip, "0.0.0.0"); + strcpy(last_server[i].name, "Non-Existing SRB2 server"); + strcpy(last_server[i].version, "0.0.0"); + } + snprintf(version, sizeof version, "%s %s", __DATE__, __TIME__); +} + +/* +** ~CServerStats() +*/ +CServerStats::~CServerStats() +{ +} + +/* +** getUptime() +*/ +const char *CServerStats::getUptime() +{ + char *res = ctime(&uptime); + res[strlen(res)-1] = '\0'; // remove the '\n' at the end + return res; +} + +/* +** getHours() +*/ +int CServerStats::getHours() +{ + return (int)(((time(NULL) - uptime)/(60*60))%24); +} + +/* +** getDays() +*/ +int CServerStats::getDays() +{ + return (int)((time(NULL) - uptime)/(60*60*24)); +} + +/* +** getMotd() +*/ +const char *CServerStats::getMotd() +{ + return motd; +} + +/* +** getLastServers() +*/ +const char *CServerStats::getLastServers() +{ + static char res[170*NUMLASTSERVERS]; + + tzset(); + res[0] = '\0'; + for (int i = 0; i < NUMLASTSERVERS; i++) + { + char str[170]; + char *ct; + + ct = ctime(&last_time[i]); + ct[strlen(ct)-1] = '\0'; // replace \n with a \0 + snprintf(str, sizeof str, + "Address: %15s Name: %-31s v%-7s (%s %s)\n000043210000", + last_server[i].ip, last_server[i].name, last_server[i].version, ct, tzname[0]); + strcat(res, str); + } + + return res; +} + +/* +** getVersion() +*/ +const char *CServerStats::getVersion() +{ + return version; +} + +/* +** putMotd() +*/ +void CServerStats::putMotd(char *motd) +{ + strcpy(this->motd, motd); +} + +/* +** putLastServer(): +*/ +void CServerStats::putLastServer(msg_server_t *server) +{ + for (int i = NUMLASTSERVERS-1; i > 0; i--) + { + memcpy(&last_server[i], &last_server[i-1], + sizeof (msg_server_t)); + last_time[i] = last_time[i-1]; + } + memcpy(&last_server[0], server, sizeof (msg_server_t)); + last_time[0] = time(NULL); +} diff --git a/stats.h b/stats.h new file mode 100644 index 0000000..4494a76 --- /dev/null +++ b/stats.h @@ -0,0 +1,68 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright (C) 2000 by DooM Legacy Team. +// +// This program 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. +// +// This program 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. +// +//----------------------------------------------------------------------------- + +#ifndef _STATS_H_ +#define _STATS_H_ +#ifndef _IPCS_H_ +#error "You forget to include ipcs.h" +#endif +#include + +#define NUMLASTSERVERS 6 +#define SERVERMOTDTEXT "No Clans, advertising websites, Chat, Hangout, Hacked, or Role Playing Servers Allowed" + + +// ================================ DEFINITIONS =============================== + +class CServerStats +{ + time_t uptime; // master server uptime + char motd[2048]; // increase that if not enough + msg_server_t last_server[NUMLASTSERVERS]; // keep last 6 named registered servers + time_t last_time[NUMLASTSERVERS]; // keep date/time of registration of those servers + char version[32]; // master server version + +public: + int num_connections; + int num_http_con; + int num_text_con; + int num_RSS92_con; + int num_RSS10_con; + int num_servers; + int num_add; + int num_removal; + int num_retrieval; + int num_autoremoval; + int num_badconnection; + + CServerStats(); + ~CServerStats(); + const char *getUptime(); + int getHours(); + int getDays(); + const char *getMotd(); + const char *getLastServers(); + const char *getVersion(); + void putMotd(char *); + void putLastServer(msg_server_t *); +}; + +// ================================== PROTOS ================================== + +// ================================== EXTERNS ================================= + +#endif