|
|
|
/*
|
|
|
|
Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
This file is part of x11vnc.
|
|
|
|
|
|
|
|
x11vnc 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.
|
|
|
|
|
|
|
|
x11vnc 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 x11vnc; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
|
|
|
|
or see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
In addition, as a special exception, Karl J. Runge
|
|
|
|
gives permission to link the code of its release of x11vnc with the
|
|
|
|
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
|
|
that use the same license as the "OpenSSL" library), and distribute
|
|
|
|
the linked executables. You must obey the GNU General Public License
|
|
|
|
in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
modify this file, you may extend this exception to your version of the
|
|
|
|
file, but you are not obligated to do so. If you do not wish to do
|
|
|
|
so, delete this exception statement from your version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* -- avahi.c -- */
|
|
|
|
|
|
|
|
#include "x11vnc.h"
|
|
|
|
#include "connections.h"
|
|
|
|
#include "cleanup.h"
|
|
|
|
|
|
|
|
void avahi_initialise(void);
|
|
|
|
void avahi_advertise(const char *name, const char *host, const uint16_t port);
|
|
|
|
void avahi_reset(void);
|
|
|
|
void avahi_cleanup(void);
|
|
|
|
|
|
|
|
static pid_t avahi_pid = 0;
|
|
|
|
|
|
|
|
static void kill_avahi_pid(void) {
|
|
|
|
if (avahi_pid != 0) {
|
|
|
|
kill(avahi_pid, SIGTERM);
|
|
|
|
avahi_pid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int try_avahi_helper(const char *name, const char *host, const uint16_t port) {
|
|
|
|
#if LIBVNCSERVER_HAVE_FORK
|
|
|
|
char *cmd, *p, *path = getenv("PATH"), portstr[32];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!name || !host || !port) {}
|
|
|
|
|
|
|
|
/* avahi-publish */
|
|
|
|
if (no_external_cmds || !cmd_ok("zeroconf")) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = strdup(path);
|
|
|
|
cmd = (char *) malloc(strlen(path) + 100);
|
|
|
|
sprintf(portstr, "%d", (int) port);
|
|
|
|
|
|
|
|
p = strtok(path, ":");
|
|
|
|
while (p) {
|
|
|
|
struct stat sbuf;
|
|
|
|
|
|
|
|
sprintf(cmd, "%s/avahi-publish", p);
|
|
|
|
if (stat(cmd, &sbuf) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sprintf(cmd, "%s/dns-sd", p);
|
|
|
|
if (stat(cmd, &sbuf) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sprintf(cmd, "%s/mDNS", p);
|
|
|
|
if (stat(cmd, &sbuf) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cmd[0] = '\0';
|
|
|
|
|
|
|
|
p = strtok(NULL, ":");
|
|
|
|
}
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
if (!strcmp(cmd, "")) {
|
|
|
|
free(cmd);
|
|
|
|
rfbLog("Could not find an external avahi/zeroconf helper program.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
avahi_pid = fork();
|
|
|
|
|
|
|
|
if (avahi_pid < 0) {
|
|
|
|
rfbLogPerror("fork");
|
|
|
|
avahi_pid = 0;
|
|
|
|
free(cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avahi_pid != 0) {
|
|
|
|
int status;
|
|
|
|
|
|
|
|
usleep(500 * 1000);
|
|
|
|
waitpid(avahi_pid, &status, WNOHANG);
|
|
|
|
if (kill(avahi_pid, 0) != 0) {
|
|
|
|
waitpid(avahi_pid, &status, WNOHANG);
|
|
|
|
avahi_pid = 0;
|
|
|
|
free(cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (! quiet) {
|
|
|
|
rfbLog("%s helper pid is: %d\n", cmd, (int) avahi_pid);
|
|
|
|
}
|
|
|
|
free(cmd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=3; i<256; i++) {
|
|
|
|
close(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(cmd, "/avahi-publish")) {
|
|
|
|
execlp(cmd, cmd, "-s", name, "_rfb._tcp", portstr, (char *) NULL);
|
|
|
|
} else {
|
|
|
|
execlp(cmd, cmd, "-R", name, "_rfb._tcp", ".", portstr, (char *) NULL);
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
#else
|
|
|
|
if (!name || !host || !port) {}
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(LIBVNCSERVER_HAVE_AVAHI) || !defined(LIBVNCSERVER_HAVE_LIBPTHREAD)
|
|
|
|
void avahi_initialise(void) {
|
|
|
|
rfbLog("avahi_initialise: no Avahi support at buildtime.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_advertise(const char *name, const char *host, const uint16_t port) {
|
|
|
|
if (!try_avahi_helper(name, host, port)) {
|
|
|
|
rfbLog("avahi_advertise: no Avahi support at buildtime.\n");
|
|
|
|
avahi = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_reset(void) {
|
|
|
|
kill_avahi_pid();
|
|
|
|
rfbLog("avahi_reset: no Avahi support at buildtime.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_cleanup(void) {
|
|
|
|
kill_avahi_pid();
|
|
|
|
rfbLog("avahi_cleanup: no Avahi support at buildtime.\n");
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
|
|
#include <avahi-common/thread-watch.h>
|
|
|
|
#include <avahi-common/alternative.h>
|
|
|
|
#include <avahi-client/client.h>
|
|
|
|
#include <avahi-client/publish.h>
|
|
|
|
|
|
|
|
#include <avahi-common/malloc.h>
|
|
|
|
#include <avahi-common/error.h>
|
|
|
|
|
|
|
|
|
|
|
|
static AvahiThreadedPoll *_poll = NULL;
|
|
|
|
static AvahiClient *_client = NULL;
|
|
|
|
static AvahiEntryGroup *_group = NULL;
|
|
|
|
|
|
|
|
static int db = 0;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
const char *name;
|
|
|
|
const char *host;
|
|
|
|
uint16_t port;
|
|
|
|
} avahi_service_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char *name;
|
|
|
|
char *host;
|
|
|
|
uint16_t port;
|
|
|
|
} avahi_reg_t;
|
|
|
|
|
|
|
|
#define NREG 16
|
|
|
|
static avahi_reg_t registered[NREG];
|
|
|
|
|
|
|
|
void avahi_initialise(void) {
|
|
|
|
int ret;
|
|
|
|
static int first = 1;
|
|
|
|
|
|
|
|
if (getenv("AVAHI_DEBUG")) {
|
|
|
|
db = 1;
|
|
|
|
}
|
|
|
|
if (first) {
|
|
|
|
int i;
|
|
|
|
for (i=0; i<NREG; i++) {
|
|
|
|
registered[i].name = NULL;
|
|
|
|
registered[i].host = NULL;
|
|
|
|
}
|
|
|
|
first = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (db) fprintf(stderr, "in avahi_initialise\n");
|
|
|
|
if (_poll) {
|
|
|
|
if (db) fprintf(stderr, " avahi_initialise: poll not null\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! (_poll = avahi_threaded_poll_new()) ) {
|
|
|
|
rfbLog("warning: unable to open Avahi poll.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_client = avahi_client_new(avahi_threaded_poll_get(_poll),
|
|
|
|
0, NULL, NULL, &ret);
|
|
|
|
if (! _client) {
|
|
|
|
rfbLog("warning: unable to open Avahi client: %s\n",
|
|
|
|
avahi_strerror(ret));
|
|
|
|
|
|
|
|
avahi_threaded_poll_free(_poll);
|
|
|
|
_poll = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avahi_threaded_poll_start(_poll) < 0) {
|
|
|
|
rfbLog("warning: unable to start Avahi poll.\n");
|
|
|
|
avahi_client_free(_client);
|
|
|
|
_client = NULL;
|
|
|
|
avahi_threaded_poll_free(_poll);
|
|
|
|
_poll = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (db) fprintf(stderr, "out avahi_initialise\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _avahi_create_services(const char *name, const char *host,
|
|
|
|
const uint16_t port);
|
|
|
|
|
|
|
|
static void _avahi_entry_group_callback(AvahiEntryGroup *g,
|
|
|
|
AvahiEntryGroupState state, void *userdata) {
|
|
|
|
char *new_name;
|
|
|
|
avahi_service_t *svc = (avahi_service_t *)userdata;
|
|
|
|
|
|
|
|
if (db) fprintf(stderr, "in _avahi_entry_group_callback %d 0x%p\n", state, svc);
|
|
|
|
if (g != _group && _group != NULL) {
|
|
|
|
rfbLog("avahi_entry_group_callback fatal error (group).\n");
|
|
|
|
clean_up_exit(1);
|
|
|
|
}
|
|
|
|
if (userdata == NULL) {
|
|
|
|
rfbLog("avahi_entry_group_callback fatal error (userdata).\n");
|
|
|
|
clean_up_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(state) {
|
|
|
|
case AVAHI_ENTRY_GROUP_ESTABLISHED:
|
|
|
|
rfbLog("Avahi group %s established.\n", svc->name);
|
|
|
|
#if 0 /* is this the segv problem? */
|
|
|
|
free(svc);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case AVAHI_ENTRY_GROUP_COLLISION:
|
|
|
|
new_name = avahi_alternative_service_name(svc->name);
|
|
|
|
_avahi_create_services(new_name, svc->host, svc->port);
|
|
|
|
rfbLog("Avahi Entry group collision\n");
|
|
|
|
avahi_free(new_name);
|
|
|
|
break;
|
|
|
|
case AVAHI_ENTRY_GROUP_FAILURE:
|
|
|
|
rfbLog("Avahi Entry group failure: %s\n",
|
|
|
|
avahi_strerror(avahi_client_errno(
|
|
|
|
avahi_entry_group_get_client(g))));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (db) fprintf(stderr, "out _avahi_entry_group_callback\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _avahi_create_services(const char *name, const char *host,
|
|
|
|
const uint16_t port) {
|
|
|
|
avahi_service_t *svc = (avahi_service_t *)malloc(sizeof(avahi_service_t));
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (db) fprintf(stderr, "in _avahi_create_services %s %s %d\n", name, host, port);
|
|
|
|
svc->name = name;
|
|
|
|
svc->host = host;
|
|
|
|
svc->port = port;
|
|
|
|
|
|
|
|
if (!_group) {
|
|
|
|
if (db) fprintf(stderr, " _avahi_create_services create group\n");
|
|
|
|
_group = avahi_entry_group_new(_client,
|
|
|
|
_avahi_entry_group_callback, svc);
|
|
|
|
}
|
|
|
|
if (!_group) {
|
|
|
|
rfbLog("avahi_entry_group_new() failed: %s\n",
|
|
|
|
avahi_strerror(avahi_client_errno(_client)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = avahi_entry_group_add_service(_group, AVAHI_IF_UNSPEC,
|
|
|
|
AVAHI_PROTO_UNSPEC, 0, name, "_rfb._tcp", NULL, NULL, port, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
rfbLog("Failed to add _rfb._tcp service: %s\n",
|
|
|
|
avahi_strerror(ret));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = avahi_entry_group_commit(_group);
|
|
|
|
if (ret < 0) {
|
|
|
|
rfbLog("Failed to commit entry_group:: %s\n",
|
|
|
|
avahi_strerror(ret));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (db) fprintf(stderr, "out _avahi_create_services\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_advertise(const char *name, const char *host, const uint16_t port) {
|
|
|
|
int i;
|
|
|
|
if (db) fprintf(stderr, "in avahi_advertise: %s %s %d\n", name, host, port);
|
|
|
|
if (!_client) {
|
|
|
|
if (db) fprintf(stderr, " avahi_advertise client null\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_poll == NULL) {
|
|
|
|
rfbLog("Avahi poll not initialized.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* well, we just track it ourselves... */
|
|
|
|
for (i=0; i<NREG; i++) {
|
|
|
|
if (!registered[i].name) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(registered[i].name, name)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strcmp(registered[i].host, host)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (registered[i].port != port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (db) fprintf(stderr, " avahi_advertise already did this one\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i=0; i<NREG; i++) {
|
|
|
|
if (!registered[i].name) {
|
|
|
|
registered[i].name = strdup(name);
|
|
|
|
registered[i].host = strdup(host);
|
|
|
|
registered[i].port = port;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
avahi_threaded_poll_lock(_poll);
|
|
|
|
_avahi_create_services(name, host, port >= 5900 ? port : 5900+port);
|
|
|
|
avahi_threaded_poll_unlock(_poll);
|
|
|
|
if (db) fprintf(stderr, "out avahi_advertise\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_reset(void) {
|
|
|
|
int i;
|
|
|
|
if (db) fprintf(stderr, "in avahi_reset\n");
|
|
|
|
for (i=0; i<NREG; i++) {
|
|
|
|
if (registered[i].name) {
|
|
|
|
free(registered[i].name);
|
|
|
|
registered[i].name = NULL;
|
|
|
|
}
|
|
|
|
if (registered[i].host) {
|
|
|
|
free(registered[i].host);
|
|
|
|
registered[i].host = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!_client || !_group) {
|
|
|
|
if (db) fprintf(stderr, " avahi_reset client/group null\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
avahi_entry_group_reset(_group);
|
|
|
|
rfbLog("Avahi resetting group.\n");
|
|
|
|
if (db) fprintf(stderr, "out avahi_reset\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void avahi_cleanup(void) {
|
|
|
|
if (db) fprintf(stderr, "in avahi_cleanup\n");
|
|
|
|
if (!_client) {
|
|
|
|
if (db) fprintf(stderr, " avahi_cleanup client null\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
avahi_threaded_poll_lock(_poll);
|
|
|
|
avahi_threaded_poll_stop(_poll);
|
|
|
|
|
|
|
|
avahi_client_free(_client);
|
|
|
|
_client = NULL;
|
|
|
|
|
|
|
|
avahi_threaded_poll_free(_poll);
|
|
|
|
_poll = NULL;
|
|
|
|
if (db) fprintf(stderr, "out avahi_cleanup\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|