func RPCAuthenticateWithKeyMessage(conn net.Conn, connection_data *structs.ConnData, packet_data *structs.PacketData) error {
	// Unmarshal the data
	msg := new(protocol.AuthenticateWithKeyMessage)
	err := proto.Unmarshal(packet_data.Content, msg)
	if err != nil {
		return err
	}

	// Generate a new connection-id based npid
	npid := structs.IdToNpid(connection_data.ConnectionId)

	// Fill in the connection data
	connection_data.Authenticated = true
	connection_data.IsServer = true
	connection_data.Token = ""
	connection_data.Npid = npid

	// Add connection to the storage
	storage.SetServerConnection(npid, connection_data)

	// Reply with the data
	return reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateResultMessage{
		Result:       proto.Int32(0),
		Npid:         proto.Uint64(npid),
		SessionToken: []byte(""),
	})
}
Example #2
0
func HandleCI2(
	conn net.Conn,
	connection_data *structs.ConnData,
	packet_data *structs.PacketData,
	stringParts []string,
) error {
	if len(stringParts) != 2 {
		return nil
	}

	// I assume that's the classic anticheat
	// I have no idea if it is supposed to work, but I did it
	reason, err := strconv.Atoi(stringParts[1])
	if err != nil {
		return err
	}

	if reason == 50001 {
		connection_data.LastCI = time.Now()
	}

	if connection_data.IsUnclean {
		return nil
	}

	connection_data.IsUnclean = true

	// Write to database
	err = environment.Env.Database.Query(`
INSERT INTO 
	misago_ban(
		test,
		ban,
		reason_user,
		expires
	)
VALUES(
	?,
	?,
	?,
	?
)`, 0, connection_data.Username, "Cheat detected ("+stringParts[1]+")", time.Now().Add(time.Hour*24*14)).Run()
	if err != nil {
		return err
	}

	logger.Info(connection_data.Npid, " marked unclean for ", reason)

	if connection_data.ServerId != 0 {
		return utils.KickUser(connection_data.ServerId, connection_data.Npid, int64(reason))
	}

	return nil
}
func handlePortAnnounced(
	conn net.Conn,
	connection_data *structs.ConnData,
	packet_data *structs.PacketData,
	stringParts []string,
) error {
	connection_data.ServerAddr = structs.StripPort(conn.RemoteAddr().String()) + ":" + stringParts[1]
	return nil
}
Example #4
0
func HandleCI25(
	conn net.Conn,
	connection_data *structs.ConnData,
	packet_data *structs.PacketData,
	stringParts []string,
) error {

	// Parse the token
	detection_id, guid, err := parseToken25(stringParts)
	if err != nil {
		return err
	}

	logger.Infof("DETECTION FROM %s: %d", connection_data.Username, detection_id)

	// We already know that the user is unclean, don't waste time handling it.
	if connection_data.IsUnclean {
		return nil
	}

	// check the hwid for bans
	if isHWIDBanned(guid) {
		detection_id = 10100
	}

	// If reason equals NOT_DETECTED, then it's a check
	if detection_id == NOT_DETECTED || detection_id == 41009 {
		// Client is checked, allow it to connect
		connection_data.LastCI = time.Now()

		// Append the GUID
		err = AppendHWID(guid, structs.NpidToId(connection_data.Npid), false)
		if err != nil {
			return err
		}
	} else {
		// Set the connection to unclean
		connection_data.IsUnclean = true

		// Append the GUID
		err = AppendHWID(guid, structs.NpidToId(connection_data.Npid), true)
		if err != nil {
			return err
		}

		if environment.Env.Config.NP.AnticheatInstant {
			// Ban that user
			err = utils.BanUser(connection_data.Username, detection_id, time.Hour*24*14)
			if err != nil {
				return err
			}

			// Kick him!
			if connection_data.ServerId != 0 {
				err = utils.KickUser(connection_data.ServerId, connection_data.Npid, detection_id)
			}
		} else {
			// Add a delayed ban
			err = utils.AddDelayedBan(connection_data.Npid, detection_id, guid)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
Example #5
0
func (n *NPServer) HandleConnection(conn net.Conn) {
	// Close the connection after it ends its execution
	defer conn.Close()

	// This is the place where you should put stuff to be executed for the "connection" event
	// The below code is the most non-thread-safe way to create such identifiers
	// If we get enough load, servers can f**k up
	TotalConnections++
	LastCid++
	cid := LastCid

	// Here comes a struct for connection data, that we pass over to handlers
	connection_data := new(structs.ConnData)
	connection_data.Authenticated = false                  // Because we don't want session stealing
	connection_data.ConnectionId = cid                     // Connection ID is mainly used in servers
	connection_data.PresenceData = make(map[string]string) // If we don't init it here, it'll panic later
	connection_data.Connection = conn
	connection_data.Valid = true

	for {
		// Set timeout
		conn.SetReadDeadline(time.Now().Add(60 * time.Second))

		// First 16 bytes are defined in the structs subpackage
		// It consists of four, 4-byte Little Endian unsigned 32bit integers
		// in the following order: signature, length, type, id
		headerBytes := make([]byte, 16)
		_, err := conn.Read(headerBytes)
		if err != nil {
			if err != io.EOF {
				logger.Warningf("Error while reading the header; %s", err)
			}

			break
		}

		// Initialize a bytes mapper
		buf := bytes.NewReader(headerBytes)

		// Map data to the struct
		var packet_header structs.PacketHeader
		err = binary.Read(buf, binary.LittleEndian, &packet_header)
		if err != nil {
			logger.Warningf("Error while mapping packet_header to struct; %s", err)
			continue
		}

		// Signature check
		if packet_header.Signature != packet_signature {
			logger.Warningf(
				"Signature doesn't match (from %s), received 0x%X",
				conn.RemoteAddr().String(),
				packet_header.Signature,
			)

			continue
		}

		// Length from the packet_header specifies how many of the next bytes are the content
		// Pass it over to the handlers which will decode it using protobuf
		contentBytes := make([]byte, packet_header.Length)

		// Set timeout
		conn.SetReadDeadline(time.Now().Add(60 * time.Second))

		// Read the body
		_, err = io.ReadFull(conn, contentBytes)
		if err != nil {
			logger.Warningf("Error while reading; %s", err)
			break
		}

		// Log that we are parsing a RPC message
		logger.Debugf("Received message %d (ID: %d) from %s", packet_header.Type, packet_header.Id, structs.StripPort(conn.RemoteAddr().String()))

		// Generate a new PacketData struct
		packet_data := new(structs.PacketData)
		packet_data.Header = packet_header
		packet_data.Content = contentBytes

		// Pass it to the handlers. Not the best idea to do it using params, we should use
		// a struct, so we will easily be able to inject more variables.
		err = HandleMessage(conn, connection_data, packet_data)
		if err != nil {
			logger.Warningf("Error while handling message %d from %s; %s", packet_data.Header.Id, conn.RemoteAddr().String(), err)
		}
	}

	if connection_data.Authenticated {
		if connection_data.IsServer {
			storage.DeleteServerConnection(connection_data.Npid)
		} else {
			storage.DeleteClientConnection(connection_data.Npid)
		}

	}

	// I guess that count is going to be used by the web API
	TotalConnections--
	connection_data.Valid = false
}
func RPCAuthenticateWithTokenMessage(conn net.Conn, connection_data *structs.ConnData, packet_data *structs.PacketData) error {
	// Unmarshal the data
	msg := new(protocol.AuthenticateWithTokenMessage)
	err := proto.Unmarshal(packet_data.Content, msg)
	if err != nil {
		return err
	}

	// Split the token
	token := string(msg.Token)
	parts := strings.Split(token, ":")

	// uid is the first part
	id, err := strconv.Atoi(parts[0])
	if err != nil {
		return err
	}

	// Convert it to the Npid
	npid := structs.IdToNpid(id)

	// Set connection data
	connection_data.Id = id
	connection_data.Npid = npid
	connection_data.Token = token

	// Verify!
	sess_string, err := environment.Env.Redis.Get("session:" + token).Result()
	if err != nil {
		return err
	}

	// Format is: ip;rank;username
	sess_parts := strings.Split(sess_string, ";")

	// Verify that user has the same IP as the one used to log in
	if sess_parts[0] != structs.StripPort(conn.RemoteAddr().String()) {
		return WrongIPAddress
	}

	// Get the rank id and save it in the session
	connection_data.RankId, err = strconv.Atoi(sess_parts[1])
	if err != nil {
		return err
	}

	// Authenticate the session
	connection_data.Authenticated = true
	connection_data.Username = sess_parts[2]

	// Add connection to the storage
	storage.SetClientConnection(npid, connection_data)

	// Return a response
	err = reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateResultMessage{
		Result:       proto.Int32(0),
		Npid:         &npid,
		SessionToken: msg.Token,
	})
	if err != nil {
		return err
	}

	// No idea what this thing is supposed to do
	time.AfterFunc(time.Millisecond*900, func() {
		err = reply.Reply(conn, 0, &protocol.AuthenticateExternalStatusMessage{
			Status: proto.Int32(0),
		})

		if err != nil {
			logger.Warning(err)
		}
	})

	return nil
}