This patch adds support for unix-domain socket based listeners
to kismet_server.

Currently nothing inside kismet server itself is using it, but
it's available to plugins, should they need to exchange lot of
data with other local processes.

TODO: make an option of sending standard kismet protocol messages
over Unix-domain socket, so that local UI could use it instead of
TCP.

Signed off by: Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com>
Other Kismet patches: http://www.total-knowledge.com/progs/kismet

Index: Makefile.in
===================================================================
--- Makefile.in	(revision 2981)
+++ Makefile.in	(working copy)
@@ -14,8 +14,8 @@
 	filtercore.o ifcontrol.o iwcontrol.o madwifing_control.o nl80211_control.o \
 	$(OBJC_LINK) \
 	psutils.o ipc_remote.o soundcontrol.o battery.o kismet_json.o \
-	netframework.o clinetframework.o tcpserver.o tcpclient.o serialclient.o \
-	packetsourcetracker.o $(CAPSOURCES) \
+	netframework.o clinetframework.o tcpserver.o tcpclient.o \
+	unixdomainserver.o serialclient.o packetsourcetracker.o $(CAPSOURCES) \
 	kis_netframe.o kis_droneframe.o \
 	gpswrapper.o gpscore.o gpsdclient.o gpsserial.o gpsdlibgps.o \
 	packetchain.o \
