📄 vnchttpconnect.cpp
字号:
// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved.
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// This file is part of the VNC system.
//
// The VNC system is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
//
// If the source code for the VNC system is not available from the place
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.
// vncHTTPConnect.cpp
// Implementation of the HTTP server class
#include "stdhdrs.h"
#include "VSocket.h"
#include "vncHTTPConnect.h"
#include "vncServer.h"
#include <omnithread.h>
#include "resource.h"
// HTTP messages / message formats
const char HTTP_MSG_OK[] = "HTTP/1.0 200 OK\n\n";
const char HTTP_FMT_INDEX[] =
"<HTML>\n"
" <HEAD><TITLE>TightVNC desktop [%.256s]</TITLE></HEAD>\n"
" <BODY>\n"
" <APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar WIDTH=%d HEIGHT=%d>\n"
" <PARAM NAME=\"PORT\" VALUE=\"%d\">\n"
"%.1024s"
" </APPLET><BR>\n"
" <A HREF=\"http://www.tightvnc.com/\">www.TightVNC.com</A>\n"
" </BODY>\n"
"</HTML>\n";
const char HTTP_MSG_NOSOCKCONN [] =
"<HTML>\n"
" <HEAD><TITLE>TightVNC desktop</TITLE></HEAD>\n"
" <BODY>\n"
" <H1>Connections Disabled</H1>\n"
" The requested desktop is not configured to accept incoming connections.\n"
" </BODY>\n"
"</HTML>\n";
const char HTTP_MSG_BADPARAMS [] =
"<HTML>\n"
" <HEAD><TITLE>TightVNC desktop</TITLE></HEAD>\n"
" <BODY>\n"
" <H1>Bad Parameters</H1>\n"
" The sequence of applet parameters specified within the URL is invalid.\n"
" </BODY>\n"
"</HTML>\n";
const char HTTP_MSG_NOSUCHFILE [] =
"HTTP/1.0 404 Not Found\n\n"
"<HTML>\n"
" <HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
" <BODY>\n"
" <H1>Not Found</H1>\n"
" The requested file could not be found.\n"
" </BODY>\n"
"</HTML>\n";
// Filename to resource ID mappings for the Java class files:
typedef struct _FileToResourceMap {
char *filename;
char *type;
int resourceID;
} FileMap;
const FileMap filemapping [] = {
{"/VncViewer.jar", "JavaArchive", IDR_VNCVIEWER_JAR},
{"/AuthPanel.class", "JavaClass", IDR_AUTHPANEL_CLASS},
{"/ClipboardFrame.class", "JavaClass", IDR_CLIPBOARDFRAME_CLASS},
{"/DesCipher.class", "JavaClass", IDR_DESCIPHER_CLASS},
{"/OptionsFrame.class", "JavaClass", IDR_OPTIONSFRAME_CLASS},
{"/RfbProto.class", "JavaClass", IDR_RFBPROTO_CLASS},
{"/VncCanvas.class", "JavaClass", IDR_VNCCANVAS_CLASS},
{"/VncCanvas2.class", "JavaClass", IDR_VNCCANVAS2_CLASS},
{"/VncViewer.class", "JavaClass", IDR_VNCVIEWER_CLASS},
{"/ButtonPanel.class", "JavaClass", IDR_BUTTONPANEL_CLASS},
{"/RecordingFrame.class", "JavaClass", IDR_RECFRAME_CLASS},
{"/SessionRecorder.class", "JavaClass", IDR_SESSIONREC_CLASS},
{"/ReloginPanel.class", "JavaClass", IDR_RELOGINPANEL_CLASS},
{"/SocketFactory.class", "JavaClass", IDR_SOCKFACTORY_CLASS},
{"/CapabilityInfo.class", "JavaClass", IDR_CAPINFO_CLASS},
{"/CapsContainer.class", "JavaClass", IDR_CAPSCONTAINER_CLASS},
{"/InStream.class", "JavaClass", IDR_INSTREAM_CLASS},
{"/MemInStream.class", "JavaClass", IDR_MEMINSTREAM_CLASS},
{"/ZlibInStream.class", "JavaClass", IDR_ZLIBINSTREAM_CLASS},
};
const int filemappingsize = 19;
//
// Connection thread -- one per each client connection.
//
class vncHTTPConnectThread : public omni_thread
{
public:
// Init routine
virtual BOOL Init(VSocket *socket, vncServer *server, BOOL allow_params);
// Code to be executed by the thread
virtual void run(void *arg);
// Routines to handle HTTP requests
virtual void DoHTTP(VSocket *socket);
virtual BOOL ParseParams(const char *request, char *result, int max_bytes);
BOOL ValidateString(char *str);
virtual char *ReadLine(VSocket *socket, char delimiter, int max);
protected:
// Fields used internally
vncServer *m_server;
VSocket *m_socket;
BOOL m_allow_params;
};
// Method implementations
BOOL vncHTTPConnectThread::Init(VSocket *socket, vncServer *server,
BOOL allow_params)
{
m_server = server;
m_socket = socket;
m_allow_params = allow_params;
// Start the thread
start();
return TRUE;
}
// Code to be executed by the thread
void vncHTTPConnectThread::run(void *arg)
{
vnclog.Print(LL_INTINFO, VNCLOG("started HTTP client thread\n"));
// Perform the transaction
DoHTTP(m_socket);
// And close the client
delete m_socket;
vnclog.Print(LL_INTINFO, VNCLOG("quitting HTTP client thread\n"));
}
void vncHTTPConnectThread::DoHTTP(VSocket *socket)
{
char filename[1024];
char *line;
// Read in the HTTP header
if ((line = ReadLine(socket, '\n', 1024)) == NULL)
return;
// Scan the header for the filename and free the storage
int result = sscanf(line, "GET %s HTTP/", filename);
delete [] line;
if (result != 1)
return;
vnclog.Print(LL_CLIENTS, VNCLOG("file %s requested\n"), filename);
// Read in the rest of the browser's request data and discard...
BOOL newline = TRUE;
char c;
for (;;) {
if (!socket->ReadExact(&c, 1))
return;
if (c == '\n') {
if (newline)
break;
newline = TRUE;
} else {
if (c >= ' ')
newline = FALSE;
}
}
vnclog.Print(LL_INTINFO, VNCLOG("HTTP headers skipped\n"));
if (filename[0] != '/') {
vnclog.Print(LL_CONNERR, VNCLOG("filename didn't begin with '/'\n"));
socket->SendExact(HTTP_MSG_NOSUCHFILE, strlen(HTTP_MSG_NOSUCHFILE));
return;
}
// Switch, dependent upon the filename:
if (filename[1] == '\0' || (m_allow_params && filename[1] == '?'))
{
char indexpage[2048];
vnclog.Print(LL_CLIENTS, VNCLOG("sending main page\n"));
if (filename[1] == '?')
vnclog.Print(LL_INTWARN, VNCLOG("applet parameters specified\n"));
// Send the OK notification message to the client
if (!socket->SendExact(HTTP_MSG_OK, strlen(HTTP_MSG_OK)))
return;
// Compose the index page
if (m_server->SockConnected())
{
int width, height, depth;
// Get the screen's dimensions
m_server->GetScreenInfo(width, height, depth);
// Get the name of this desktop
char desktopname[MAX_COMPUTERNAME_LENGTH + 1];
DWORD desktopnamelen = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(desktopname, &desktopnamelen))
{
// Make the name lowercase
for (size_t x = 0; x < strlen(desktopname); x++)
{
desktopname[x] = tolower(desktopname[x]);
}
}
else
{
strcpy(desktopname, "WinVNC");
}
// Parse the applet parameters if specified within URL
char params[1024] = "";
if (filename[1] == '?') {
if (!ParseParams(&filename[2], params, 1024)) {
socket->SendExact(HTTP_MSG_BADPARAMS,
strlen(HTTP_MSG_BADPARAMS));
return;
}
}
// Send the java applet page
sprintf(indexpage, HTTP_FMT_INDEX,
desktopname, width, height+32, m_server->GetPort(), params);
}
else
{
// Send a "sorry, not allowed" page
sprintf(indexpage, HTTP_MSG_NOSOCKCONN);
}
// Send the page
if (socket->SendExact(indexpage, strlen(indexpage)))
vnclog.Print(LL_INTINFO, VNCLOG("sent page\n"));
return;
}
// File requested was not the index so check the mappings
// list for a different file.
// Now search the mappings for the desired file
for (int x=0; x < filemappingsize; x++)
{
if (strcmp(filename, filemapping[x].filename) == 0)
{
HRSRC resource;
HGLOBAL resourcehan;
char *resourceptr;
int resourcesize;
vnclog.Print(LL_INTINFO, VNCLOG("requested file recognised\n"));
// Find the resource here
resource = FindResource(NULL,
MAKEINTRESOURCE(filemapping[x].resourceID),
filemapping[x].type
);
if (resource == NULL)
return;
// Get its size
resourcesize = SizeofResource(NULL, resource);
// Load the resource
resourcehan = LoadResource(NULL, resource);
if (resourcehan == NULL)
return;
// Lock the resource
resourceptr = (char *)LockResource(resourcehan);
if (resourceptr == NULL)
return;
vnclog.Print(LL_INTINFO, VNCLOG("sending file...\n"));
// Send the OK message
if (!socket->SendExact(HTTP_MSG_OK, strlen(HTTP_MSG_OK)))
return;
// Now send the entirety of the data to the client
if (!socket->SendExact(resourceptr, resourcesize))
return;
vnclog.Print(LL_INTINFO, VNCLOG("file successfully sent\n"));
return;
}
}
// Send the NoSuchFile notification message to the client
if (!socket->SendExact(HTTP_MSG_NOSUCHFILE, strlen(HTTP_MSG_NOSUCHFILE)))
return;
}
//
// Parse the request tail after the '?' character, and format a sequence
// of <param> tags for the index HTML page with embedded applet.
//
BOOL vncHTTPConnectThread::ParseParams(const char *request,
char *result, int max_bytes)
{
char param_request[128];
char param_formatted[196];
result[0] = '\0';
int cur_bytes = 0;
const char *tail = request;
for (;;) {
// Copy individual "name=value" string into a buffer
char *delim_ptr = strchr((char *)tail, '&');
if (delim_ptr == NULL) {
if (strlen(tail) >= sizeof(param_request)) {
return FALSE;
}
strcpy(param_request, tail);
} else {
int len = delim_ptr - tail;
if (len >= sizeof(param_request)) {
return FALSE;
}
memcpy(param_request, tail, len);
param_request[len] = '\0';
}
// Split the request into parameter name and value
char *value_str = strchr(¶m_request[1], '=');
if (value_str == NULL) {
return FALSE;
}
*value_str++ = '\0';
if (strlen(value_str) == 0) {
return FALSE;
}
// Validate both parameter name and value
if (!ValidateString(param_request) || !ValidateString(value_str)) {
return FALSE;
}
// Prepare HTML-formatted representation of the name=value pair
int len = sprintf(param_formatted,
" <PARAM NAME=\"%s\" VALUE=\"%s\">\n",
param_request, value_str);
if (cur_bytes + len + 1 > max_bytes) {
return FALSE;
}
strcat(result, param_formatted);
cur_bytes += len;
// Go to the next parameter
if (delim_ptr == NULL) {
break;
}
tail = delim_ptr + 1;
}
return TRUE;
}
//
// Check if the string consists only of alphanumeric characters, '+' signs,
// underscores, and dots. Replace all '+' signs with spaces.
//
BOOL vncHTTPConnectThread::ValidateString(char *str)
{
for (char *ptr = str; *ptr != '\0'; ptr++) {
if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') {
if (*ptr == '+') {
*ptr = ' ';
} else {
return FALSE;
}
}
}
return TRUE;
}
char *vncHTTPConnectThread::ReadLine(VSocket *socket, char delimiter, int max)
{
// Allocate the maximum required buffer
char *buffer = new char[max+1];
int buffpos = 0;
// Read in data until a delimiter is read
for (;;)
{
char c;
if (!socket->ReadExact(&c, 1))
{
delete [] buffer;
return NULL;
}
if (c == delimiter)
{
buffer[buffpos] = 0;
return buffer;
}
buffer[buffpos] = c;
buffpos++;
if (buffpos == (max-1))
{
buffer[buffpos] = 0;
return buffer;
}
}
}
//
// Listening thread.
//
class vncHTTPListenThread : public omni_thread
{
public:
// Init routine
virtual BOOL Init(VSocket *socket, vncServer *server, BOOL allow_params);
// Code to be executed by the thread
virtual void *run_undetached(void * arg);
// Fields used internally
BOOL m_shutdown;
protected:
vncServer *m_server;
VSocket *m_listen_socket;
BOOL m_allow_params;
};
BOOL vncHTTPListenThread::Init(VSocket *listen_socket, vncServer *server,
BOOL allow_params)
{
m_server = server;
m_listen_socket = listen_socket;
m_allow_params = allow_params;
// Start the thread
m_shutdown = FALSE;
start_undetached();
return TRUE;
}
// Code to be executed by the thread
void *vncHTTPListenThread::run_undetached(void * arg)
{
vnclog.Print(LL_INTINFO, VNCLOG("started HTTP server thread\n"));
// Go into a loop, listening for connections on the given socket
while (!m_shutdown) {
// Accept an incoming connection
VSocket *new_socket;
if (!m_listen_socket->TryAccept(&new_socket, 100))
break;
if (new_socket != NULL) {
// Start a client thread for this connection
vnclog.Print(LL_CLIENTS, VNCLOG("HTTP client connected\n"));
omni_thread *m_thread = new vncHTTPConnectThread;
if (m_thread == NULL)
break;
((vncHTTPConnectThread *)m_thread)->Init(new_socket, m_server,
m_allow_params);
}
}
vnclog.Print(LL_INTINFO, VNCLOG("quitting HTTP server thread\n"));
return NULL;
}
//
// The vncSockConnect class implementation
//
vncHTTPConnect::vncHTTPConnect()
{
m_listen_thread = NULL;
}
BOOL vncHTTPConnect::Init(vncServer *server, UINT listen_port, BOOL allow_params)
{
// Save the port number
m_listen_port = listen_port;
// Create the listening socket
if (!m_listen_socket.Create())
return FALSE;
// Bind it
if (!m_listen_socket.Bind(m_listen_port, server->LoopbackOnly()))
return FALSE;
// Set it to listen
if (!m_listen_socket.Listen())
return FALSE;
// Create the new thread
m_listen_thread = new vncHTTPListenThread;
if (m_listen_thread == NULL)
return FALSE;
// And start it running
return ((vncHTTPListenThread *)m_listen_thread)->Init(&m_listen_socket, server,
allow_params);
}
vncHTTPConnect::~vncHTTPConnect()
{
m_listen_socket.Shutdown();
// Join with our lovely thread
if (m_listen_thread != NULL) {
((vncHTTPListenThread *)m_listen_thread)->m_shutdown = TRUE;
void *returnval;
m_listen_thread->join(&returnval);
m_listen_thread = NULL;
m_listen_socket.Close();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -