VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers-http.cpp@ 87067

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

Shared Clipboard/Transfers: Added ShClTransferHttpServerIsRunning(). bugref:9874

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: clipboard-transfers-http.cpp 87067 2020-12-09 14:12:21Z vboxsync $ */
2/** @file
3 * Shared Clipboard: HTTP server implementation for Shared Clipboard transfers on UNIX-y hosts.
4 */
5
6/*
7 * Copyright (C) 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <signal.h>
23
24#include <iprt/http.h>
25#include <iprt/http-server.h>
26
27#include <iprt/net.h> /* To make use of IPv4Addr in RTGETOPTUNION. */
28
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/ctype.h>
32#include <iprt/err.h>
33#include <iprt/file.h>
34#include <iprt/getopt.h>
35#include <iprt/initterm.h>
36#include <iprt/list.h>
37#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
38#include <iprt/log.h>
39#include <iprt/mem.h>
40#include <iprt/message.h>
41#include <iprt/path.h>
42#include <iprt/rand.h>
43#include <iprt/stream.h>
44#include <iprt/string.h>
45#include <iprt/thread.h>
46#include <iprt/uuid.h>
47#include <iprt/vfs.h>
48
49#include <VBox/HostServices/VBoxClipboardSvc.h>
50#include <VBox/GuestHost/SharedClipboard-transfers.h>
51
52
53/*********************************************************************************************************************************
54* Definitations *
55*********************************************************************************************************************************/
56
57typedef struct _SHCLHTTPSERVERTRANSFER
58{
59 /** The node list. */
60 RTLISTNODE Node;
61 /** Pointer to associated transfer. */
62 PSHCLTRANSFER pTransfer;
63 /** The virtual path of the HTTP server's root directory for this transfer. */
64 char szPathVirtual[RTPATH_MAX];
65} SHCLHTTPSERVERTRANSFER;
66typedef SHCLHTTPSERVERTRANSFER *PSHCLHTTPSERVERTRANSFER;
67
68
69/*********************************************************************************************************************************
70* Prototypes *
71*********************************************************************************************************************************/
72int ShClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pThis);
73
74
75/*********************************************************************************************************************************
76* Internal functions *
77*********************************************************************************************************************************/
78
79/**
80 * Return the HTTP server transfer for a specific transfer ID.
81 *
82 * @returns Pointer to HTTP server transfer if found, NULL if not found.
83 * @param pSrv HTTP server instance.
84 * @param idTransfer Transfer ID to return HTTP server transfer for.
85 */
86static PSHCLHTTPSERVERTRANSFER shClTransferHttpServerGetTransferById(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
87{
88 PSHCLHTTPSERVERTRANSFER pSrvTx;
89 RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node) /** @todo Slow O(n) lookup, but does it for now. */
90 {
91 if (pSrvTx->pTransfer->State.uID == idTransfer)
92 return pSrvTx;
93 }
94
95 return NULL;
96}
97
98/**
99 * Validates a given URL whether it matches a registered HTTP transfer.
100 *
101 * @returns VBox status code.
102 * @param pThis HTTP server instance data.
103 * @param pszUrl URL to validate.
104 */
105static int shClTransferHttpPathValidate(PSHCLHTTPSERVER pThis, const char *pszUrl)
106{
107 PSHCLHTTPSERVERTRANSFER pSrvTx;
108 RTListForEach(&pThis->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node)
109 {
110 AssertPtr(pSrvTx->pTransfer);
111 /* Be picky here, do a case sensitive comparison. */
112 if (RTStrStartsWith(pszUrl, pSrvTx->szPathVirtual))
113 return VINF_SUCCESS;
114 }
115
116 return VERR_PATH_NOT_FOUND;
117}
118
119static DECLCALLBACK(int) shClTransferHttpOpen(PRTHTTPCALLBACKDATA pData, PRTHTTPSERVERREQ pReq, void **ppvHandle)
120{
121 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
122 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
123
124 int rc = shClTransferHttpPathValidate(pThis, pReq->pszUrl);
125 if (RT_FAILURE(rc))
126 return rc;
127
128 RT_NOREF(ppvHandle);
129
130 LogFlowFuncLeaveRC(rc);
131 return rc;
132}
133
134static DECLCALLBACK(int) shClTransferHttpRead(PRTHTTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbBuf, size_t *pcbRead)
135{
136 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
137 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
138
139 RT_NOREF(pThis, pvHandle, pvBuf, cbBuf, pcbRead);
140
141 int rc = 0;
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147static DECLCALLBACK(int) shClTransferHttpClose(PRTHTTPCALLBACKDATA pData, void *pvHandle)
148{
149 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
150 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
151
152 RT_NOREF(pThis, pvHandle);
153
154 int rc = 0;
155
156 LogFlowFuncLeaveRC(rc);
157 return rc;
158}
159
160static DECLCALLBACK(int) shClTransferHttpQueryInfo(PRTHTTPCALLBACKDATA pData,
161 PRTHTTPSERVERREQ pReq, PRTFSOBJINFO pObjInfo, char **ppszMIMEHint)
162{
163 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
164 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
165
166 int rc = shClTransferHttpPathValidate(pThis, pReq->pszUrl);
167 if (RT_FAILURE(rc))
168 return rc;
169
170 RT_NOREF(pObjInfo, ppszMIMEHint);
171
172 LogFlowFuncLeaveRC(rc);
173 return rc;
174}
175
176static DECLCALLBACK(int) shClTransferHttpDestroy(PRTHTTPCALLBACKDATA pData)
177{
178 PSHCLHTTPSERVER pThis = (PSHCLHTTPSERVER)pData->pvUser;
179 Assert(pData->cbUser == sizeof(SHCLHTTPSERVER));
180
181 return ShClTransferHttpServerDestroyInternal(pThis);
182}
183
184int shClTransferHttpServerDestroyInternal(PSHCLHTTPSERVER pSrv)
185{
186 PSHCLHTTPSERVERTRANSFER pSrvTx, pSrvTxNext;
187 RTListForEachSafe(&pSrv->lstTransfers, pSrvTx, pSrvTxNext, SHCLHTTPSERVERTRANSFER, Node)
188 {
189 RTListNodeRemove(&pSrvTx->Node);
190
191 RTMemFree(pSrvTx);
192 pSrvTx = NULL;
193 }
194
195 RTHttpServerResponseDestroy(&pSrv->Resp);
196
197 return RTCritSectDelete(&pSrv->CritSect);
198}
199
200DECLINLINE(void) shClTransferHttpServerLock(PSHCLHTTPSERVER pSrv)
201{
202 int rc2 = RTCritSectEnter(&pSrv->CritSect);
203 AssertRC(rc2);
204}
205
206DECLINLINE(void) shClTransferHttpServerUnlock(PSHCLHTTPSERVER pSrv)
207{
208 int rc2 = RTCritSectLeave(&pSrv->CritSect);
209 AssertRC(rc2);
210}
211
212
213/*********************************************************************************************************************************
214* Public functions *
215*********************************************************************************************************************************/
216
217/**
218 * Initializes a new Shared Clipboard HTTP server instance.
219 *
220 * @param pSrv HTTP server instance to initialize.
221 */
222static void shClTransferHttpServerInitInternal(PSHCLHTTPSERVER pSrv)
223{
224 pSrv->hHTTPServer = NIL_RTHTTPSERVER;
225 pSrv->uPort = 0;
226 RTListInit(&pSrv->lstTransfers);
227 pSrv->cTransfers = 0;
228 int rc2 = RTHttpServerResponseInit(&pSrv->Resp);
229 AssertRC(rc2);
230}
231
232/**
233 * Creates a new Shared Clipboard HTTP server instance, extended version.
234 *
235 * @returns VBox status code.
236 * @param pSrv HTTP server instance to create.
237 * @param uPort TCP port number to use.
238 */
239int ShClTransferHttpServerCreateEx(PSHCLHTTPSERVER pSrv, uint16_t uPort)
240{
241 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
242
243 shClTransferHttpServerInitInternal(pSrv);
244
245 RTHTTPSERVERCALLBACKS Callbacks;
246 RT_ZERO(Callbacks);
247
248 Callbacks.pfnOpen = shClTransferHttpOpen;
249 Callbacks.pfnRead = shClTransferHttpRead;
250 Callbacks.pfnClose = shClTransferHttpClose;
251 Callbacks.pfnQueryInfo = shClTransferHttpQueryInfo;
252 Callbacks.pfnDestroy = shClTransferHttpDestroy;
253
254 /* Note: The server always and *only* runs against the localhost interface. */
255 int rc = RTHttpServerCreate(&pSrv->hHTTPServer, "localhost", uPort, &Callbacks,
256 pSrv, sizeof(SHCLHTTPSERVER));
257 if (RT_SUCCESS(rc))
258 {
259 rc = RTCritSectInit(&pSrv->CritSect);
260 AssertRCReturn(rc, rc);
261
262 pSrv->uPort = uPort;
263
264 LogRel2(("Shared Clipboard: HTTP server running at port %RU16\n", pSrv->uPort));
265 }
266 else
267 {
268 int rc2 = shClTransferHttpServerDestroyInternal(pSrv);
269 AssertRC(rc2);
270 }
271
272 if (RT_FAILURE(rc))
273 LogRel(("Shared Clipboard: HTTP server failed to run, rc=%Rrc\n", rc));
274
275 return rc;
276}
277
278/**
279 * Creates a new Shared Clipboard HTTP server instance.
280 *
281 * This does automatic probing of TCP ports if one already is being used.
282 *
283 * @returns VBox status code.
284 * @param pSrv HTTP server instance to create.
285 * @param puPort Where to return the TCP port number being used on success. Optional.
286 */
287int ShClTransferHttpServerCreate(PSHCLHTTPSERVER pSrv, uint16_t *puPort)
288{
289 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
290 /* puPort is optional. */
291
292 /** @todo Try favorite ports first (e.g. 8080, 8000, ...)? */
293
294 RTRAND hRand;
295 int rc = RTRandAdvCreateSystemFaster(&hRand); /* Should be good enough for this task. */
296 if (RT_SUCCESS(rc))
297 {
298 uint16_t uPort;
299 for (int i = 0; i < 32; i++)
300 {
301 uPort = RTRandAdvU32Ex(hRand, 1024, UINT16_MAX);
302 rc = ShClTransferHttpServerCreateEx(pSrv, (uint32_t)uPort);
303 if (RT_SUCCESS(rc))
304 {
305 if (puPort)
306 *puPort = uPort;
307 break;
308 }
309 }
310
311 RTRandAdvDestroy(hRand);
312 }
313
314 return rc;
315}
316
317/**
318 * Destroys a Shared Clipboard HTTP server instance.
319 *
320 * @returns VBox status code.
321 * @param pSrv HTTP server instance to destroy.
322 */
323int ShClTransferHttpServerDestroy(PSHCLHTTPSERVER pSrv)
324{
325 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
326
327 if (pSrv->hHTTPServer == NIL_RTHTTPSERVER)
328 return VINF_SUCCESS;
329
330 Assert(pSrv->cTransfers == 0); /* Sanity. */
331
332 int rc = RTHttpServerDestroy(pSrv->hHTTPServer);
333 if (RT_SUCCESS(rc))
334 rc = shClTransferHttpServerDestroyInternal(pSrv);
335
336 if (RT_SUCCESS(rc))
337 LogRel2(("Shared Clipboard: HTTP server stopped\n"));
338 else
339 LogRel(("Shared Clipboard: HTTP server failed to stop, rc=%Rrc\n", rc));
340
341 return rc;
342}
343
344/**
345 * Registers a Shared Clipboard transfer to a HTTP server instance.
346 *
347 * @returns VBox status code.
348 * @param pSrv HTTP server instance to register transfer for.
349 * @param pTransfer Transfer to register.
350 */
351int ShClTransferHttpServerRegisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
352{
353 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
354 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
355
356 shClTransferHttpServerLock(pSrv);
357
358 PSHCLHTTPSERVERTRANSFER pSrvTx = (PSHCLHTTPSERVERTRANSFER)RTMemAllocZ(sizeof(SHCLHTTPSERVERTRANSFER));
359 AssertPtrReturn(pSrvTx, VERR_NO_MEMORY);
360
361 RTUUID Uuid;
362 int rc = RTUuidCreate(&Uuid);
363 if (RT_SUCCESS(rc))
364 {
365 char szUuid[64];
366 rc = RTUuidToStr(&Uuid, szUuid, sizeof(szUuid));
367 if (RT_SUCCESS(rc))
368 {
369 AssertReturn(pTransfer->State.uID, VERR_INVALID_PARAMETER); /* Paranoia. */
370
371 /* Create the virtual HTTP path for the transfer.
372 * Every transfer has a dedicated HTTP path. */
373 ssize_t cch = RTStrPrintf2(pSrvTx->szPathVirtual, sizeof(pSrvTx->szPathVirtual), "%s/%RU16/", szUuid, pTransfer->State.uID);
374 AssertReturn(cch, VERR_BUFFER_OVERFLOW);
375
376 RTListAppend(&pSrv->lstTransfers, &pSrvTx->Node);
377
378 pSrv->cTransfers++;
379 }
380 }
381
382 if (RT_FAILURE(rc))
383 RTMemFree(pSrvTx);
384
385 shClTransferHttpServerUnlock(pSrv);
386
387 return rc;
388}
389
390/**
391 * Unregisters a formerly registered Shared Clipboard transfer.
392 *
393 * @returns VBox status code.
394 * @param pSrv HTTP server instance to unregister transfer from.
395 * @param pTransfer Transfer to unregister.
396 */
397int ShClTransferHttpServerUnregisterTransfer(PSHCLHTTPSERVER pSrv, PSHCLTRANSFER pTransfer)
398{
399 AssertPtrReturn(pSrv, VERR_INVALID_POINTER);
400 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
401
402 shClTransferHttpServerLock(pSrv);
403
404 AssertReturn(pSrv->cTransfers, VERR_WRONG_ORDER);
405
406 PSHCLHTTPSERVERTRANSFER pSrvTx;
407 RTListForEach(&pSrv->lstTransfers, pSrvTx, SHCLHTTPSERVERTRANSFER, Node)
408 {
409 AssertPtr(pSrvTx->pTransfer);
410 if (pSrvTx->pTransfer->State.uID == pTransfer->State.uID)
411 {
412 RTListNodeRemove(&pSrvTx->Node);
413
414 RTMemFree(pSrvTx);
415 pSrvTx = NULL;
416
417 Assert(pSrv->cTransfers);
418 pSrv->cTransfers--;
419
420 shClTransferHttpServerUnlock(pSrv);
421 return VINF_SUCCESS;
422 }
423 }
424
425 shClTransferHttpServerUnlock(pSrv);
426 return VERR_NOT_FOUND;
427}
428
429/**
430 * Returns whether a specific transfer ID is registered with a HTTP server instance or not.
431 *
432 * @returns \c true if the transfer ID is registered, \c false if not.
433 * @param pSrv HTTP server instance.
434 * @param idTransfer Transfer ID to check for.
435 */
436bool ShClTransferHttpServerHasTransfer(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
437{
438 AssertPtrReturn(pSrv, false);
439
440 shClTransferHttpServerLock(pSrv);
441
442 const bool fRc = shClTransferHttpServerGetTransferById(pSrv, idTransfer) != NULL;
443
444 shClTransferHttpServerUnlock(pSrv);
445
446 return fRc;
447}
448
449/**
450 * Returns the used TCP port number of a HTTP server instance.
451 *
452 * @returns TCP port number. 0 if not specified yet.
453 * @param pSrv HTTP server instance to return port for.
454 */
455uint16_t ShClTransferHttpServerGetPort(PSHCLHTTPSERVER pSrv)
456{
457 AssertPtrReturn(pSrv, 0);
458
459 shClTransferHttpServerLock(pSrv);
460
461 const uint16_t uPort = pSrv->uPort;
462
463 shClTransferHttpServerUnlock(pSrv);
464
465 return uPort;
466}
467
468/**
469 * Returns the number of registered HTTP server transfers of a HTTP server instance.
470 *
471 * @returns Number of registered transfers.
472 * @param pSrv HTTP server instance to return registered transfers for.
473 */
474uint32_t ShClTransferHttpServerGetTransferCount(PSHCLHTTPSERVER pSrv)
475{
476 AssertPtrReturn(pSrv, 0);
477
478 shClTransferHttpServerLock(pSrv);
479
480 const uint32_t cTransfers = pSrv->cTransfers;
481
482 shClTransferHttpServerUnlock(pSrv);
483
484 return cTransfers;
485}
486
487/**
488 * Returns the host name (scheme) of a HTTP server instance.
489 *
490 * @param pSrv HTTP server instance to return host name (scheme) for.
491 *
492 * @returns Host name (scheme).
493 */
494static const char *shClTransferHttpServerGetHost(PSHCLHTTPSERVER pSrv)
495{
496 RT_NOREF(pSrv);
497 return "http://localhost"; /* Hardcoded for now. */
498}
499
500/**
501 * Returns an allocated string with a HTTP server instance's address.
502 *
503 * @returns Allocated string with a HTTP server instance's address, or NULL on OOM.
504 * Needs to be free'd by the caller using RTStrFree().
505 * @param pSrv HTTP server instance to return address for.
506 */
507char *ShClTransferHttpServerGetAddressA(PSHCLHTTPSERVER pSrv)
508{
509 AssertPtrReturn(pSrv, NULL);
510
511 shClTransferHttpServerLock(pSrv);
512
513 char *pszAddress = RTStrAPrintf2("%s:%RU16", shClTransferHttpServerGetHost(pSrv), pSrv->uPort);
514 AssertPtr(pszAddress);
515
516 shClTransferHttpServerUnlock(pSrv);
517
518 return pszAddress;
519}
520
521/**
522 * Returns an allocated string with the URL of a given Shared Clipboard transfer ID.
523 *
524 * @returns Allocated string with the URL of a given Shared Clipboard transfer ID, or NULL if not found.
525 * Needs to be free'd by the caller using RTStrFree().
526 * @param pSrv HTTP server instance to return URL for.
527 */
528char *ShClTransferHttpServerGetUrlA(PSHCLHTTPSERVER pSrv, SHCLTRANSFERID idTransfer)
529{
530 AssertPtrReturn(pSrv, NULL);
531 AssertReturn(idTransfer != NIL_SHCLTRANSFERID, NULL);
532
533 shClTransferHttpServerLock(pSrv);
534
535 PSHCLHTTPSERVERTRANSFER pSrvTx = shClTransferHttpServerGetTransferById(pSrv, idTransfer);
536 if (!pSrvTx)
537 {
538 AssertFailed();
539 shClTransferHttpServerUnlock(pSrv);
540 return NULL;
541 }
542
543 AssertReturn(RTStrNLen(pSrvTx->szPathVirtual, RTPATH_MAX), NULL);
544 char *pszUrl = RTStrAPrintf2("%s:%RU16/%s", shClTransferHttpServerGetHost(pSrv), pSrv->uPort, pSrvTx->szPathVirtual);
545 AssertPtr(pszUrl);
546
547 shClTransferHttpServerUnlock(pSrv);
548
549 return pszUrl;
550}
551
552/**
553 * Returns whether a given HTTP server instance is running or not.
554 *
555 * @returns \c true if running, or \c false if not.
556 * @param pSrv HTTP server instance to check running state for.
557 */
558bool ShClTransferHttpServerIsRunning(PSHCLHTTPSERVER pSrv)
559{
560 AssertPtrReturn(pSrv, false);
561
562 return (pSrv->hHTTPServer != NIL_RTHTTPSERVER); /* Seems enough for now. */
563}
564
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette