diff options
author | Perttu Ahola <celeron55@gmail.com> | 2011-05-21 01:11:58 +0300 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2011-05-21 01:11:58 +0300 |
commit | 76e241392d2ad232b751c74df7cba9e5acfa7855 (patch) | |
tree | e58f72b379c1a4e1b86a4e85c6c0007109635b32 | |
parent | 73a18b9271bebddfe6716529bb13cad30869bc16 (diff) | |
parent | 52d857cf1527948e1db5eead457e51b6741de8a2 (diff) | |
download | minetest-76e241392d2ad232b751c74df7cba9e5acfa7855.tar.gz minetest-76e241392d2ad232b751c74df7cba9e5acfa7855.tar.bz2 minetest-76e241392d2ad232b751c74df7cba9e5acfa7855.zip |
Merge: New map directory structure and player passwords
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/base64.cpp | 123 | ||||
-rw-r--r-- | src/base64.h | 4 | ||||
-rw-r--r-- | src/client.cpp | 21 | ||||
-rw-r--r-- | src/client.h | 9 | ||||
-rw-r--r-- | src/clientserver.h | 6 | ||||
-rw-r--r-- | src/filesys.cpp | 19 | ||||
-rw-r--r-- | src/filesys.h | 3 | ||||
-rw-r--r-- | src/game.cpp | 22 | ||||
-rw-r--r-- | src/game.h | 1 | ||||
-rw-r--r-- | src/guiMainMenu.cpp | 45 | ||||
-rw-r--r-- | src/guiMainMenu.h | 1 | ||||
-rw-r--r-- | src/main.cpp | 28 | ||||
-rw-r--r-- | src/map.cpp | 124 | ||||
-rw-r--r-- | src/map.h | 12 | ||||
-rw-r--r-- | src/player.cpp | 6 | ||||
-rw-r--r-- | src/player.h | 13 | ||||
-rw-r--r-- | src/server.cpp | 59 | ||||
-rw-r--r-- | src/server.h | 6 | ||||
-rw-r--r-- | src/sha1.cpp | 207 | ||||
-rw-r--r-- | src/sha1.h | 51 |
21 files changed, 680 insertions, 82 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 74396cee9..58596a101 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,6 +95,8 @@ set(minetest_SRCS tile.cpp game.cpp main.cpp + sha1.cpp + base64.cpp ) # Server sources diff --git a/src/base64.cpp b/src/base64.cpp new file mode 100644 index 000000000..2a863d161 --- /dev/null +++ b/src/base64.cpp @@ -0,0 +1,123 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include <iostream> + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 000000000..65d5db8b2 --- /dev/null +++ b/src/base64.h @@ -0,0 +1,4 @@ +#include <string> + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); diff --git a/src/client.cpp b/src/client.cpp index ce862a002..e2cda97c1 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -68,6 +68,7 @@ void * MeshUpdateThread::Thread() Client::Client( IrrlichtDevice *device, const char *playername, + std::string password, MapDrawControl &control): m_mesh_update_thread(), m_env( @@ -83,7 +84,9 @@ Client::Client( m_server_ser_ver(SER_FMT_VER_INVALID), m_inventory_updated(false), m_time_of_day(0), - m_map_seed(0) + m_map_seed(0), + m_password(password), + m_access_denied(false) { m_packetcounter_timer = 0.0; m_delete_unused_sectors_timer = 0.0; @@ -299,11 +302,14 @@ void Client::step(float dtime) // [0] u16 TOSERVER_INIT // [2] u8 SER_FMT_VER_HIGHEST // [3] u8[20] player_name - SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE); + // [23] u8[28] password + SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE); writeU16(&data[0], TOSERVER_INIT); writeU8(&data[2], SER_FMT_VER_HIGHEST); memset((char*)&data[3], 0, PLAYERNAME_SIZE); snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName()); + snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str()); + // Send as unreliable Send(0, data, false); } @@ -597,7 +603,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) return; } - + + if(command == TOCLIENT_ACCESS_DENIED) + { + // The server didn't like our password. Note, this needs + // to be processed even if the serialisation format has + // not been agreed yet, the same as TOCLIENT_INIT. + m_access_denied = true; + return; + } + if(ser_version == SER_FMT_VER_INVALID) { dout_client<<DTIME<<"WARNING: Client: Server serialization" diff --git a/src/client.h b/src/client.h index ee73cc42c..222f83ab7 100644 --- a/src/client.h +++ b/src/client.h @@ -207,6 +207,7 @@ public: Client( IrrlichtDevice *device, const char *playername, + std::string password, MapDrawControl &control ); @@ -377,6 +378,11 @@ public: // Get event from queue. CE_NONE is returned if queue is empty. ClientEvent getClientEvent(); + inline bool accessDenied() + { + return m_access_denied; + } + private: // Virtual methods from con::PeerHandler @@ -430,6 +436,9 @@ private: // The seed returned by the server in TOCLIENT_INIT is stored here u64 m_map_seed; + std::string m_password; + bool m_access_denied; + InventoryContext m_inventory_context; Queue<ClientEvent> m_client_event_queue; diff --git a/src/clientserver.h b/src/clientserver.h index 46ffa5eab..a64a11f08 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -150,6 +150,11 @@ enum ToClientCommand f1000 player pitch f1000 player yaw */ + + TOCLIENT_ACCESS_DENIED = 0x35, + /* + u16 command + */ }; enum ToServerCommand @@ -161,6 +166,7 @@ enum ToServerCommand [0] u16 TOSERVER_INIT [2] u8 SER_FMT_VER_HIGHEST [3] u8[20] player_name + [23] u8[28] password */ TOSERVER_INIT2 = 0x11, diff --git a/src/filesys.cpp b/src/filesys.cpp index 287090e8a..8248a13d4 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -290,5 +290,24 @@ bool RecursiveDeleteContent(std::string path) return true; } +bool CreateAllDirs(std::string path) +{ + + size_t pos; + std::vector<std::string> tocreate; + std::string basepath = path; + while(!PathExists(basepath)) + { + tocreate.push_back(basepath); + pos = basepath.rfind('/'); + if(pos == std::string::npos) + return false; + basepath = basepath.substr(0,pos); + } + for(int i=tocreate.size()-1;i>=0;i--) + CreateDir(tocreate[i]); + return true; +} + } // namespace fs diff --git a/src/filesys.h b/src/filesys.h index 4dd90b84e..b74b34f3d 100644 --- a/src/filesys.h +++ b/src/filesys.h @@ -38,6 +38,9 @@ std::vector<DirListNode> GetDirListing(std::string path); // Returns true if already exists bool CreateDir(std::string path); +// Create all directories on the given path that don't already exist. +bool CreateAllDirs(std::string path); + bool PathExists(std::string path); // Only pass full paths to this one. True on success. diff --git a/src/game.cpp b/src/game.cpp index d90262060..69e673fa4 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -634,6 +634,7 @@ void the_game( gui::IGUIFont* font, std::string map_dir, std::string playername, + std::string password, std::string address, u16 port, std::wstring &error_message @@ -672,7 +673,6 @@ void the_game( guienv->drawAll(); driver->endScene(); - std::cout<<DTIME<<"Creating server and client"<<std::endl; /* Create server. @@ -680,6 +680,7 @@ void the_game( */ SharedPtr<Server> server; if(address == ""){ + std::cout<<DTIME<<"Creating server"<<std::endl; server = new Server(map_dir); server->start(port); } @@ -688,7 +689,8 @@ void the_game( Create client */ - Client client(device, playername.c_str(), draw_control); + std::cout<<DTIME<<"Creating client"<<std::endl; + Client client(device, playername.c_str(), password, draw_control); Address connect_address(0,0,0,0, port); try{ @@ -727,6 +729,10 @@ void the_game( could_connect = true; break; } + if(client.accessDenied()) + { + break; + } // Wait for 10 seconds if(time_counter >= 10.0) { @@ -755,8 +761,16 @@ void the_game( if(could_connect == false) { - std::cout<<DTIME<<"Timed out."<<std::endl; - error_message = L"Connection timed out."; + if(client.accessDenied()) + { + error_message = L"Access denied. Check your password and try again."; + std::cout<<DTIME<<"Access denied."<<std::endl; + } + else + { + error_message = L"Connection timed out."; + std::cout<<DTIME<<"Timed out."<<std::endl; + } gui_loadingtext->remove(); return; } diff --git a/src/game.h b/src/game.h index 7cba1299e..eb289b8f2 100644 --- a/src/game.h +++ b/src/game.h @@ -67,6 +67,7 @@ void the_game( gui::IGUIFont* font, std::string map_dir, std::string playername, + std::string password, std::string address, u16 port, std::wstring &error_message diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index ac02f79e3..a30e006a6 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -164,30 +164,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); } - // Nickname + // Nickname + password { - core::rect<s32> rect(0, 0, 100, 20); - rect += topleft_client + v2s32(40, 50+6); - const wchar_t *text = L"Nickname"; + core::rect<s32> rect(0, 0, 110, 20); + rect += topleft_client + v2s32(35, 50+6); + const wchar_t *text = L"Name/Password"; Environment->addStaticText(text, rect, false, true, this, -1); } { - core::rect<s32> rect(0, 0, 250, 30); + core::rect<s32> rect(0, 0, 230, 30); rect += topleft_client + v2s32(160, 50); gui::IGUIElement *e = Environment->addEditBox(text_name.c_str(), rect, true, this, 258); if(text_name == L"") Environment->setFocus(e); } + { + core::rect<s32> rect(0, 0, 120, 30); + rect += topleft_client + v2s32(size_client.X-60-100, 50); + gui::IGUIEditBox *e = + Environment->addEditBox(L"", rect, true, this, 264); + e->setPasswordBox(true); + + } // Address + port { - core::rect<s32> rect(0, 0, 100, 20); - rect += topleft_client + v2s32(40, 100+6); - const wchar_t *text = L"Address + Port"; + core::rect<s32> rect(0, 0, 110, 20); + rect += topleft_client + v2s32(35, 100+6); + const wchar_t *text = L"Address/Port"; Environment->addStaticText(text, rect, false, true, this, -1); } { - core::rect<s32> rect(0, 0, 250, 30); + core::rect<s32> rect(0, 0, 230, 30); rect += topleft_client + v2s32(160, 100); gui::IGUIElement *e = Environment->addEditBox(text_address.c_str(), rect, true, this, 256); @@ -195,9 +203,9 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) Environment->setFocus(e); } { - core::rect<s32> rect(0, 0, 100, 30); + core::rect<s32> rect(0, 0, 120, 30); //rect += topleft_client + v2s32(160+250+20, 125); - rect += topleft_client + v2s32(size_client.X-40-100, 100); + rect += topleft_client + v2s32(size_client.X-60-100, 100); Environment->addEditBox(text_port.c_str(), rect, true, this, 257); } { @@ -208,13 +216,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) } { core::rect<s32> rect(0, 0, 250, 30); - rect += topleft_client + v2s32(40, 150); + rect += topleft_client + v2s32(35, 150); Environment->addCheckBox(fancy_trees, rect, this, 263, L"Fancy trees"); } { core::rect<s32> rect(0, 0, 250, 30); - rect += topleft_client + v2s32(40, 150+30); + rect += topleft_client + v2s32(35, 150+30); Environment->addCheckBox(smooth_lighting, rect, this, 262, L"Smooth Lighting"); } @@ -245,12 +253,12 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) // Server parameters { core::rect<s32> rect(0, 0, 250, 30); - rect += topleft_server + v2s32(40, 30); + rect += topleft_server + v2s32(35, 30); Environment->addCheckBox(creative_mode, rect, this, 259, L"Creative Mode"); } { core::rect<s32> rect(0, 0, 250, 30); - rect += topleft_server + v2s32(40, 60); + rect += topleft_server + v2s32(35, 60); Environment->addCheckBox(enable_damage, rect, this, 261, L"Enable Damage"); } // Map delete button @@ -297,6 +305,11 @@ void GUIMainMenu::acceptInput() m_data->name = e->getText(); } { + gui::IGUIElement *e = getElementFromId(264); + if(e != NULL) + m_data->password = e->getText(); + } + { gui::IGUIElement *e = getElementFromId(256); if(e != NULL) m_data->address = e->getText(); @@ -380,7 +393,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event) { switch(event.GUIEvent.Caller->getID()) { - case 256: case 257: case 258: + case 256: case 257: case 258: case 264: acceptInput(); quitMenu(); return true; diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h index 4999d68ba..edd519024 100644 --- a/src/guiMainMenu.h +++ b/src/guiMainMenu.h @@ -46,6 +46,7 @@ struct MainMenuData std::wstring address; std::wstring port; std::wstring name; + std::wstring password; bool fancy_trees; bool smooth_lighting; // Server options diff --git a/src/main.cpp b/src/main.cpp index 5d607b2d8..184643b9f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -325,6 +325,8 @@ Making it more portable: #include "materials.h"
#include "game.h"
#include "keycode.h"
+#include "sha1.h"
+#include "base64.h"
// This makes textures
ITextureSource *g_texturesource = NULL;
@@ -1170,6 +1172,7 @@ int main(int argc, char *argv[]) return 0;
}
+
/*
More parameters
*/
@@ -1324,11 +1327,15 @@ int main(int argc, char *argv[]) */
std::wstring error_message = L"";
+ // The password entered during the menu screen,
+ std::string password;
+
/*
Menu-game loop
*/
while(device->run() && kill == false)
{
+
// This is used for catching disconnects
try
{
@@ -1428,6 +1435,26 @@ int main(int argc, char *argv[]) }
playername = wide_to_narrow(menudata.name);
+
+ // Get an sha-1 hash of the player's name combined with
+ // the password entered. That's what the server uses as
+ // their password. (Exception : if the password field is
+ // blank, we send a blank password - this is for backwards
+ // compatibility with password-less players).
+ if(menudata.password.length() > 0)
+ {
+ std::string slt=playername + wide_to_narrow(menudata.password);
+ SHA1 *sha1 = new SHA1();
+ sha1->addBytes(slt.c_str(), slt.length());
+ unsigned char *digest = sha1->getDigest();
+ password = base64_encode(digest, 20);
+ free(digest);
+ }
+ else
+ {
+ password = "";
+ }
+
address = wide_to_narrow(menudata.address);
int newport = stoi(wide_to_narrow(menudata.port));
if(newport != 0)
@@ -1474,6 +1501,7 @@ int main(int argc, char *argv[]) font,
map_dir,
playername,
+ password,
address,
port,
error_message
diff --git a/src/map.cpp b/src/map.cpp index 63f01ddee..cd2ba9154 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -4875,9 +4875,9 @@ plan_b: return (s16)level; } -void ServerMap::createDir(std::string path) +void ServerMap::createDirs(std::string path) { - if(fs::CreateDir(path) == false) + if(fs::CreateAllDirs(path) == false) { m_dout<<DTIME<<"ServerMap: Failed to create directory " <<"\""<<path<<"\""<<std::endl; @@ -4885,29 +4885,52 @@ void ServerMap::createDir(std::string path) } } -std::string ServerMap::getSectorSubDir(v2s16 pos) +std::string ServerMap::getSectorDir(v2s16 pos, int layout) { char cc[9]; - snprintf(cc, 9, "%.4x%.4x", - (unsigned int)pos.X&0xffff, - (unsigned int)pos.Y&0xffff); + switch(layout) + { + case 1: + snprintf(cc, 9, "%.4x%.4x", + (unsigned int)pos.X&0xffff, + (unsigned int)pos.Y&0xffff); - return std::string(cc); -} + return m_savedir + "/sectors/" + cc; + case 2: + snprintf(cc, 9, "%.3x/%.3x", + (unsigned int)pos.X&0xfff, + (unsigned int)pos.Y&0xfff); -std::string ServerMap::getSectorDir(v2s16 pos) -{ - return m_savedir + "/sectors/" + getSectorSubDir(pos); + return m_savedir + "/sectors2/" + cc; + default: + assert(false); + } } v2s16 ServerMap::getSectorPos(std::string dirname) { - if(dirname.size() != 8) - throw InvalidFilenameException("Invalid sector directory name"); unsigned int x, y; - int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y); - if(r != 2) - throw InvalidFilenameException("Invalid sector directory name"); + int r; + size_t spos = dirname.rfind('/') + 1; + assert(spos != std::string::npos); + if(dirname.size() - spos == 8) + { + // Old layout + r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y); + } + else if(dirname.size() - spos == 3) + { + // New layout + r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y); + // Sign-extend the 12 bit values up to 16 bits... + if(x&0x800) x|=0xF000; + if(y&0x800) y|=0xF000; + } + else + { + assert(false); + } + assert(r == 2); v2s16 pos((s16)x, (s16)y); return pos; } @@ -5106,7 +5129,7 @@ void ServerMap::saveMapMeta() <<"seed="<<m_seed<<", chunksize="<<m_chunksize <<std::endl; - createDir(m_savedir); + createDirs(m_savedir); std::string fullpath = m_savedir + "/map_meta.txt"; std::ofstream os(fullpath.c_str(), std::ios_base::binary); @@ -5179,7 +5202,7 @@ void ServerMap::saveChunkMeta() dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of " <<count<<" chunks"<<std::endl; - createDir(m_savedir); + createDirs(m_savedir); std::string fullpath = m_savedir + "/chunk_meta"; std::ofstream os(fullpath.c_str(), std::ios_base::binary); @@ -5267,10 +5290,8 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector) u8 version = SER_FMT_VER_HIGHEST; // Get destination v2s16 pos = sector->getPos(); - createDir(m_savedir); - createDir(m_savedir+"/sectors"); std::string dir = getSectorDir(pos); - createDir(dir); + createDirs(dir); std::string fullpath = dir + "/meta"; std::ofstream o(fullpath.c_str(), std::ios_base::binary); @@ -5282,22 +5303,21 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector) sector->differs_from_disk = false; } -MapSector* ServerMap::loadSectorMeta(std::string dirname) +MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load) { DSTACK(__FUNCTION_NAME); // Get destination - v2s16 p2d = getSectorPos(dirname); - std::string dir = m_savedir + "/sectors/" + dirname; + v2s16 p2d = getSectorPos(sectordir); ServerMapSector *sector = NULL; - - std::string fullpath = dir + "/meta"; + + std::string fullpath = sectordir + "/meta"; std::ifstream is(fullpath.c_str(), std::ios_base::binary); if(is.good() == false) { // If the directory exists anyway, it probably is in some old // format. Just go ahead and create the sector. - if(fs::PathExists(dir)) + if(fs::PathExists(sectordir)) { dstream<<"ServerMap::loadSectorMeta(): Sector metafile " <<fullpath<<" doesn't exist but directory does." @@ -5307,12 +5327,16 @@ MapSector* ServerMap::loadSectorMeta(std::string dirname) m_sectors.insert(p2d, sector); } else + { throw FileNotGoodException("Cannot open sector metafile"); + } } else { sector = ServerMapSector::deSerialize (is, this, p2d, m_sectors); + if(save_after_load) + saveSectorMeta(sector); } sector->differs_from_disk = false; @@ -5323,14 +5347,31 @@ MapSector* ServerMap::loadSectorMeta(std::string dirname) bool ServerMap::loadSectorFull(v2s16 p2d) { DSTACK(__FUNCTION_NAME); - std::string sectorsubdir = getSectorSubDir(p2d); MapSector *sector = NULL; + // The directory layout we're going to load from. + // 1 - original sectors/xxxxzzzz/ + // 2 - new sectors2/xxx/zzz/ + // If we load from anything but the latest structure, we will + // immediately save to the new one, and remove the old. + int loadlayout = 1; + std::string sectordir1 = getSectorDir(p2d, 1); + std::string sectordir; + if(fs::PathExists(sectordir1)) + { + sectordir = sectordir1; + } + else + { + loadlayout = 2; + sectordir = getSectorDir(p2d, 2); + } + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out try{ - sector = loadSectorMeta(sectorsubdir); + sector = loadSectorMeta(sectordir, loadlayout != 2); } catch(InvalidFilenameException &e) { @@ -5349,7 +5390,7 @@ bool ServerMap::loadSectorFull(v2s16 p2d) Load blocks */ std::vector<fs::DirListNode> list2 = fs::GetDirListing - (m_savedir+"/sectors/"+sectorsubdir); + (sectordir); std::vector<fs::DirListNode>::iterator i2; for(i2=list2.begin(); i2!=list2.end(); i2++) { @@ -5357,16 +5398,25 @@ bool ServerMap::loadSectorFull(v2s16 p2d) if(i2->dir) continue; try{ - loadBlock(sectorsubdir, i2->name, sector); + loadBlock(sectordir, i2->name, sector, loadlayout != 2); } catch(InvalidFilenameException &e) { // This catches unknown crap in directory } } + + if(loadlayout != 2) + { + dstream<<"Sector converted to new layout - deleting "<< + sectordir1<<std::endl; + fs::RecursiveDelete(sectordir1); + } + return true; } + void ServerMap::saveBlock(MapBlock *block) { DSTACK(__FUNCTION_NAME); @@ -5386,12 +5436,9 @@ void ServerMap::saveBlock(MapBlock *block) // Get destination v3s16 p3d = block->getPos(); v2s16 p2d(p3d.X, p3d.Z); - createDir(m_savedir); - createDir(m_savedir+"/sectors"); std::string dir = getSectorDir(p2d); - createDir(dir); + createDirs(dir); - // Block file is map/sectors/xxxxxxxx/xxxx char cc[5]; snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff); std::string fullpath = dir + "/" + cc; @@ -5427,12 +5474,11 @@ void ServerMap::saveBlock(MapBlock *block) block->resetChangedFlag(); } -void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector) +void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load) { DSTACK(__FUNCTION_NAME); - // Block file is map/sectors/xxxxxxxx/xxxx - std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile; + std::string fullpath = sectordir+"/"+blockfile; try{ std::ifstream is(fullpath.c_str(), std::ios_base::binary); @@ -5496,7 +5542,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto */ // Save old format blocks in new format - if(version < SER_FMT_VER_HIGHEST) + if(version < SER_FMT_VER_HIGHEST || save_after_load) { saveBlock(block); } @@ -545,13 +545,9 @@ public: Misc. helper functions for fiddling with directory and file names when saving */ - void createDir(std::string path); - void createSaveDir(); - // returns something like "xxxxxxxx" - std::string getSectorSubDir(v2s16 pos); + void createDirs(std::string path); // returns something like "map/sectors/xxxxxxxx" - std::string getSectorDir(v2s16 pos); - std::string createSectorDir(v2s16 pos); + std::string getSectorDir(v2s16 pos, int layout = 2); // dirname: final directory name v2s16 getSectorPos(std::string dirname); v3s16 getBlockPos(std::string sectordir, std::string blockfile); @@ -572,7 +568,7 @@ public: // (no MapBlocks) // DEPRECATED? Sectors have no metadata anymore. void saveSectorMeta(ServerMapSector *sector); - MapSector* loadSectorMeta(std::string dirname); + MapSector* loadSectorMeta(std::string dirname, bool save_after_load); // Full load of a sector including all blocks. // returns true on success, false on failure. @@ -583,7 +579,7 @@ public: void saveBlock(MapBlock *block); // This will generate a sector with getSector if not found. - void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector); + void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false); // For debug printing virtual void PrintInfo(std::ostream &out); diff --git a/src/player.cpp b/src/player.cpp index 3f92e899c..539244709 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -87,6 +87,7 @@ Player::Player(): m_position(0,0,0) { updateName("<not set>"); + updatePassword(""); resetInventory(); } @@ -145,6 +146,7 @@ void Player::serialize(std::ostream &os) Settings args; args.setS32("version", 1); args.set("name", m_name); + args.set("password", m_password); args.setFloat("pitch", m_pitch); args.setFloat("yaw", m_yaw); args.setV3F("position", m_position); @@ -179,6 +181,10 @@ void Player::deSerialize(std::istream &is) //args.getS32("version"); std::string name = args.get("name"); updateName(name.c_str()); + std::string password = ""; + if(args.exists("password")) + password = args.get("password"); + updatePassword(password.c_str()); m_pitch = args.getFloat("pitch"); m_yaw = args.getFloat("yaw"); m_position = args.getV3F("position"); diff --git a/src/player.h b/src/player.h index be93766fd..925252e49 100644 --- a/src/player.h +++ b/src/player.h @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #define PLAYERNAME_SIZE 20 +#define PASSWORD_SIZE 28 // Maximum password length. Allows for + // base64-encoded SHA-1. #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.," @@ -121,6 +123,16 @@ public: return m_name; } + virtual void updatePassword(const char *password) + { + snprintf(m_password, PASSWORD_SIZE, "%s", password); + } + + const char * getPassword() + { + return m_password; + } + virtual bool isLocal() const = 0; virtual void updateLight(u8 light_at_pos) {}; @@ -157,6 +169,7 @@ public: protected: char m_name[PLAYERNAME_SIZE]; + char m_password[PASSWORD_SIZE]; f32 m_pitch; f32 m_yaw; v3f m_speed; diff --git a/src/server.cpp b/src/server.cpp index d211186eb..b5cb48a36 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1734,8 +1734,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // [0] u16 TOSERVER_INIT // [2] u8 SER_FMT_VER_HIGHEST // [3] u8[20] player_name + // [23] u8[28] password <--- can be sent without this, from old versions - if(datasize < 3) + if(datasize < 2+1+PLAYERNAME_SIZE) return; derr_server<<DTIME<<"Server: Got TOSERVER_INIT from " @@ -1767,17 +1768,41 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ // Get player name - const u32 playername_size = 20; - char playername[playername_size]; - for(u32 i=0; i<playername_size-1; i++) + char playername[PLAYERNAME_SIZE]; + for(u32 i=0; i<PLAYERNAME_SIZE-1; i++) { playername[i] = data[3+i]; } - playername[playername_size-1] = 0; - + playername[PLAYERNAME_SIZE-1] = 0; + + // Get password + char password[PASSWORD_SIZE]; + if(datasize == 2+1+PLAYERNAME_SIZE) + { + // old version - assume blank password + password[0] = 0; + } + else + { + for(u32 i=0; i<PASSWORD_SIZE-1; i++) + { + password[i] = data[23+i]; + } + password[PASSWORD_SIZE-1] = 0; + } + Player *checkplayer = m_env.getPlayer(playername); + if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password)) + { + derr_server<<DTIME<<"Server: peer_id="<<peer_id + <<": supplied invalid password for " + <<playername<<std::endl; + SendAccessDenied(m_con, peer_id); + return; + } + // Get player - Player *player = emergePlayer(playername, "", peer_id); - //Player *player = m_env.getPlayer(peer_id); + Player *player = emergePlayer(playername, password, peer_id); + /*{ // DEBUG: Test serialization @@ -3138,6 +3163,20 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) con.Send(peer_id, 0, data, true); } +void Server::SendAccessDenied(con::Connection &con, u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_ACCESS_DENIED); + + // Make data buffer + std::string s = os.str(); + SharedBuffer<u8> data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + /* Non-static send methods */ @@ -4052,8 +4091,7 @@ v3f findSpawnPos(ServerMap &map) ), BS); } -Player *Server::emergePlayer(const char *name, const char *password, - u16 peer_id) +Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id) { /* Try to get an existing player @@ -4099,6 +4137,7 @@ Player *Server::emergePlayer(const char *name, const char *password, //player->peer_id = PEER_ID_INEXISTENT; player->peer_id = peer_id; player->updateName(name); + player->updatePassword(password); /* Set player position diff --git a/src/server.h b/src/server.h index d8b47aef9..289f09618 100644 --- a/src/server.h +++ b/src/server.h @@ -436,6 +436,7 @@ private: */ static void SendHP(con::Connection &con, u16 peer_id, u8 hp); + static void SendAccessDenied(con::Connection &con, u16 peer_id); /* Non-static send methods @@ -476,11 +477,12 @@ private: /* Get a player from memory or creates one. If player is already connected, return NULL + The password is not checked here - it is only used to + set the password if a new player is created. Call with env and con locked. */ - Player *emergePlayer(const char *name, const char *password, - u16 peer_id); + Player *emergePlayer(const char *name, const char *password, u16 peer_id); /* Update water pressure. diff --git a/src/sha1.cpp b/src/sha1.cpp new file mode 100644 index 000000000..98180adc7 --- /dev/null +++ b/src/sha1.cpp @@ -0,0 +1,207 @@ +/* sha1.cpp + +Copyright (c) 2005 Michael D. Leonhard + +http://tamale.net/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "sha1.h" + +// print out memory in hexadecimal +void SHA1::hexPrinter( unsigned char* c, int l ) +{ + assert( c ); + assert( l > 0 ); + while( l > 0 ) + { + printf( " %02x", *c ); + l--; + c++; + } +} + +// circular left bit rotation. MSB wraps around to LSB +Uint32 SHA1::lrot( Uint32 x, int bits ) +{ + return (x<<bits) | (x>>(32 - bits)); +}; + +// Save a 32-bit unsigned integer to memory, in big-endian order +void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num ) +{ + assert( byte ); + byte[0] = (unsigned char)(num>>24); + byte[1] = (unsigned char)(num>>16); + byte[2] = (unsigned char)(num>>8); + byte[3] = (unsigned char)num; +} + + +// Constructor ******************************************************* +SHA1::SHA1() +{ + // make sure that the data type is the right size + assert( sizeof( Uint32 ) * 5 == 20 ); + + // initialize + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + unprocessedBytes = 0; + size = 0; +} + +// Destructor ******************************************************** +SHA1::~SHA1() +{ + // erase data + H0 = H1 = H2 = H3 = H4 = 0; + for( int c = 0; c < 64; c++ ) bytes[c] = 0; + unprocessedBytes = size = 0; +} + +// process *********************************************************** +void SHA1::process() +{ + assert( unprocessedBytes == 64 ); + //printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" ); + int t; + Uint32 a, b, c, d, e, K, f, W[80]; + // starting values + a = H0; + b = H1; + c = H2; + d = H3; + e = H4; + // copy and expand the message block + for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24) + +(bytes[t*4 + 1] << 16) + +(bytes[t*4 + 2] << 8) + + bytes[t*4 + 3]; + for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 ); + + /* main loop */ + Uint32 temp; + for( t = 0; t < 80; t++ ) + { + if( t < 20 ) { + K = 0x5a827999; + f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~ + } else if( t < 40 ) { + K = 0x6ed9eba1; + f = b ^ c ^ d; + } else if( t < 60 ) { + K = 0x8f1bbcdc; + f = (b & c) | (b & d) | (c & d); + } else { + K = 0xca62c1d6; + f = b ^ c ^ d; + } + temp = lrot(a,5) + f + e + W[t] + K; + e = d; + d = c; + c = lrot(b,30); + b = a; + a = temp; + //printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e ); + } + /* add variables */ + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += e; + //printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 ); + /* all bytes have been processed */ + unprocessedBytes = 0; +} + +// addBytes ********************************************************** +void SHA1::addBytes( const char* data, int num ) +{ + assert( data ); + assert( num > 0 ); + // add these bytes to the running total + size += num; + // repeat until all data is processed + while( num > 0 ) + { + // number of bytes required to complete block + int needed = 64 - unprocessedBytes; + assert( needed > 0 ); + // number of bytes to copy (use smaller of two) + int toCopy = (num < needed) ? num : needed; + // Copy the bytes + memcpy( bytes + unprocessedBytes, data, toCopy ); + // Bytes have been copied + num -= toCopy; + data += toCopy; + unprocessedBytes += toCopy; + + // there is a full block + if( unprocessedBytes == 64 ) process(); + } +} + +// digest ************************************************************ +unsigned char* SHA1::getDigest() +{ + // save the message size + Uint32 totalBitsL = size << 3; + Uint32 totalBitsH = size >> 29; + // add 0x80 to the message + addBytes( "\x80", 1 ); + + unsigned char footer[64] = { + 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, 0 }; + // block has no room for 8-byte filesize, so finish it + if( unprocessedBytes > 56 ) + addBytes( (char*)footer, 64 - unprocessedBytes); + assert( unprocessedBytes <= 56 ); + // how many zeros do we need + int neededZeros = 56 - unprocessedBytes; + // store file size (in bits) in big-endian format + storeBigEndianUint32( footer + neededZeros , totalBitsH ); + storeBigEndianUint32( footer + neededZeros + 4, totalBitsL ); + // finish the final block + addBytes( (char*)footer, neededZeros + 8 ); + // allocate memory for the digest bytes + unsigned char* digest = (unsigned char*)malloc( 20 ); + // copy the digest bytes + storeBigEndianUint32( digest, H0 ); + storeBigEndianUint32( digest + 4, H1 ); + storeBigEndianUint32( digest + 8, H2 ); + storeBigEndianUint32( digest + 12, H3 ); + storeBigEndianUint32( digest + 16, H4 ); + // return the digest + return digest; +} diff --git a/src/sha1.h b/src/sha1.h new file mode 100644 index 000000000..c04947373 --- /dev/null +++ b/src/sha1.h @@ -0,0 +1,51 @@ +/* sha1.h + +Copyright (c) 2005 Michael D. Leonhard + +http://tamale.net/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#ifndef SHA1_HEADER +typedef unsigned int Uint32; + +class SHA1 +{ + private: + // fields + Uint32 H0, H1, H2, H3, H4; + unsigned char bytes[64]; + int unprocessedBytes; + Uint32 size; + void process(); + public: + SHA1(); + ~SHA1(); + void addBytes( const char* data, int num ); + unsigned char* getDigest(); + // utility methods + static Uint32 lrot( Uint32 x, int bits ); + static void storeBigEndianUint32( unsigned char* byte, Uint32 num ); + static void hexPrinter( unsigned char* c, int l ); +}; + +#define SHA1_HEADER +#endif |