Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

x0vncserver.cxx 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. // FIXME: Check cases when screen width/height is not a multiply of 32.
  20. // e.g. 800x600.
  21. #include <strings.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <unistd.h>
  25. #include <errno.h>
  26. #include <rfb/Logger_stdio.h>
  27. #include <rfb/LogWriter.h>
  28. #include <rfb/VNCServerST.h>
  29. #include <rfb/Configuration.h>
  30. #include <rfb/SSecurityFactoryStandard.h>
  31. #include <rfb/Timer.h>
  32. #include <network/TcpSocket.h>
  33. #include <tx/TXWindow.h>
  34. #include <vncconfig/QueryConnectDialog.h>
  35. #include <signal.h>
  36. #include <X11/X.h>
  37. #include <X11/Xlib.h>
  38. #include <X11/Xutil.h>
  39. #ifdef HAVE_XTEST
  40. #include <X11/extensions/XTest.h>
  41. #endif
  42. #include <x0vncserver/Geometry.h>
  43. #include <x0vncserver/Image.h>
  44. #include <x0vncserver/XPixelBuffer.h>
  45. #include <x0vncserver/PollingScheduler.h>
  46. // XXX Lynx/OS 2.3: protos for select(), bzero()
  47. #ifdef Lynx
  48. #include <sys/proto.h>
  49. #endif
  50. extern char buildtime[];
  51. using namespace rfb;
  52. using namespace network;
  53. static LogWriter vlog("Main");
  54. IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
  55. "cycle; actual interval may be dynamically "
  56. "adjusted to satisfy MaxProcessorUsage setting", 30);
  57. IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
  58. "CPU time to be consumed", 35);
  59. BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
  60. BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
  61. "IRIX or Solaris", true);
  62. StringParameter displayname("display", "The X display", "");
  63. IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
  64. IntParameter queryConnectTimeout("QueryConnectTimeout",
  65. "Number of seconds to show the Accept Connection dialog before "
  66. "rejecting the connection",
  67. 10);
  68. StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
  69. //
  70. // Allow the main loop terminate itself gracefully on receiving a signal.
  71. //
  72. static bool caughtSignal = false;
  73. static void CleanupSignalHandler(int sig)
  74. {
  75. caughtSignal = true;
  76. }
  77. class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
  78. public QueryResultCallback {
  79. public:
  80. QueryConnHandler(Display* dpy, VNCServerST* vs)
  81. : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
  82. ~QueryConnHandler() { delete queryConnectDialog; }
  83. // -=- VNCServerST::QueryConnectionHandler interface
  84. virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
  85. const char* userName,
  86. char** reason) {
  87. if (queryConnectSock) {
  88. *reason = strDup("Another connection is currently being queried.");
  89. return VNCServerST::REJECT;
  90. }
  91. if (!userName) userName = "(anonymous)";
  92. queryConnectSock = sock;
  93. CharArray address(sock->getPeerAddress());
  94. delete queryConnectDialog;
  95. queryConnectDialog = new QueryConnectDialog(display, address.buf,
  96. userName, queryConnectTimeout,
  97. this);
  98. queryConnectDialog->map();
  99. return VNCServerST::PENDING;
  100. }
  101. // -=- QueryResultCallback interface
  102. virtual void queryApproved() {
  103. server->approveConnection(queryConnectSock, true, 0);
  104. queryConnectSock = 0;
  105. }
  106. virtual void queryRejected() {
  107. server->approveConnection(queryConnectSock, false,
  108. "Connection rejected by local user");
  109. queryConnectSock = 0;
  110. }
  111. private:
  112. Display* display;
  113. VNCServerST* server;
  114. QueryConnectDialog* queryConnectDialog;
  115. network::Socket* queryConnectSock;
  116. };
  117. class XDesktop : public SDesktop, public ColourMap
  118. {
  119. public:
  120. XDesktop(Display* dpy_, Geometry *geometry_)
  121. : dpy(dpy_), geometry(geometry_), pb(0), server(0),
  122. oldButtonMask(0), haveXtest(false), maxButtons(0), running(false)
  123. {
  124. #ifdef HAVE_XTEST
  125. int xtestEventBase;
  126. int xtestErrorBase;
  127. int major, minor;
  128. if (XTestQueryExtension(dpy, &xtestEventBase,
  129. &xtestErrorBase, &major, &minor)) {
  130. XTestGrabControl(dpy, True);
  131. vlog.info("XTest extension present - version %d.%d",major,minor);
  132. haveXtest = true;
  133. } else {
  134. #endif
  135. vlog.info("XTest extension not present");
  136. vlog.info("Unable to inject events or display while server is grabbed");
  137. #ifdef HAVE_XTEST
  138. }
  139. #endif
  140. }
  141. virtual ~XDesktop() {
  142. stop();
  143. }
  144. inline void poll() {
  145. if (pb)
  146. pb->poll(server);
  147. }
  148. // -=- SDesktop interface
  149. virtual void start(VNCServer* vs) {
  150. // Determine actual number of buttons of the X pointer device.
  151. unsigned char btnMap[8];
  152. int numButtons = XGetPointerMapping(dpy, btnMap, 8);
  153. maxButtons = (numButtons > 8) ? 8 : numButtons;
  154. vlog.info("Enabling %d button%s of X pointer device",
  155. maxButtons, (maxButtons != 1) ? "s" : "");
  156. // Create an ImageFactory instance for producing Image objects.
  157. ImageFactory factory((bool)useShm, (bool)useOverlay);
  158. // Create pixel buffer and provide it to the server object.
  159. pb = new XPixelBuffer(dpy, factory, geometry->getRect(), this);
  160. vlog.info("Allocated %s", pb->getImage()->classDesc());
  161. server = (VNCServerST *)vs;
  162. server->setPixelBuffer(pb);
  163. running = true;
  164. }
  165. virtual void stop() {
  166. running = false;
  167. delete pb;
  168. pb = 0;
  169. }
  170. inline bool isRunning() {
  171. return running;
  172. }
  173. virtual void pointerEvent(const Point& pos, int buttonMask) {
  174. #ifdef HAVE_XTEST
  175. if (!haveXtest) return;
  176. XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
  177. geometry->offsetLeft() + pos.x,
  178. geometry->offsetTop() + pos.y,
  179. CurrentTime);
  180. if (buttonMask != oldButtonMask) {
  181. for (int i = 0; i < maxButtons; i++) {
  182. if ((buttonMask ^ oldButtonMask) & (1<<i)) {
  183. if (buttonMask & (1<<i)) {
  184. XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
  185. } else {
  186. XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
  187. }
  188. }
  189. }
  190. }
  191. oldButtonMask = buttonMask;
  192. #endif
  193. }
  194. virtual void keyEvent(rdr::U32 key, bool down) {
  195. #ifdef HAVE_XTEST
  196. if (!haveXtest) return;
  197. int keycode = XKeysymToKeycode(dpy, key);
  198. if (keycode)
  199. XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
  200. #endif
  201. }
  202. virtual void clientCutText(const char* str, int len) {
  203. }
  204. virtual Point getFbSize() {
  205. return Point(pb->width(), pb->height());
  206. }
  207. // -=- ColourMap callbacks
  208. virtual void lookup(int index, int* r, int* g, int* b) {
  209. XColor xc;
  210. xc.pixel = index;
  211. if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
  212. XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
  213. } else {
  214. xc.red = xc.green = xc.blue = 0;
  215. }
  216. *r = xc.red;
  217. *g = xc.green;
  218. *b = xc.blue;
  219. }
  220. protected:
  221. Display* dpy;
  222. Geometry* geometry;
  223. XPixelBuffer* pb;
  224. VNCServerST* server;
  225. int oldButtonMask;
  226. bool haveXtest;
  227. int maxButtons;
  228. bool running;
  229. };
  230. class FileTcpFilter : public TcpFilter
  231. {
  232. public:
  233. FileTcpFilter(const char *fname)
  234. : TcpFilter("-"), fileName(NULL), lastModTime(0)
  235. {
  236. if (fname != NULL)
  237. fileName = strdup((char *)fname);
  238. }
  239. virtual ~FileTcpFilter()
  240. {
  241. if (fileName != NULL)
  242. free(fileName);
  243. }
  244. virtual bool verifyConnection(Socket* s)
  245. {
  246. if (!reloadRules()) {
  247. vlog.error("Could not read IP filtering rules: rejecting all clients");
  248. filter.clear();
  249. filter.push_back(parsePattern("-"));
  250. return false;
  251. }
  252. return TcpFilter::verifyConnection(s);
  253. }
  254. protected:
  255. bool reloadRules()
  256. {
  257. if (fileName == NULL)
  258. return true;
  259. struct stat st;
  260. if (stat(fileName, &st) != 0)
  261. return false;
  262. if (st.st_mtime != lastModTime) {
  263. // Actually reload only if the file was modified
  264. FILE *fp = fopen(fileName, "r");
  265. if (fp == NULL)
  266. return false;
  267. // Remove all the rules from the parent class
  268. filter.clear();
  269. // Parse the file contents adding rules to the parent class
  270. char buf[32];
  271. while (readLine(buf, 32, fp)) {
  272. if (buf[0] && strchr("+-?", buf[0])) {
  273. filter.push_back(parsePattern(buf));
  274. }
  275. }
  276. fclose(fp);
  277. lastModTime = st.st_mtime;
  278. }
  279. return true;
  280. }
  281. protected:
  282. char *fileName;
  283. time_t lastModTime;
  284. private:
  285. //
  286. // NOTE: we silently truncate long lines in this function.
  287. //
  288. bool readLine(char *buf, int bufSize, FILE *fp)
  289. {
  290. if (fp == NULL || buf == NULL || bufSize == 0)
  291. return false;
  292. if (fgets(buf, bufSize, fp) == NULL)
  293. return false;
  294. char *ptr = strchr(buf, '\n');
  295. if (ptr != NULL) {
  296. *ptr = '\0'; // remove newline at the end
  297. } else {
  298. if (!feof(fp)) {
  299. int c;
  300. do { // skip the rest of a long line
  301. c = getc(fp);
  302. } while (c != '\n' && c != EOF);
  303. }
  304. }
  305. return true;
  306. }
  307. };
  308. char* programName;
  309. static void printVersion(FILE *fp)
  310. {
  311. fprintf(fp, "TigerVNC Server version %s, built %s\n",
  312. PACKAGE_VERSION, buildtime);
  313. }
  314. static void usage()
  315. {
  316. printVersion(stderr);
  317. fprintf(stderr, "\nUsage: %s [<parameters>]\n", programName);
  318. fprintf(stderr, " %s --version\n", programName);
  319. fprintf(stderr,"\n"
  320. "Parameters can be turned on with -<param> or off with -<param>=0\n"
  321. "Parameters which take a value can be specified as "
  322. "-<param> <value>\n"
  323. "Other valid forms are <param>=<value> -<param>=<value> "
  324. "--<param>=<value>\n"
  325. "Parameter names are case-insensitive. The parameters are:\n\n");
  326. Configuration::listParams(79, 14);
  327. exit(1);
  328. }
  329. int main(int argc, char** argv)
  330. {
  331. initStdIOLoggers();
  332. LogWriter::setLogParams("*:stderr:30");
  333. programName = argv[0];
  334. Display* dpy;
  335. for (int i = 1; i < argc; i++) {
  336. if (Configuration::setParam(argv[i]))
  337. continue;
  338. if (argv[i][0] == '-') {
  339. if (i+1 < argc) {
  340. if (Configuration::setParam(&argv[i][1], argv[i+1])) {
  341. i++;
  342. continue;
  343. }
  344. }
  345. if (strcmp(argv[i], "-v") == 0 ||
  346. strcmp(argv[i], "-version") == 0 ||
  347. strcmp(argv[i], "--version") == 0) {
  348. printVersion(stdout);
  349. return 0;
  350. }
  351. usage();
  352. }
  353. usage();
  354. }
  355. CharArray dpyStr(displayname.getData());
  356. if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
  357. // FIXME: Why not vlog.error(...)?
  358. fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
  359. programName, XDisplayName(dpyStr.buf));
  360. exit(1);
  361. }
  362. signal(SIGHUP, CleanupSignalHandler);
  363. signal(SIGINT, CleanupSignalHandler);
  364. signal(SIGTERM, CleanupSignalHandler);
  365. try {
  366. TXWindow::init(dpy,"x0vncserver");
  367. Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
  368. DisplayHeight(dpy, DefaultScreen(dpy)));
  369. if (geo.getRect().is_empty()) {
  370. vlog.error("Exiting with error");
  371. return 1;
  372. }
  373. XDesktop desktop(dpy, &geo);
  374. VNCServerST server("x0vncserver", &desktop);
  375. QueryConnHandler qcHandler(dpy, &server);
  376. server.setQueryConnectionHandler(&qcHandler);
  377. TcpListener listener((int)rfbport);
  378. vlog.info("Listening on port %d", (int)rfbport);
  379. const char *hostsData = hostsFile.getData();
  380. FileTcpFilter fileTcpFilter(hostsData);
  381. if (strlen(hostsData) != 0)
  382. listener.setFilter(&fileTcpFilter);
  383. delete[] hostsData;
  384. PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
  385. while (!caughtSignal) {
  386. struct timeval tv;
  387. fd_set rfds;
  388. std::list<Socket*> sockets;
  389. std::list<Socket*>::iterator i;
  390. // Process any incoming X events
  391. TXWindow::handleXEvents(dpy);
  392. FD_ZERO(&rfds);
  393. FD_SET(listener.getFd(), &rfds);
  394. server.getSockets(&sockets);
  395. int clients_connected = 0;
  396. for (i = sockets.begin(); i != sockets.end(); i++) {
  397. if ((*i)->isShutdown()) {
  398. server.removeSocket(*i);
  399. delete (*i);
  400. } else {
  401. FD_SET((*i)->getFd(), &rfds);
  402. clients_connected++;
  403. }
  404. }
  405. if (!clients_connected)
  406. sched.reset();
  407. if (sched.isRunning()) {
  408. int wait_ms = sched.millisRemaining();
  409. if (wait_ms > 500) {
  410. wait_ms = 500;
  411. }
  412. tv.tv_usec = wait_ms * 1000;
  413. #ifdef DEBUG
  414. // fprintf(stderr, "[%d]\t", wait_ms);
  415. #endif
  416. } else {
  417. tv.tv_usec = 100000;
  418. }
  419. tv.tv_sec = 0;
  420. // Do the wait...
  421. sched.sleepStarted();
  422. int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
  423. sched.sleepFinished();
  424. if (n < 0) {
  425. if (errno == EINTR) {
  426. vlog.debug("Interrupted select() system call");
  427. continue;
  428. } else {
  429. throw rdr::SystemException("select", errno);
  430. }
  431. }
  432. // Accept new VNC connections
  433. if (FD_ISSET(listener.getFd(), &rfds)) {
  434. Socket* sock = listener.accept();
  435. if (sock) {
  436. server.addSocket(sock);
  437. } else {
  438. vlog.status("Client connection rejected");
  439. }
  440. }
  441. Timer::checkTimeouts();
  442. server.checkTimeouts();
  443. // Client list could have been changed.
  444. server.getSockets(&sockets);
  445. // Nothing more to do if there are no client connections.
  446. if (sockets.empty())
  447. continue;
  448. // Process events on existing VNC connections
  449. for (i = sockets.begin(); i != sockets.end(); i++) {
  450. if (FD_ISSET((*i)->getFd(), &rfds))
  451. server.processSocketEvent(*i);
  452. }
  453. if (desktop.isRunning() && sched.goodTimeToPoll()) {
  454. sched.newPass();
  455. desktop.poll();
  456. }
  457. }
  458. } catch (rdr::Exception &e) {
  459. vlog.error(e.str());
  460. return 1;
  461. }
  462. TXWindow::handleXEvents(dpy);
  463. vlog.info("Terminated");
  464. return 0;
  465. }