Index: unixdomainserver.h
===================================================================
--- unixdomainserver.h	(revision 0)
+++ unixdomainserver.h	(revision 0)
@@ -0,0 +1,107 @@
+/*
+	This file is part of Kismet
+
+	Kismet 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.
+
+	Kismet 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 Kismet; if not, write to the Free Software
+	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __UNIXDOMAINSERVER_H__
+#define __UNIXDOMAINSERVER_H__
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string>
+#include <time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <map>
+#include <vector>
+
+#include "ringbuf.h"
+#include "messagebus.h"
+#include "timetracker.h"
+#include "netframework.h"
+
+// Arbitrary 64k ring by default
+#define SRV_RING_LEN (65536)
+
+/* man 7 unix */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 108
+#endif
+
+class UnixDomainServer : public NetworkServer {
+public:
+	UnixDomainServer();
+	UnixDomainServer(GlobalRegistry *in_globalreg);
+	virtual ~UnixDomainServer();
+
+	// Set up the Unix-domain socket and listening
+	int SetupServer(const std::string& path, int mode, bool force, unsigned int in_maxcli);
+
+	// Enable server
+	virtual int EnableServer();
+
+	// Kill a connection by client ID
+	virtual void KillConnection(int in_fd);
+
+	// Fetch the info for a client id
+	virtual int FetchClientConnectInfo(int in_clid, void *ret_info);
+
+	// Shutdown the entire server
+	virtual void Shutdown();
+
+	virtual string GetRemoteAddr(int in_fd);
+
+	// Set the size of ring buffers.  This ONLY affects new connections, not
+	// existing!
+	virtual void SetRingSize(int in_sz);
+
+protected:
+	// Broker various acceptance stuff
+	virtual int Accept();
+
+	// Validate a connection
+	virtual int Validate(int in_fd);
+
+	// Read pending bytes from the socket into the read ring buffer
+	virtual int ReadBytes(int in_fd);
+
+	// Write bytes from the write ring buffer to the socket
+	virtual int WriteBytes(int in_fd);
+
+	// Server info
+	string socket_path;
+	int socket_mode; /// File mode of the socket
+	short int port;
+	unsigned int maxcli;
+	// Is it configured?
+	int sv_configured;
+
+	struct sockaddr_un serv_sock;
+
+	// Ring length, if we resize it
+	int int_ring_len;
+};
+
+#endif
Index: unixdomainserver.cc
===================================================================
--- unixdomainserver.cc	(revision 0)
+++ unixdomainserver.cc	(revision 0)
@@ -0,0 +1,283 @@
+#include "unixdomainserver.h"
+#include "configfile.h"
+#include <limits.h>
+
+UnixDomainServer::UnixDomainServer() {
+	fprintf(stderr, "*** UnixDomainServer() called with no global registry\n");
+}
+
+UnixDomainServer::UnixDomainServer(GlobalRegistry *in_globalreg) : NetworkServer(in_globalreg) {
+	globalreg = in_globalreg;
+	// Init stuff
+	sv_valid = 0;
+	sv_configured = 0;
+
+	serv_fd = 0;
+	max_fd = 0;
+
+	int_ring_len = SRV_RING_LEN;
+
+	FD_ZERO(&server_fdset);
+}
+
+UnixDomainServer::~UnixDomainServer() {
+	if(serv_fd)
+		::unlink(socket_path.c_str());
+}
+
+// Set up the Unix-domain socket and listening
+int UnixDomainServer::SetupServer(const std::string& path, int mode, bool force, unsigned int in_maxcli) {
+	socket_path = path;
+	maxcli = in_maxcli;
+	sv_configured = 1;
+	socket_mode = mode;
+	if(force)
+		::unlink(path.c_str());
+	globalreg->RegisterPollableSubsys(this);
+	return 1;
+}
+
+// Set the length of the rings for new connections
+void UnixDomainServer::SetRingSize(int in_sz) {
+	int_ring_len = in_sz;
+}
+
+int UnixDomainServer::EnableServer() {
+	if (sv_configured == 0) {
+		_MSG("Attempted to enable unconfigured Unix domain server", MSGFLAG_FATAL);
+		globalreg->fatal_condition = 1;
+		return -1;
+	}
+
+	// Set up socket stuff
+	memset(&serv_sock, 0, sizeof(serv_sock));
+	serv_sock.sun_family = AF_UNIX;
+	strncpy(serv_sock.sun_path, socket_path.c_str(), UNIX_PATH_MAX);
+
+	if ((serv_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server socket() failed: %s",
+				 strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	// Bind the socket
+	if (bind(serv_fd, (struct sockaddr *) &serv_sock, sizeof(serv_sock)) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server bind() failed: %s",
+				 strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	// Listen for connections
+	if (listen(serv_fd, 5) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server listen() failed: %s",
+				 strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	// Zero and set the FDs and maxfd
+	FD_ZERO(&server_fdset);
+
+	if (serv_fd > (int) max_fd)
+		max_fd = serv_fd;
+
+	// We're valid
+	sv_valid = 1;
+
+	::chmod(socket_path.c_str(), socket_mode);
+	snprintf(errstr, 1024, "Created Unix-domain listener on %s", socket_path.c_str());
+	_MSG(errstr, MSGFLAG_INFO);
+	return 1;
+}
+
+void UnixDomainServer::KillConnection(int in_fd) {
+	NetworkServer::KillConnection(in_fd);
+
+	// Close the fd
+	if (in_fd)
+		close(in_fd);
+}
+
+void UnixDomainServer::Shutdown() {
+	for (map<int, RingBuffer *>::iterator miter = read_buf_map.begin();
+		 miter != read_buf_map.end(); ++miter) {
+		KillConnection(miter->first);
+		// Reset the iterator since we cascade through the generic
+		// netserver::killconnection which removes the ringbuf and
+		// iterator
+		miter = read_buf_map.begin();
+		if (miter == read_buf_map.end())
+			break;
+	}
+
+	sv_valid = 0;
+
+	if (serv_fd)
+		close(serv_fd);
+
+	max_fd = 0;
+}
+
+int UnixDomainServer::FetchClientConnectInfo(int in_clid, void *ret_info) {
+	struct sockaddr_un client_addr;
+#ifdef HAVE_SOCKLEN_T
+	socklen_t client_len;
+#else
+	int client_len;
+#endif
+
+	memset(&client_addr, 0, sizeof(client_addr));
+	client_len = sizeof(client_addr);
+
+	if (getsockname(in_clid, (struct sockaddr *) &client_addr, &client_len) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server unable to get sockname for "
+				 "client info: %s", strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	memcpy(ret_info, &client_addr, client_len);
+	
+	return 1;
+}
+
+string UnixDomainServer::GetRemoteAddr(int in_fd) {
+	struct sockaddr_un client_addr;
+#ifdef HAVE_SOCKLEN_T
+	socklen_t client_len;
+#else
+	int client_len;
+#endif
+
+	memset(&client_addr, 0, sizeof(client_addr));
+	client_len = sizeof(client_addr);
+
+	if (getsockname(in_fd, (struct sockaddr *) &client_addr, &client_len) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server unable to get sockname: %s",
+				 strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return "";
+	}
+
+	return client_addr.sun_path;
+}
+
+int UnixDomainServer::Accept() {
+	int new_fd;
+	struct sockaddr_un client_addr;
+#ifdef HAVE_SOCKLEN_T
+	socklen_t client_len;
+#else
+	int client_len;
+#endif
+
+	memset(&client_addr, 0, sizeof(client_addr));
+	client_len = sizeof(client_addr);
+
+	// Socket accept
+	if ((new_fd = accept(serv_fd, (struct sockaddr *) &client_addr,
+						 &client_len)) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix-domain server accept() failed: %s",
+				 strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	// Bail right now if we have too many connections
+	if (FetchNumClients() >= (int) maxcli) {
+		snprintf(errstr, STATUS_MAX, "Unix-domain server maximum clients (%d) already "
+				 "reached.", maxcli);
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		close(new_fd);
+		return -1;
+	}
+
+	// Set it to nonblocking
+	int save_mode = fcntl(new_fd, F_GETFL, 0);
+	fcntl(new_fd, F_SETFL, save_mode | O_NONBLOCK);
+	
+	if (new_fd > (int) max_fd)
+		max_fd = new_fd;
+
+	FD_SET(new_fd, &server_fdset);
+
+	// There should never be overlapping fds and there should never be
+	// remnants of an old person here, so we'll make the connection 
+	// lightweight and not do more tree searching.  If this ever proves
+	// wrong, we need to reevaluate
+	write_buf_map[new_fd] = new RingBuffer(int_ring_len);
+	read_buf_map[new_fd] = new RingBuffer(int_ring_len);
+
+	return new_fd;
+}
+
+int UnixDomainServer::Validate(int in_fd) {
+	return 1; // Perhaps check remote user?
+}
+
+int UnixDomainServer::ReadBytes(int in_fd) {
+	uint8_t recv_bytes[1024];
+	int ret;
+
+	if ((ret = read(in_fd, recv_bytes, 1024)) < 0) {
+		if (errno == EINTR || errno == EAGAIN)
+			return 0;
+
+		snprintf(errstr, STATUS_MAX, "Unix domain server client read() error for %s: %s", 
+				 GetRemoteAddr(in_fd).c_str(), strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	if (ret == 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server client read() ended for %s",
+				 GetRemoteAddr(in_fd).c_str());
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	if (read_buf_map[in_fd]->InsertData(recv_bytes, ret) == 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server client %s read error, ring "
+				 "buffer full", GetRemoteAddr(in_fd).c_str());
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		return -1;
+	}
+
+	return ret;
+}
+
+int UnixDomainServer::WriteBytes(int in_fd) {
+	uint8_t dptr[1024];
+	int dlen, ret;
+
+	// This can't get called on invalid fds, so save some time and
+	// don't check
+	write_buf_map[in_fd]->FetchPtr(dptr, 1024, &dlen);
+
+	if ((ret = write(in_fd, dptr, dlen)) <= 0) {
+		if (errno == EINTR || errno == EAGAIN)
+			return 0;
+
+		snprintf(errstr, STATUS_MAX, "Unix domain server: Killing client %s, write "
+				 "error %s", GetRemoteAddr(in_fd).c_str(), strerror(errno));
+		globalreg->messagebus->InjectMessage(errstr, MSGFLAG_ERROR);
+		KillConnection(in_fd);
+		return -1;
+	}
+
+	write_buf_map[in_fd]->MarkRead(ret);
+
+	if (srvframework->BufferDrained(in_fd) < 0) {
+		snprintf(errstr, STATUS_MAX, "Unix domain server: Error occured calling framework "
+				 "buffer drain notification on client %s", 
+				 GetRemoteAddr(in_fd).c_str());
+		_MSG(errstr, MSGFLAG_ERROR);
+		KillConnection(in_fd);
+		return -1;
+	}
+
+	return ret;
+}
+
