onc_translator_shill_to_onc.cc revision 010d83a9304c5a91596085d917d248abff47903a
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chromeos/network/onc/onc_translator.h" 6 7#include <string> 8 9#include "base/basictypes.h" 10#include "base/json/json_reader.h" 11#include "base/json/json_writer.h" 12#include "base/logging.h" 13#include "base/values.h" 14#include "chromeos/network/network_state.h" 15#include "chromeos/network/onc/onc_signature.h" 16#include "chromeos/network/onc/onc_translation_tables.h" 17#include "chromeos/network/shill_property_util.h" 18#include "components/onc/onc_constants.h" 19#include "third_party/cros_system_api/dbus/service_constants.h" 20 21namespace chromeos { 22namespace onc { 23 24namespace { 25 26// Converts |str| to a base::Value of the given |type|. If the conversion fails, 27// returns NULL. 28scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, 29 base::Value::Type type) { 30 base::Value* value; 31 if (type == base::Value::TYPE_STRING) { 32 value = base::Value::CreateStringValue(str); 33 } else { 34 value = base::JSONReader::Read(str); 35 } 36 37 if (value == NULL || value->GetType() != type) { 38 delete value; 39 value = NULL; 40 } 41 return make_scoped_ptr(value); 42} 43 44// This class implements the translation of properties from the given 45// |shill_dictionary| to a new ONC object of signature |onc_signature|. Using 46// recursive calls to CreateTranslatedONCObject of new instances, nested objects 47// are translated. 48class ShillToONCTranslator { 49 public: 50 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, 51 const OncValueSignature& onc_signature) 52 : shill_dictionary_(&shill_dictionary), 53 onc_signature_(&onc_signature) { 54 field_translation_table_ = GetFieldTranslationTable(onc_signature); 55 } 56 57 // Translates the associated Shill dictionary and creates an ONC object of the 58 // given signature. 59 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); 60 61 private: 62 void TranslateEthernet(); 63 void TranslateOpenVPN(); 64 void TranslateIPsec(); 65 void TranslateVPN(); 66 void TranslateWiFiWithState(); 67 void TranslateCellularWithState(); 68 void TranslateNetworkWithState(); 69 void TranslateIPConfig(); 70 71 // Creates an ONC object from |dictionary| according to the signature 72 // associated to |onc_field_name| and adds it to |onc_object_| at 73 // |onc_field_name|. 74 void TranslateAndAddNestedObject(const std::string& onc_field_name, 75 const base::DictionaryValue& dictionary); 76 77 // Creates an ONC object from |shill_dictionary_| according to the signature 78 // associated to |onc_field_name| and adds it to |onc_object_| at 79 // |onc_field_name|. 80 void TranslateAndAddNestedObject(const std::string& onc_field_name); 81 82 // Translates a list of nested objects and adds the list to |onc_object_| at 83 // |onc_field_name|. If there are errors while parsing individual objects or 84 // if the resulting list contains no entries, the result will not be added to 85 // |onc_object_|. 86 void TranslateAndAddListOfObjects(const std::string& onc_field_name, 87 const base::ListValue& list); 88 89 // Applies function CopyProperty to each field of |value_signature| and its 90 // base signatures. 91 void CopyPropertiesAccordingToSignature( 92 const OncValueSignature* value_signature); 93 94 // Applies function CopyProperty to each field of |onc_signature_| and its 95 // base signatures. 96 void CopyPropertiesAccordingToSignature(); 97 98 // If |shill_property_name| is defined in |field_signature|, copies this 99 // entry from |shill_dictionary_| to |onc_object_| if it exists. 100 void CopyProperty(const OncFieldSignature* field_signature); 101 102 // If existent, translates the entry at |shill_property_name| in 103 // |shill_dictionary_| using |table|. It is an error if no matching table 104 // entry is found. Writes the result as entry at |onc_field_name| in 105 // |onc_object_|. 106 void TranslateWithTableAndSet(const std::string& shill_property_name, 107 const StringTranslationEntry table[], 108 const std::string& onc_field_name); 109 110 const base::DictionaryValue* shill_dictionary_; 111 const OncValueSignature* onc_signature_; 112 const FieldTranslationEntry* field_translation_table_; 113 scoped_ptr<base::DictionaryValue> onc_object_; 114 115 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); 116}; 117 118scoped_ptr<base::DictionaryValue> 119ShillToONCTranslator::CreateTranslatedONCObject() { 120 onc_object_.reset(new base::DictionaryValue); 121 if (onc_signature_ == &kNetworkWithStateSignature) { 122 TranslateNetworkWithState(); 123 } else if (onc_signature_ == &kEthernetSignature) { 124 TranslateEthernet(); 125 } else if (onc_signature_ == &kVPNSignature) { 126 TranslateVPN(); 127 } else if (onc_signature_ == &kOpenVPNSignature) { 128 TranslateOpenVPN(); 129 } else if (onc_signature_ == &kIPsecSignature) { 130 TranslateIPsec(); 131 } else if (onc_signature_ == &kWiFiWithStateSignature) { 132 TranslateWiFiWithState(); 133 } else if (onc_signature_ == &kCellularWithStateSignature) { 134 TranslateCellularWithState(); 135 } else if (onc_signature_ == &kIPConfigSignature) { 136 TranslateIPConfig(); 137 } else { 138 CopyPropertiesAccordingToSignature(); 139 } 140 return onc_object_.Pass(); 141} 142 143void ShillToONCTranslator::TranslateEthernet() { 144 std::string shill_network_type; 145 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 146 &shill_network_type); 147 const char* onc_auth = ::onc::ethernet::kNone; 148 if (shill_network_type == shill::kTypeEthernetEap) 149 onc_auth = ::onc::ethernet::k8021X; 150 onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 151 onc_auth); 152} 153 154void ShillToONCTranslator::TranslateOpenVPN() { 155 if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty)) 156 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509); 157 158 // Shill supports only one RemoteCertKU but ONC requires a list. If existing, 159 // wraps the value into a list. 160 std::string certKU; 161 if (shill_dictionary_->GetStringWithoutPathExpansion( 162 shill::kOpenVPNRemoteCertKUProperty, &certKU)) { 163 scoped_ptr<base::ListValue> certKUs(new base::ListValue); 164 certKUs->AppendString(certKU); 165 onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU, 166 certKUs.release()); 167 } 168 169 for (const OncFieldSignature* field_signature = onc_signature_->fields; 170 field_signature->onc_field_name != NULL; ++field_signature) { 171 const std::string& onc_field_name = field_signature->onc_field_name; 172 if (onc_field_name == ::onc::vpn::kSaveCredentials || 173 onc_field_name == ::onc::openvpn::kRemoteCertKU || 174 onc_field_name == ::onc::openvpn::kServerCAPEMs) { 175 CopyProperty(field_signature); 176 continue; 177 } 178 179 std::string shill_property_name; 180 const base::Value* shill_value = NULL; 181 if (!field_translation_table_ || 182 !GetShillPropertyName(field_signature->onc_field_name, 183 field_translation_table_, 184 &shill_property_name) || 185 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 186 &shill_value)) { 187 continue; 188 } 189 190 scoped_ptr<base::Value> translated; 191 std::string shill_str; 192 if (shill_value->GetAsString(&shill_str)) { 193 // Shill wants all Provider/VPN fields to be strings. Translates these 194 // strings back to the correct ONC type. 195 translated = ConvertStringToValue( 196 shill_str, 197 field_signature->value_signature->onc_type); 198 199 if (translated.get() == NULL) { 200 LOG(ERROR) << "Shill property '" << shill_property_name 201 << "' with value " << *shill_value 202 << " couldn't be converted to base::Value::Type " 203 << field_signature->value_signature->onc_type; 204 } else { 205 onc_object_->SetWithoutPathExpansion(onc_field_name, 206 translated.release()); 207 } 208 } else { 209 LOG(ERROR) << "Shill property '" << shill_property_name 210 << "' has value " << *shill_value 211 << ", but expected a string"; 212 } 213 } 214} 215 216void ShillToONCTranslator::TranslateIPsec() { 217 CopyPropertiesAccordingToSignature(); 218 if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty)) 219 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH); 220} 221 222void ShillToONCTranslator::TranslateVPN() { 223 TranslateWithTableAndSet( 224 shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType); 225 CopyPropertiesAccordingToSignature(); 226 227 std::string vpn_type; 228 if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType, 229 &vpn_type)) { 230 if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) { 231 TranslateAndAddNestedObject(::onc::vpn::kIPsec); 232 TranslateAndAddNestedObject(::onc::vpn::kL2TP); 233 } else { 234 TranslateAndAddNestedObject(vpn_type); 235 } 236 } 237} 238 239void ShillToONCTranslator::TranslateWiFiWithState() { 240 TranslateWithTableAndSet( 241 shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity); 242 std::string ssid = shill_property_util::GetSSIDFromProperties( 243 *shill_dictionary_, NULL /* ignore unknown encoding */); 244 if (!ssid.empty()) 245 onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid); 246 CopyPropertiesAccordingToSignature(); 247} 248 249void ShillToONCTranslator::TranslateCellularWithState() { 250 CopyPropertiesAccordingToSignature(); 251 const base::DictionaryValue* dictionary = NULL; 252 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 253 shill::kServingOperatorProperty, &dictionary)) { 254 TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary); 255 } 256 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 257 shill::kCellularApnProperty, &dictionary)) { 258 TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary); 259 } 260 const base::ListValue* shill_apns = NULL; 261 if (shill_dictionary_->GetListWithoutPathExpansion( 262 shill::kCellularApnListProperty, &shill_apns)) { 263 TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns); 264 } 265} 266 267void ShillToONCTranslator::TranslateNetworkWithState() { 268 CopyPropertiesAccordingToSignature(); 269 270 std::string shill_network_type; 271 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 272 &shill_network_type); 273 std::string onc_network_type = ::onc::network_type::kEthernet; 274 if (shill_network_type != shill::kTypeEthernet && 275 shill_network_type != shill::kTypeEthernetEap) { 276 TranslateStringToONC( 277 kNetworkTypeTable, shill_network_type, &onc_network_type); 278 } 279 if (!onc_network_type.empty()) { 280 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType, 281 onc_network_type); 282 TranslateAndAddNestedObject(onc_network_type); 283 } 284 285 // Since Name is a read only field in Shill unless it's a VPN, it is copied 286 // here, but not when going the other direction (if it's not a VPN). 287 std::string name; 288 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, 289 &name); 290 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName, 291 name); 292 293 std::string state; 294 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty, 295 &state)) { 296 std::string onc_state = ::onc::connection_state::kNotConnected; 297 if (NetworkState::StateIsConnected(state)) { 298 onc_state = ::onc::connection_state::kConnected; 299 } else if (NetworkState::StateIsConnecting(state)) { 300 onc_state = ::onc::connection_state::kConnecting; 301 } 302 onc_object_->SetStringWithoutPathExpansion( 303 ::onc::network_config::kConnectionState, onc_state); 304 } 305 306 // Shill's Service has an IPConfig property (note the singular, and not a 307 // IPConfigs property). However, we require the caller of the translation to 308 // patch the Shill dictionary before passing it to the translator. 309 const base::ListValue* shill_ipconfigs = NULL; 310 if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty, 311 &shill_ipconfigs)) { 312 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs, 313 *shill_ipconfigs); 314 } 315} 316 317void ShillToONCTranslator::TranslateIPConfig() { 318 CopyPropertiesAccordingToSignature(); 319 std::string shill_ip_method; 320 shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty, 321 &shill_ip_method); 322 std::string type; 323 if (shill_ip_method == shill::kTypeIPv4 || 324 shill_ip_method == shill::kTypeDHCP) { 325 type = ::onc::ipconfig::kIPv4; 326 } else if (shill_ip_method == shill::kTypeIPv6 || 327 shill_ip_method == shill::kTypeDHCP6) { 328 type = ::onc::ipconfig::kIPv6; 329 } else { 330 return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp 331 } 332 333 onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type); 334} 335 336void ShillToONCTranslator::TranslateAndAddNestedObject( 337 const std::string& onc_field_name) { 338 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); 339} 340 341void ShillToONCTranslator::TranslateAndAddNestedObject( 342 const std::string& onc_field_name, 343 const base::DictionaryValue& dictionary) { 344 const OncFieldSignature* field_signature = 345 GetFieldSignature(*onc_signature_, onc_field_name); 346 DCHECK(field_signature) << "Unable to find signature for field " 347 << onc_field_name << "."; 348 ShillToONCTranslator nested_translator(dictionary, 349 *field_signature->value_signature); 350 scoped_ptr<base::DictionaryValue> nested_object = 351 nested_translator.CreateTranslatedONCObject(); 352 if (nested_object->empty()) 353 return; 354 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 355} 356 357void ShillToONCTranslator::TranslateAndAddListOfObjects( 358 const std::string& onc_field_name, 359 const base::ListValue& list) { 360 const OncFieldSignature* field_signature = 361 GetFieldSignature(*onc_signature_, onc_field_name); 362 if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) { 363 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '" 364 << field_signature->value_signature->onc_type 365 << "', expected: base::Value::TYPE_LIST."; 366 return; 367 } 368 DCHECK(field_signature->value_signature->onc_array_entry_signature); 369 scoped_ptr<base::ListValue> result(new base::ListValue()); 370 for (base::ListValue::const_iterator it = list.begin(); 371 it != list.end(); ++it) { 372 const base::DictionaryValue* shill_value = NULL; 373 if (!(*it)->GetAsDictionary(&shill_value)) 374 continue; 375 ShillToONCTranslator nested_translator( 376 *shill_value, 377 *field_signature->value_signature->onc_array_entry_signature); 378 scoped_ptr<base::DictionaryValue> nested_object = 379 nested_translator.CreateTranslatedONCObject(); 380 // If the nested object couldn't be parsed, simply omit it. 381 if (nested_object->empty()) 382 continue; 383 result->Append(nested_object.release()); 384 } 385 // If there are no entries in the list, there is no need to expose this field. 386 if (result->empty()) 387 return; 388 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release()); 389} 390 391void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 392 CopyPropertiesAccordingToSignature(onc_signature_); 393} 394 395void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 396 const OncValueSignature* value_signature) { 397 if (value_signature->base_signature) 398 CopyPropertiesAccordingToSignature(value_signature->base_signature); 399 for (const OncFieldSignature* field_signature = value_signature->fields; 400 field_signature->onc_field_name != NULL; ++field_signature) { 401 CopyProperty(field_signature); 402 } 403} 404 405void ShillToONCTranslator::CopyProperty( 406 const OncFieldSignature* field_signature) { 407 std::string shill_property_name; 408 const base::Value* shill_value = NULL; 409 if (!field_translation_table_ || 410 !GetShillPropertyName(field_signature->onc_field_name, 411 field_translation_table_, 412 &shill_property_name) || 413 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 414 &shill_value)) { 415 return; 416 } 417 418 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 419 LOG(ERROR) << "Shill property '" << shill_property_name 420 << "' with value " << *shill_value 421 << " has base::Value::Type " << shill_value->GetType() 422 << " but ONC field '" << field_signature->onc_field_name 423 << "' requires type " 424 << field_signature->value_signature->onc_type << "."; 425 return; 426 } 427 428 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 429 shill_value->DeepCopy()); 430} 431 432void ShillToONCTranslator::TranslateWithTableAndSet( 433 const std::string& shill_property_name, 434 const StringTranslationEntry table[], 435 const std::string& onc_field_name) { 436 std::string shill_value; 437 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 438 &shill_value)) { 439 return; 440 } 441 std::string onc_value; 442 if (TranslateStringToONC(table, shill_value, &onc_value)) { 443 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 444 return; 445 } 446 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 447 << shill_value << " couldn't be translated to ONC"; 448} 449 450} // namespace 451 452scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 453 const base::DictionaryValue& shill_dictionary, 454 const OncValueSignature* onc_signature) { 455 CHECK(onc_signature != NULL); 456 457 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 458 return translator.CreateTranslatedONCObject(); 459} 460 461} // namespace onc 462} // namespace chromeos 463