/** * @interface_method_impl{TXSTRANSPORT,pfnSendPkt} */ static DECLCALLBACK(int) txsTcpSendPkt(PCTXSPKTHDR pPktHdr) { Assert(pPktHdr->cb >= sizeof(TXSPKTHDR)); /* * Fail if no client connection. */ RTSOCKET hTcpClient = g_hTcpClient; if (hTcpClient == NIL_RTSOCKET) return VERR_NET_NOT_CONNECTED; /* * Write it. */ size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT); int rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend); if ( RT_FAILURE(rc) && rc != VERR_INTERRUPTED) { /* assume fatal connection error. */ Log(("RTTcpWrite -> %Rrc -> txsTcpDisconnectClient(%RTsock)\n", rc, g_hTcpClient)); txsTcpDisconnectClient(); } return rc; }
/** * Initiates a new List Exported Devices request. * * @returns VBox status code. */ int USBProxyBackendUsbIp::startListExportedDevicesReq() { int rc = VINF_SUCCESS; /* * Reset the current state and reconnect in case we were called in the middle * of another transfer (which should not happen). */ Assert(m->enmRecvState == kUsbIpRecvState_None); if (m->enmRecvState != kUsbIpRecvState_None) rc = reconnect(); if (RT_SUCCESS(rc)) { /* Send of the request. */ UsbIpReqDevList ReqDevList; ReqDevList.u16Version = RT_H2N_U16(USBIP_VERSION); ReqDevList.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_DEVLIST); ReqDevList.u32Status = RT_H2N_U32(0); rc = RTTcpWrite(m->hSocket, &ReqDevList, sizeof(ReqDevList)); if (RT_SUCCESS(rc)) advanceState(kUsbIpRecvState_Hdr); } return rc; }
static int teleporterTcpWriteACK(TeleporterStateTrg *pState, bool fAutomaticUnlock = true) { int rc = RTTcpWrite(pState->mhSocket, "ACK\n", sizeof("ACK\n") - 1); if (RT_FAILURE(rc)) { LogRel(("Teleporter: RTTcpWrite(,ACK,) -> %Rrc\n", rc)); if (fAutomaticUnlock) teleporterTrgUnlockMedia(pState); } return rc; }
/** * @copydoc SSMSTRMOPS::pfnClose */ static DECLCALLBACK(int) teleporterTcpOpClose(void *pvUser, bool fCanceled) { TeleporterState *pState = (TeleporterState *)pvUser; if (pState->mfIsSource) { TELEPORTERTCPHDR EofHdr; EofHdr.u32Magic = TELEPORTERTCPHDR_MAGIC; EofHdr.cb = fCanceled ? UINT32_MAX : 0; int rc = RTTcpWrite(pState->mhSocket, &EofHdr, sizeof(EofHdr)); if (RT_FAILURE(rc)) { LogRel(("Teleporter/TCP: EOF Header write error: %Rrc\n", rc)); return rc; } } else { ASMAtomicWriteBool(&pState->mfStopReading, true); } return VINF_SUCCESS; }
/** * @interface_method_impl{TXSTRANSPORT,pfnBabble} */ static DECLCALLBACK(void) txsTcpBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout) { /* * Quietly ignore already disconnected client. */ RTSOCKET hTcpClient = g_hTcpClient; if (hTcpClient == NIL_RTSOCKET) return; /* * Try send the babble reply. */ NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */ int rc; size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT); do rc = RTTcpWrite(hTcpClient, pPktHdr, cbToSend); while (rc == VERR_INTERRUPTED); /* * Disconnect the client. */ Log(("txsTcpBabble: txsTcpDisconnectClient(%RTsock) (RTTcpWrite rc=%Rrc)\n", g_hTcpClient, rc)); txsTcpDisconnectClient(); }
static int teleporterTcpWriteNACK(TeleporterStateTrg *pState, int32_t rc2, const char *pszMsgText = NULL) { /* * Unlock media sending the NACK. That way the other doesn't have to spin * waiting to regain the locks. */ teleporterTrgUnlockMedia(pState); char szMsg[256]; size_t cch; if (pszMsgText && *pszMsgText) { cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText); for (size_t off = 6; off + 1 < cch; off++) if (szMsg[off] == '\n') szMsg[off] = '\r'; } else cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2); int rc = RTTcpWrite(pState->mhSocket, szMsg, cch); if (RT_FAILURE(rc)) LogRel(("Teleporter: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc)); return rc; }
/** * Do the teleporter. * * @returns VBox status code. * @param pState The teleporter state. */ HRESULT Console::teleporterSrc(TeleporterStateSrc *pState) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* * Wait for Console::Teleport to change the state. */ { AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS); } BOOL fCanceled = TRUE; HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled); if (FAILED(hrc)) return hrc; if (fCanceled) return setError(E_FAIL, tr("canceled")); /* * Try connect to the destination machine, disable Nagle. * (Note. The caller cleans up mhSocket, so we can return without worries.) */ int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket); if (RT_FAILURE(vrc)) return setError(E_FAIL, tr("Failed to connect to port %u on '%s': %Rrc"), pState->muPort, pState->mstrHostname.c_str(), vrc); vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/); AssertRC(vrc); /* Read and check the welcome message. */ char szLine[RT_MAX(128, sizeof(g_szWelcome))]; RT_ZERO(szLine); vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL); if (RT_FAILURE(vrc)) return setError(E_FAIL, tr("Failed to read welcome message: %Rrc"), vrc); if (strcmp(szLine, g_szWelcome)) return setError(E_FAIL, tr("Unexpected welcome %.*Rhxs"), sizeof(g_szWelcome) - 1, szLine); /* password */ pState->mstrPassword.append('\n'); vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length()); if (RT_FAILURE(vrc)) return setError(E_FAIL, tr("Failed to send password: %Rrc"), vrc); /* ACK */ hrc = teleporterSrcReadACK(pState, "password", tr("Invalid password")); if (FAILED(hrc)) return hrc; /* * Start loading the state. * * Note! The saved state includes vital configuration data which will be * verified against the VM config on the other end. This is all done * in the first pass, so we should fail pretty promptly on misconfig. */ hrc = teleporterSrcSubmitCommand(pState, "load"); if (FAILED(hrc)) return hrc; RTSocketRetain(pState->mhSocket); void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState)); vrc = VMR3Teleport(VMR3GetVM(pState->mpUVM), pState->mcMsMaxDowntime, &g_teleporterTcpOps, pvUser, teleporterProgressCallback, pvUser, &pState->mfSuspendedByUs); RTSocketRelease(pState->mhSocket); if (RT_FAILURE(vrc)) { if ( vrc == VERR_SSM_CANCELLED && RT_SUCCESS(RTTcpSelectOne(pState->mhSocket, 1))) { hrc = teleporterSrcReadACK(pState, "load-complete"); if (FAILED(hrc)) return hrc; } return setError(E_FAIL, tr("VMR3Teleport -> %Rrc"), vrc); } hrc = teleporterSrcReadACK(pState, "load-complete"); if (FAILED(hrc)) return hrc; /* * We're at the point of no return. */ if (!pState->mptrProgress->notifyPointOfNoReturn()) { teleporterSrcSubmitCommand(pState, "cancel", false /*fWaitForAck*/); return E_FAIL; } /* * Hand over any media which we might be sharing. * * Note! This is only important on localhost teleportations. */ /** @todo Maybe we should only do this if it's a local teleportation... */ hrc = mControl->UnlockMedia(); if (FAILED(hrc)) return hrc; pState->mfUnlockedMedia = true; hrc = teleporterSrcSubmitCommand(pState, "lock-media"); if (FAILED(hrc)) return hrc; /* * The FINAL step is giving the target instructions how to proceed with the VM. */ if ( vrc == VINF_SSM_LIVE_SUSPENDED || pState->menmOldMachineState == MachineState_Paused) hrc = teleporterSrcSubmitCommand(pState, "hand-over-paused"); else hrc = teleporterSrcSubmitCommand(pState, "hand-over-resume"); if (FAILED(hrc)) return hrc; /* * teleporterSrcThreadWrapper will do the automatic power off because it * has to release the AutoVMCaller. */ return S_OK; }
Console::teleporterTrgServeConnection(RTSOCKET Sock, void *pvUser) { TeleporterStateTrg *pState = (TeleporterStateTrg *)pvUser; pState->mhSocket = Sock; /* * Disable Nagle and say hello. */ int vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/); AssertRC(vrc); vrc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1); if (RT_FAILURE(vrc)) { LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", vrc)); return VINF_SUCCESS; } /* * Password (includes '\n', see teleporterTrg). */ const char *pszPassword = pState->mstrPassword.c_str(); unsigned off = 0; while (pszPassword[off]) { char ch; vrc = RTTcpRead(Sock, &ch, sizeof(ch), NULL); if ( RT_FAILURE(vrc) || pszPassword[off] != ch) { if (RT_FAILURE(vrc)) LogRel(("Teleporter: Password read failure (off=%u): %Rrc\n", off, vrc)); else LogRel(("Teleporter: Invalid password (off=%u)\n", off)); teleporterTcpWriteNACK(pState, VERR_AUTHENTICATION_FAILURE); return VINF_SUCCESS; } off++; } vrc = teleporterTcpWriteACK(pState); if (RT_FAILURE(vrc)) return VINF_SUCCESS; /* * Update the progress bar, with peer name if available. */ HRESULT hrc; RTNETADDR Addr; vrc = RTTcpGetPeerAddress(Sock, &Addr); if (RT_SUCCESS(vrc)) { LogRel(("Teleporter: Incoming VM from %RTnaddr!\n", &Addr)); hrc = pState->mptrProgress->SetNextOperation(BstrFmt(tr("Teleporting VM from %RTnaddr"), &Addr).raw(), 8); } else { LogRel(("Teleporter: Incoming VM!\n")); hrc = pState->mptrProgress->SetNextOperation(Bstr(tr("Teleporting VM")).raw(), 8); } AssertMsg(SUCCEEDED(hrc) || hrc == E_FAIL, ("%Rhrc\n", hrc)); /* * Stop the server and cancel the timeout timer. * * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior * to it we must not return that value! */ RTTcpServerShutdown(pState->mhServer); RTTimerLRDestroy(*pState->mphTimerLR); *pState->mphTimerLR = NIL_RTTIMERLR; /* * Command processing loop. */ bool fDone = false; for (;;) { char szCmd[128]; vrc = teleporterTcpReadLine(pState, szCmd, sizeof(szCmd)); if (RT_FAILURE(vrc)) break; if (!strcmp(szCmd, "load")) { vrc = teleporterTcpWriteACK(pState); if (RT_FAILURE(vrc)) break; int vrc2 = VMR3AtErrorRegisterU(pState->mpUVM, Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2); RTSocketRetain(pState->mhSocket); /* For concurrent access by I/O thread and EMT. */ pState->moffStream = 0; void *pvUser2 = static_cast<void *>(static_cast<TeleporterState *>(pState)); vrc = VMR3LoadFromStream(VMR3GetVM(pState->mpUVM), &g_teleporterTcpOps, pvUser2, teleporterProgressCallback, pvUser2); RTSocketRelease(pState->mhSocket); vrc2 = VMR3AtErrorDeregister(VMR3GetVM(pState->mpUVM), Console::genericVMSetErrorCallback, &pState->mErrorText); AssertRC(vrc2); if (RT_FAILURE(vrc)) { LogRel(("Teleporter: VMR3LoadFromStream -> %Rrc\n", vrc)); teleporterTcpWriteNACK(pState, vrc, pState->mErrorText.c_str()); break; } /* The EOS might not have been read, make sure it is. */ pState->mfStopReading = false; size_t cbRead; vrc = teleporterTcpOpRead(pvUser2, pState->moffStream, szCmd, 1, &cbRead); if (vrc != VERR_EOF) { LogRel(("Teleporter: Draining teleporterTcpOpRead -> %Rrc\n", vrc)); teleporterTcpWriteNACK(pState, vrc); break; } vrc = teleporterTcpWriteACK(pState); } else if (!strcmp(szCmd, "cancel")) { /* Don't ACK this. */ LogRel(("Teleporter: Received cancel command.\n")); vrc = VERR_SSM_CANCELLED; } else if (!strcmp(szCmd, "lock-media")) { hrc = pState->mpControl->LockMedia(); if (SUCCEEDED(hrc)) { pState->mfLockedMedia = true; vrc = teleporterTcpWriteACK(pState); } else { vrc = VERR_FILE_LOCK_FAILED; teleporterTcpWriteNACK(pState, vrc); } } else if ( !strcmp(szCmd, "hand-over-resume") || !strcmp(szCmd, "hand-over-paused")) { /* * Point of no return. * * Note! Since we cannot tell whether a VMR3Resume failure is * destructive for the source or not, we have little choice * but to ACK it first and take any failures locally. * * Ideally, we should try resume it first and then ACK (or * NACK) the request since this would reduce latency and * make it possible to recover from some VMR3Resume failures. */ if ( pState->mptrProgress->notifyPointOfNoReturn() && pState->mfLockedMedia) { vrc = teleporterTcpWriteACK(pState); if (RT_SUCCESS(vrc)) { if (!strcmp(szCmd, "hand-over-resume")) vrc = VMR3Resume(VMR3GetVM(pState->mpUVM)); else pState->mptrConsole->setMachineState(MachineState_Paused); fDone = true; break; } } else { vrc = pState->mfLockedMedia ? VERR_WRONG_ORDER : VERR_SSM_CANCELLED; teleporterTcpWriteNACK(pState, vrc); } } else { LogRel(("Teleporter: Unknown command '%s' (%.*Rhxs)\n", szCmd, strlen(szCmd), szCmd)); vrc = VERR_NOT_IMPLEMENTED; teleporterTcpWriteNACK(pState, vrc); } if (RT_FAILURE(vrc)) break; } if (RT_SUCCESS(vrc) && !fDone) vrc = VERR_WRONG_ORDER; if (RT_FAILURE(vrc)) teleporterTrgUnlockMedia(pState); pState->mRc = vrc; pState->mhSocket = NIL_RTSOCKET; LogFlowFunc(("returns mRc=%Rrc\n", vrc)); return VERR_TCP_SERVER_STOP; }