onc_translator_shill_to_onc.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 if (shill_ip_method != shill::kTypeIPv4 && 323 shill_ip_method != shill::kTypeIPv6) { 324 LOG(ERROR) << "Unhandled IPConfig Method value " << shill_ip_method; 325 return; 326 } 327 328 std::string type = ::onc::ipconfig::kIPv4; 329 if (shill_ip_method == shill::kTypeIPv6) 330 type = ::onc::ipconfig::kIPv6; 331 onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type); 332} 333 334void ShillToONCTranslator::TranslateAndAddNestedObject( 335 const std::string& onc_field_name) { 336 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); 337} 338 339void ShillToONCTranslator::TranslateAndAddNestedObject( 340 const std::string& onc_field_name, 341 const base::DictionaryValue& dictionary) { 342 const OncFieldSignature* field_signature = 343 GetFieldSignature(*onc_signature_, onc_field_name); 344 DCHECK(field_signature) << "Unable to find signature for field " 345 << onc_field_name << "."; 346 ShillToONCTranslator nested_translator(dictionary, 347 *field_signature->value_signature); 348 scoped_ptr<base::DictionaryValue> nested_object = 349 nested_translator.CreateTranslatedONCObject(); 350 if (nested_object->empty()) 351 return; 352 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 353} 354 355void ShillToONCTranslator::TranslateAndAddListOfObjects( 356 const std::string& onc_field_name, 357 const base::ListValue& list) { 358 const OncFieldSignature* field_signature = 359 GetFieldSignature(*onc_signature_, onc_field_name); 360 if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) { 361 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '" 362 << field_signature->value_signature->onc_type 363 << "', expected: base::Value::TYPE_LIST."; 364 return; 365 } 366 DCHECK(field_signature->value_signature->onc_array_entry_signature); 367 scoped_ptr<base::ListValue> result(new base::ListValue()); 368 for (base::ListValue::const_iterator it = list.begin(); 369 it != list.end(); ++it) { 370 const base::DictionaryValue* shill_value = NULL; 371 if (!(*it)->GetAsDictionary(&shill_value)) 372 continue; 373 ShillToONCTranslator nested_translator( 374 *shill_value, 375 *field_signature->value_signature->onc_array_entry_signature); 376 scoped_ptr<base::DictionaryValue> nested_object = 377 nested_translator.CreateTranslatedONCObject(); 378 // If the nested object couldn't be parsed, simply omit it. 379 if (nested_object->empty()) 380 continue; 381 result->Append(nested_object.release()); 382 } 383 // If there are no entries in the list, there is no need to expose this field. 384 if (result->empty()) 385 return; 386 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release()); 387} 388 389void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 390 CopyPropertiesAccordingToSignature(onc_signature_); 391} 392 393void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 394 const OncValueSignature* value_signature) { 395 if (value_signature->base_signature) 396 CopyPropertiesAccordingToSignature(value_signature->base_signature); 397 for (const OncFieldSignature* field_signature = value_signature->fields; 398 field_signature->onc_field_name != NULL; ++field_signature) { 399 CopyProperty(field_signature); 400 } 401} 402 403void ShillToONCTranslator::CopyProperty( 404 const OncFieldSignature* field_signature) { 405 std::string shill_property_name; 406 const base::Value* shill_value = NULL; 407 if (!field_translation_table_ || 408 !GetShillPropertyName(field_signature->onc_field_name, 409 field_translation_table_, 410 &shill_property_name) || 411 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 412 &shill_value)) { 413 return; 414 } 415 416 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 417 LOG(ERROR) << "Shill property '" << shill_property_name 418 << "' with value " << *shill_value 419 << " has base::Value::Type " << shill_value->GetType() 420 << " but ONC field '" << field_signature->onc_field_name 421 << "' requires type " 422 << field_signature->value_signature->onc_type << "."; 423 return; 424 } 425 426 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 427 shill_value->DeepCopy()); 428} 429 430void ShillToONCTranslator::TranslateWithTableAndSet( 431 const std::string& shill_property_name, 432 const StringTranslationEntry table[], 433 const std::string& onc_field_name) { 434 std::string shill_value; 435 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 436 &shill_value)) { 437 return; 438 } 439 std::string onc_value; 440 if (TranslateStringToONC(table, shill_value, &onc_value)) { 441 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 442 return; 443 } 444 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 445 << shill_value << " couldn't be translated to ONC"; 446} 447 448} // namespace 449 450scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 451 const base::DictionaryValue& shill_dictionary, 452 const OncValueSignature* onc_signature) { 453 CHECK(onc_signature != NULL); 454 455 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 456 return translator.CreateTranslatedONCObject(); 457} 458 459} // namespace onc 460} // namespace chromeos 461