// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // A PoloWireAdapter implementation that uses protocol buffers for transmitting // messages. #include "polo/wire/protobuf/protobufwireadapter.h" #include <glog/logging.h> #include <algorithm> #include <set> #include <string> #include <vector> #include "polo/util/poloutil.h" namespace polo { namespace wire { namespace protobuf { ProtobufWireAdapter::ProtobufWireAdapter(PoloWireInterface* interface) : PoloWireAdapter(interface), read_state_(kNone) { } void ProtobufWireAdapter::GetNextMessage() { if (read_state_ != kNone) { LOG(ERROR) << "Invalid state: GetNextMessage called during a read"; listener()->OnError(pairing::kErrorProtocol); return; } // Read the 4 byte preable which contains the length of the next message. read_state_ = kPreamble; interface()->Receive(4); } void ProtobufWireAdapter::SendConfigurationMessage( const pairing::message::ConfigurationMessage& message) { Configuration configuration; configuration.mutable_encoding()->set_symbol_length( message.encoding().symbol_length()); configuration.mutable_encoding()->set_type( EncodingTypeToProto(message.encoding().encoding_type())); configuration.set_client_role(RoleToProto(message.client_role())); SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION, configuration.SerializeAsString()); } void ProtobufWireAdapter::SendConfigurationAckMessage( const pairing::message::ConfigurationAckMessage& message) { ConfigurationAck ack; SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION_ACK, ack.SerializeAsString()); } void ProtobufWireAdapter::SendOptionsMessage( const pairing::message::OptionsMessage& message) { LOG(INFO) << "Sending " << message.ToString(); Options options; encoding::EncodingOption::EncodingSet::const_iterator iter; for (iter = message.input_encodings().begin(); iter != message.input_encodings().end(); iter++) { encoding::EncodingOption option = *iter; Options_Encoding* encoding = options.add_input_encodings(); encoding->set_symbol_length(option.symbol_length()); encoding->set_type(EncodingTypeToProto(option.encoding_type())); } for (iter = message.output_encodings().begin(); iter != message.output_encodings().end(); iter++) { encoding::EncodingOption option = *iter; Options_Encoding* encoding = options.add_output_encodings(); encoding->set_symbol_length(option.symbol_length()); encoding->set_type(EncodingTypeToProto(option.encoding_type())); } options.set_preferred_role(RoleToProto(message.protocol_role_preference())); SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_OPTIONS, options.SerializeAsString()); } void ProtobufWireAdapter::SendPairingRequestMessage( const pairing::message::PairingRequestMessage& message) { LOG(INFO) << "Sending " << message.ToString(); PairingRequest request; request.set_service_name(message.service_name()); if (message.has_client_name()) { request.set_client_name(message.client_name()); } SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST, request.SerializeAsString()); } void ProtobufWireAdapter::SendPairingRequestAckMessage( const pairing::message::PairingRequestAckMessage& message) { LOG(INFO) << "Sending " << message.ToString(); PairingRequestAck ack; if (message.has_server_name()) { ack.set_server_name(message.server_name()); } SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST_ACK, ack.SerializeAsString()); } void ProtobufWireAdapter::SendSecretMessage( const pairing::message::SecretMessage& message) { LOG(INFO) << "Sending " << message.ToString(); Secret secret; secret.set_secret(&message.secret()[0], message.secret().size()); SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_SECRET, secret.SerializeAsString()); } void ProtobufWireAdapter::SendSecretAckMessage( const pairing::message::SecretAckMessage& message) { LOG(INFO) << "Sending " << message.ToString(); SecretAck ack; ack.set_secret(&message.secret()[0], message.secret().size()); SendMessagePayload(OuterMessage_MessageType_MESSAGE_TYPE_SECRET_ACK, ack.SerializeAsString()); } void ProtobufWireAdapter::OnBytesReceived( const std::vector<uint8_t>& data) { if (read_state_ == kMessage) { // We were waiting for a message, so parse the message and reset the read // state. read_state_ = kNone; ParseMessage(data); } else if (read_state_ == kPreamble && data.size() == 4) { // If we were waiting for the preamble and we received the expected 4 bytes, // then wait for the rest of the message now that we know the size. read_state_ = kMessage; uint32_t message_length = util::PoloUtil::BigEndianBytesToInt(&data[0]); interface()->Receive(message_length); } else { LOG(ERROR) << "Unexpected state: " << read_state_ << " bytes: " << data.size(); listener()->OnError(pairing::kErrorProtocol); } } void ProtobufWireAdapter::ParseMessage(const std::vector<uint8_t>& data) { OuterMessage outer; std::string string(reinterpret_cast<const char*>(&data[0]), data.size()); if (!outer.ParseFromString(string)) { LOG(ERROR) << "Error parsing outer message"; listener()->OnError(pairing::kErrorProtocol); return; } if (outer.status() != OuterMessage_Status_STATUS_OK) { LOG(ERROR) << "Got error message: " << outer.status(); pairing::PoloError error = pairing::kErrorProtocol; switch (outer.status()) { case OuterMessage_Status_STATUS_BAD_CONFIGURATION: error = pairing::kErrorBadConfiguration; break; case OuterMessage_Status_STATUS_BAD_SECRET: error = pairing::kErrorInvalidChallengeResponse; break; } listener()->OnError(error); return; } LOG(INFO) << "Parsing message type: " << outer.type(); switch (outer.type()) { case OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION: ParseConfigurationMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_CONFIGURATION_ACK: ParseConfigurationAckMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_OPTIONS: ParseOptionsMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST: ParsePairingRequestMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_PAIRING_REQUEST_ACK: ParsePairingRequestAckMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_SECRET: ParseSecretMessage(outer.payload()); break; case OuterMessage_MessageType_MESSAGE_TYPE_SECRET_ACK: ParseSecretAckMessage(outer.payload()); break; default: LOG(ERROR) << "Unknown message type " << outer.type(); listener()->OnError(pairing::kErrorProtocol); return; } } void ProtobufWireAdapter::ParseConfigurationMessage( const std::string& payload) { Configuration configuration; if (!configuration.ParseFromString(payload)) { LOG(ERROR) << "Invalid ConfigurationMessage"; listener()->OnError(pairing::kErrorProtocol); return; } encoding::EncodingOption encoding( EncodingTypeFromProto(configuration.encoding().type()), configuration.encoding().symbol_length()); pairing::message::OptionsMessage::ProtocolRole role = RoleFromProto(configuration.client_role()); pairing::message::ConfigurationMessage message(encoding, role); listener()->OnConfigurationMessage(message); } void ProtobufWireAdapter::ParseConfigurationAckMessage( const std::string& payload) { ConfigurationAck ack; if (!ack.ParseFromString(payload)) { LOG(ERROR) << "Invalid ConfigurationAckMessage"; listener()->OnError(pairing::kErrorProtocol); return; } pairing::message::ConfigurationAckMessage message; listener()->OnConfigurationAckMessage(message); } void ProtobufWireAdapter::ParseOptionsMessage(const std::string& payload) { Options options; if (!options.ParseFromString(payload)) { LOG(ERROR) << "Invalid OptionsMessage"; listener()->OnError(pairing::kErrorProtocol); return; } pairing::message::OptionsMessage message; for (int i = 0; i < options.input_encodings().size(); i++) { const Options_Encoding& encoding = options.input_encodings(i); encoding::EncodingOption option(EncodingTypeFromProto(encoding.type()), encoding.symbol_length()); message.AddInputEncoding(option); } for (int i = 0; i < options.output_encodings().size(); i++) { const Options_Encoding& encoding = options.output_encodings(i); encoding::EncodingOption option(EncodingTypeFromProto(encoding.type()), encoding.symbol_length()); message.AddOutputEncoding(option); } message.set_protocol_role_preference( RoleFromProto(options.preferred_role())); listener()->OnOptionsMessage(message); } void ProtobufWireAdapter::ParsePairingRequestMessage( const std::string& payload) { PairingRequest request; if (!request.ParseFromString(payload)) { LOG(ERROR) << "Invalid PairingRequestMessage"; listener()->OnError(pairing::kErrorProtocol); return; } if (request.has_client_name()) { pairing::message::PairingRequestMessage message(request.service_name(), request.client_name()); listener()->OnPairingRequestMessage(message); } else { pairing::message::PairingRequestMessage message(request.service_name()); listener()->OnPairingRequestMessage(message); } } void ProtobufWireAdapter::ParsePairingRequestAckMessage( const std::string& payload) { PairingRequestAck ack; if (!ack.ParseFromString(payload)) { LOG(ERROR) << "Invalid PairingRequestAckMessage"; listener()->OnError(pairing::kErrorProtocol); return; } if (ack.has_server_name()) { pairing::message::PairingRequestAckMessage message(ack.server_name()); listener()->OnPairingRequestAckMessage(message); } else { pairing::message::PairingRequestAckMessage message; listener()->OnPairingRequestAckMessage(message); } } void ProtobufWireAdapter::ParseSecretMessage(const std::string& payload) { Secret secret; if (!secret.ParseFromString(payload)) { LOG(ERROR) << "Invalid SecretMessage"; listener()->OnError(pairing::kErrorProtocol); return; } const std::vector<uint8_t> secret_bytes(secret.secret().begin(), secret.secret().end()); pairing::message::SecretMessage message(secret_bytes); listener()->OnSecretMessage(message); } void ProtobufWireAdapter::ParseSecretAckMessage(const std::string& payload) { SecretAck ack; if (!ack.ParseFromString(payload)) { LOG(ERROR) << "Invalid SecretAckMessage"; listener()->OnError(pairing::kErrorProtocol); return; } std::vector<uint8_t> secret_bytes(ack.secret().begin(), ack.secret().end()); pairing::message::SecretAckMessage message(secret_bytes); listener()->OnSecretAckMessage(message); } void ProtobufWireAdapter::OnError() { LOG(ERROR) << "OnError"; listener()->OnError(pairing::kErrorNetwork); } void ProtobufWireAdapter::SendErrorMessage(pairing::PoloError error) { OuterMessage outer; outer.set_protocol_version(1); OuterMessage_Status status; switch (error) { case pairing::kErrorBadConfiguration: status = OuterMessage_Status_STATUS_BAD_CONFIGURATION; break; case pairing::kErrorInvalidChallengeResponse: status = OuterMessage_Status_STATUS_BAD_SECRET; break; default: status = OuterMessage_Status_STATUS_ERROR; } outer.set_status(status); SendOuterMessage(outer); } void ProtobufWireAdapter::SendMessagePayload(OuterMessage_MessageType type, const std::string& payload) { // Create the outer message which specifies the message type and payload data. OuterMessage outer; outer.set_type(type); outer.set_payload(payload); outer.set_protocol_version(1); outer.set_status(OuterMessage_Status_STATUS_OK); SendOuterMessage(outer); } void ProtobufWireAdapter::SendOuterMessage(const OuterMessage& message) { // Send the message as a string, prepended with a 4 byte preamble containing // the length of the message in bytes. std::string outer_string = message.SerializeAsString(); uint8_t* size_bytes; util::PoloUtil::IntToBigEndianBytes(outer_string.length(), size_bytes); std::vector<uint8_t> data(outer_string.length() + 4); std::vector<uint8_t>::iterator iter = data.begin(); std::copy(size_bytes, size_bytes + 4, iter); std::copy(outer_string.begin(), outer_string.end(), iter + 4); delete[] size_bytes; interface()->Send(data); } Options_Encoding_EncodingType ProtobufWireAdapter::EncodingTypeToProto( encoding::EncodingOption::EncodingType type) { switch (type) { case encoding::EncodingOption::kAlphaNumeric: return Options_Encoding_EncodingType_ENCODING_TYPE_ALPHANUMERIC; case encoding::EncodingOption::kHexadecimal: return Options_Encoding_EncodingType_ENCODING_TYPE_HEXADECIMAL; case encoding::EncodingOption::kNumeric: return Options_Encoding_EncodingType_ENCODING_TYPE_NUMERIC; case encoding::EncodingOption::kQRCode: return Options_Encoding_EncodingType_ENCODING_TYPE_QRCODE; default: return Options_Encoding_EncodingType_ENCODING_TYPE_UNKNOWN; } } encoding::EncodingOption::EncodingType ProtobufWireAdapter::EncodingTypeFromProto( Options_Encoding_EncodingType type) { switch (type) { case Options_Encoding_EncodingType_ENCODING_TYPE_ALPHANUMERIC: return encoding::EncodingOption::kAlphaNumeric; case Options_Encoding_EncodingType_ENCODING_TYPE_HEXADECIMAL: return encoding::EncodingOption::kHexadecimal; case Options_Encoding_EncodingType_ENCODING_TYPE_NUMERIC: return encoding::EncodingOption::kNumeric; case Options_Encoding_EncodingType_ENCODING_TYPE_QRCODE: return encoding::EncodingOption::kQRCode; default: return encoding::EncodingOption::kUnknown; } } Options_RoleType ProtobufWireAdapter::RoleToProto( pairing::message::OptionsMessage::ProtocolRole role) { switch (role) { case pairing::message::OptionsMessage::kInputDevice: return Options_RoleType_ROLE_TYPE_INPUT; case pairing::message::OptionsMessage::kDisplayDevice: return Options_RoleType_ROLE_TYPE_OUTPUT; default: return Options_RoleType_ROLE_TYPE_UNKNOWN; } } pairing::message::OptionsMessage::ProtocolRole ProtobufWireAdapter::RoleFromProto(Options_RoleType role) { switch (role) { case Options_RoleType_ROLE_TYPE_INPUT: return pairing::message::OptionsMessage::kInputDevice; case Options_RoleType_ROLE_TYPE_OUTPUT: return pairing::message::OptionsMessage::kDisplayDevice; default: return pairing::message::OptionsMessage::kUnknown; } } } // namespace protobuf } // namespace wire } // namespace polo