/******************************************************************************
(c) 2001-2005 Patrick Caulfield patrick@debian.org
(c) 2003 Dmitri Popov
This program 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
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.
******************************************************************************/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#ifdef HAVE_NET_ETHERNET_H
#include <net/ethernet.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <syslog.h>
#include <ctype.h>
#include <regex.h>
#include <stdlib.h>
#include <utmp.h>
#include <grp.h>
#include <signal.h>
#include <assert.h>
#include <netinet/in.h>
#include <list>
#include <queue>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <iomanip>
#include "lat.h"
#include "utils.h"
#include "session.h"
#include "localport.h"
#include "connection.h"
#include "circuit.h"
#include "latcpcircuit.h"
#include "llogincircuit.h"
#include "server.h"
#include "services.h"
#include "lat_messages.h"
#include "dn_endian.h"
#if defined (__APPLE__) && !defined(_SOCKLEN_T)
typedef int socklen_t;
#endif
// Remove any dangling symlinks in the /dev/lat directory
void LATServer::tidy_dev_directory()
{
DIR *dir;
struct dirent *de;
char current_dir[PATH_MAX];
getcwd(current_dir, sizeof(current_dir));
chdir(LAT_DIRECTORY);
dir = opendir(LAT_DIRECTORY);
if (!dir) return; // Doesn't exist - this is OK
while ( (de = readdir(dir)) )
{
if (de->d_name[0] != '.')
unlink(de->d_name);
}
closedir(dir);
chdir(current_dir);
}
unsigned char *LATServer::get_local_node(void)
{
unsigned int i;
if (local_name[0] == '\0')
{
struct utsname uts;
char *trailer;
uname(&uts);
if (strchr(uts.nodename, '.'))
*strchr(uts.nodename, '.') = '\0';
// This gets rid of the name-Computer bit on Darwin
trailer = strstr(uts.nodename, "-Computer");
if (trailer)
*trailer = '\0';
// LAT node names must be <= 16 characters long
if (strlen(uts.nodename) > 16)
uts.nodename[16] = '\0';
strcpy((char *)local_name, uts.nodename);
// Make it all upper case
for (i=0; i<strlen((char *)local_name); i++)
{
if (islower(local_name[i])) local_name[i] = toupper(local_name[i]);
}
}
return local_name;
}
void LATServer::alarm_signal(int sig)
{
if (Instance()->alarm_mode == 0)
{
Instance()->send_service_announcement(sig);
}
Instance()->send_solicit_messages(sig);
}
// Send ENQUIRY for a node - mainly needed for DS90L servers that
// don't advertise.
void LATServer::send_enq(const char *node)
{
unsigned char packet[1600];
static unsigned char id = 1;
LAT_Enquiry *enqmsg = (LAT_Enquiry *)packet;
int ptr = sizeof(LAT_Enquiry);
enqmsg->cmd = LAT_CCMD_ENQUIRE;
enqmsg->dummy = 0;
enqmsg->hiver = LAT_VERSION;
enqmsg->lover = LAT_VERSION;
enqmsg->latver = LAT_VERSION;
enqmsg->latver_eco = LAT_VERSION_ECO;
enqmsg->mtu = dn_htons(1500);
enqmsg->id = id++;
enqmsg->retrans_timer = 75; // * 10ms - give it more time to respond
add_string(packet, &ptr, (const unsigned char *)node);
packet[ptr++] = 1; /* Length of group data */
packet[ptr++] = 1; /* Group mask */
add_string(packet, &ptr, local_name);
packet[ptr++] = 0x00;
packet[ptr++] = 0x00;
packet[ptr++] = 0x00;
packet[ptr++] = 0x00;
/* This is the LAT multicast address */
static unsigned char addr[6] = { 0x09, 0x00, 0x2b, 0x00, 0x00, 0x0f };
debuglog(("send_enq : node: %s, local_name: %s\n", node, local_name));
for (int i=0; i<num_interfaces;i++)
{
if (iface->send_packet(interface_num[i], addr, packet, ptr) < 0)
{
debuglog(("Error send packet\n"));
interface_error(interface_num[i], errno);
}
else
{
interface_errs[interface_num[i]] = 0; // Clear errors
}
}
LATServices::Instance()->touch_dummy_node_respond_counter(node);
}
void LATServer::add_slave_node(const char *node_name)
{
sig_blk_t _block(SIGALRM);
for (std::list<std::string>::iterator iter = slave_nodes.begin();
iter != slave_nodes.end();
iter++)
{
if (*iter == node_name) {
// do not duplicate nodes
return;
}
}
slave_nodes.push_front(node_name);
}
/* Called on the multicast timer - advertise our service on the LAN */
void LATServer::send_service_announcement(int sig)
{
unsigned char packet[1600];
int ptr;
struct utsname uinfo;
char *myname;
// Only send it if we have some services
if (servicelist.size())
{
LAT_ServiceAnnounce *announce = (LAT_ServiceAnnounce *)packet;
ptr = sizeof(LAT_ServiceAnnounce);
announce->cmd = LAT_CCMD_SERVICE;
announce->circuit_timer = circuit_timer;
announce->hiver = LAT_VERSION;
announce->lover = LAT_VERSION;
announce->latver = LAT_VERSION;
announce->latver_eco = LAT_VERSION_ECO;
announce->incarnation = --multicast_incarnation;
announce->flags = 0x1f;
announce->mtu = dn_htons(1500);
announce->multicast_timer = multicast_timer;
if (do_shutdown)
{
announce->node_status = 3; // Not accepting connections
}
else
{
announce->node_status = 2; // Accepting connections
}
// Send group codes
if (groups_set)
{
announce->group_length = 32;
memcpy(&packet[ptr], groups, 32);
ptr += 32;
announce->flags |= 1;
}
else
{
announce->group_length = 1;
packet[ptr++] = 01;
}
/* Get host info */
uname(&uinfo);
// Node name
myname = (char*)get_local_node();
packet[ptr++] = strlen(myname);
strcpy((char*)packet+ptr, myname);
ptr += strlen(myname);
// Greeting
packet[ptr++] = strlen((char*)greeting);
strcpy((char*)packet+ptr, (char*)greeting);
ptr += strlen((char*)greeting);
// Number of services
packet[ptr++] = servicelist.size();
std::list<serviceinfo>::iterator i(servicelist.begin());
for (; i != servicelist.end(); i++)
{
// Service rating
unsigned char real_rating;
if (i->get_static())
{
real_rating = i->get_rating();
}
else
{
/* Calculate dynamic rating */
real_rating = (unsigned char)(i->get_rating() / (get_loadavg()+1.0));
debuglog(("Dynamic service rating is %d\n", real_rating));
}
packet[ptr++] = real_rating;
// Service name
const std::string name = i->get_name();
packet[ptr++] = name.length();
strcpy((char *)packet+ptr, i->get_name().c_str());
ptr += name.length();
// Service Identification
std::string id = i->get_id();
if (id.length() == 0)
{
// Default service identification string
char stringbuf[1024];
sprintf(stringbuf, "%s %s", uinfo.sysname, uinfo.release);
id = std::string(stringbuf);
}
packet[ptr++] = id.length();
strcpy((char *)packet+ptr, id.c_str());
ptr += id.length();
// Make sure the service table knows about all our services
unsigned char dummy_macaddr[6];
memset(dummy_macaddr, 0, sizeof(dummy_macaddr));
if (!do_shutdown)
LATServices::Instance()->add_service(std::string((char*)get_local_node()),
name,
id, real_rating, 0, dummy_macaddr);
}
// Not sure what node service classes are
// probably somthing to do with port services and stuff.
packet[ptr++] = 0x01; // Node service classes length
packet[ptr++] = 0x01; // Node service classes
packet[ptr++] = 0x00;
packet[ptr++] = 0x00;
unsigned char addr[6];
/* This is the LAT multicast address */
addr[0] = 0x09;
addr[1] = 0x00;
addr[2] = 0x2b;
addr[3] = 0x00;
addr[4] = 0x00;
addr[5] = 0x0f;
for (int i=0; i<num_interfaces;i++)
{
if (iface->send_packet(interface_num[i], addr, packet, ptr) < 0)
{
debuglog(("sending service announcement, send error: %d\n", errno));
interface_error(interface_num[i], errno);
}
else
interface_errs[interface_num[i]] = 0; // Clear errors
}
}
/* Send it every minute */
signal(SIGALRM, &alarm_signal);
alarm(multicast_timer);
}
// Send solicit messages to slave nodes
void LATServer::send_solicit_messages(int sig)
{
static unsigned int counter = 0;
static unsigned int last_list_size = 0;
if (alarm_mode == 0)
{
counter = 0;
alarm_mode = 1;
debuglog(("set alarm_mode to 1\n"));
if (!known_slave_nodes.empty())
{
std::string known_node = known_slave_nodes.front();
known_slave_nodes.pop_front();
slave_nodes.push_back(known_node);
debuglog(("known(%d) => slave(%d) : %s\n", known_slave_nodes.size(),
slave_nodes.size(), known_node.c_str()));
if (slave_nodes.size() == 1)
{
alarm_mode = 0;
debuglog(("set alarm_mode to 0 - one slave\n"));
send_enq(slave_nodes.front().c_str());
// alarm() is already charged by send_service_announcement()
return;
}
}
}
else
{
if (slave_nodes.size() < last_list_size)
{
counter -= last_list_size - slave_nodes.size();
}
}
if (slave_nodes.size() > counter && !slave_nodes.empty())
{
std::string node_name = slave_nodes.front();
slave_nodes.pop_front();
slave_nodes.push_back(node_name);
send_enq(node_name.c_str());
alarm(1);
counter++;
last_list_size = slave_nodes.size();
}
else
{
alarm_mode = 0;
//well, it's not quite correct, I know...
alarm(counter >= multicast_timer ? 1 : multicast_timer - counter);
debuglog(("set alarm_mode to 0, timer %d\n", counter >= multicast_timer ? 1 : multicast_timer - counter));
}
}
// Log an error against an interface. If we get three of these
// then we remove it.
// If that means we have no interfaces, then closedown.
void LATServer::interface_error(int ifnum, int err)
{
syslog(LOG_ERR, "Error on interface %s: %s\n", iface->ifname(ifnum).c_str(), strerror(err));
// Too many errors, remove it
if (++interface_errs[ifnum] > 3)
{
int i,j;
syslog(LOG_ERR, "Interface will be removed from LATD\n");
// Look for it
for (i=0; i<num_interfaces; i++)
{
if (interface_num[i] == ifnum) goto remove_if;
}
// Ugh, didn't find it. that can't be right!
if (i>=num_interfaces)
{
syslog(LOG_ERR, "Don't seem to have a reference to this interface....\n");
return;
}
remove_if:
// Shuffle the list down
for (j=i; j<num_interfaces-1; j++)
{
interface_num[j] = interface_num[j+1];
}
if (--num_interfaces == 0)
{
syslog(LOG_ERR, "No valid interfaces left. LATD closing down\n");
// No point in going down cleanly as that just sends network messages
// and we have nowhere to send then through.
exit(9);
}
}
}
/* Main loop */
void LATServer::run()
{
int status;
// Bind interfaces
for (int i=0; i<num_interfaces;i++)
{
iface->set_lat_multicast(interface_num[i]);
}
// Add it/them to the sockets list
if (iface->one_fd_per_interface())
{
for (int i=0; i<num_interfaces;i++)
{
fdlist.push_back(fdinfo(iface->get_fd(interface_num[i]), 0, LAT_SOCKET));
}
}
else
{
fdlist.push_back(fdinfo(iface->get_fd(0), 0, LAT_SOCKET));
}
// Open LATCP socket
unlink(LATCP_SOCKNAME);
latcp_socket = socket(PF_UNIX, SOCK_STREAM, 0);
if (latcp_socket < 0)
{
syslog(LOG_ERR, "Can't create latcp socket: %m");
exit(1);
}
struct sockaddr_un sockaddr;
strcpy(sockaddr.sun_path, LATCP_SOCKNAME);
sockaddr.sun_family = AF_UNIX;
if (bind(latcp_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)))
{
syslog(LOG_ERR, "can't bind latcp socket: %m");
exit(1);
}
if (listen(latcp_socket, 1) != 0)
{
syslog(LOG_ERR, "listen latcp: %m");
}
// Make sure only root can talk to us via the latcp socket
chmod(LATCP_SOCKNAME, 0600);
fdlist.push_back(fdinfo(latcp_socket, 0, LATCP_RENDEZVOUS));
// Open llogin socket
unlink(LLOGIN_SOCKNAME);
llogin_socket = socket(PF_UNIX, SOCK_STREAM, 0);
if (llogin_socket < 0)
{
syslog(LOG_ERR, "Can't create llogin socket: %m");
exit(1);
}
strcpy(sockaddr.sun_path, LLOGIN_SOCKNAME);
sockaddr.sun_family = AF_UNIX;
if (bind(llogin_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)))
{
syslog(LOG_ERR, "can't bind llogin socket: %m");
exit(1);
}
if (listen(llogin_socket, 1) != 0)
{
syslog(LOG_ERR, "listen llogin: %m");
}
// Make sure everyone can use it
chmod(LLOGIN_SOCKNAME, 0666);
fdlist.push_back(fdinfo(llogin_socket, 0, LLOGIN_RENDEZVOUS));
// Don't start sending service announcements
// until we get an UNLOCK message from latcp.
// We rely on POSIX's select() behaviour in that we use the
// time left in the timeval parameter to make sure the circuit timer
// goes off at a reasonably predictable interval.
#ifndef __linux__
struct timeval next_circuit_time;
next_circuit_time.tv_sec = 0;
next_circuit_time.tv_usec = 0;
#endif
struct timeval tv = {0, circuit_timer*10000};
time_t last_node_expiry = time(NULL);
do_shutdown = false;
do
{
fd_set fds;
FD_ZERO(&fds);
std::list<fdinfo>::iterator i(fdlist.begin());
for (; i != fdlist.end(); i++)
{
if (i->active())
FD_SET(i->get_fd(), &fds);
}
// Only Linux seems to have this select behaviour...
// for BSDs we need to work our for ourselves the delay
// until the next circuit timer tick.
#ifndef __linux__
struct timeval now;
gettimeofday(&now, NULL);
if (next_circuit_time.tv_sec == 0)
{
next_circuit_time = now;
next_circuit_time.tv_usec += circuit_timer*10000;
next_circuit_time.tv_sec += (next_circuit_time.tv_usec / 1000000);
next_circuit_time.tv_usec = (next_circuit_time.tv_usec % 1000000);
}
tv = next_circuit_time;
if (tv.tv_sec < now.tv_sec
|| (tv.tv_sec == now.tv_sec
&& tv.tv_usec <= now.tv_usec))
{
tv.tv_sec = tv.tv_usec = 0;
}
else
{
tv.tv_sec -= now.tv_sec;
if ((tv.tv_usec -= now.tv_usec) < 0)
{
tv.tv_sec--;
tv.tv_usec += 1000000;
}
}
#endif
status = select(FD_SETSIZE, &fds, NULL, NULL, &tv);
if (status < 0)
{
if (errno != EINTR)
{
syslog(LOG_WARNING, "Error in select: %m");
debuglog(("Error in select: %s\n", strerror(errno)));
do_shutdown = true;
}
}
else
{
if (status == 0) // Circuit timer
{
for (int i=1; i<MAX_CONNECTIONS; i++)
if (connections[i])
connections[i]->circuit_timer();
tv.tv_usec = circuit_timer*10000;
// Is it time we expired some more nodes ?
if (time(NULL) - last_node_expiry > 15)
LATServices::Instance()->expire_nodes();
#ifndef __linux__
next_circuit_time.tv_sec = 0;
#endif
}
else
{
// Unix will never scale while this is necessary
std::list<fdinfo>::iterator fdl(fdlist.begin());
for (; fdl != fdlist.end(); fdl++)
{
if (fdl->active() &&
FD_ISSET(fdl->get_fd(), &fds))
{
process_data(*fdl);
}
}
}
}
// Tidy deleted sessions
if (!dead_session_list.empty())
{
std::list<deleted_session>::iterator dsl(dead_session_list.begin());
for (; dsl != dead_session_list.end(); dsl++)
{
delete_entry(*dsl);
}
dead_session_list.clear();
}
// Tidy deleted connections
if (!dead_connection_list.empty())
{
std::list<int>::iterator dcl(dead_connection_list.begin());
for (; dcl != dead_connection_list.end(); dcl++)
{
if (connections[*dcl])
{
delete connections[*dcl];
connections[*dcl] = NULL;
}
}
dead_connection_list.clear();
}
} while (!do_shutdown);
send_service_announcement(-1); // Say we are unavailable
close(latcp_socket);
unlink(LATCP_SOCKNAME);
unlink(LLOGIN_SOCKNAME);
tidy_dev_directory();
}
/* LAT socket has something for us */
void LATServer::read_lat(int sock)
{
unsigned char buf[1600];
unsigned char macaddr[6];
int len;
int i;
int ifn;
bool more = true;
LAT_Header *header = (LAT_Header *)buf;
while (more)
{
len = iface->recv_packet(sock, ifn, macaddr, buf, sizeof(buf), more);
if (len == 0)
continue; // Probably a rogue packet
if (len < 0)
{
if (errno != EINTR && errno != EAGAIN)
{
syslog(LOG_ERR, "recvmsg: %m");
return;
}
}
// Not listening yet, but we must read the message otherwise we
// we will spin until latcp unlocks us.
if (locked)
continue;
// Parse & dispatch it.
switch(header->cmd)
{
case LAT_CCMD_SREPLY:
case LAT_CCMD_SDATA:
case LAT_CCMD_SESSION:
{
debuglog(("session cmd for connid %d\n", header->remote_connid));
LATConnection *conn = NULL;
if(header->remote_connid < MAX_CONNECTIONS)
{
conn = connections[header->remote_connid];
}
if (conn)
{
conn->process_session_cmd(buf, len, macaddr);
}
else
{
// Message format error
send_connect_error(2, header, ifn, macaddr);
}
}
break;
case LAT_CCMD_CONNECT:
{
// Make a new connection
// Check that the connection is really for one of our services
unsigned char name[256];
int ptr = sizeof(LAT_Start);
get_string(buf, &ptr, name);
debuglog(("got connect for node %s\n", name));
if (strcmp((char *)name, (char *)get_local_node()))
{
// How the &?* did that happen?
send_connect_error(2, header, ifn, macaddr);
continue;
}
// Make a new connection.
if ( ((i=make_new_connection(buf, len, ifn, header, macaddr) )) > 0)
{
debuglog(("Made new connection: %d\n", i));
connections[i]->send_connect_ack();
}
}
break;
case LAT_CCMD_CONACK:
{
LATConnection *conn = NULL;
debuglog(("Got connect ACK for %d\n", header->remote_connid));
if (header->remote_connid < MAX_CONNECTIONS)
{
conn = connections[header->remote_connid];
}
if (conn)
{
conn->got_connect_ack(buf);
}
else
{
// Insufficient resources
send_connect_error(7, header, ifn, macaddr);
}
}
break;
case LAT_CCMD_CONREF:
case LAT_CCMD_DISCON:
{
debuglog(("Disconnecting connection %d: status %x(%s)\n",
header->remote_connid,
buf[sizeof(LAT_Header)],
lat_messages::connection_disconnect_msg(buf[sizeof(LAT_Header)]) ));
if (header->remote_connid < MAX_CONNECTIONS)
{
LATConnection *conn = connections[header->remote_connid];
if (conn)
{
// We don't delete clients, we just quiesce them.
if (conn->isClient())
{
conn->disconnect_client();
if (conn->num_clients() == 0)
{
delete conn;
connections[header->remote_connid] = NULL;
}
}
else
{
delete conn;
connections[header->remote_connid] = NULL;
}
}
}
else
{
// Message format error
send_connect_error(2, header, ifn, macaddr);
}
}
break;
case LAT_CCMD_SERVICE:
// Keep a list of known services
add_services(buf, len, ifn, macaddr);
break;
case LAT_CCMD_ENQUIRE:
reply_to_enq(buf, len, ifn, macaddr);
break;
case LAT_CCMD_ENQREPLY:
got_enqreply(buf, len, ifn, macaddr);
break;
case LAT_CCMD_STATUS:
forward_status_messages(buf, len);
break;
// Request for a reverse-LAT connection.
case LAT_CCMD_COMMAND:
process_command_msg(buf, len, ifn, macaddr);
break;
}
}
}
// Add an FD to the list of FDs to listen to
void LATServer::add_fd(int fd, fd_type type)
{
std::list<fdinfo>::iterator fdi;
debuglog(("Add_fd: %d\n", fd));
fdi = find(fdlist.begin(), fdlist.end(), fd);
if (fdi != fdlist.end()) return; // Already exists
fdlist.push_back(fdinfo(fdinfo(fd, NULL, type)));
}
// Remove FD from the FD list
void LATServer::remove_fd(int fd)
{
std::list<fdinfo>::iterator fdi;
debuglog(("remove_fd: %d\n", fd));
fdi = find(fdlist.begin(), fdlist.end(), fd);
if (fdi == fdlist.end()) return; // Does not exist
fdlist.remove(*fdi);
}
// Change the DISABLED state of a PTY fd
void LATServer::set_fd_state(int fd, bool disabled)
{
std::list<fdinfo>::iterator fdi;
debuglog(("set_fd_state: %d, %d\n", fd, disabled));
fdi = find(fdlist.begin(), fdlist.end(), fd);
if (fdi == fdlist.end()) return; // Does not exist
fdi->set_disabled(disabled);
}
/* Send a LAT message to a specified MAC address */
int LATServer::send_message(unsigned char *buf, int len, int interface, unsigned char *macaddr)
{
if (len < 46) len = 46; // Minimum packet length
if (len%2) len++; // Must be an even number
#ifdef PACKET_LOSS
static int c=0;
if ((++c % PACKET_LOSS) == 0) {
debuglog(("NOT SENDING THIS MESSAGE!\n"));
return 0;
}
#endif
if (interface == 0) // Send to all
{
for (int i=0; i<num_interfaces;i++)
{
if (iface->send_packet(interface_num[i], macaddr, buf, len) < 0)
{
interface_error(interface_num[i], errno);
}
else
interface_errs[interface_num[i]] = 0; // Clear errors
}
}
else
{
if (iface->send_packet(interface, macaddr, buf, len) < 0)
{
interface_error(interface, errno);
return -1;
}
interface_errs[interface] = 0; // Clear errors
}
return 0;
}
/* Get the system load average */
double LATServer::get_loadavg(void)
{
double avg[3];
if (getloadavg(avg, 3) > 0)
{
return avg[0];
}
else
{
return 0;
}
}
// Forward status messages to their recipient connection objects.
void LATServer::forward_status_messages(unsigned char *inbuf, int len)
{
int ptr = sizeof(LAT_Status);
unsigned char node[256];
get_string(inbuf, &ptr, node);
// Forward all the StatusEntry messages
while (ptr <= len)
{
if (ptr%2) ptr++; // Word aligned
LAT_StatusEntry *entry = (LAT_StatusEntry *)&inbuf[ptr];
if (entry->length == 0) break;
ptr += sizeof(LAT_StatusEntry);
get_string(inbuf, &ptr, node); // Service name
ptr += inbuf[ptr]+1; // Past port name
ptr += inbuf[ptr]+1; // Past service description
if (entry->request_id < MAX_CONNECTIONS &&
connections[entry->request_id])
connections[entry->request_id]->got_status(node, entry);
}
}
/* Reply to an ENQUIRE message with our MAC address & service name */
void LATServer::reply_to_enq(unsigned char *inbuf, int len, int interface,
unsigned char *remote_mac)
{
int inptr, outptr, i;
unsigned char outbuf[1600];
unsigned char req_service[1600];
LAT_Header *outhead = (LAT_Header *)outbuf;
LAT_Header *inhead = (LAT_Header *)inbuf;
inptr = sizeof(LAT_Header)+4;
/* This is the service being enquired about */
get_string(inbuf, &inptr, req_service);
debuglog(("got ENQ for %s\n", req_service));
// Ignore empty requests:: TODO: is this right?? - maybe we issue
// a response with all our services in it.
if (strlen((char*)req_service) == 0) return;
unsigned char reply_macaddr[6];
unsigned char *reply_node;
// See if it is for us if we're not doing responder work.
if (!responder)
{
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), (char *)req_service);
if (sii == servicelist.end()) return; // Not ours
}
std::string node;
if (LATServices::Instance()->get_highest(std::string((char *)req_service),
node, reply_macaddr, &interface))
{
reply_node = (unsigned char *)node.c_str();
}
else
{
return; // nevver 'eard of 'im.
}
debuglog(("Sending ENQ reply for %s\n", req_service));
inptr += 2;
outptr = sizeof(LAT_Header);
outbuf[outptr++] = 0x93;
outbuf[outptr++] = 0x07;
outbuf[outptr++] = 0x00;
outbuf[outptr++] = 0x00;
/* The service's MAC address */
outbuf[outptr++] = 0x06;
outbuf[outptr++] = 0x00;
for (i=0; i<5; i++)
outbuf[outptr++] = reply_macaddr[i];
/* Don't know what this is */
outbuf[outptr++] = 0x14;
outbuf[outptr++] = 0x00;
add_string(outbuf, &outptr, reply_node);
outbuf[outptr++] = 0x01;
outbuf[outptr++] = 0x01;
add_string(outbuf, &outptr, req_service);
outhead->cmd = LAT_CCMD_ENQREPLY;
outhead->num_slots = 0;
outhead->remote_connid = inhead->remote_connid;
outhead->local_connid = inhead->local_connid;
outhead->sequence_number = inhead->sequence_number;
outhead->ack_number = inhead->ack_number;
send_message(outbuf, outptr, interface, remote_mac);
}
void LATServer::shutdown()
{
do_shutdown = true;
}
void LATServer::init(bool _static_rating, int _rating,
char *_service, char *_greeting, char **_interfaces,
int _verbosity, int _timer)
{
// Server is locked until latcp has finished
locked = true;
/* Initialise the platform-specific interface code */
iface = LATinterfaces::Create();
if (iface->Start(LATinterfaces::ProtoLAT) == -1)
{
syslog(LOG_ERR, "Can't create LAT protocol socket: %m\n");
exit(1);
}
#ifdef ENABLE_DEFAULT_SERVICE
// Add the default session
servicelist.push_back(serviceinfo(_service,
_rating,
_static_rating));
#endif
strcpy((char *)greeting, _greeting);
verbosity = _verbosity;
// Convert all the interface names to numbers and check they are usable
if (_interfaces[0])
{
int i=0;
while (_interfaces[i])
{
interface_num[num_interfaces] = iface->find_interface(_interfaces[i]);
if (interface_num[num_interfaces] == -1)
{
syslog(LOG_ERR, "Can't use interface %s: ignored\n", _interfaces[i]);
}
else
{
interface_errs[num_interfaces] = 0; // Clear errors
num_interfaces++;
}
i++;
}
if (num_interfaces == 0)
{
syslog(LOG_ERR, "No usable interfaces, latd exiting.\n");
exit(9);
}
}
else
{
iface->get_all_interfaces(interface_num, num_interfaces);
}
// Remove any old /dev/lat symlinks
tidy_dev_directory();
// Save these two for any newly added services
rating = _rating;
static_rating = _static_rating;
next_connection = 1;
multicast_incarnation = 0;
circuit_timer = _timer/10;
local_name[0] = '\0'; // Use default node name
memset(connections, 0, sizeof(connections));
// Enable user group 0
memset(user_groups, 0, 32);
user_groups[0] = 1;
// Look in /etc/group for a group called "lat"
struct group *gr = getgrnam("lat");
if (gr)
lat_group = gr->gr_gid;
else
lat_group = 0;
}
// Create a new connection object for this remote node.
int LATServer::make_new_connection(unsigned char *buf, int len,
int interface,
LAT_Header *header,
unsigned char *macaddr)
{
int i;
i = get_next_connection_number();
if (i >= 0)
{
next_connection = i+1;
connections[i] = new LATConnection(i, buf, len, interface,
header->sequence_number,
header->ack_number,
macaddr);
}
else
{
// Number of virtual circuits exceeded
send_connect_error(9, header, interface, macaddr);
return -1;
}
return i;
}
void LATServer::delete_session(int conn, unsigned char id, int fd)
{
// Add this to the list and delete them later
deleted_session s(LOCAL_PTY, conn, id, fd);
dead_session_list.push_back(s);
}
void LATServer::delete_connection(int conn)
{
// Add this to the list and delete them later
debuglog(("connection %d pending deletion\n", conn));
dead_connection_list.push_back(conn);
}
// Got a reply from a DS90L - add it to the services list in a dummy service
void LATServer::got_enqreply(unsigned char *buf, int len, int interface, unsigned char *macaddr)
{
int off = 14;
unsigned char node_addr[6];
char node_description[256];
char node_name[256];
memset(node_addr, 0x00, sizeof(node_addr));
if (0 == memcmp(node_addr, buf + off, sizeof(node_addr)))
{
return;
}
memset(node_addr, 0xFF, sizeof(node_addr));
if (0 == memcmp(node_addr, buf + off, sizeof(node_addr)))
{
return;
}
// Node MAC address
memcpy(node_addr, buf + off, sizeof(node_addr));
// Skip destination node name
off = 22;
off += buf[off] + 1;
// Skip node groups
off += buf[off] + 1;
get_string(buf, &off, (unsigned char*)node_name);
get_string(buf, &off, (unsigned char*)node_description);
#if defined(debuglog)
if (memcmp(node_addr, macaddr, sizeof(node_addr))) {
debuglog(("got_enqreply : macaddr is different : %02hhX-%02hhX-%02hhX-%02hhX-%02hhX-%02hhX\n",
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]));
}
#endif
// Add it to the dummy service.
debuglog(("got_enqreply : node: '%s' : %02hhX-%02hhX-%02hhX-%02hhX-%02hhX-%02hhX\n",
node_name,
node_addr[0], node_addr[1], node_addr[2], node_addr[3], node_addr[4], node_addr[5]));
LATServices::Instance()->add_service(std::string(node_name),
std::string(""), // Dummy service name
std::string(node_description),
0,
interface, node_addr);
{
sig_blk_t _block(SIGALRM);
std::list<std::string>::iterator sl_iter;
sl_iter = find(slave_nodes.begin(), slave_nodes.end(), node_name);
if (sl_iter != slave_nodes.end()) {
debuglog(("got_enqreply : to remove from slave(%d), node: '%s'\n",
slave_nodes.size(), sl_iter->c_str()));
slave_nodes.erase(sl_iter);
}
sl_iter = find(known_slave_nodes.begin(), known_slave_nodes.end(), node_name);
if (sl_iter == known_slave_nodes.end()) {
known_slave_nodes.push_back(node_name);
debuglog(("got_enqreply : added to known(%d), node: '%s'\n",
known_slave_nodes.size(),
node_name));
}
}
}
// Got a COMMAND message from a remote node. it probably wants
// to set up a reverse-LAT session to us.
void LATServer::process_command_msg(unsigned char *buf, int len, int interface, unsigned char *macaddr)
{
LAT_Command *msg = (LAT_Command *)buf;
int ptr = sizeof(LAT_Command);
unsigned char remnode[256];
unsigned char remport[256];
unsigned char service[256];
unsigned char portname[256];
// TODO: Check the params in the LAT_Command message...
debuglog(("REVLAT cmd: %d, opcode:%d request_id: %d\n", msg->cmd, msg->opcode, msg->request_id));
ptr += buf[ptr]+1; // Skip past groups for now.
ptr += buf[ptr]+1; // Skip past groups for now.
// Get the remote node name.
get_string(buf, &ptr, remnode);
get_string(buf, &ptr, remport);
ptr += buf[ptr]+1; // Skip past greeting...
get_string(buf, &ptr, service);
get_string(buf, &ptr, portname);
debuglog(("Got request for reverse-LAT connection to %s/%s from %s/%s\n",
service, portname, remnode, remport));
// Make a new connection
// We can reuse connections here...
int connid = find_connection_by_node((char *)remnode);
if (connid == -1)
{
connid = get_next_connection_number();
connections[connid] = new LATConnection(connid,
(char *)service,
(char *)remport,
(char *)portname,
(char *)remnode,
false,
true);
}
// Make a reverse-session and connect it.
LAT_SessionStartCmd startcmd;
memset(&startcmd, 0, sizeof(startcmd));
startcmd.dataslotsize = 255;
if (connections[connid]->create_reverse_session((const char *)service,
(const char *)&startcmd,
dn_ntohs(msg->request_id),
interface, macaddr) == -1)
{
// If we created this connection just for the new session
// then get rid of it.
if (connections[connid]->num_clients() == 0)
delete_connection(connid);
}
}
// Add services received from a service announcement multicast
void LATServer::add_services(unsigned char *buf, int len, int interface, unsigned char *macaddr)
{
LAT_ServiceAnnounce *announce = (LAT_ServiceAnnounce *)buf;
int ptr = sizeof(LAT_ServiceAnnounce);
unsigned char service_groups[32];
unsigned char nodename[256];
unsigned char greeting[255];
unsigned char service[255];
unsigned char ident[255];
// Set the groups to all zeros initially
memset(service_groups, 0, sizeof(service_groups));
// Make sure someone isn't pulling our leg.
if (announce->group_length > 32)
announce->group_length = 32;
// Get group numbers
memcpy(service_groups, buf+ptr, announce->group_length);
// Compare with our user groups mask (which is always either completely
// empty or the full 32 bytes)
int i;
bool gotone = false;
for (i=0; i<announce->group_length; i++)
{
if (user_groups[i] & service_groups[i])
{
gotone = true;
break;
}
}
if (!gotone)
{
debuglog(("remote node not in our user groups list\n"));
return;
}
ptr += announce->group_length;
get_string(buf, &ptr, nodename);
get_string(buf, &ptr, greeting);
int num_services = buf[ptr++];
debuglog(("service announcement: status = %d\n", announce->node_status));
// Get all the services & register them
for (int i=1; i<=num_services; i++)
{
int rating = buf[ptr++];
get_string(buf, &ptr, service);
get_string(buf, &ptr, ident);
if ( (announce->node_status & 1) == 0)
{
LATServices::Instance()->add_service(std::string((char*)nodename),
std::string((char*)service),
std::string((char*)ident),
rating,
interface, macaddr);
}
else
{
LATServices::Instance()->remove_node(std::string((char*)nodename));
}
}
}
// Wait for data available on a client PTY
void LATServer::add_pty(LocalPort *port, int fd)
{
fdlist.push_back(fdinfo(fd, port, LOCAL_PTY));
}
// Got a new connection from latcp
void LATServer::accept_latcp(int fd)
{
struct sockaddr_un socka;
socklen_t sl = sizeof(socka);
int latcp_client_fd = accept(fd, (struct sockaddr *)&socka, &sl);
if (latcp_client_fd >= 0)
{
debuglog(("Got LATCP connection\n"));
latcp_circuits[latcp_client_fd] = new LATCPCircuit(latcp_client_fd);
fdlist.push_back(fdinfo(latcp_client_fd, 0, LATCP_SOCKET));
}
else
syslog(LOG_WARNING, "accept on latcp failed: %m");
}
// Got a new connection from llogin
void LATServer::accept_llogin(int fd)
{
struct sockaddr_un socka;
socklen_t sl = sizeof(socka);
int llogin_client_fd = accept(fd, (struct sockaddr *)&socka, &sl);
if (llogin_client_fd >= 0)
{
debuglog(("Got llogin connection\n"));
latcp_circuits[llogin_client_fd] = new LLOGINCircuit(llogin_client_fd);
fdlist.push_back(fdinfo(llogin_client_fd, 0, LLOGIN_SOCKET));
}
else
syslog(LOG_WARNING, "accept on llogin failed: %m");
}
// Read a request from LATCP
void LATServer::read_latcp(int fd)
{
debuglog(("Got command on latcp socket: %d\n", fd));
if (!latcp_circuits[fd]->do_command())
{
// Mark the FD for removal
deleted_session s(LATCP_SOCKET, 0, 0, fd);
dead_session_list.push_back(s);
}
}
// Read a request from LLOGIN
void LATServer::read_llogin(int fd)
{
debuglog(("Got command on llogin socket: %d\n", fd));
if (!latcp_circuits[fd]->do_command())
{
// Mark the FD for removal
deleted_session s(LLOGIN_SOCKET, 0, 0, fd);
dead_session_list.push_back(s);
}
}
void LATServer::delete_entry(deleted_session &dsl)
{
switch (dsl.get_type())
{
case INACTIVE:
break; // do nothing;
case LAT_SOCKET:
case LATCP_RENDEZVOUS:
case LLOGIN_RENDEZVOUS:
// These never get deleted
break;
case LATCP_SOCKET:
case LLOGIN_SOCKET:
remove_fd(dsl.get_fd());
close(dsl.get_fd());
delete latcp_circuits[dsl.get_fd()];
latcp_circuits.erase(dsl.get_fd());
break;
case LOCAL_PTY:
case DISABLED_PTY:
remove_fd(dsl.get_fd());
if (connections[dsl.get_conn()])
connections[dsl.get_conn()]->remove_session(dsl.get_id());
break;
default:
debuglog(("Unknown socket type: %d\n", dsl.get_type()));
break;
}
}
void LATServer::process_data(fdinfo &fdi)
{
switch (fdi.get_type())
{
case INACTIVE:
case DISABLED_PTY:
break; // do nothing;
case LOCAL_PTY:
fdi.get_localport()->do_read();
break;
case LAT_SOCKET:
read_lat(fdi.get_fd());
break;
case LATCP_RENDEZVOUS:
accept_latcp(fdi.get_fd());
break;
case LLOGIN_RENDEZVOUS:
accept_llogin(fdi.get_fd());
break;
case LATCP_SOCKET:
read_latcp(fdi.get_fd());
break;
case LLOGIN_SOCKET:
read_llogin(fdi.get_fd());
break;
}
}
// Send a circuit disconnect with error code
void LATServer::send_connect_error(int reason, LAT_Header *msg, int interface,
unsigned char *macaddr)
{
unsigned char buf[1600];
LAT_Header *header = (LAT_Header *)buf;
int ptr=sizeof(LAT_Header);
memset(buf, 0, sizeof(buf));
header->cmd = LAT_CCMD_DISCON;
header->num_slots = 0;
header->local_connid = 0; // Servers expect this to be clear
header->remote_connid = msg->local_connid;
header->sequence_number = msg->sequence_number;
header->ack_number = msg->ack_number;
buf[ptr++] = reason;
send_message(buf, ptr, interface, macaddr);
}
// Shutdown by latcp
void LATServer::Shutdown()
{
// Shutdown all the connections first
for (int i=1; i<MAX_CONNECTIONS; i++)
{
if (connections[i])
dead_connection_list.push_back(i);
}
// Exit the main loop.
do_shutdown = true;
}
// Return true if 'name' is a local service
bool LATServer::is_local_service(char *name)
{
// Look for it.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii != servicelist.end()) return true;
return false;
}
// Return the command info for a service
int LATServer::get_service_info(char *name, std::string &cmd, int &maxcon, int &curcon, uid_t &uid, gid_t &gid)
{
// Look for it.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii != servicelist.end())
{
cmd = sii->get_command();
maxcon = sii->get_max_connections();
curcon = sii->get_cur_connections();
uid = sii->get_uid();
gid = sii->get_gid();
return 0;
}
return -1;
}
// Add a new service from latcp
bool LATServer::add_service(char *name, char *ident, char *command, int max_conn,
uid_t uid, gid_t gid, int _rating, bool _static_rating)
{
// Look for it.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii != servicelist.end()) return false; // Already exists
// if rating is 0 then use the node default.
if (!_rating) _rating = rating;
servicelist.push_back(serviceinfo(name,
_rating,
_static_rating,
ident,
max_conn,
command,
uid, gid));
// Resend the announcement message.
send_service_announcement(-1);
return true;
}
// Change the rating of a service
bool LATServer::set_rating(char *name, int _rating, bool _static_rating)
{
// Look for it.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii == servicelist.end()) return false; // Not found it
sii->set_rating(_rating, _static_rating);
// Resend the announcement message.
send_service_announcement(-1);
return true;
}
// Change the ident of a service
bool LATServer::set_ident(char *name, char *ident)
{
// Look for it.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii == servicelist.end()) return false; // Not found it
debuglog(("Setting ident for %s to '%s'\n", name, ident));
sii->set_ident(ident);
// Resend the announcement message.
send_service_announcement(-1);
return true;
}
// Remove reverse-LAT port via latcp
bool LATServer::remove_port(char *name)
{
debuglog(("remove port %s\n", name));
// Search for it.
std::list<LocalPort>::iterator p(portlist.begin());
for (; p != portlist.end(); p++)
{
if (strcmp(p->get_devname().c_str(), name) == 0)
{
p->close_and_delete();
portlist.erase(p);
return true;
}
}
return false;
}
// Remove service via latcp
bool LATServer::remove_service(char *name)
{
// Only add the service if it does not already exist.
std::list<serviceinfo>::iterator sii;
sii = find(servicelist.begin(), servicelist.end(), name);
if (sii == servicelist.end()) return false; // Does not exist
servicelist.erase(sii);
// This is overkill but it gets rid of the service in the known
// services table.
LATServices::Instance()->remove_node(std::string((char*)get_local_node()));
// Resend the announcement message -- this will re-add our node
// services back into the known services list.
send_service_announcement(-1);
return true;
}
// Change the multicast timer
void LATServer::set_multicast(int newtime)
{
if (newtime)
{
multicast_timer = newtime;
alarm(newtime);
}
}
// Change the node name
void LATServer::set_nodename(unsigned char *name)
{
debuglog(("New node name is %s\n", name));
// Remove all existing node services
LATServices::Instance()->remove_node(std::string((char*)get_local_node()));
// Set the new name
strcpy((char *)local_name, (char *)name);
// Resend the announcement message -- this will re-add our node
// services back into the known services list.
send_service_announcement(-1);
}
// Start sending service announcements
void LATServer::unlock()
{
sig_blk_t _block(SIGALRM);
locked = false;
alarm_signal(SIGALRM);
}
// Generic make_connection code for llogin & port sessions.
int LATServer::make_connection(int fd, const char *service, const char *rnode, const char *port,
const char *localport, const char *password, bool queued)
{
unsigned char macaddr[6];
std::string servicenode;
int this_int;
char node[255];
// Take a local copy of the node so we can overwrite it.
strcpy(node, rnode);
// If no node was specified then use the highest rated one
if (node[0] == '\0')
{
debuglog(("make_connection : no node, use highest\n"));
if (!LATServices::Instance()->get_highest(std::string((char*)service),
servicenode, macaddr,
&this_int))
{
debuglog(("Can't find service %s\n", service));
return -2; // Never eard of it!
}
strcpy((char *)node, servicenode.c_str());
}
else
{
debuglog(("make_connection : node : %s\n", node));
// Try to find the node
if (!LATServices::Instance()->get_node(std::string((char*)service),
std::string((char*)node), macaddr,
&this_int))
{
debuglog(("Can't find node %s in service\n", node, service));
return -2;
}
}
// Look for a connection that's already in use for this node, unless we
// are queued in which case we need to allocate a new connection for
// initiating the connection and receiving status requests on.
int connid = -1;
if (!queued) connid = find_connection_by_node(node);
if (connid == -1)
{
// None: create a new one
connid = get_next_connection_number();
connections[connid] = new LATConnection(connid,
(char *)service,
(char *)port,
(char *)localport,
(char *)node,
queued,
false);
}
return connid;
}
int LATServer::make_llogin_connection(int fd, const char *service, const char *rnode, const char *port,
const char *localport, const char *password, bool queued)
{
int ret;
int connid;
connid = make_connection(fd, service, rnode, port, localport, password, queued);
if (connid < 0)
return connid;
debuglog(("lloginSession for %s has connid %d\n", service, connid));
ret = connections[connid]->create_llogin_session(fd, service, port, localport, password);
// Remove LLOGIN socket from the list as it's now been
// added as a PTY (honest!)
deleted_session s(LLOGIN_SOCKET, 0, 0, fd);
dead_session_list.push_back(s);
return ret;
}
// Called when activity is detected on a LocalPort - we connect it
// to the service.
int LATServer::make_port_connection(int fd, LocalPort *lport,
const char *service, const char *rnode,
const char *port,
const char *localport,
const char *password,
bool queued)
{
int ret;
int connid;
debuglog(("LATServer::make_port_connection : fd %d, lport '0x%X', service '%s', rnode '%s', port '%s', localport '%s', pwd '%s'\n",
fd, lport, service, rnode, port, localport, password));
connid = make_connection(fd, service, rnode, port, localport, password, queued);
if (connid < 0)
return connid;
debuglog(("localport for %s has connid %d\n", service, connid));
ret = connections[connid]->create_localport_session(fd, lport, service,
port, localport, password);
return ret;
}
// Called from latcp to create a /dev/lat/ port
// Here we simply add it to a lookaside list and wait
// for it to be activated by a user.
int LATServer::create_local_port(unsigned char *service,
unsigned char *portname,
unsigned char *devname,
unsigned char *remnode,
bool queued,
bool clean,
unsigned char *password)
{
debuglog(("Server::create_local_port: %s\n", devname));
// Don't create it if it already exists.
std::list<LocalPort>::iterator i(portlist.begin());
for (; i != portlist.end(); i++)
{
if (strcmp(i->get_devname().c_str(), (char *)devname) == 0)
{
return 1; // already in use
}
}
portlist.push_back(LocalPort(service, portname, devname, remnode, queued, clean, password));
// Find the actual port in the list and start it up, this is because
// the STL containers hold actual objects rather then pointers
std::list<LocalPort>::iterator p(portlist.begin());
for (; p != portlist.end(); p++)
{
if (strcmp(p->get_devname().c_str(), (char *)devname) == 0)
{
p->init_port();
break;
}
}
return 0;
}
// Make this as much like VMS LATCP SHOW NODE as possible.
bool LATServer::show_characteristics(bool verbose, std::ostringstream &output)
{
output <<std::endl;
output.setf(std::ios::left, std::ios::adjustfield);
output << "Node Name: ";
output.width(15);
output << get_local_node() << " " << " LAT Protocol Version: " << LAT_VERSION << "." << LAT_VERSION_ECO << std::endl;
output.setf(std::ios::right, std::ios::adjustfield);
output << "Node State: On " << " LATD Version: " << VERSION << std::endl;
output << "Node Ident: " << greeting << std::endl;
output << std::endl;
output << "Service Responder : " << (responder?"Enabled":"Disabled") << std::endl;
output << "Interfaces : ";
for (int i=0; i<num_interfaces; i++)
{
output << iface->ifname(interface_num[i]) << " ";
}
output << std::endl << std::endl;
output << "Circuit Timer (msec): " << std::setw(6) << circuit_timer*10 << " Keepalive Timer (sec): " << std::setw(6) << keepalive_timer << std::endl;
output << "Retransmit Limit: " << std::setw(6) << retransmit_limit << std::endl;
output << "Multicast Timer (sec):" << std::setw(6) << multicast_timer << std::endl;
output << std::endl;
// Show groups
output << "User Groups: ";
print_bitmap(output, true, user_groups);
output << "Service Groups: ";
print_bitmap(output, groups_set, groups);
output << std::endl;
// Show services we are accepting for.
output << "Service Name Status Rating Identification" << std::endl;
std::list<serviceinfo>::iterator i(servicelist.begin());
for (; i != servicelist.end(); i++)
{
output.width(15);
output.setf(std::ios::left, std::ios::adjustfield);
output << i->get_name().c_str();
output.setf(std::ios::right, std::ios::adjustfield);
output << "Enabled" << std::setw(6) << i->get_rating() <<
(i->get_static()?" ":" D ") << i->get_id() << std::endl;
}
output << std::endl << "Port Service Node Remote Port Queued" << std::endl;
// Show allocated ports
std::list<LocalPort>::iterator p(portlist.begin());
for (; p != portlist.end(); p++)
{
p->show_info(verbose, output);
}
// NUL-terminate it.
output << std::endl << std::ends;
return true;
}
bool LATServer::show_nodes(bool verbose, std::ostringstream &output)
{
return LATServices::Instance()->list_dummy_nodes(verbose, output);
}
// Return a number for a new connection
int LATServer::get_next_connection_number()
{
int i;
for (i=next_connection; i < MAX_CONNECTIONS; i++)
{
if (!connections[i])
{
next_connection = i+1;
return i;
}
}
// Didn't find a slot here - try from the start
for (i=1; i < next_connection; i++)
{
if (!connections[i])
{
next_connection = i+1;
return i;
}
}
return -1;
}
int LATServer::set_servergroups(unsigned char *bitmap)
{
// Assume all unset if this is the first mention of groups
if (!groups_set)
{
memset(groups, 0, 32);
user_groups[0] = 1; // But enable group 0
}
groups_set = true;
for (int i=0; i<32; i++)
{
groups[i] |= bitmap[i];
}
return true;
}
int LATServer::unset_servergroups(unsigned char *bitmap)
{
// Assume all set if this is the first mention of groups
if (!groups_set)
{
memset(groups, 0xFF, 32);
}
groups_set = true;
for (int i=0; i<32; i++)
{
groups[i] &= ~bitmap[i];
}
return true;
}
int LATServer::set_usergroups(unsigned char *bitmap)
{
for (int i=0; i<32; i++)
{
user_groups[i] |= bitmap[i];
}
return true;
}
int LATServer::unset_usergroups(unsigned char *bitmap)
{
for (int i=0; i<32; i++)
{
user_groups[i] &= ~bitmap[i];
}
return true;
}
// Look for a connection for this node name, if not found then
// return -1
int LATServer::find_connection_by_node(const char *node)
{
debuglog(("Looking for connection to node %s\n", node));
for (int i=1; i<MAX_CONNECTIONS; i++)
{
if (connections[i] &&
connections[i]->node_is(node) &&
connections[i]->num_clients() < MAX_CONNECTIONS-1)
{
debuglog(("Reusing connection for node %s\n", node));
return i;
}
}
return -1;
}
// Print a groups bitmap
// TODO: print x-y format like we accept in latcp.
void LATServer::print_bitmap(std::ostringstream &output, bool isset, unsigned char *bitmap)
{
if (!isset)
{
output << "0" << std::endl;
return;
}
bool printed = false;
for (int i=0; i<32; i++) // Show bytes
{
unsigned char thebyte = bitmap[i];
for (int j=0; j<8; j++) // Bits in the byte
{
if (thebyte&1)
{
if (printed) output << ",";
printed = true;
output << i*8+j;
}
thebyte = thebyte>>1;
}
}
output << std::endl;
}
LATServer *LATServer::instance = NULL;
unsigned char LATServer::greeting[255] = {'\0'};
syntax highlighted by Code2HTML, v. 0.9.1