Lower log level for unexpected behaviour
[oweals/minetest.git] / src / network / serverpackethandler.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "server.h"
21 #include "log.h"
22
23 #include "content_abm.h"
24 #include "content_sao.h"
25 #include "emerge.h"
26 #include "nodedef.h"
27 #include "player.h"
28 #include "rollback_interface.h"
29 #include "scripting_game.h"
30 #include "settings.h"
31 #include "tool.h"
32 #include "version.h"
33 #include "network/networkprotocol.h"
34 #include "network/serveropcodes.h"
35 #include "util/auth.h"
36 #include "util/base64.h"
37 #include "util/pointedthing.h"
38 #include "util/serialize.h"
39 #include "util/srp.h"
40
41 void Server::handleCommand_Deprecated(NetworkPacket* pkt)
42 {
43         infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
44                 << " not supported anymore" << std::endl;
45 }
46
47 void Server::handleCommand_Init(NetworkPacket* pkt)
48 {
49
50         if(pkt->getSize() < 1)
51                 return;
52
53         RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
54
55         std::string addr_s;
56         try {
57                 Address address = getPeerAddress(pkt->getPeerId());
58                 addr_s = address.serializeString();
59         }
60         catch (con::PeerNotFoundException &e) {
61                 /*
62                  * no peer for this packet found
63                  * most common reason is peer timeout, e.g. peer didn't
64                  * respond for some time, your server was overloaded or
65                  * things like that.
66                  */
67                 infostream << "Server::ProcessData(): Canceling: peer "
68                                 << pkt->getPeerId() << " not found" << std::endl;
69                 return;
70         }
71
72         // If net_proto_version is set, this client has already been handled
73         if (client->getState() > CS_Created) {
74                 verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
75                                 << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
76                 return;
77         }
78
79         verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
80                         << pkt->getPeerId() << ")" << std::endl;
81
82         // Do not allow multiple players in simple singleplayer mode.
83         // This isn't a perfect way to do it, but will suffice for now
84         if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
85                 infostream << "Server: Not allowing another client (" << addr_s
86                                 << ") to connect in simple singleplayer mode" << std::endl;
87                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER);
88                 return;
89         }
90
91         // First byte after command is maximum supported
92         // serialization version
93         u8 client_max;
94         u16 supp_compr_modes;
95         u16 min_net_proto_version = 0;
96         u16 max_net_proto_version;
97         std::string playerName;
98
99         *pkt >> client_max >> supp_compr_modes >> min_net_proto_version
100                         >> max_net_proto_version >> playerName;
101
102         u8 our_max = SER_FMT_VER_HIGHEST_READ;
103         // Use the highest version supported by both
104         u8 depl_serial_v = std::min(client_max, our_max);
105         // If it's lower than the lowest supported, give up.
106         if (depl_serial_v < SER_FMT_VER_LOWEST)
107                 depl_serial_v = SER_FMT_VER_INVALID;
108
109         if (depl_serial_v == SER_FMT_VER_INVALID) {
110                 actionstream << "Server: A mismatched client tried to connect from "
111                                 << addr_s << std::endl;
112                 infostream<<"Server: Cannot negotiate serialization version with "
113                                 << addr_s << std::endl;
114                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
115                 return;
116         }
117
118         client->setPendingSerializationVersion(depl_serial_v);
119
120         /*
121                 Read and check network protocol version
122         */
123
124         u16 net_proto_version = 0;
125
126         // Figure out a working version if it is possible at all
127         if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
128                         min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
129                 // If maximum is larger than our maximum, go with our maximum
130                 if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
131                         net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
132                 // Else go with client's maximum
133                 else
134                         net_proto_version = max_net_proto_version;
135         }
136
137         verbosestream << "Server: " << addr_s << ": Protocol version: min: "
138                         << min_net_proto_version << ", max: " << max_net_proto_version
139                         << ", chosen: " << net_proto_version << std::endl;
140
141         client->net_proto_version = net_proto_version;
142
143         // On this handler at least protocol version 25 is required
144         if (net_proto_version < 25 ||
145                         net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
146                         net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
147                 actionstream << "Server: A mismatched client tried to connect from "
148                                 << addr_s << std::endl;
149                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
150                 return;
151         }
152
153         if (g_settings->getBool("strict_protocol_version_checking")) {
154                 if (net_proto_version != LATEST_PROTOCOL_VERSION) {
155                         actionstream << "Server: A mismatched (strict) client tried to "
156                                         << "connect from " << addr_s << std::endl;
157                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
158                         return;
159                 }
160         }
161
162         /*
163                 Validate player name
164         */
165         const char* playername = playerName.c_str();
166
167         if (playerName.size() > PLAYERNAME_SIZE) {
168                 actionstream << "Server: Player with an too long name "
169                                 << "tried to connect from " << addr_s << std::endl;
170                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
171                 return;
172         }
173
174         if (string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS) == false) {
175                 actionstream << "Server: Player with an invalid name "
176                                 << "tried to connect from " << addr_s << std::endl;
177                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
178                 return;
179         }
180
181         m_clients.setPlayerName(pkt->getPeerId(), playername);
182         //TODO (later) case insensitivity
183
184         std::string legacyPlayerNameCasing = playerName;
185
186         if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
187                 actionstream << "Server: Player with the name \"singleplayer\" "
188                                 << "tried to connect from " << addr_s << std::endl;
189                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
190                 return;
191         }
192
193         {
194                 std::string reason;
195                 if(m_script->on_prejoinplayer(playername, addr_s, reason)) {
196                         actionstream << "Server: Player with the name \"" << playerName << "\" "
197                                         << "tried to connect from " << addr_s << " "
198                                         << "but it was disallowed for the following reason: "
199                                         << reason << std::endl;
200                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
201                                         reason.c_str());
202                         return;
203                 }
204         }
205
206         infostream << "Server: New connection: \"" << playerName << "\" from "
207                         << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
208
209         // Enforce user limit.
210         // Don't enforce for users that have some admin right
211         if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
212                         !checkPriv(playername, "server") &&
213                         !checkPriv(playername, "ban") &&
214                         !checkPriv(playername, "privs") &&
215                         !checkPriv(playername, "password") &&
216                         playername != g_settings->get("name")) {
217                 actionstream << "Server: " << playername << " tried to join from "
218                                 << addr_s << ", but there" << " are already max_users="
219                                 << g_settings->getU16("max_users") << " players." << std::endl;
220                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
221                 return;
222         }
223
224         /*
225                 Compose auth methods for answer
226         */
227         std::string encpwd; // encrypted Password field for the user
228         bool has_auth = m_script->getAuth(playername, &encpwd, NULL);
229         u32 auth_mechs = 0;
230
231         client->chosen_mech = AUTH_MECHANISM_NONE;
232
233         if (has_auth) {
234                 std::vector<std::string> pwd_components = str_split(encpwd, '#');
235                 if (pwd_components.size() == 4) {
236                         if (pwd_components[1] == "1") { // 1 means srp
237                                 auth_mechs |= AUTH_MECHANISM_SRP;
238                                 client->enc_pwd = encpwd;
239                         } else {
240                                 actionstream << "User " << playername
241                                         << " tried to log in, but password field"
242                                         << " was invalid (unknown mechcode)." << std::endl;
243                                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
244                                 return;
245                         }
246                 } else if (base64_is_valid(encpwd)) {
247                         auth_mechs |= AUTH_MECHANISM_LEGACY_PASSWORD;
248                         client->enc_pwd = encpwd;
249                 } else {
250                         actionstream << "User " << playername
251                                 << " tried to log in, but password field"
252                                 << " was invalid (invalid base64)." << std::endl;
253                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
254                         return;
255                 }
256         } else {
257                 std::string default_password = g_settings->get("default_password");
258                 if (default_password.length() == 0) {
259                         auth_mechs |= AUTH_MECHANISM_FIRST_SRP;
260                 } else {
261                         // Take care of default passwords.
262                         client->enc_pwd = getSRPVerifier(playerName, default_password);
263                         auth_mechs |= AUTH_MECHANISM_SRP;
264                 }
265         }
266
267         /*
268                 Answer with a TOCLIENT_HELLO
269         */
270
271         verbosestream << "Sending TOCLIENT_HELLO with auth method field: "
272                 << auth_mechs << std::endl;
273
274         NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4
275                 + legacyPlayerNameCasing.size(), pkt->getPeerId());
276
277         u16 depl_compress_mode = NETPROTO_COMPRESSION_NONE;
278         resp_pkt << depl_serial_v << depl_compress_mode << net_proto_version
279                 << auth_mechs << legacyPlayerNameCasing;
280
281         Send(&resp_pkt);
282
283         client->allowed_auth_mechs = auth_mechs;
284         client->setDeployedCompressionMode(depl_compress_mode);
285
286         m_clients.event(pkt->getPeerId(), CSE_Hello);
287 }
288
289 void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
290 {
291         // [0] u8 SER_FMT_VER_HIGHEST_READ
292         // [1] u8[20] player_name
293         // [21] u8[28] password <--- can be sent without this, from old versions
294
295         if (pkt->getSize() < 1+PLAYERNAME_SIZE)
296                 return;
297
298         RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
299
300         std::string addr_s;
301         try {
302                 Address address = getPeerAddress(pkt->getPeerId());
303                 addr_s = address.serializeString();
304         }
305         catch (con::PeerNotFoundException &e) {
306                 /*
307                  * no peer for this packet found
308                  * most common reason is peer timeout, e.g. peer didn't
309                  * respond for some time, your server was overloaded or
310                  * things like that.
311                  */
312                 infostream << "Server::ProcessData(): Canceling: peer "
313                                 << pkt->getPeerId() << " not found" << std::endl;
314                 return;
315         }
316
317         // If net_proto_version is set, this client has already been handled
318         if (client->getState() > CS_Created) {
319                 verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
320                                 << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
321                 return;
322         }
323
324         verbosestream << "Server: Got TOSERVER_INIT_LEGACY from " << addr_s << " (peer_id="
325                         << pkt->getPeerId() << ")" << std::endl;
326
327         // Do not allow multiple players in simple singleplayer mode.
328         // This isn't a perfect way to do it, but will suffice for now
329         if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
330                 infostream << "Server: Not allowing another client (" << addr_s
331                                 << ") to connect in simple singleplayer mode" << std::endl;
332                 DenyAccess_Legacy(pkt->getPeerId(), L"Running in simple singleplayer mode.");
333                 return;
334         }
335
336         // First byte after command is maximum supported
337         // serialization version
338         u8 client_max;
339
340         *pkt >> client_max;
341
342         u8 our_max = SER_FMT_VER_HIGHEST_READ;
343         // Use the highest version supported by both
344         int deployed = std::min(client_max, our_max);
345         // If it's lower than the lowest supported, give up.
346         if (deployed < SER_FMT_VER_LOWEST)
347                 deployed = SER_FMT_VER_INVALID;
348
349         if (deployed == SER_FMT_VER_INVALID) {
350                 actionstream << "Server: A mismatched client tried to connect from "
351                                 << addr_s << std::endl;
352                 infostream<<"Server: Cannot negotiate serialization version with "
353                                 << addr_s << std::endl;
354                 DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
355                                 L"Your client's version is not supported.\n"
356                                 L"Server version is ")
357                                 + narrow_to_wide(g_version_string) + L"."
358                 );
359                 return;
360         }
361
362         client->setPendingSerializationVersion(deployed);
363
364         /*
365                 Read and check network protocol version
366         */
367
368         u16 min_net_proto_version = 0;
369         if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
370                 min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
371
372         // Use same version as minimum and maximum if maximum version field
373         // doesn't exist (backwards compatibility)
374         u16 max_net_proto_version = min_net_proto_version;
375         if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
376                 max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
377
378         // Start with client's maximum version
379         u16 net_proto_version = max_net_proto_version;
380
381         // Figure out a working version if it is possible at all
382         if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
383                         min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
384                 // If maximum is larger than our maximum, go with our maximum
385                 if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
386                         net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
387                 // Else go with client's maximum
388                 else
389                         net_proto_version = max_net_proto_version;
390         }
391
392         // The client will send up to date init packet, ignore this one
393         if (net_proto_version >= 25)
394                 return;
395
396         verbosestream << "Server: " << addr_s << ": Protocol version: min: "
397                         << min_net_proto_version << ", max: " << max_net_proto_version
398                         << ", chosen: " << net_proto_version << std::endl;
399
400         client->net_proto_version = net_proto_version;
401
402         if (net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
403                         net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
404                 actionstream << "Server: A mismatched client tried to connect from "
405                                 << addr_s << std::endl;
406                 DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
407                                 L"Your client's version is not supported.\n"
408                                 L"Server version is ")
409                                 + narrow_to_wide(g_version_string) + L",\n"
410                                 + L"server's PROTOCOL_VERSION is "
411                                 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
412                                 + L"..."
413                                 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
414                                 + L", client's PROTOCOL_VERSION is "
415                                 + narrow_to_wide(itos(min_net_proto_version))
416                                 + L"..."
417                                 + narrow_to_wide(itos(max_net_proto_version))
418                 );
419                 return;
420         }
421
422         if (g_settings->getBool("strict_protocol_version_checking")) {
423                 if (net_proto_version != LATEST_PROTOCOL_VERSION) {
424                         actionstream << "Server: A mismatched (strict) client tried to "
425                                         << "connect from " << addr_s << std::endl;
426                         DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
427                                         L"Your client's version is not supported.\n"
428                                         L"Server version is ")
429                                         + narrow_to_wide(g_version_string) + L",\n"
430                                         + L"server's PROTOCOL_VERSION (strict) is "
431                                         + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
432                                         + L", client's PROTOCOL_VERSION is "
433                                         + narrow_to_wide(itos(min_net_proto_version))
434                                         + L"..."
435                                         + narrow_to_wide(itos(max_net_proto_version))
436                         );
437                         return;
438                 }
439         }
440
441         /*
442                 Set up player
443         */
444         char playername[PLAYERNAME_SIZE];
445         unsigned int playername_length = 0;
446         for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
447                 playername[playername_length] = pkt->getChar(1+playername_length);
448                 if (pkt->getChar(1+playername_length) == 0)
449                         break;
450         }
451
452         if (playername_length == PLAYERNAME_SIZE) {
453                 actionstream << "Server: Player with name exceeding max length "
454                                 << "tried to connect from " << addr_s << std::endl;
455                 DenyAccess_Legacy(pkt->getPeerId(), L"Name too long");
456                 return;
457         }
458
459
460         if (playername[0]=='\0') {
461                 actionstream << "Server: Player with an empty name "
462                                 << "tried to connect from " << addr_s << std::endl;
463                 DenyAccess_Legacy(pkt->getPeerId(), L"Empty name");
464                 return;
465         }
466
467         if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
468                 actionstream << "Server: Player with an invalid name "
469                                 << "tried to connect from " << addr_s << std::endl;
470                 DenyAccess_Legacy(pkt->getPeerId(), L"Name contains unallowed characters");
471                 return;
472         }
473
474         if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
475                 actionstream << "Server: Player with the name \"singleplayer\" "
476                                 << "tried to connect from " << addr_s << std::endl;
477                 DenyAccess_Legacy(pkt->getPeerId(), L"Name is not allowed");
478                 return;
479         }
480
481         {
482                 std::string reason;
483                 if (m_script->on_prejoinplayer(playername, addr_s, reason)) {
484                         actionstream << "Server: Player with the name \"" << playername << "\" "
485                                         << "tried to connect from " << addr_s << " "
486                                         << "but it was disallowed for the following reason: "
487                                         << reason << std::endl;
488                         DenyAccess_Legacy(pkt->getPeerId(), narrow_to_wide(reason.c_str()));
489                         return;
490                 }
491         }
492
493         infostream<<"Server: New connection: \""<<playername<<"\" from "
494                         <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
495
496         // Get password
497         char given_password[PASSWORD_SIZE];
498         if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
499                 // old version - assume blank password
500                 given_password[0] = 0;
501         }
502         else {
503                 for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
504                         given_password[i] = pkt->getChar(21 + i);
505                 }
506                 given_password[PASSWORD_SIZE - 1] = 0;
507         }
508
509         if (!base64_is_valid(given_password)) {
510                 actionstream << "Server: " << playername
511                                 << " supplied invalid password hash" << std::endl;
512                 DenyAccess_Legacy(pkt->getPeerId(), L"Invalid password hash");
513                 return;
514         }
515
516         // Enforce user limit.
517         // Don't enforce for users that have some admin right
518         if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
519                         !checkPriv(playername, "server") &&
520                         !checkPriv(playername, "ban") &&
521                         !checkPriv(playername, "privs") &&
522                         !checkPriv(playername, "password") &&
523                         playername != g_settings->get("name")) {
524                 actionstream << "Server: " << playername << " tried to join, but there"
525                                 << " are already max_users="
526                                 << g_settings->getU16("max_users") << " players." << std::endl;
527                 DenyAccess_Legacy(pkt->getPeerId(), L"Too many users.");
528                 return;
529         }
530
531         std::string checkpwd; // Password hash to check against
532         bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
533
534         // If no authentication info exists for user, create it
535         if (!has_auth) {
536                 if (!isSingleplayer() &&
537                                 g_settings->getBool("disallow_empty_password") &&
538                                 std::string(given_password) == "") {
539                         actionstream << "Server: " << playername
540                                         << " supplied empty password" << std::endl;
541                         DenyAccess_Legacy(pkt->getPeerId(), L"Empty passwords are "
542                                         L"disallowed. Set a password and try again.");
543                         return;
544                 }
545                 std::string raw_default_password =
546                         g_settings->get("default_password");
547                 std::string initial_password =
548                         translatePassword(playername, raw_default_password);
549
550                 // If default_password is empty, allow any initial password
551                 if (raw_default_password.length() == 0)
552                         initial_password = given_password;
553
554                 m_script->createAuth(playername, initial_password);
555         }
556
557         has_auth = m_script->getAuth(playername, &checkpwd, NULL);
558
559         if (!has_auth) {
560                 actionstream << "Server: " << playername << " cannot be authenticated"
561                                 << " (auth handler does not work?)" << std::endl;
562                 DenyAccess_Legacy(pkt->getPeerId(), L"Not allowed to login");
563                 return;
564         }
565
566         if (given_password != checkpwd) {
567                 actionstream << "Server: " << playername << " supplied wrong password"
568                                 << std::endl;
569                 DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
570                 return;
571         }
572
573         RemotePlayer *player =
574                         static_cast<RemotePlayer*>(m_env->getPlayer(playername));
575
576         if (player && player->peer_id != 0) {
577                 errorstream << "Server: " << playername << ": Failed to emerge player"
578                                 << " (player allocated to an another client)" << std::endl;
579                 DenyAccess_Legacy(pkt->getPeerId(), L"Another client is connected with this "
580                                 L"name. If your client closed unexpectedly, try again in "
581                                 L"a minute.");
582         }
583
584         m_clients.setPlayerName(pkt->getPeerId(), playername);
585
586         /*
587                 Answer with a TOCLIENT_INIT
588         */
589
590         NetworkPacket resp_pkt(TOCLIENT_INIT_LEGACY, 1 + 6 + 8 + 4,
591                         pkt->getPeerId());
592
593         resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
594                         << (u64) m_env->getServerMap().getSeed()
595                         << g_settings->getFloat("dedicated_server_step");
596
597         Send(&resp_pkt);
598         m_clients.event(pkt->getPeerId(), CSE_InitLegacy);
599 }
600
601 void Server::handleCommand_Init2(NetworkPacket* pkt)
602 {
603         verbosestream << "Server: Got TOSERVER_INIT2 from "
604                         << pkt->getPeerId() << std::endl;
605
606         m_clients.event(pkt->getPeerId(), CSE_GotInit2);
607         u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
608
609
610         ///// begin compatibility code
611         PlayerSAO* playersao = NULL;
612         if (protocol_version <= 22) {
613                 playersao = StageTwoClientInit(pkt->getPeerId());
614
615                 if (playersao == NULL) {
616                         actionstream
617                                 << "TOSERVER_INIT2 stage 2 client init failed for peer "
618                                 << pkt->getPeerId() << std::endl;
619                         return;
620                 }
621         }
622         ///// end compatibility code
623
624         /*
625                 Send some initialization data
626         */
627
628         infostream << "Server: Sending content to "
629                         << getPlayerName(pkt->getPeerId()) << std::endl;
630
631         // Send player movement settings
632         SendMovement(pkt->getPeerId());
633
634         // Send item definitions
635         SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
636
637         // Send node definitions
638         SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
639
640         m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
641
642         // Send media announcement
643         sendMediaAnnouncement(pkt->getPeerId());
644
645         // Send detached inventories
646         sendDetachedInventories(pkt->getPeerId());
647
648         // Send time of day
649         u16 time = m_env->getTimeOfDay();
650         float time_speed = g_settings->getFloat("time_speed");
651         SendTimeOfDay(pkt->getPeerId(), time, time_speed);
652
653         ///// begin compatibility code
654         if (protocol_version <= 22) {
655                 m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
656                 m_script->on_joinplayer(playersao);
657         }
658         ///// end compatibility code
659
660         // Warnings about protocol version can be issued here
661         if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
662                 SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
663                                 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
664         }
665 }
666
667 void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
668 {
669         std::vector<std::string> tosend;
670         u16 numfiles;
671
672         *pkt >> numfiles;
673
674         infostream << "Sending " << numfiles << " files to "
675                         << getPlayerName(pkt->getPeerId()) << std::endl;
676         verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
677
678         for (u16 i = 0; i < numfiles; i++) {
679                 std::string name;
680
681                 *pkt >> name;
682
683                 tosend.push_back(name);
684                 verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
685                                 << name << std::endl;
686         }
687
688         sendRequestedMedia(pkt->getPeerId(), tosend);
689 }
690
691 void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
692 {
693 }
694
695 void Server::handleCommand_ClientReady(NetworkPacket* pkt)
696 {
697         u16 peer_id = pkt->getPeerId();
698         u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
699
700         // clients <= protocol version 22 did not send ready message,
701         // they're already initialized
702         if (peer_proto_ver <= 22) {
703                 infostream << "Client sent message not expected by a "
704                         << "client using protocol version <= 22,"
705                         << "disconnecting peer_id: " << peer_id << std::endl;
706                 m_con.DisconnectPeer(peer_id);
707                 return;
708         }
709
710         PlayerSAO* playersao = StageTwoClientInit(peer_id);
711
712         if (playersao == NULL) {
713                 actionstream
714                         << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
715                         << peer_id << std::endl;
716                 m_con.DisconnectPeer(peer_id);
717                 return;
718         }
719
720
721         if (pkt->getSize() < 8) {
722                 errorstream
723                         << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
724                         << peer_id << std::endl;
725                 m_con.DisconnectPeer(peer_id);
726                 return;
727         }
728
729         u8 major_ver, minor_ver, patch_ver, reserved;
730         std::string full_ver;
731         *pkt >> major_ver >> minor_ver >> patch_ver >> reserved >> full_ver;
732
733         m_clients.setClientVersion(
734                         peer_id, major_ver, minor_ver, patch_ver,
735                         full_ver);
736
737         m_clients.event(peer_id, CSE_SetClientReady);
738         m_script->on_joinplayer(playersao);
739 }
740
741 void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
742 {
743         if (pkt->getSize() < 1)
744                 return;
745
746         /*
747                 [0] u16 command
748                 [2] u8 count
749                 [3] v3s16 pos_0
750                 [3+6] v3s16 pos_1
751                 ...
752         */
753
754         u8 count;
755         *pkt >> count;
756
757         RemoteClient *client = getClient(pkt->getPeerId());
758
759         for (u16 i = 0; i < count; i++) {
760                 if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
761                         throw con::InvalidIncomingDataException
762                                 ("GOTBLOCKS length is too short");
763                 v3s16 p;
764
765                 *pkt >> p;
766
767                 client->GotBlock(p);
768         }
769 }
770
771 void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
772 {
773         if (pkt->getSize() < 12 + 12 + 4 + 4)
774                 return;
775
776         v3s32 ps, ss;
777         s32 f32pitch, f32yaw;
778
779         *pkt >> ps;
780         *pkt >> ss;
781         *pkt >> f32pitch;
782         *pkt >> f32yaw;
783
784         f32 pitch = (f32)f32pitch / 100.0;
785         f32 yaw = (f32)f32yaw / 100.0;
786         u32 keyPressed = 0;
787
788         if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
789                 *pkt >> keyPressed;
790
791         v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
792         v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
793
794         pitch = modulo360f(pitch);
795         yaw = modulo360f(yaw);
796
797         Player *player = m_env->getPlayer(pkt->getPeerId());
798         if (player == NULL) {
799                 errorstream << "Server::ProcessData(): Canceling: "
800                                 "No player for peer_id=" << pkt->getPeerId()
801                                 << " disconnecting peer!" << std::endl;
802                 m_con.DisconnectPeer(pkt->getPeerId());
803                 return;
804         }
805
806         // If player is dead we don't care of this packet
807         if (player->isDead()) {
808                 verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
809                         << " is dead. Ignoring packet";
810                 return;
811         }
812
813         PlayerSAO *playersao = player->getPlayerSAO();
814         if (playersao == NULL) {
815                 errorstream << "Server::ProcessData(): Canceling: "
816                                 "No player object for peer_id=" << pkt->getPeerId()
817                                 << " disconnecting peer!" << std::endl;
818                 m_con.DisconnectPeer(pkt->getPeerId());
819                 return;
820         }
821
822         player->setPosition(position);
823         player->setSpeed(speed);
824         player->setPitch(pitch);
825         player->setYaw(yaw);
826         player->keyPressed = keyPressed;
827         player->control.up = (keyPressed & 1);
828         player->control.down = (keyPressed & 2);
829         player->control.left = (keyPressed & 4);
830         player->control.right = (keyPressed & 8);
831         player->control.jump = (keyPressed & 16);
832         player->control.aux1 = (keyPressed & 32);
833         player->control.sneak = (keyPressed & 64);
834         player->control.LMB = (keyPressed & 128);
835         player->control.RMB = (keyPressed & 256);
836
837         if (playersao->checkMovementCheat()) {
838                 // Call callbacks
839                 m_script->on_cheat(playersao, "moved_too_fast");
840                 SendMovePlayer(pkt->getPeerId());
841         }
842 }
843
844 void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
845 {
846         if (pkt->getSize() < 1)
847                 return;
848
849         /*
850                 [0] u16 command
851                 [2] u8 count
852                 [3] v3s16 pos_0
853                 [3+6] v3s16 pos_1
854                 ...
855         */
856
857         u8 count;
858         *pkt >> count;
859
860         RemoteClient *client = getClient(pkt->getPeerId());
861
862         for (u16 i = 0; i < count; i++) {
863                 if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
864                         throw con::InvalidIncomingDataException
865                                 ("DELETEDBLOCKS length is too short");
866                 v3s16 p;
867                 *pkt >> p;
868
869                 client->SetBlockNotSent(p);
870         }
871 }
872
873 void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
874 {
875         Player *player = m_env->getPlayer(pkt->getPeerId());
876         if (player == NULL) {
877                 errorstream << "Server::ProcessData(): Canceling: "
878                                 "No player for peer_id=" << pkt->getPeerId()
879                                 << " disconnecting peer!" << std::endl;
880                 m_con.DisconnectPeer(pkt->getPeerId());
881                 return;
882         }
883
884         PlayerSAO *playersao = player->getPlayerSAO();
885         if (playersao == NULL) {
886                 errorstream << "Server::ProcessData(): Canceling: "
887                                 "No player object for peer_id=" << pkt->getPeerId()
888                                 << " disconnecting peer!" << std::endl;
889                 m_con.DisconnectPeer(pkt->getPeerId());
890                 return;
891         }
892
893         // Strip command and create a stream
894         std::string datastring(pkt->getString(0), pkt->getSize());
895         verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
896                 << std::endl;
897         std::istringstream is(datastring, std::ios_base::binary);
898         // Create an action
899         InventoryAction *a = InventoryAction::deSerialize(is);
900         if (a == NULL) {
901                 infostream << "TOSERVER_INVENTORY_ACTION: "
902                                 << "InventoryAction::deSerialize() returned NULL"
903                                 << std::endl;
904                 return;
905         }
906
907         // If something goes wrong, this player is to blame
908         RollbackScopeActor rollback_scope(m_rollback,
909                         std::string("player:")+player->getName());
910
911         /*
912                 Note: Always set inventory not sent, to repair cases
913                 where the client made a bad prediction.
914         */
915
916         /*
917                 Handle restrictions and special cases of the move action
918         */
919         if (a->getType() == IACTION_MOVE) {
920                 IMoveAction *ma = (IMoveAction*)a;
921
922                 ma->from_inv.applyCurrentPlayer(player->getName());
923                 ma->to_inv.applyCurrentPlayer(player->getName());
924
925                 setInventoryModified(ma->from_inv, false);
926                 setInventoryModified(ma->to_inv, false);
927
928                 bool from_inv_is_current_player =
929                         (ma->from_inv.type == InventoryLocation::PLAYER) &&
930                         (ma->from_inv.name == player->getName());
931
932                 bool to_inv_is_current_player =
933                         (ma->to_inv.type == InventoryLocation::PLAYER) &&
934                         (ma->to_inv.name == player->getName());
935
936                 /*
937                         Disable moving items out of craftpreview
938                 */
939                 if (ma->from_list == "craftpreview") {
940                         infostream << "Ignoring IMoveAction from "
941                                         << (ma->from_inv.dump()) << ":" << ma->from_list
942                                         << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
943                                         << " because src is " << ma->from_list << std::endl;
944                         delete a;
945                         return;
946                 }
947
948                 /*
949                         Disable moving items into craftresult and craftpreview
950                 */
951                 if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
952                         infostream << "Ignoring IMoveAction from "
953                                         << (ma->from_inv.dump()) << ":" << ma->from_list
954                                         << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
955                                         << " because dst is " << ma->to_list << std::endl;
956                         delete a;
957                         return;
958                 }
959
960                 // Disallow moving items in elsewhere than player's inventory
961                 // if not allowed to interact
962                 if (!checkPriv(player->getName(), "interact") &&
963                                 (!from_inv_is_current_player ||
964                                 !to_inv_is_current_player)) {
965                         infostream << "Cannot move outside of player's inventory: "
966                                         << "No interact privilege" << std::endl;
967                         delete a;
968                         return;
969                 }
970         }
971         /*
972                 Handle restrictions and special cases of the drop action
973         */
974         else if (a->getType() == IACTION_DROP) {
975                 IDropAction *da = (IDropAction*)a;
976
977                 da->from_inv.applyCurrentPlayer(player->getName());
978
979                 setInventoryModified(da->from_inv, false);
980
981                 /*
982                         Disable dropping items out of craftpreview
983                 */
984                 if (da->from_list == "craftpreview") {
985                         infostream << "Ignoring IDropAction from "
986                                         << (da->from_inv.dump()) << ":" << da->from_list
987                                         << " because src is " << da->from_list << std::endl;
988                         delete a;
989                         return;
990                 }
991
992                 // Disallow dropping items if not allowed to interact
993                 if (!checkPriv(player->getName(), "interact")) {
994                         delete a;
995                         return;
996                 }
997         }
998         /*
999                 Handle restrictions and special cases of the craft action
1000         */
1001         else if (a->getType() == IACTION_CRAFT) {
1002                 ICraftAction *ca = (ICraftAction*)a;
1003
1004                 ca->craft_inv.applyCurrentPlayer(player->getName());
1005
1006                 setInventoryModified(ca->craft_inv, false);
1007
1008                 //bool craft_inv_is_current_player =
1009                 //      (ca->craft_inv.type == InventoryLocation::PLAYER) &&
1010                 //      (ca->craft_inv.name == player->getName());
1011
1012                 // Disallow crafting if not allowed to interact
1013                 if (!checkPriv(player->getName(), "interact")) {
1014                         infostream << "Cannot craft: "
1015                                         << "No interact privilege" << std::endl;
1016                         delete a;
1017                         return;
1018                 }
1019         }
1020
1021         // Do the action
1022         a->apply(this, playersao, this);
1023         // Eat the action
1024         delete a;
1025
1026         SendInventory(playersao);
1027 }
1028
1029 void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
1030 {
1031         /*
1032                 u16 command
1033                 u16 length
1034                 wstring message
1035         */
1036         u16 len;
1037         *pkt >> len;
1038
1039         std::wstring message;
1040         for (u16 i = 0; i < len; i++) {
1041                 u16 tmp_wchar;
1042                 *pkt >> tmp_wchar;
1043
1044                 message += (wchar_t)tmp_wchar;
1045         }
1046
1047         Player *player = m_env->getPlayer(pkt->getPeerId());
1048         if (player == NULL) {
1049                 errorstream << "Server::ProcessData(): Canceling: "
1050                                 "No player for peer_id=" << pkt->getPeerId()
1051                                 << " disconnecting peer!" << std::endl;
1052                 m_con.DisconnectPeer(pkt->getPeerId());
1053                 return;
1054         }
1055
1056         // If something goes wrong, this player is to blame
1057         RollbackScopeActor rollback_scope(m_rollback,
1058                         std::string("player:")+player->getName());
1059
1060         // Get player name of this client
1061         std::wstring name = narrow_to_wide(player->getName());
1062
1063         // Run script hook
1064         bool ate = m_script->on_chat_message(player->getName(),
1065                         wide_to_narrow(message));
1066         // If script ate the message, don't proceed
1067         if (ate)
1068                 return;
1069
1070         // Line to send to players
1071         std::wstring line;
1072         // Whether to send to the player that sent the line
1073         bool send_to_sender_only = false;
1074
1075         // Commands are implemented in Lua, so only catch invalid
1076         // commands that were not "eaten" and send an error back
1077         if (message[0] == L'/') {
1078                 message = message.substr(1);
1079                 send_to_sender_only = true;
1080                 if (message.length() == 0)
1081                         line += L"-!- Empty command";
1082                 else
1083                         line += L"-!- Invalid command: " + str_split(message, L' ')[0];
1084         }
1085         else {
1086                 if (checkPriv(player->getName(), "shout")) {
1087                         line += L"<";
1088                         line += name;
1089                         line += L"> ";
1090                         line += message;
1091                 } else {
1092                         line += L"-!- You don't have permission to shout.";
1093                         send_to_sender_only = true;
1094                 }
1095         }
1096
1097         if (line != L"")
1098         {
1099                 /*
1100                         Send the message to sender
1101                 */
1102                 if (send_to_sender_only) {
1103                         SendChatMessage(pkt->getPeerId(), line);
1104                 }
1105                 /*
1106                         Send the message to others
1107                 */
1108                 else {
1109                         actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
1110
1111                         std::vector<u16> clients = m_clients.getClientIDs();
1112
1113                         for (std::vector<u16>::iterator i = clients.begin();
1114                                 i != clients.end(); ++i) {
1115                                 if (*i != pkt->getPeerId())
1116                                         SendChatMessage(*i, line);
1117                         }
1118                 }
1119         }
1120 }
1121
1122 void Server::handleCommand_Damage(NetworkPacket* pkt)
1123 {
1124         u8 damage;
1125
1126         *pkt >> damage;
1127
1128         Player *player = m_env->getPlayer(pkt->getPeerId());
1129         if (player == NULL) {
1130                 errorstream << "Server::ProcessData(): Canceling: "
1131                                 "No player for peer_id=" << pkt->getPeerId()
1132                                 << " disconnecting peer!" << std::endl;
1133                 m_con.DisconnectPeer(pkt->getPeerId());
1134                 return;
1135         }
1136
1137         PlayerSAO *playersao = player->getPlayerSAO();
1138         if (playersao == NULL) {
1139                 errorstream << "Server::ProcessData(): Canceling: "
1140                                 "No player object for peer_id=" << pkt->getPeerId()
1141                                 << " disconnecting peer!" << std::endl;
1142                 m_con.DisconnectPeer(pkt->getPeerId());
1143                 return;
1144         }
1145
1146         if (g_settings->getBool("enable_damage")) {
1147                 actionstream << player->getName() << " damaged by "
1148                                 << (int)damage << " hp at " << PP(player->getPosition() / BS)
1149                                 << std::endl;
1150
1151                 playersao->setHP(playersao->getHP() - damage);
1152                 SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
1153         }
1154 }
1155
1156 void Server::handleCommand_Breath(NetworkPacket* pkt)
1157 {
1158         u16 breath;
1159
1160         *pkt >> breath;
1161
1162         Player *player = m_env->getPlayer(pkt->getPeerId());
1163         if (player == NULL) {
1164                 errorstream << "Server::ProcessData(): Canceling: "
1165                                 "No player for peer_id=" << pkt->getPeerId()
1166                                 << " disconnecting peer!" << std::endl;
1167                 m_con.DisconnectPeer(pkt->getPeerId());
1168                 return;
1169         }
1170
1171         /*
1172          * If player is dead, we don't need to update the breath
1173          * He is dead !
1174          */
1175         if (player->isDead()) {
1176                 verbosestream << "TOSERVER_BREATH: " << player->getName()
1177                         << " is dead. Ignoring packet";
1178                 return;
1179         }
1180
1181
1182         PlayerSAO *playersao = player->getPlayerSAO();
1183         if (playersao == NULL) {
1184                 errorstream << "Server::ProcessData(): Canceling: "
1185                                 "No player object for peer_id=" << pkt->getPeerId()
1186                                 << " disconnecting peer!" << std::endl;
1187                 m_con.DisconnectPeer(pkt->getPeerId());
1188                 return;
1189         }
1190
1191         playersao->setBreath(breath);
1192         SendPlayerBreath(pkt->getPeerId());
1193 }
1194
1195 void Server::handleCommand_Password(NetworkPacket* pkt)
1196 {
1197         if (pkt->getSize() != PASSWORD_SIZE * 2)
1198                 return;
1199
1200         std::string oldpwd;
1201         std::string newpwd;
1202
1203         // Deny for clients using the new protocol
1204         RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
1205         if (client->net_proto_version >= 25) {
1206                 infostream << "Server::handleCommand_Password(): Denying change: "
1207                         << " Client protocol version for peer_id=" << pkt->getPeerId()
1208                         << " too new!" << std::endl;
1209                 return;
1210         }
1211
1212         for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
1213                 char c = pkt->getChar(i);
1214                 if (c == 0)
1215                         break;
1216                 oldpwd += c;
1217         }
1218
1219         for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
1220                 char c = pkt->getChar(PASSWORD_SIZE + i);
1221                 if (c == 0)
1222                         break;
1223                 newpwd += c;
1224         }
1225
1226         Player *player = m_env->getPlayer(pkt->getPeerId());
1227         if (player == NULL) {
1228                 errorstream << "Server::ProcessData(): Canceling: "
1229                                 "No player for peer_id=" << pkt->getPeerId()
1230                                 << " disconnecting peer!" << std::endl;
1231                 m_con.DisconnectPeer(pkt->getPeerId());
1232                 return;
1233         }
1234
1235         if (!base64_is_valid(newpwd)) {
1236                 infostream<<"Server: " << player->getName() <<
1237                                 " supplied invalid password hash" << std::endl;
1238                 // Wrong old password supplied!!
1239                 SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
1240                 return;
1241         }
1242
1243         infostream << "Server: Client requests a password change from "
1244                         << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
1245
1246         std::string playername = player->getName();
1247
1248         std::string checkpwd;
1249         m_script->getAuth(playername, &checkpwd, NULL);
1250
1251         if (oldpwd != checkpwd) {
1252                 infostream << "Server: invalid old password" << std::endl;
1253                 // Wrong old password supplied!!
1254                 SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
1255                 return;
1256         }
1257
1258         bool success = m_script->setPassword(playername, newpwd);
1259         if (success) {
1260                 actionstream << player->getName() << " changes password" << std::endl;
1261                 SendChatMessage(pkt->getPeerId(), L"Password change successful.");
1262         } else {
1263                 actionstream << player->getName() << " tries to change password but "
1264                                 << "it fails" << std::endl;
1265                 SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
1266         }
1267 }
1268
1269 void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
1270 {
1271         if (pkt->getSize() < 2)
1272                 return;
1273
1274         Player *player = m_env->getPlayer(pkt->getPeerId());
1275         if (player == NULL) {
1276                 errorstream << "Server::ProcessData(): Canceling: "
1277                                 "No player for peer_id=" << pkt->getPeerId()
1278                                 << " disconnecting peer!" << std::endl;
1279                 m_con.DisconnectPeer(pkt->getPeerId());
1280                 return;
1281         }
1282
1283         PlayerSAO *playersao = player->getPlayerSAO();
1284         if (playersao == NULL) {
1285                 errorstream << "Server::ProcessData(): Canceling: "
1286                                 "No player object for peer_id=" << pkt->getPeerId()
1287                                 << " disconnecting peer!" << std::endl;
1288                 m_con.DisconnectPeer(pkt->getPeerId());
1289                 return;
1290         }
1291
1292         u16 item;
1293
1294         *pkt >> item;
1295
1296         playersao->setWieldIndex(item);
1297 }
1298
1299 void Server::handleCommand_Respawn(NetworkPacket* pkt)
1300 {
1301         Player *player = m_env->getPlayer(pkt->getPeerId());
1302         if (player == NULL) {
1303                 errorstream << "Server::ProcessData(): Canceling: "
1304                                 "No player for peer_id=" << pkt->getPeerId()
1305                                 << " disconnecting peer!" << std::endl;
1306                 m_con.DisconnectPeer(pkt->getPeerId());
1307                 return;
1308         }
1309
1310         if (!player->isDead())
1311                 return;
1312
1313         RespawnPlayer(pkt->getPeerId());
1314
1315         actionstream << player->getName() << " respawns at "
1316                         << PP(player->getPosition()/BS) << std::endl;
1317
1318         // ActiveObject is added to environment in AsyncRunStep after
1319         // the previous addition has been successfully removed
1320 }
1321
1322 void Server::handleCommand_Interact(NetworkPacket* pkt)
1323 {
1324         std::string datastring(pkt->getString(0), pkt->getSize());
1325         std::istringstream is(datastring, std::ios_base::binary);
1326
1327         /*
1328                 [0] u16 command
1329                 [2] u8 action
1330                 [3] u16 item
1331                 [5] u32 length of the next item
1332                 [9] serialized PointedThing
1333                 actions:
1334                 0: start digging (from undersurface) or use
1335                 1: stop digging (all parameters ignored)
1336                 2: digging completed
1337                 3: place block or item (to abovesurface)
1338                 4: use item
1339         */
1340         u8 action = readU8(is);
1341         u16 item_i = readU16(is);
1342         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1343         PointedThing pointed;
1344         pointed.deSerialize(tmp_is);
1345
1346         verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
1347                         << item_i << ", pointed=" << pointed.dump() << std::endl;
1348
1349         Player *player = m_env->getPlayer(pkt->getPeerId());
1350         if (player == NULL) {
1351                 errorstream << "Server::ProcessData(): Canceling: "
1352                                 "No player for peer_id=" << pkt->getPeerId()
1353                                 << " disconnecting peer!" << std::endl;
1354                 m_con.DisconnectPeer(pkt->getPeerId());
1355                 return;
1356         }
1357
1358         PlayerSAO *playersao = player->getPlayerSAO();
1359         if (playersao == NULL) {
1360                 errorstream << "Server::ProcessData(): Canceling: "
1361                                 "No player object for peer_id=" << pkt->getPeerId()
1362                                 << " disconnecting peer!" << std::endl;
1363                 m_con.DisconnectPeer(pkt->getPeerId());
1364                 return;
1365         }
1366
1367         if (player->isDead()) {
1368                 verbosestream << "TOSERVER_INTERACT: " << player->getName()
1369                         << " is dead. Ignoring packet";
1370                 return;
1371         }
1372
1373         v3f player_pos = playersao->getLastGoodPosition();
1374
1375         // Update wielded item
1376         playersao->setWieldIndex(item_i);
1377
1378         // Get pointed to node (undefined if not POINTEDTYPE_NODE)
1379         v3s16 p_under = pointed.node_undersurface;
1380         v3s16 p_above = pointed.node_abovesurface;
1381
1382         // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
1383         ServerActiveObject *pointed_object = NULL;
1384         if (pointed.type == POINTEDTHING_OBJECT) {
1385                 pointed_object = m_env->getActiveObject(pointed.object_id);
1386                 if (pointed_object == NULL) {
1387                         verbosestream << "TOSERVER_INTERACT: "
1388                                 "pointed object is NULL" << std::endl;
1389                         return;
1390                 }
1391
1392         }
1393
1394         v3f pointed_pos_under = player_pos;
1395         v3f pointed_pos_above = player_pos;
1396         if (pointed.type == POINTEDTHING_NODE) {
1397                 pointed_pos_under = intToFloat(p_under, BS);
1398                 pointed_pos_above = intToFloat(p_above, BS);
1399         }
1400         else if (pointed.type == POINTEDTHING_OBJECT) {
1401                 pointed_pos_under = pointed_object->getBasePosition();
1402                 pointed_pos_above = pointed_pos_under;
1403         }
1404
1405         /*
1406                 Check that target is reasonably close
1407                 (only when digging or placing things)
1408         */
1409         if (action == 0 || action == 2 || action == 3) {
1410                 float d = player_pos.getDistanceFrom(pointed_pos_under);
1411                 float max_d = BS * 14; // Just some large enough value
1412                 if (d > max_d) {
1413                         actionstream << "Player " << player->getName()
1414                                         << " tried to access " << pointed.dump()
1415                                         << " from too far: "
1416                                         << "d=" << d <<", max_d=" << max_d
1417                                         << ". ignoring." << std::endl;
1418                         // Re-send block to revert change on client-side
1419                         RemoteClient *client = getClient(pkt->getPeerId());
1420                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1421                         client->SetBlockNotSent(blockpos);
1422                         // Call callbacks
1423                         m_script->on_cheat(playersao, "interacted_too_far");
1424                         // Do nothing else
1425                         return;
1426                 }
1427         }
1428
1429         /*
1430                 Make sure the player is allowed to do it
1431         */
1432         if (!checkPriv(player->getName(), "interact")) {
1433                 actionstream<<player->getName()<<" attempted to interact with "
1434                                 <<pointed.dump()<<" without 'interact' privilege"
1435                                 <<std::endl;
1436                 // Re-send block to revert change on client-side
1437                 RemoteClient *client = getClient(pkt->getPeerId());
1438                 // Digging completed -> under
1439                 if (action == 2) {
1440                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1441                         client->SetBlockNotSent(blockpos);
1442                 }
1443                 // Placement -> above
1444                 if (action == 3) {
1445                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
1446                         client->SetBlockNotSent(blockpos);
1447                 }
1448                 return;
1449         }
1450
1451         /*
1452                 If something goes wrong, this player is to blame
1453         */
1454         RollbackScopeActor rollback_scope(m_rollback,
1455                         std::string("player:")+player->getName());
1456
1457         /*
1458                 0: start digging or punch object
1459         */
1460         if (action == 0) {
1461                 if (pointed.type == POINTEDTHING_NODE) {
1462                         /*
1463                                 NOTE: This can be used in the future to check if
1464                                 somebody is cheating, by checking the timing.
1465                         */
1466                         MapNode n(CONTENT_IGNORE);
1467                         bool pos_ok;
1468                         n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1469                         if (pos_ok)
1470                                 n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1471
1472                         if (!pos_ok) {
1473                                 infostream << "Server: Not punching: Node not found."
1474                                                 << " Adding block to emerge queue."
1475                                                 << std::endl;
1476                                 m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
1477                         }
1478
1479                         if (n.getContent() != CONTENT_IGNORE)
1480                                 m_script->node_on_punch(p_under, n, playersao, pointed);
1481                         // Cheat prevention
1482                         playersao->noCheatDigStart(p_under);
1483                 }
1484                 else if (pointed.type == POINTEDTHING_OBJECT) {
1485                         // Skip if object has been removed
1486                         if (pointed_object->m_removed)
1487                                 return;
1488
1489                         actionstream<<player->getName()<<" punches object "
1490                                         <<pointed.object_id<<": "
1491                                         <<pointed_object->getDescription()<<std::endl;
1492
1493                         ItemStack punchitem = playersao->getWieldedItem();
1494                         ToolCapabilities toolcap =
1495                                         punchitem.getToolCapabilities(m_itemdef);
1496                         v3f dir = (pointed_object->getBasePosition() -
1497                                         (player->getPosition() + player->getEyeOffset())
1498                                                 ).normalize();
1499                         float time_from_last_punch =
1500                                 playersao->resetTimeFromLastPunch();
1501
1502                         s16 src_original_hp = pointed_object->getHP();
1503                         s16 dst_origin_hp = playersao->getHP();
1504
1505                         pointed_object->punch(dir, &toolcap, playersao,
1506                                         time_from_last_punch);
1507
1508                         // If the object is a player and its HP changed
1509                         if (src_original_hp != pointed_object->getHP() &&
1510                                         pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1511                                 SendPlayerHPOrDie(((PlayerSAO*)pointed_object)->getPeerID(),
1512                                                 pointed_object->getHP() == 0);
1513                         }
1514
1515                         // If the puncher is a player and its HP changed
1516                         if (dst_origin_hp != playersao->getHP()) {
1517                                 SendPlayerHPOrDie(playersao->getPeerID(), playersao->getHP() == 0);
1518                         }
1519                 }
1520
1521         } // action == 0
1522
1523         /*
1524                 1: stop digging
1525         */
1526         else if (action == 1) {
1527         } // action == 1
1528
1529         /*
1530                 2: Digging completed
1531         */
1532         else if (action == 2) {
1533                 // Only digging of nodes
1534                 if (pointed.type == POINTEDTHING_NODE) {
1535                         bool pos_ok;
1536                         MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
1537                         if (!pos_ok) {
1538                                 infostream << "Server: Not finishing digging: Node not found."
1539                                                    << " Adding block to emerge queue."
1540                                                    << std::endl;
1541                                 m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
1542                         }
1543
1544                         /* Cheat prevention */
1545                         bool is_valid_dig = true;
1546                         if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
1547                                 v3s16 nocheat_p = playersao->getNoCheatDigPos();
1548                                 float nocheat_t = playersao->getNoCheatDigTime();
1549                                 playersao->noCheatDigEnd();
1550                                 // If player didn't start digging this, ignore dig
1551                                 if (nocheat_p != p_under) {
1552                                         infostream << "Server: NoCheat: " << player->getName()
1553                                                         << " started digging "
1554                                                         << PP(nocheat_p) << " and completed digging "
1555                                                         << PP(p_under) << "; not digging." << std::endl;
1556                                         is_valid_dig = false;
1557                                         // Call callbacks
1558                                         m_script->on_cheat(playersao, "finished_unknown_dig");
1559                                 }
1560                                 // Get player's wielded item
1561                                 ItemStack playeritem;
1562                                 InventoryList *mlist = playersao->getInventory()->getList("main");
1563                                 if (mlist != NULL)
1564                                         playeritem = mlist->getItem(playersao->getWieldIndex());
1565                                 ToolCapabilities playeritem_toolcap =
1566                                                 playeritem.getToolCapabilities(m_itemdef);
1567                                 // Get diggability and expected digging time
1568                                 DigParams params = getDigParams(m_nodedef->get(n).groups,
1569                                                 &playeritem_toolcap);
1570                                 // If can't dig, try hand
1571                                 if (!params.diggable) {
1572                                         const ItemDefinition &hand = m_itemdef->get("");
1573                                         const ToolCapabilities *tp = hand.tool_capabilities;
1574                                         if (tp)
1575                                                 params = getDigParams(m_nodedef->get(n).groups, tp);
1576                                 }
1577                                 // If can't dig, ignore dig
1578                                 if (!params.diggable) {
1579                                         infostream << "Server: NoCheat: " << player->getName()
1580                                                         << " completed digging " << PP(p_under)
1581                                                         << ", which is not diggable with tool. not digging."
1582                                                         << std::endl;
1583                                         is_valid_dig = false;
1584                                         // Call callbacks
1585                                         m_script->on_cheat(playersao, "dug_unbreakable");
1586                                 }
1587                                 // Check digging time
1588                                 // If already invalidated, we don't have to
1589                                 if (!is_valid_dig) {
1590                                         // Well not our problem then
1591                                 }
1592                                 // Clean and long dig
1593                                 else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
1594                                         // All is good, but grab time from pool; don't care if
1595                                         // it's actually available
1596                                         playersao->getDigPool().grab(params.time);
1597                                 }
1598                                 // Short or laggy dig
1599                                 // Try getting the time from pool
1600                                 else if (playersao->getDigPool().grab(params.time)) {
1601                                         // All is good
1602                                 }
1603                                 // Dig not possible
1604                                 else {
1605                                         infostream << "Server: NoCheat: " << player->getName()
1606                                                         << " completed digging " << PP(p_under)
1607                                                         << "too fast; not digging." << std::endl;
1608                                         is_valid_dig = false;
1609                                         // Call callbacks
1610                                         m_script->on_cheat(playersao, "dug_too_fast");
1611                                 }
1612                         }
1613
1614                         /* Actually dig node */
1615
1616                         if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
1617                                 m_script->node_on_dig(p_under, n, playersao);
1618
1619                         v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1620                         RemoteClient *client = getClient(pkt->getPeerId());
1621                         // Send unusual result (that is, node not being removed)
1622                         if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) {
1623                                 // Re-send block to revert change on client-side
1624                                 client->SetBlockNotSent(blockpos);
1625                         }
1626                         else {
1627                                 client->ResendBlockIfOnWire(blockpos);
1628                         }
1629                 }
1630         } // action == 2
1631
1632         /*
1633                 3: place block or right-click object
1634         */
1635         else if (action == 3) {
1636                 ItemStack item = playersao->getWieldedItem();
1637
1638                 // Reset build time counter
1639                 if (pointed.type == POINTEDTHING_NODE &&
1640                                 item.getDefinition(m_itemdef).type == ITEM_NODE)
1641                         getClient(pkt->getPeerId())->m_time_from_building = 0.0;
1642
1643                 if (pointed.type == POINTEDTHING_OBJECT) {
1644                         // Right click object
1645
1646                         // Skip if object has been removed
1647                         if (pointed_object->m_removed)
1648                                 return;
1649
1650                         actionstream << player->getName() << " right-clicks object "
1651                                         << pointed.object_id << ": "
1652                                         << pointed_object->getDescription() << std::endl;
1653
1654                         // Do stuff
1655                         pointed_object->rightClick(playersao);
1656                 }
1657                 else if (m_script->item_OnPlace(
1658                                 item, playersao, pointed)) {
1659                         // Placement was handled in lua
1660
1661                         // Apply returned ItemStack
1662                         if (playersao->setWieldedItem(item)) {
1663                                 SendInventory(playersao);
1664                         }
1665                 }
1666
1667                 // If item has node placement prediction, always send the
1668                 // blocks to make sure the client knows what exactly happened
1669                 RemoteClient *client = getClient(pkt->getPeerId());
1670                 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
1671                 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
1672                 if (item.getDefinition(m_itemdef).node_placement_prediction != "") {
1673                         client->SetBlockNotSent(blockpos);
1674                         if (blockpos2 != blockpos) {
1675                                 client->SetBlockNotSent(blockpos2);
1676                         }
1677                 }
1678                 else {
1679                         client->ResendBlockIfOnWire(blockpos);
1680                         if (blockpos2 != blockpos) {
1681                                 client->ResendBlockIfOnWire(blockpos2);
1682                         }
1683                 }
1684         } // action == 3
1685
1686         /*
1687                 4: use
1688         */
1689         else if (action == 4) {
1690                 ItemStack item = playersao->getWieldedItem();
1691
1692                 actionstream << player->getName() << " uses " << item.name
1693                                 << ", pointing at " << pointed.dump() << std::endl;
1694
1695                 if (m_script->item_OnUse(
1696                                 item, playersao, pointed)) {
1697                         // Apply returned ItemStack
1698                         if (playersao->setWieldedItem(item)) {
1699                                 SendInventory(playersao);
1700                         }
1701                 }
1702
1703         } // action == 4
1704
1705
1706         /*
1707                 Catch invalid actions
1708         */
1709         else {
1710                 infostream << "WARNING: Server: Invalid action "
1711                                 << action << std::endl;
1712         }
1713 }
1714
1715 void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
1716 {
1717         u16 num;
1718         *pkt >> num;
1719         for (u16 k = 0; k < num; k++) {
1720                 s32 id;
1721
1722                 *pkt >> id;
1723
1724                 std::map<s32, ServerPlayingSound>::iterator i =
1725                         m_playing_sounds.find(id);
1726
1727                 if (i == m_playing_sounds.end())
1728                         continue;
1729
1730                 ServerPlayingSound &psound = i->second;
1731                 psound.clients.erase(pkt->getPeerId());
1732                 if (psound.clients.empty())
1733                         m_playing_sounds.erase(i++);
1734         }
1735 }
1736
1737 void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
1738 {
1739         v3s16 p;
1740         std::string formname;
1741         u16 num;
1742
1743         *pkt >> p >> formname >> num;
1744
1745         std::map<std::string, std::string> fields;
1746         for (u16 k = 0; k < num; k++) {
1747                 std::string fieldname;
1748                 *pkt >> fieldname;
1749                 fields[fieldname] = pkt->readLongString();
1750         }
1751
1752         Player *player = m_env->getPlayer(pkt->getPeerId());
1753         if (player == NULL) {
1754                 errorstream << "Server::ProcessData(): Canceling: "
1755                                 "No player for peer_id=" << pkt->getPeerId()
1756                                 << " disconnecting peer!" << std::endl;
1757                 m_con.DisconnectPeer(pkt->getPeerId());
1758                 return;
1759         }
1760
1761         PlayerSAO *playersao = player->getPlayerSAO();
1762         if (playersao == NULL) {
1763                 errorstream << "Server::ProcessData(): Canceling: "
1764                                 "No player object for peer_id=" << pkt->getPeerId()
1765                                 << " disconnecting peer!"  << std::endl;
1766                 m_con.DisconnectPeer(pkt->getPeerId());
1767                 return;
1768         }
1769
1770         // If something goes wrong, this player is to blame
1771         RollbackScopeActor rollback_scope(m_rollback,
1772                         std::string("player:")+player->getName());
1773
1774         // Check the target node for rollback data; leave others unnoticed
1775         RollbackNode rn_old(&m_env->getMap(), p, this);
1776
1777         m_script->node_on_receive_fields(p, formname, fields, playersao);
1778
1779         // Report rollback data
1780         RollbackNode rn_new(&m_env->getMap(), p, this);
1781         if (rollback() && rn_new != rn_old) {
1782                 RollbackAction action;
1783                 action.setSetNode(p, rn_old, rn_new);
1784                 rollback()->reportAction(action);
1785         }
1786 }
1787
1788 void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
1789 {
1790         std::string formname;
1791         u16 num;
1792
1793         *pkt >> formname >> num;
1794
1795         std::map<std::string, std::string> fields;
1796         for (u16 k = 0; k < num; k++) {
1797                 std::string fieldname;
1798                 *pkt >> fieldname;
1799                 fields[fieldname] = pkt->readLongString();
1800         }
1801
1802         Player *player = m_env->getPlayer(pkt->getPeerId());
1803         if (player == NULL) {
1804                 errorstream << "Server::ProcessData(): Canceling: "
1805                                 "No player for peer_id=" << pkt->getPeerId()
1806                                 << " disconnecting peer!" << std::endl;
1807                 m_con.DisconnectPeer(pkt->getPeerId());
1808                 return;
1809         }
1810
1811         PlayerSAO *playersao = player->getPlayerSAO();
1812         if (playersao == NULL) {
1813                 errorstream << "Server::ProcessData(): Canceling: "
1814                                 "No player object for peer_id=" << pkt->getPeerId()
1815                                 << " disconnecting peer!" << std::endl;
1816                 m_con.DisconnectPeer(pkt->getPeerId());
1817                 return;
1818         }
1819
1820         m_script->on_playerReceiveFields(playersao, formname, fields);
1821 }
1822
1823 void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
1824 {
1825         RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
1826         ClientState cstate = client->getState();
1827
1828         std::string playername = client->getName();
1829
1830         std::string salt;
1831         std::string verification_key;
1832
1833         std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
1834         u8 is_empty;
1835
1836         *pkt >> salt >> verification_key >> is_empty;
1837
1838         verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s
1839                 << ", with is_empty= " << is_empty << std::endl;
1840
1841         // Either this packet is sent because the user is new or to change the password
1842         if (cstate == CS_HelloSent) {
1843                 if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) {
1844                         actionstream << "Server: Client from " << addr_s
1845                                         << " tried to set password without being "
1846                                         << "authenticated, or the username being new." << std::endl;
1847                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1848                         return;
1849                 }
1850
1851                 if (!isSingleplayer() &&
1852                                 g_settings->getBool("disallow_empty_password") &&
1853                                 is_empty == 1) {
1854                         actionstream << "Server: " << playername
1855                                         << " supplied empty password from " << addr_s << std::endl;
1856                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
1857                         return;
1858                 }
1859
1860                 std::string initial_ver_key;
1861                 std::string raw_default_password = g_settings->get("default_password");
1862                 // If default_password is empty, allow any initial password
1863                 if (raw_default_password.length() == 0) {
1864                         initial_ver_key = encodeSRPVerifier(verification_key, salt);
1865                 } else {
1866                         initial_ver_key = getSRPVerifier(playername, raw_default_password);
1867                 }
1868
1869                 m_script->createAuth(playername, initial_ver_key);
1870
1871                 acceptAuth(pkt->getPeerId(), false);
1872         } else {
1873                 if (cstate < CS_SudoMode) {
1874                         infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from "
1875                                         << addr_s << ": " << "Client has wrong state " << cstate << "."
1876                                         << std::endl;
1877                         return;
1878                 }
1879                 m_clients.event(pkt->getPeerId(), CSE_SudoLeave);
1880                 std::string pw_db_field = encodeSRPVerifier(verification_key, salt);
1881                 bool success = m_script->setPassword(playername, pw_db_field);
1882                 if (success) {
1883                         actionstream << playername << " changes password" << std::endl;
1884                         SendChatMessage(pkt->getPeerId(), L"Password change successful.");
1885                 } else {
1886                         actionstream << playername << " tries to change password but "
1887                                 << "it fails" << std::endl;
1888                         SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
1889                 }
1890         }
1891 }
1892
1893 void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
1894 {
1895         RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
1896         ClientState cstate = client->getState();
1897
1898         bool wantSudo = (cstate == CS_Active);
1899
1900         if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
1901                 actionstream << "Server: got SRP _A packet in wrong state "
1902                         << cstate << " from "
1903                         << getPeerAddress(pkt->getPeerId()).serializeString()
1904                         << ". Ignoring." << std::endl;
1905                 return;
1906         }
1907
1908         if (client->chosen_mech != AUTH_MECHANISM_NONE) {
1909                 actionstream << "Server: got SRP _A packet, while auth"
1910                         << "is already going on with mech " << client->chosen_mech
1911                         << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
1912                         << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl;
1913                 if (wantSudo) {
1914                         DenySudoAccess(pkt->getPeerId());
1915                         return;
1916                 } else {
1917                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1918                         return;
1919                 }
1920         }
1921
1922         std::string bytes_A;
1923         u8 based_on;
1924         *pkt >> bytes_A >> based_on;
1925
1926         infostream << "Server: TOSERVER_SRP_BYTES_A received with "
1927                 << "based_on=" << int(based_on) << " and len_A="
1928                 << bytes_A.length() << "." << std::endl;
1929
1930         AuthMechanism chosen = (based_on == 0) ?
1931                 AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP;
1932
1933         if (wantSudo) {
1934                 if (!client->isSudoMechAllowed(chosen)) {
1935                         actionstream << "Server: Player \"" << client->getName()
1936                                 << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString()
1937                                 << " tried to change password using unallowed mech "
1938                                 << chosen << "." << std::endl;
1939                         DenySudoAccess(pkt->getPeerId());
1940                         return;
1941                 }
1942         } else {
1943                 if (!client->isMechAllowed(chosen)) {
1944                         actionstream << "Server: Client tried to authenticate from "
1945                                 << getPeerAddress(pkt->getPeerId()).serializeString()
1946                                 << " using unallowed mech " << chosen << "." << std::endl;
1947                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1948                         return;
1949                 }
1950         }
1951
1952         client->chosen_mech = chosen;
1953
1954         std::string bytes_s;
1955         std::string bytes_v;
1956
1957         if (based_on == 0) {
1958                 char *p_bytes_s = 0;
1959                 size_t len_s = 0;
1960                 char *p_bytes_v = 0;
1961                 size_t len_v = 0;
1962                 getSRPVerifier(client->getName(), client->enc_pwd,
1963                         &p_bytes_s, &len_s,
1964                         &p_bytes_v, &len_v);
1965                 bytes_s = std::string(p_bytes_s, len_s);
1966                 bytes_v = std::string(p_bytes_v, len_v);
1967                 free(p_bytes_s);
1968                 free(p_bytes_v);
1969         } else if (!decodeSRPVerifier(client->enc_pwd, &bytes_s, &bytes_v)) {
1970                 // Non-base64 errors should have been catched in the init handler
1971                 actionstream << "Server: User " << client->getName()
1972                         << " tried to log in, but srp verifier field"
1973                         << " was invalid (most likely invalid base64)." << std::endl;
1974                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
1975                 return;
1976         }
1977
1978         char *bytes_B = 0;
1979         size_t len_B = 0;
1980
1981         client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048,
1982                 client->getName().c_str(),
1983                 (const unsigned char *) bytes_s.c_str(), bytes_s.size(),
1984                 (const unsigned char *) bytes_v.c_str(), bytes_v.size(),
1985                 (const unsigned char *) bytes_A.c_str(), bytes_A.size(),
1986                 NULL, 0,
1987                 (unsigned char **) &bytes_B, &len_B, NULL, NULL);
1988
1989         if (!bytes_B) {
1990                 actionstream << "Server: User " << client->getName()
1991                         << " tried to log in, SRP-6a safety check violated in _A handler."
1992                         << std::endl;
1993                 if (wantSudo) {
1994                         DenySudoAccess(pkt->getPeerId());
1995                         return;
1996                 } else {
1997                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1998                         return;
1999                 }
2000         }
2001
2002         NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId());
2003         resp_pkt << bytes_s << std::string(bytes_B, len_B);
2004         Send(&resp_pkt);
2005 }
2006
2007 void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
2008 {
2009         RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
2010         ClientState cstate = client->getState();
2011
2012         bool wantSudo = (cstate == CS_Active);
2013
2014         verbosestream << "Server: Recieved TOCLIENT_SRP_BYTES_M." << std::endl;
2015
2016         if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
2017                 actionstream << "Server: got SRP _M packet in wrong state "
2018                         << cstate << " from "
2019                         << getPeerAddress(pkt->getPeerId()).serializeString()
2020                         << ". Ignoring." << std::endl;
2021                 return;
2022         }
2023
2024         if ((client->chosen_mech != AUTH_MECHANISM_SRP)
2025                 && (client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD)) {
2026                 actionstream << "Server: got SRP _M packet, while auth"
2027                         << "is going on with mech " << client->chosen_mech
2028                         << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
2029                         << " (wantSudo=" << wantSudo << "). Denying." << std::endl;
2030                 if (wantSudo) {
2031                         DenySudoAccess(pkt->getPeerId());
2032                         return;
2033                 } else {
2034                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
2035                         return;
2036                 }
2037         }
2038
2039         std::string bytes_M;
2040         *pkt >> bytes_M;
2041
2042         if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data)
2043                         != bytes_M.size()) {
2044                 actionstream << "Server: User " << client->getName()
2045                         << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
2046                         << " sent bytes_M with invalid length " << bytes_M.size() << std::endl;
2047                 DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
2048                 return;
2049         }
2050
2051         unsigned char *bytes_HAMK = 0;
2052
2053         srp_verifier_verify_session((SRPVerifier *) client->auth_data,
2054                 (unsigned char *)bytes_M.c_str(), &bytes_HAMK);
2055
2056         if (!bytes_HAMK) {
2057                 if (wantSudo) {
2058                         actionstream << "Server: User " << client->getName()
2059                                 << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
2060                                 << " tried to change their password, but supplied wrong"
2061                                 << " (SRP) password for authentication." << std::endl;
2062                         DenySudoAccess(pkt->getPeerId());
2063                         return;
2064                 } else {
2065                         actionstream << "Server: User " << client->getName()
2066                                 << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
2067                                 << " supplied wrong (SRP) password from address "
2068                                 << getPeerAddress(pkt->getPeerId()).serializeString()
2069                                 << "." << std::endl;
2070                         DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
2071                         return;
2072                 }
2073         }
2074
2075         acceptAuth(pkt->getPeerId(), wantSudo);
2076 }