VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/CloudGateway.cpp@ 86506

Last change on this file since 86506 was 85650, checked in by vboxsync, 4 years ago

IPRT/http,VBoxManage,CloudGateway: Corrections to the proxy information retrival interface. Main problem was that it did not include the possibility of indicating that no proxying was needed. Corrected user code to not use uProxyPort when it's set to UINT32_MAX. Bunch of cleanups. Completely untested. bugref:9469

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
1/* $Id: CloudGateway.cpp 85650 2020-08-08 14:06:23Z vboxsync $ */
2/** @file
3 * Implementation of local and cloud gateway management.
4 */
5
6/*
7 * Copyright (C) 2019-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "ApplianceImpl.h"
30#include "CloudNetworkImpl.h"
31#include "CloudGateway.h"
32
33#include <iprt/http.h>
34#include <iprt/inifile.h>
35#include <iprt/net.h>
36#include <iprt/path.h>
37#include <iprt/vfs.h>
38#include <iprt/uri.h>
39
40static HRESULT setMacAddress(const Utf8Str& str, RTMAC& mac)
41{
42 int rc = RTNetStrToMacAddr(str.c_str(), &mac);
43 if (RT_FAILURE(rc))
44 {
45 LogRel(("CLOUD-NET: Invalid MAC address '%s'\n", str.c_str()));
46 return E_INVALIDARG;
47 }
48 return S_OK;
49}
50
51HRESULT GatewayInfo::setCloudMacAddress(const Utf8Str& mac)
52{
53 return setMacAddress(mac, mCloudMacAddress);
54}
55
56HRESULT GatewayInfo::setLocalMacAddress(const Utf8Str& mac)
57{
58 return setMacAddress(mac, mLocalMacAddress);
59}
60
61Utf8Str GatewayInfo::getCloudMacAddressWithoutColons() const
62{
63 return Utf8StrFmt("%02X%02X%02X%02X%02X%02X",
64 mCloudMacAddress.au8[0], mCloudMacAddress.au8[1], mCloudMacAddress.au8[2],
65 mCloudMacAddress.au8[3], mCloudMacAddress.au8[4], mCloudMacAddress.au8[5]);
66}
67
68Utf8Str GatewayInfo::getLocalMacAddressWithoutColons() const
69{
70 return Utf8StrFmt("%02X%02X%02X%02X%02X%02X",
71 mLocalMacAddress.au8[0], mLocalMacAddress.au8[1], mLocalMacAddress.au8[2],
72 mLocalMacAddress.au8[3], mLocalMacAddress.au8[4], mLocalMacAddress.au8[5]);
73}
74
75Utf8Str GatewayInfo::getLocalMacAddressWithColons() const
76{
77 return Utf8StrFmt("%RTmac", &mLocalMacAddress);
78}
79
80class CloudError
81{
82public:
83 CloudError(HRESULT hrc, const Utf8Str& strText) : mHrc(hrc), mText(strText) {};
84 HRESULT getRc() { return mHrc; };
85 Utf8Str getText() { return mText; };
86
87private:
88 HRESULT mHrc;
89 Utf8Str mText;
90};
91
92static void handleErrors(HRESULT hrc, const char *pszFormat, ...)
93{
94 if (FAILED(hrc))
95 {
96 va_list va;
97 va_start(va, pszFormat);
98 Utf8Str strError(pszFormat, va);
99 va_end(va);
100 LogRel(("CLOUD-NET: %s (rc=%x)\n", strError.c_str(), hrc));
101 throw CloudError(hrc, strError);
102 }
103
104}
105
106class CloudClient
107{
108public:
109 CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile);
110 ~CloudClient() {};
111
112 void startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateways);
113 void stopCloudGateway(const GatewayInfo& gateways);
114
115private:
116 ComPtr<ICloudProviderManager> mManager;
117 ComPtr<ICloudProvider> mProvider;
118 ComPtr<ICloudProfile> mProfile;
119 ComPtr<ICloudClient> mClient;
120};
121
122CloudClient::CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile)
123{
124 HRESULT hrc = virtualBox->COMGETTER(CloudProviderManager)(mManager.asOutParam());
125 handleErrors(hrc, "Failed to obtain cloud provider manager object");
126 hrc = mManager->GetProviderByShortName(strProvider.raw(), mProvider.asOutParam());
127 handleErrors(hrc, "Failed to obtain cloud provider '%ls'", strProvider.raw());
128 hrc = mProvider->GetProfileByName(strProfile.raw(), mProfile.asOutParam());
129 handleErrors(hrc, "Failed to obtain cloud profile '%ls'", strProfile.raw());
130 hrc = mProfile->CreateCloudClient(mClient.asOutParam());
131 handleErrors(hrc, "Failed to create cloud client");
132}
133
134void CloudClient::startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateways)
135{
136 ComPtr<IProgress> progress;
137 ComPtr<ICloudNetworkGatewayInfo> gatewayInfo;
138 HRESULT hrc = mClient->StartCloudNetworkGateway(network, Bstr(gateways.mPublicSshKey).raw(),
139 gatewayInfo.asOutParam(), progress.asOutParam());
140 handleErrors(hrc, "Failed to launch compute instance");
141 hrc = progress->WaitForCompletion(-1);
142 handleErrors(hrc, "Failed to launch compute instance (wait)");
143
144 Bstr instanceId;
145 hrc = gatewayInfo->COMGETTER(InstanceId)(instanceId.asOutParam());
146 handleErrors(hrc, "Failed to get launched compute instance id");
147 gateways.mGatewayInstanceId = instanceId;
148
149 Bstr publicIP;
150 hrc = gatewayInfo->COMGETTER(PublicIP)(publicIP.asOutParam());
151 handleErrors(hrc, "Failed to get cloud gateway public IP address");
152 gateways.mCloudPublicIp = publicIP;
153
154 Bstr secondaryPublicIP;
155 hrc = gatewayInfo->COMGETTER(SecondaryPublicIP)(secondaryPublicIP.asOutParam());
156 handleErrors(hrc, "Failed to get cloud gateway secondary public IP address");
157 gateways.mCloudSecondaryPublicIp = secondaryPublicIP;
158
159 Bstr macAddress;
160 hrc = gatewayInfo->COMGETTER(MacAddress)(macAddress.asOutParam());
161 handleErrors(hrc, "Failed to get cloud gateway public IP address");
162 gateways.setCloudMacAddress(macAddress);
163}
164
165void CloudClient::stopCloudGateway(const GatewayInfo& gateways)
166{
167 ComPtr<IProgress> progress;
168 HRESULT hrc = mClient->TerminateInstance(Bstr(gateways.mGatewayInstanceId).raw(), progress.asOutParam());
169 handleErrors(hrc, "Failed to terminate compute instance");
170#if 0
171 /* Someday we may want to wait until the cloud gateway has terminated. */
172 hrc = progress->WaitForCompletion(-1);
173 handleErrors(hrc, "Failed to terminate compute instance (wait)");
174#endif
175}
176
177
178static HRESULT startCloudGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateways)
179{
180 HRESULT hrc = S_OK;
181
182 try {
183 hrc = network->COMGETTER(Provider)(gateways.mCloudProvider.asOutParam());
184 hrc = network->COMGETTER(Profile)(gateways.mCloudProfile.asOutParam());
185 CloudClient client(virtualBox, gateways.mCloudProvider, gateways.mCloudProfile);
186 client.startCloudGateway(network, gateways);
187 }
188 catch (CloudError e)
189 {
190 hrc = e.getRc();
191 }
192
193 return hrc;
194}
195
196
197static HRESULT attachToLocalNetwork(ComPtr<ISession> aSession, const com::Utf8Str &aCloudNetwork)
198{
199 ComPtr<IMachine> sessionMachine;
200 HRESULT hrc = aSession->COMGETTER(Machine)(sessionMachine.asOutParam());
201 if (FAILED(hrc))
202 {
203 LogRel(("CLOUD-NET: Failed to obtain a mutable machine. hrc=%x\n", hrc));
204 return hrc;
205 }
206
207 ComPtr<INetworkAdapter> networkAdapter;
208 hrc = sessionMachine->GetNetworkAdapter(1, networkAdapter.asOutParam());
209 if (FAILED(hrc))
210 {
211 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
212 return hrc;
213 }
214
215 BstrFmt network("cloud-%s", aCloudNetwork.c_str());
216 hrc = networkAdapter->COMSETTER(InternalNetwork)(network.raw());
217 if (FAILED(hrc))
218 {
219 LogRel(("CLOUD-NET: Failed to set network name for the second network adapter. hrc=%x\n", hrc));
220 return hrc;
221 }
222
223 hrc = sessionMachine->SaveSettings();
224 if (FAILED(hrc))
225 LogRel(("CLOUD-NET: Failed to save 'lgw' settings. hrc=%x\n", hrc));
226 return hrc;
227}
228
229static HRESULT startLocalGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ISession> aSession, const com::Utf8Str &aCloudNetwork, GatewayInfo& gateways)
230{
231 /*
232 * It would be really beneficial if we do not create a local gateway VM each time a target starts.
233 * We probably just need to make sure its configuration matches the one required by the cloud network
234 * attachment and update configuration if necessary.
235 */
236 Bstr strGatewayVM = BstrFmt("lgw-%ls", gateways.mTargetVM.raw());
237 ComPtr<IMachine> machine;
238 HRESULT hrc = virtualBox->FindMachine(strGatewayVM.raw(), machine.asOutParam());
239 if (SUCCEEDED(hrc))
240 {
241 hrc = machine->LockMachine(aSession, LockType_Write);
242 if (FAILED(hrc))
243 {
244 LogRel(("CLOUD-NET: Failed to lock '%ls' for modifications. hrc=%x\n", strGatewayVM.raw(), hrc));
245 return hrc;
246 }
247
248 hrc = attachToLocalNetwork(aSession, aCloudNetwork);
249 }
250 else
251 {
252 SafeArray<IN_BSTR> groups;
253 groups.push_back(Bstr("/gateways").mutableRaw());
254 hrc = virtualBox->CreateMachine(NULL, Bstr(strGatewayVM).raw(), ComSafeArrayAsInParam(groups), Bstr("Ubuntu_64").raw(), Bstr("").raw(), machine.asOutParam());
255 if (FAILED(hrc))
256 {
257 LogRel(("CLOUD-NET: Failed to create '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
258 return hrc;
259 }
260 /* Initial configuration */
261 hrc = machine->ApplyDefaults(NULL);
262 if (FAILED(hrc))
263 {
264 LogRel(("CLOUD-NET: Failed to apply defaults to '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
265 return hrc;
266 }
267
268 /* Add second network adapter */
269 ComPtr<INetworkAdapter> networkAdapter;
270 hrc = machine->GetNetworkAdapter(1, networkAdapter.asOutParam());
271 if (FAILED(hrc))
272 {
273 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
274 return hrc;
275 }
276
277 hrc = networkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Internal);
278 if (FAILED(hrc))
279 {
280 LogRel(("CLOUD-NET: Failed to set attachment type for the second network adapter. hrc=%x\n", hrc));
281 return hrc;
282 }
283
284 BstrFmt network("cloud-%s", aCloudNetwork.c_str());
285 hrc = networkAdapter->COMSETTER(InternalNetwork)(network.raw());
286 if (FAILED(hrc))
287 {
288 LogRel(("CLOUD-NET: Failed to set network name for the second network adapter. hrc=%x\n", hrc));
289 return hrc;
290 }
291
292 hrc = networkAdapter->COMSETTER(PromiscModePolicy)(NetworkAdapterPromiscModePolicy_AllowAll);
293 if (FAILED(hrc))
294 {
295 LogRel(("CLOUD-NET: Failed to set promiscuous mode policy for the second network adapter. hrc=%x\n", hrc));
296 return hrc;
297 }
298
299 hrc = networkAdapter->COMSETTER(Enabled)(TRUE);
300 if (FAILED(hrc))
301 {
302 LogRel(("CLOUD-NET: Failed to enable the second network adapter. hrc=%x\n", hrc));
303 return hrc;
304 }
305
306 /* No need for audio -- disable it. */
307 ComPtr<IAudioAdapter> audioAdapter;
308 hrc = machine->GetNetworkAdapter(1, networkAdapter.asOutParam());
309 if (FAILED(hrc))
310 {
311 LogRel(("CLOUD-NET: Failed to locate the second network adapter. hrc=%x\n", hrc));
312 return hrc;
313 }
314
315 hrc = machine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
316 if (FAILED(hrc))
317 {
318 LogRel(("CLOUD-NET: Failed to set attachment type for the second network adapter. hrc=%x\n", hrc));
319 return hrc;
320 }
321
322 hrc = audioAdapter->COMSETTER(Enabled)(FALSE);
323 if (FAILED(hrc))
324 {
325 LogRel(("CLOUD-NET: Failed to disable the audio adapter. hrc=%x\n", hrc));
326 return hrc;
327 }
328
329 /** @todo Disable USB? */
330
331 /* Register the local gateway VM */
332 hrc = virtualBox->RegisterMachine(machine);
333 if (FAILED(hrc))
334 {
335 LogRel(("CLOUD-NET: Failed to register '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
336 return hrc;
337 }
338
339 /*
340 * Storage can only be attached to registered VMs which means we need to use session
341 * to lock VM in order to make it mutable again.
342 */
343 ComPtr<ISystemProperties> systemProperties;
344 ComPtr<IMedium> hd;
345 Bstr defaultMachineFolder;
346 hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
347 if (FAILED(hrc))
348 {
349 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
350 return hrc;
351 }
352 hrc = systemProperties->COMGETTER(DefaultMachineFolder)(defaultMachineFolder.asOutParam());
353 if (FAILED(hrc))
354 {
355 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
356 return hrc;
357 }
358 hrc = virtualBox->OpenMedium(BstrFmt("%ls\\gateways\\lgw.vdi", defaultMachineFolder.raw()).raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE, hd.asOutParam());
359 if (FAILED(hrc))
360 {
361 LogRel(("CLOUD-NET: Failed to open medium for '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
362 return hrc;
363 }
364
365 hrc = machine->LockMachine(aSession, LockType_Write);
366 if (FAILED(hrc))
367 {
368 LogRel(("CLOUD-NET: Failed to lock '%ls' for modifications. hrc=%x\n", strGatewayVM.raw(), hrc));
369 return hrc;
370 }
371
372 ComPtr<IMachine> sessionMachine;
373 hrc = aSession->COMGETTER(Machine)(sessionMachine.asOutParam());
374 if (FAILED(hrc))
375 {
376 LogRel(("CLOUD-NET: Failed to obtain a mutable machine. hrc=%x\n", hrc));
377 return hrc;
378 }
379
380 hrc = sessionMachine->AttachDevice(Bstr("SATA").raw(), 0, 0, DeviceType_HardDisk, hd);
381 if (FAILED(hrc))
382 {
383 LogRel(("CLOUD-NET: Failed to attach HD to '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
384 return hrc;
385 }
386
387 /* Save settings */
388 hrc = sessionMachine->SaveSettings();
389 if (FAILED(hrc))
390 {
391 LogRel(("CLOUD-NET: Failed to save '%ls' settings. hrc=%x\n", strGatewayVM.raw(), hrc));
392 return hrc;
393 }
394 }
395 /* Unlock the machine before start, it will be re-locked by LaunchVMProcess */
396 aSession->UnlockMachine();
397
398#ifdef DEBUG
399 #define LGW_FRONTEND "gui"
400#else
401 #define LGW_FRONTEND "headless"
402#endif
403 ComPtr<IProgress> progress;
404 hrc = machine->LaunchVMProcess(aSession, Bstr(LGW_FRONTEND).raw(), ComSafeArrayNullInParam(), progress.asOutParam());
405 if (FAILED(hrc))
406 {
407 LogRel(("CLOUD-NET: Failed to launch '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
408 return hrc;
409 }
410
411 hrc = progress->WaitForCompletion(-1);
412 if (FAILED(hrc))
413 LogRel(("CLOUD-NET: Failed to launch '%ls'. hrc=%x\n", strGatewayVM.raw(), hrc));
414
415 gateways.mGatewayVM = strGatewayVM;
416
417 ComPtr<IEventSource> es;
418 hrc = virtualBox->COMGETTER(EventSource)(es.asOutParam());
419 ComPtr<IEventListener> listener;
420 hrc = es->CreateListener(listener.asOutParam());
421 com::SafeArray <VBoxEventType_T> eventTypes(1);
422 eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
423 hrc = es->RegisterListener(listener, ComSafeArrayAsInParam(eventTypes), false);
424
425 Bstr publicKey;
426 Bstr aMachStrGuid;
427 machine->COMGETTER(Id)(aMachStrGuid.asOutParam());
428 Guid aMachGuid(aMachStrGuid);
429
430 uint64_t u64Started = RTTimeMilliTS();
431 do
432 {
433 ComPtr<IEvent> ev;
434 hrc = es->GetEvent(listener, 1000 /* seconds */, ev.asOutParam());
435 if (ev)
436 {
437 VBoxEventType_T aType;
438 hrc = ev->COMGETTER(Type)(&aType);
439 if (aType == VBoxEventType_OnGuestPropertyChanged)
440 {
441 ComPtr<IGuestPropertyChangedEvent> gpcev = ev;
442 Assert(gpcev);
443 Bstr aNextStrGuid;
444 gpcev->COMGETTER(MachineId)(aNextStrGuid.asOutParam());
445 if (aMachGuid != Guid(aNextStrGuid))
446 continue;
447 Bstr aNextName;
448 gpcev->COMGETTER(Name)(aNextName.asOutParam());
449 if (aNextName == "/VirtualBox/Gateway/PublicKey")
450 {
451 gpcev->COMGETTER(Value)(publicKey.asOutParam());
452 LogRel(("CLOUD-NET: Got public key from local gateway '%ls'\n", publicKey.raw()));
453 break;
454 }
455 }
456
457 }
458 } while (RTTimeMilliTS() - u64Started < 300 * 1000); /** @todo reasonable timeout */
459
460 if (publicKey.isEmpty())
461 {
462 LogRel(("CLOUD-NET: Failed to get ssh public key from '%ls'\n", strGatewayVM.raw()));
463 return E_FAIL;
464 }
465
466 gateways.mPublicSshKey = publicKey;
467
468 return hrc;
469}
470
471static bool getProxyForIpAddr(ComPtr<IVirtualBox> virtualBox, const com::Utf8Str &strIpAddr, Bstr &strProxyType, Bstr &strProxyHost, Bstr &strProxyPort)
472{
473#ifndef VBOX_WITH_PROXY_INFO
474 RT_NOREF(virtualBox, strIpAddr, strProxyType, strProxyHost, strProxyPort);
475 LogRel(("CLOUD-NET: Proxy support is disabled. Using direct connection.\n"));
476 return false;
477#else /* VBOX_WITH_PROXY_INFO */
478 ComPtr<ISystemProperties> systemProperties;
479 ProxyMode_T enmProxyMode;
480 HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
481 if (FAILED(hrc))
482 {
483 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
484 return false;
485 }
486 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
487 if (FAILED(hrc))
488 {
489 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
490 return false;
491 }
492 if (enmProxyMode == ProxyMode_NoProxy)
493 return false;
494
495 Bstr proxyUrl;
496 if (enmProxyMode == ProxyMode_Manual)
497 {
498 hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam());
499 if (FAILED(hrc))
500 {
501 LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc));
502 return false;
503 }
504 Utf8Str strProxyUrl = proxyUrl;
505 if (!strProxyUrl.contains("://"))
506 strProxyUrl = "http://" + strProxyUrl;
507 const char *pcszProxyUrl = strProxyUrl.c_str();
508 RTURIPARSED Parsed;
509 int rc = RTUriParse(pcszProxyUrl, &Parsed);
510 if (RT_FAILURE(rc))
511 {
512 LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (rc=%d)\n", proxyUrl.raw(), rc));
513 return false;
514 }
515 char *pszHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed);
516 if (!pszHost)
517 {
518 LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl));
519 return false;
520 }
521 strProxyHost = pszHost;
522 RTStrFree(pszHost);
523 char *pszScheme = RTUriParsedScheme(pcszProxyUrl, &Parsed);
524 if (!pszScheme)
525 {
526 LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl));
527 return false;
528 }
529 strProxyType = Utf8Str(pszScheme).toUpper();
530 RTStrFree(pszScheme);
531 uint32_t uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed);
532 if (uProxyPort == UINT32_MAX)
533 if (!pszScheme)
534 {
535 LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl));
536 return false;
537 }
538 strProxyPort = BstrFmt("%d", uProxyPort);
539 }
540 else
541 {
542 /* Attempt to use system proxy settings (ProxyMode_System) */
543 RTHTTP hHttp;
544 int rc = RTHttpCreate(&hHttp);
545 if (RT_FAILURE(rc))
546 {
547 LogRel(("CLOUD-NET: Failed to create HTTP context (rc=%Rrc)\n", rc));
548 return false;
549 }
550 rc = RTHttpUseSystemProxySettings(hHttp);
551 if (RT_FAILURE(rc))
552 {
553 LogRel(("CLOUD-NET: Failed to use system proxy (rc=%Rrc)\n", rc));
554 RTHttpDestroy(hHttp);
555 return false;
556 }
557
558 RTHTTPPROXYINFO proxy;
559 rc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &proxy);
560 if (RT_FAILURE(rc))
561 {
562 LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%Rrc)\n", strIpAddr.c_str(), rc));
563 RTHttpDestroy(hHttp);
564 return false;
565 }
566 switch (proxy.enmProxyType)
567 {
568 case RTHTTPPROXYTYPE_NOPROXY:
569 RTHttpFreeProxyInfo(&proxy);
570 RTHttpDestroy(hHttp);
571 return false;
572 case RTHTTPPROXYTYPE_HTTP:
573 strProxyType = "HTTP";
574 break;
575 case RTHTTPPROXYTYPE_HTTPS:
576 strProxyType = "HTTPS";
577 break;
578 case RTHTTPPROXYTYPE_SOCKS4:
579 strProxyType = "SOCKS4";
580 break;
581 case RTHTTPPROXYTYPE_SOCKS5:
582 strProxyType = "SOCKS5";
583 break;
584 case RTHTTPPROXYTYPE_UNKNOWN:
585 case RTHTTPPROXYTYPE_INVALID:
586 case RTHTTPPROXYTYPE_END:
587 case RTHTTPPROXYTYPE_32BIT_HACK:
588 break;
589 }
590 AssertStmt(strProxyType.isNotEmpty(), LogRel(("CLOUD-NET: Unknown proxy type: %d\n", proxy.enmProxyType)));
591 strProxyHost = proxy.pszProxyHost;
592 if (proxy.uProxyPort != UINT32_MAX)
593 strProxyPort.printf("%d", proxy.uProxyPort);
594 RTHttpFreeProxyInfo(&proxy);
595 RTHttpDestroy(hHttp);
596 }
597 return true;
598#endif /* VBOX_WITH_PROXY_INFO */
599}
600
601
602static HRESULT exchangeInfoBetweenGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ISession> aSession, GatewayInfo& gateways)
603{
604 RT_NOREF(virtualBox);
605 HRESULT hrc = S_OK;
606 do
607 {
608 ComPtr<IConsole> console;
609 hrc = aSession->COMGETTER(Console)(console.asOutParam());
610 if (FAILED(hrc))
611 {
612 LogRel(("CLOUD-NET: Failed to obtain console for 'lgw'. hrc=%x\n", hrc));
613 break;
614 }
615
616 ComPtr<IGuest> guest;
617 hrc = console->COMGETTER(Guest)(guest.asOutParam());
618 if (FAILED(hrc))
619 {
620 LogRel(("CLOUD-NET: Failed to obtain guest for 'lgw'. hrc=%x\n", hrc));
621 break;
622 }
623
624 ComPtr<IGuestSession> guestSession;
625
626 GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None;
627 for (int cTriesLeft = 6; cTriesLeft > 0; cTriesLeft--)
628 {
629 RTThreadSleep(5000 /* ms */);
630 hrc = guest->CreateSession(Bstr("vbox").raw(), Bstr("vbox").raw(), NULL, Bstr("Cloud Gateway Impersonation").raw(), guestSession.asOutParam());
631 if (FAILED(hrc))
632 {
633 LogRel(("CLOUD-NET: Failed to create guest session for 'lgw'%s. hrc=%x\n", cTriesLeft > 1 ? ", will re-try" : "", hrc));
634 continue;
635 }
636 hrc = guestSession->WaitFor(GuestSessionWaitForFlag_Start, 30 * 1000, &enmWaitResult);
637 if (FAILED(hrc))
638 {
639 LogRel(("CLOUD-NET: WARNING! Failed to wait in guest session for 'lgw'%s. waitResult=%x hrc=%x\n",
640 cTriesLeft > 1 ? ", will re-try" : "", enmWaitResult, hrc));
641 guestSession->Close();
642 guestSession.setNull();
643 continue;
644 }
645 if (enmWaitResult == GuestSessionWaitResult_Start)
646 break;
647 LogRel(("CLOUD-NET: WARNING! 'lgw' guest session waitResult=%x%s\n",
648 enmWaitResult, cTriesLeft > 1 ? ", will re-try" : ""));
649 guestSession->Close();
650 guestSession.setNull();
651 }
652
653 if (FAILED(hrc))
654 {
655 LogRel(("CLOUD-NET: Failed to start guest session for 'lgw'. waitResult=%x hrc=%x\n", enmWaitResult, hrc));
656 break;
657 }
658
659 GuestSessionStatus_T enmSessionStatus;
660 hrc = guestSession->COMGETTER(Status)(&enmSessionStatus);
661 if (FAILED(hrc))
662 {
663 LogRel(("CLOUD-NET: Failed to get guest session status for 'lgw'. hrc=%x\n", hrc));
664 break;
665 }
666 LogRel(("CLOUD-NET: Session status: %d\n", enmSessionStatus));
667
668 Bstr strPrimaryProxyType;
669 Bstr strPrimaryProxyHost;
670 Bstr strPrimaryProxyPort;
671 Bstr strSecondaryProxyType;
672 Bstr strSecondaryProxyHost;
673 Bstr strSecondaryProxyPort;
674
675 ComPtr<IGuestProcess> guestProcess;
676 com::SafeArray<IN_BSTR> aArgs;
677 com::SafeArray<IN_BSTR> aEnv;
678 com::SafeArray<ProcessCreateFlag_T> aCreateFlags;
679 com::SafeArray<ProcessWaitForFlag_T> aWaitFlags;
680 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut);
681 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr);
682#define GUEST_CMD "/bin/sh"
683 aArgs.push_back(Bstr(GUEST_CMD).mutableRaw());
684 aArgs.push_back(Bstr("-x").mutableRaw());
685 aArgs.push_back(Bstr("/home/vbox/local-bridge.sh").mutableRaw());
686 aArgs.push_back(Bstr(gateways.mCloudPublicIp).mutableRaw());
687 aArgs.push_back(Bstr(gateways.mCloudSecondaryPublicIp).mutableRaw());
688 aArgs.push_back(Bstr(gateways.getLocalMacAddressWithColons()).mutableRaw());
689 if (getProxyForIpAddr(virtualBox, gateways.mCloudPublicIp, strPrimaryProxyType, strPrimaryProxyHost, strPrimaryProxyPort))
690 {
691 aArgs.push_back(strPrimaryProxyType.mutableRaw());
692 aArgs.push_back(strPrimaryProxyHost.mutableRaw());
693 aArgs.push_back(strPrimaryProxyPort.mutableRaw());
694 if (getProxyForIpAddr(virtualBox, gateways.mCloudSecondaryPublicIp, strSecondaryProxyType, strSecondaryProxyHost, strSecondaryProxyPort))
695 {
696 aArgs.push_back(strSecondaryProxyType.mutableRaw());
697 aArgs.push_back(strSecondaryProxyHost.mutableRaw());
698 aArgs.push_back(strSecondaryProxyPort.mutableRaw());
699 }
700 }
701 hrc = guestSession->ProcessCreate(Bstr(GUEST_CMD).raw(),
702 ComSafeArrayAsInParam(aArgs),
703 ComSafeArrayAsInParam(aEnv),
704 ComSafeArrayAsInParam(aCreateFlags),
705 180 * 1000 /* ms */,
706 guestProcess.asOutParam());
707 if (FAILED(hrc))
708 {
709 LogRel(("CLOUD-NET: Failed to create guest process '/bin/sh' for 'lgw'. hrc=%x\n", hrc));
710 break;
711 }
712
713 ProcessWaitResult_T waitResult;
714 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Start, 10 * 1000 /* ms */, &waitResult);
715 if (FAILED(hrc) || (waitResult != ProcessWaitResult_Start))
716 {
717 LogRel(("CLOUD-NET: Failed to wait for guest process to start for 'lgw'. waitResult=%x hrc=%x\n",
718 waitResult, hrc));
719 break;
720 }
721 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
722
723 uint32_t cNotSupported = 0;
724 bool fRead, fDone = false;
725 uint64_t u64Start = RTTimeMilliTS();
726 do
727 {
728 /** @todo wait for stdout when it becomes supported! */
729 hrc = guestProcess->WaitFor(ProcessWaitForFlag_Terminate | ProcessWaitForFlag_StdOut, 1000 /* ms */, &waitResult);
730 if (FAILED(hrc))
731 {
732 LogRel(("CLOUD-NET: Failed to get output from guest process for 'lgw'. waitResult=%x hrc=%x\n",
733 waitResult, hrc));
734 break;
735 }
736 if (waitResult == ProcessWaitResult_WaitFlagNotSupported)
737 ++cNotSupported;
738 else
739 {
740 if (cNotSupported)
741 {
742 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
743 cNotSupported = 0;
744 }
745 LogRel(("CLOUD-NET: waitResult=%x\n", waitResult));
746 }
747
748 fRead = false;
749 switch (waitResult)
750 {
751 case ProcessWaitResult_WaitFlagNotSupported:
752 RTThreadYield();
753 /* Fall through */
754 case ProcessWaitResult_StdOut:
755 fRead = true;
756 break;
757 case ProcessWaitResult_Terminate:
758 fDone = true;
759 break;
760 case ProcessWaitResult_Timeout:
761 {
762 ProcessStatus_T enmProcStatus;
763 hrc = guestProcess->COMGETTER(Status)(&enmProcStatus);
764 if (FAILED(hrc))
765 {
766 LogRel(("CLOUD-NET: Failed to query guest process status for 'lgw'. hrc=%x\n", hrc));
767 fDone = true;
768 }
769 else
770 {
771 LogRel(("CLOUD-NET: Guest process timeout for 'lgw'. status=%d\n", enmProcStatus));
772 if ( enmProcStatus == ProcessStatus_TimedOutKilled
773 || enmProcStatus == ProcessStatus_TimedOutAbnormally)
774 fDone = true;
775 }
776 fRead = true;
777 break;
778 }
779 default:
780 LogRel(("CLOUD-NET: Unexpected waitResult=%x\n", waitResult));
781 break;
782 }
783
784 if (fRead)
785 {
786 SafeArray<BYTE> aStdOutData, aStdErrData;
787 hrc = guestProcess->Read(1 /* StdOut */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdOutData));
788 if (FAILED(hrc))
789 {
790 LogRel(("CLOUD-NET: Failed to read stdout from guest process for 'lgw'. hrc=%x\n", hrc));
791 break;
792 }
793 hrc = guestProcess->Read(2 /* StdErr */, _64K, 60 * 1000 /* ms */, ComSafeArrayAsOutParam(aStdErrData));
794 if (FAILED(hrc))
795 {
796 LogRel(("CLOUD-NET: Failed to read stderr from guest process for 'lgw'. hrc=%x\n", hrc));
797 break;
798 }
799
800 size_t cbStdOutData = aStdOutData.size();
801 size_t cbStdErrData = aStdErrData.size();
802 if (cbStdOutData == 0 && cbStdErrData == 0)
803 {
804 //LogRel(("CLOUD-NET: Empty output from guest process for 'lgw'. hrc=%x\n", hrc));
805 continue;
806 }
807
808 if (cNotSupported)
809 {
810 LogRel(("CLOUD-NET: waitResult=9, repeated %u times\n", cNotSupported));
811 cNotSupported = 0;
812 }
813 if (cbStdOutData)
814 LogRel(("CLOUD-NET: Got stdout from 'lgw':\n%.*s", aStdOutData.size(), aStdOutData.raw()));
815 if (cbStdErrData)
816 LogRel(("CLOUD-NET: Got stderr from 'lgw':\n%.*s", aStdErrData.size(), aStdErrData.raw()));
817 }
818 }
819 while (!fDone && RTTimeMilliTS() - u64Start < 180 * 1000 /* ms */);
820
821 } while (false);
822
823 return hrc;
824}
825
826
827HRESULT destroyLocalGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
828{
829 if (gateways.mGatewayVM.isEmpty())
830 return S_OK;
831
832 LogRel(("CLOUD-NET: Shutting down local gateway '%s'...\n", gateways.mGatewayVM.c_str()));
833
834 ComPtr<IMachine> localGateway;
835 HRESULT hrc = virtualBox->FindMachine(Bstr(gateways.mGatewayVM).raw(), localGateway.asOutParam());
836 if (FAILED(hrc))
837 {
838 LogRel(("CLOUD-NET: Failed to locate '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
839 return hrc;
840 }
841
842 MachineState_T tmp;
843 hrc = localGateway->COMGETTER(State)(&tmp);
844 if (tmp == MachineState_Running)
845 {
846 /* If the gateway VM is running we need to stop it */
847 ComPtr<ISession> session;
848 hrc = session.createInprocObject(CLSID_Session);
849 if (FAILED(hrc))
850 {
851 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
852 return hrc;
853 }
854
855 hrc = localGateway->LockMachine(session, LockType_Shared);
856 if (FAILED(hrc))
857 {
858 LogRel(("CLOUD-NET: Failed to lock '%s' for control. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
859 return hrc;
860 }
861
862 ComPtr<IConsole> console;
863 hrc = session->COMGETTER(Console)(console.asOutParam());
864 if (FAILED(hrc))
865 {
866 LogRel(("CLOUD-NET: Failed to obtain console for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
867 return hrc;
868 }
869
870 ComPtr<IProgress> progress;
871 console->PowerDown(progress.asOutParam()); /* We assume the gateway disk to be immutable! */
872
873#if 0
874 hrc = progress->WaitForCompletion(-1);
875 if (FAILED(hrc))
876 LogRel(("CLOUD-NET: Failed to stop '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
877#endif
878 session->UnlockMachine();
879 }
880#if 0
881 /*
882 * Unfortunately we cannot unregister a machine we've just powered down and unlocked.
883 * It takes some time for the machine to unlock completely.
884 */
885 /** @todo Removal of VM should probably be optional in the future. */
886 SafeIfaceArray<IMedium> media;
887 hrc = pLocalGateway->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media));
888 if (FAILED(hrc))
889 {
890 LogRel(("CLOUD-NET: Failed to unregister '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
891 return hrc;
892 }
893 ComPtr<IProgress> removalProgress;
894 hrc = pLocalGateway->DeleteConfig(ComSafeArrayAsInParam(media), removalProgress.asOutParam());
895 if (FAILED(hrc))
896 {
897 LogRel(("CLOUD-NET: Failed to delete machine files for '%s'. hrc=%x\n", gateways.mGatewayVM.c_str(), hrc));
898 }
899#endif
900 return hrc;
901}
902
903static HRESULT terminateCloudGateway(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
904{
905 if (gateways.mGatewayInstanceId.isEmpty())
906 return S_OK;
907
908 LogRel(("CLOUD-NET: Terminating cloud gateway instance '%s'...\n", gateways.mGatewayInstanceId.c_str()));
909
910 HRESULT hrc = S_OK;
911 try {
912 CloudClient client(virtualBox, gateways.mCloudProvider, gateways.mCloudProfile);
913 client.stopCloudGateway(gateways);
914 }
915 catch (CloudError e)
916 {
917 hrc = e.getRc();
918 LogRel(("CLOUD-NET: Failed to terminate cloud gateway instance (rc=%x).\n", hrc));
919 }
920 return hrc;
921}
922
923HRESULT startGateways(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateways)
924{
925 /* Session used to launch and control the local gateway VM */
926 ComPtr<ISession> session;
927 Bstr strNetwork;
928 HRESULT hrc = session.createInprocObject(CLSID_Session);
929 if (FAILED(hrc))
930 LogRel(("CLOUD-NET: Failed to create a session. hrc=%x\n", hrc));
931 else
932 hrc = network->COMGETTER(NetworkName)(strNetwork.asOutParam());
933 if (SUCCEEDED(hrc))
934 hrc = startLocalGateway(virtualBox, session, strNetwork, gateways);
935 if (SUCCEEDED(hrc))
936 hrc = startCloudGateway(virtualBox, network, gateways);
937 if (SUCCEEDED(hrc))
938 hrc = exchangeInfoBetweenGateways(virtualBox, session, gateways);
939
940 session->UnlockMachine();
941 /** @todo Destroy gateways if unsuccessful (cleanup) */
942
943 return hrc;
944}
945
946HRESULT stopGateways(ComPtr<IVirtualBox> virtualBox, const GatewayInfo& gateways)
947{
948 HRESULT hrc = destroyLocalGateway(virtualBox, gateways);
949 AssertComRC(hrc);
950 hrc = terminateCloudGateway(virtualBox, gateways);
951 AssertComRC(hrc);
952 return hrc;
953}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use