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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +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::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; +} +