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(""),
	})
}
예제 #2
0
func KickUser(serverID uint64, clientID uint64, reason int64) error {
	data := storage.GetServerConnection(serverID)

	if data != nil {
		return reply.Reply(data.Connection, 0, &protocol.AuthenticateKickUserMessage{
			Npid:         proto.Uint64(clientID),
			Reason:       proto.Int32(1),
			ReasonString: proto.String("Cheat detected (" + strconv.FormatInt(reason, 10) + ")"),
		})
	}

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

	mutexCreation.Lock()
	mutex, exists := downloadMutexes[msg.GetGuid()]
	if !exists {
		downloadMutexes[msg.GetGuid()] = new(sync.Mutex)
		mutex = downloadMutexes[msg.GetGuid()]
	}
	mutexCreation.Unlock()

	mutex.Lock()
	defer mutex.Unlock()

	// Check for data in the cache
	cached, ok := avatarCache[msg.GetGuid()]
	if ok {
		//logger.Errorf("Getting %d avatar from cache", msg.GetGuid())

		return reply.Reply(conn, packet_data.Header.Id, &protocol.FriendsGetUserAvatarResultMessage{
			Result:   proto.Int32(0),
			Guid:     msg.Guid,
			FileData: cached,
		})
	}

	// Query the database for the avatar info
	var rows []*struct {
		Email string
		Type  string
		Image string
	}

	err = environment.Env.Database.Query(`
SELECT
	email,
	avatar_type as type,
	avatar_image as image
FROM 
	misago_user
WHERE
	id = ?`, int(msg.GetGuid())).Rows(&rows)

	// Query error, something's wrong!
	if err != nil {
		return err
	}

	// No user found, might be a bug or invalid client request
	if len(rows) < 1 {
		return nil
	}

	// Load file from disk
	if rows[0].Type == "upload" {
		// Generate the filepath
		filename := filepath.Join(environment.Env.Config.NP.AvatarsPath, rows[0].Image)

		//logger.Errorf("Getting %d avatar from disk; %s", msg.GetGuid(), filename)

		// Ensure the file exists
		if !utility.FileExists(filename) {
			return nil
		}

		// Read file contents
		filecontents, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}

		// Get the extension of the file
		ext := filepath.Ext(filename)

		// Convert from jpg to png
		if ext == ".jpg" || ext == ".jpeg" {
			rd := bytes.NewReader(filecontents)
			img, err := jpeg.Decode(rd)
			if err != nil {
				return err
			}

			buf := make([]byte, 0)
			wr := bytes.NewBuffer(buf)
			err = png.Encode(wr, img)
			if err != nil {
				return err
			}

			filecontents = wr.Bytes()
		}

		// Convert from gif to png
		if ext == ".gif" {
			rd := bytes.NewReader(filecontents)
			img, err := gif.Decode(rd)
			if err != nil {
				return err
			}

			buf := make([]byte, 0)
			wr := bytes.NewBuffer(buf)
			err = png.Encode(wr, img)
			if err != nil {
				return err
			}

			filecontents = wr.Bytes()
		}

		// Cache it
		avatarCache[msg.GetGuid()] = filecontents
		//avatarCache.Set(strconv.Itoa(int(msg.GetGuid())), filecontents, -1)

		// Return it to the client
		return reply.Reply(conn, packet_data.Header.Id, &protocol.FriendsGetUserAvatarResultMessage{
			Result:   proto.Int32(0),
			Guid:     msg.Guid,
			FileData: filecontents,
		})
	} else if rows[0].Type == "gravatar" {
		// Download the avatar
		data, err := gravatar.GetAvatar("http", gravatar.EmailHash(rows[0].Email), 96, gravatar.DefaultIdentIcon)
		if err != nil {
			return err
		}

		//logger.Errorf("Getting %d avatar from internet; %s", msg.GetGuid(), rows[0].Email)

		// Cache it
		//avatarCache.Set(strconv.Itoa(int(msg.GetGuid())), data, -1)
		avatarCache[msg.GetGuid()] = data

		// Return it to the client
		return reply.Reply(conn, packet_data.Header.Id, &protocol.FriendsGetUserAvatarResultMessage{
			Result:   proto.Int32(0),
			Guid:     msg.Guid,
			FileData: data,
		})
	}

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

	// Create a new buffer based on the ticket data, in order to read data from it.
	buf := bytes.NewBuffer(msg.Ticket)

	// Structure:
	// <xxxx>  <xxxxxxxx> <xxxxxxxx> <xxxx>
	// version clientID   serverID   timeIssued

	var version, issued uint32
	var clientId, serverId uint64

	err = binary.Read(buf, binary.LittleEndian, &version)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.LittleEndian, &clientId)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.LittleEndian, &serverId)
	if err != nil {
		return err
	}

	err = binary.Read(buf, binary.LittleEndian, &issued)
	if err != nil {
		return err
	}

	// Only version 1 is valid
	if version == 1 {
		// Verify that the request isn't spoofed
		if connection_data.Npid == serverId {
			// Get client's connection
			session := storage.GetClientConnection(clientId)

			// Make sure the session is here
			if session == nil {
				return nil
			}

			// Make sure it's not a server
			if session.IsServer {
				return nil
			}

			// Make sure that the IDs are valid
			if session.Npid != clientId {
				return reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateValidateTicketResultMessage{
					Result:  proto.Int32(1),
					Npid:    &clientId,
					GroupID: proto.Int32(1),
				})
			}

			// Make sure there's no aCI detection
			if session.IsUnclean {
				return reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateValidateTicketResultMessage{
					Result:  proto.Int32(1),
					Npid:    &clientId,
					GroupID: proto.Int32(1),
				})
			}

			// Set the server ID
			session.ServerId = serverId

			// Heartbeat detection
			/*time.AfterFunc(time.Minute*2, func() {
				if session != nil && session.Valid && session.Username != "" && !session.IsUnclean {
					if session.LastCI.IsZero() { //Before(time.Now().Truncate(time.Minute)) {
						err = utils.BanUser(session.Username, 10000, time.Hour*24*14)
						if err != nil {
							logger.Warning(err)
						}

						err = utils.KickUser(serverId, session.Npid, 10000)
						if err != nil {
							logger.Warning(err)
						}

						session.IsUnclean = true
					}
				}
			})*/

			// Reply that everything is fine
			return reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateValidateTicketResultMessage{
				Result:  proto.Int32(0),
				Npid:    &clientId,
				GroupID: proto.Int32(1),
			})
		}
	}

	// Wrong version or wrong NPID
	return reply.Reply(conn, packet_data.Header.Id, &protocol.AuthenticateValidateTicketResultMessage{
		Result:  proto.Int32(1),
		Npid:    &clientId,
		GroupID: proto.Int32(1),
	})
}
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
}