19c749625f
ISDN4BSD is the work of our brand-new comitter: Hellmuth Michaelis, who has done a tremendous amount of work to bring us this far. There are still some outstanding issues and files to bring into the tree, and for now it will be needed to pick up all the extra docs from the isdn4bsd release. It is probably also a very good idea to subscribe to the isdn@freebsd.org mailing list before you try this out. These files correspond to release "beta Version 0.70.00 / December 1998" from Hellmuth.
812 lines
22 KiB
C
812 lines
22 KiB
C
/*
|
|
* Copyright (c) 1998 Martin Husemann. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the author nor the names of any co-contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* 4. Altered versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software and/or documentation.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*---------------------------------------------------------------------------
|
|
*
|
|
* i4b daemon - network monitor server module
|
|
* ------------------------------------------
|
|
*
|
|
* $Id: monitor.c,v 1.6 1998/08/10 13:55:24 hm Exp $
|
|
*
|
|
* last edit-date: [Fri Jul 17 18:07:00 1998]
|
|
*
|
|
* -mh created
|
|
*
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include "isdnd.h"
|
|
|
|
#ifndef I4B_EXTERNAL_MONITOR
|
|
/* dummy version of routines needed by config file parser
|
|
* (config files should be valid with and without external montioring
|
|
* support compiled into the daemon) */
|
|
void monitor_clear_rights()
|
|
{ }
|
|
int monitor_start_rights(const char *clientspec)
|
|
{ return I4BMAR_OK; }
|
|
void monitor_add_rights(int rights_mask)
|
|
{ }
|
|
void monitor_fixup_rights()
|
|
{ }
|
|
#else
|
|
|
|
#include "monitor.h"
|
|
#include "vararray.h"
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
VARA_DECL(struct monitor_rights) rights = VARA_INITIALIZER;
|
|
#define INITIAL_RIGHTS_ALLOC 10 /* most users will have one or two entries */
|
|
|
|
static int local_rights = -1; /* index of entry for local socket, -1 if none */
|
|
|
|
/* for each active monitor connection we have one of this: */
|
|
struct monitor_connection {
|
|
int sock; /* socket for this connection */
|
|
int rights; /* active rights for this connection */
|
|
int events; /* bitmask of events client is interested in */
|
|
};
|
|
static VARA_DECL(struct monitor_connection) connections = VARA_INITIALIZER;
|
|
#define INITIAL_CONNECTIONS_ALLOC 30 /* high guess */
|
|
|
|
/* derive channel number from config pointer */
|
|
#define CHNO(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
|
|
|
|
/* local prototypes */
|
|
static int cmp_rights(const void *a, const void *b);
|
|
static int monitor_command(int con_index, int fd, int rights);
|
|
static void cmd_dump_rights(int fd, int rights, BYTE *cmd);
|
|
static void cmd_dump_mcons(int fd, int rights, BYTE *cmd);
|
|
static void cmd_reread_cfg(int fd, int rights, BYTE *cmd);
|
|
static void cmd_hangup(int fd, int rights, BYTE *cmd);
|
|
static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes);
|
|
static int anybody(int mask);
|
|
|
|
/*
|
|
* Due to the way we structure config files, the rights for an external
|
|
* monitor might be stated in multiple steps. First a call to
|
|
* monitor_start_rights opens an entry. Further (optional) calls to
|
|
* montior_add_rights assemble additional rights for this "current"
|
|
* entry. When closing the sys-file section of the config file, the
|
|
* "current" entry becomes invalid.
|
|
*/
|
|
static int cur_add_entry = -1;
|
|
|
|
/*
|
|
* Initialize the monitor server module. This affects only active
|
|
* connections, the access rights are not modified here!
|
|
*/
|
|
void monitor_init()
|
|
{
|
|
accepted = 0;
|
|
VARA_EMPTY(connections);
|
|
}
|
|
|
|
/*
|
|
* Prepare for exit
|
|
*/
|
|
void monitor_exit()
|
|
{
|
|
int i;
|
|
|
|
/* Close all open connections. */
|
|
VARA_FOREACH(connections, i)
|
|
close(VARA_AT(connections, i).sock);
|
|
|
|
/* Remove their descriptions */
|
|
VARA_EMPTY(connections);
|
|
}
|
|
|
|
/*
|
|
* Initialize access rights. No active connections are affected!
|
|
*/
|
|
void monitor_clear_rights()
|
|
{
|
|
VARA_EMPTY(rights);
|
|
cur_add_entry = -1;
|
|
}
|
|
|
|
/*
|
|
* Add an entry to the access lists. The clientspec either is
|
|
* the name of the local socket or a host- or networkname or
|
|
* numeric ip/host-bit-len spec.
|
|
*/
|
|
int monitor_start_rights(const char *clientspec)
|
|
{
|
|
int i;
|
|
struct monitor_rights r;
|
|
|
|
/* initialize the new rights entry */
|
|
memset(&r, 0, sizeof r);
|
|
|
|
/* check clientspec */
|
|
if (*clientspec == '/') {
|
|
struct sockaddr_un sa;
|
|
|
|
/* this is a local socket spec, check if we already have one */
|
|
if (VARA_VALID(rights, local_rights))
|
|
return I4BMAR_DUP;
|
|
/* does it fit in a local socket address? */
|
|
if (strlen(clientspec) > sizeof sa.sun_path)
|
|
return I4BMAR_LENGTH;
|
|
r.local = 1;
|
|
strcpy(r.name, clientspec);
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
} else {
|
|
/* remote entry, parse host/net and cidr */
|
|
char hostname[FILENAME_MAX];
|
|
char *p;
|
|
p = strchr(clientspec, '/');
|
|
if (!p) {
|
|
struct hostent *host;
|
|
u_int32_t hn;
|
|
/* must be a host spec */
|
|
r.mask = ~0;
|
|
host = gethostbyname(clientspec);
|
|
if (!host)
|
|
return I4BMAR_NOIP;
|
|
memcpy(&hn, host->h_addr_list[0], sizeof hn);
|
|
r.net = (u_int32_t)ntohl(hn);
|
|
} else if (p[1]) {
|
|
/* must be net/cidr spec */
|
|
int l;
|
|
struct netent *net;
|
|
u_int32_t s = ~0U;
|
|
int num = strtol(p+1, NULL, 10);
|
|
if (num < 0 || num > 32)
|
|
return I4BMAR_CIDR;
|
|
s >>= num;
|
|
s ^= ~0U;
|
|
l = p - clientspec;
|
|
if (l >= sizeof hostname)
|
|
return I4BMAR_LENGTH;
|
|
strncpy(hostname, clientspec, l);
|
|
hostname[l] = '\0';
|
|
net = getnetbyname(hostname);
|
|
if (net == NULL)
|
|
r.net = (u_int32_t)inet_network(hostname);
|
|
else
|
|
r.net = (u_int32_t)net->n_net;
|
|
r.mask = s;
|
|
r.net &= s;
|
|
} else
|
|
return I4BMAR_CIDR;
|
|
|
|
/* check for duplicate entry */
|
|
VARA_FOREACH(rights, i)
|
|
if (VARA_AT(rights, i).mask == r.mask &&
|
|
VARA_AT(rights, i).net == r.net &&
|
|
VARA_AT(rights, i).local == r.local)
|
|
return I4BMAR_DUP;
|
|
#endif
|
|
}
|
|
r.rights = 0;
|
|
|
|
/* entry ok, add it to the collection */
|
|
cur_add_entry = i = VARA_NUM(rights);
|
|
VARA_ADD_AT(rights, i, struct monitor_rights, INITIAL_RIGHTS_ALLOC);
|
|
memcpy(&VARA_AT(rights, i), &r, sizeof r);
|
|
if (r.local)
|
|
local_rights = i;
|
|
|
|
log(LL_DBG, "system: monitor = %s", clientspec);
|
|
|
|
return I4BMAR_OK;
|
|
}
|
|
|
|
/*
|
|
* Add rights to the currently constructed entry - if any.
|
|
*/
|
|
void monitor_add_rights(int rights_mask)
|
|
{
|
|
if (cur_add_entry < 0) return; /* noone under construction */
|
|
|
|
VARA_AT(rights, cur_add_entry).rights |= rights_mask;
|
|
|
|
log(LL_DBG, "system: monitor-access = 0x%x", rights_mask);
|
|
}
|
|
|
|
/*
|
|
* All rights have been added now. Sort the to get most specific
|
|
* host/net masks first, so we can travel the list and use the first
|
|
* match for actual rights.
|
|
*/
|
|
void monitor_fixup_rights()
|
|
{
|
|
int i;
|
|
|
|
/* no more rights may be added to the current entry */
|
|
cur_add_entry = -1;
|
|
|
|
/* sort the rights array */
|
|
qsort(VARA_PTR(rights), VARA_NUM(rights), sizeof(struct monitor_rights), cmp_rights);
|
|
|
|
/* now the local entry may have moved, update its index */
|
|
if (VARA_VALID(rights, local_rights)) {
|
|
local_rights = -1;
|
|
VARA_FOREACH(rights, i) {
|
|
if (VARA_AT(rights, i).local) {
|
|
local_rights = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* comparator for rights */
|
|
static int cmp_rights(const void *a, const void *b)
|
|
{
|
|
u_int32_t mask;
|
|
struct monitor_rights const * pa = (struct monitor_rights const*)a;
|
|
struct monitor_rights const * pb = (struct monitor_rights const*)b;
|
|
|
|
/* local sorts first */
|
|
if (pa->local)
|
|
return -1;
|
|
|
|
/* which is the less specific netmask? */
|
|
mask = pa->mask;
|
|
if ((pb->mask & mask) == 0)
|
|
mask = pb->mask;
|
|
/* are the entries disjunct? */
|
|
if ((pa->net & mask) != (pb->net & mask)) {
|
|
/* simply compare net part of address */
|
|
return ((pa->net & mask) < (pb->net & mask)) ? -1 : 1;
|
|
}
|
|
/* One entry is part of the others net. We already now "mask" is
|
|
* the netmask of the less specific (i.e. greater) one */
|
|
return (pa->mask == mask) ? 1 : -1;
|
|
}
|
|
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
/*
|
|
* Check if access rights for a remote socket are specified and
|
|
* create this socket. Return -1 otherwise.
|
|
*/
|
|
int monitor_create_remote_socket(int portno)
|
|
{
|
|
struct sockaddr_in sa;
|
|
int val;
|
|
int remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (remotesockfd == -1) {
|
|
log(LL_ERR, "could not create remote monitor socket, errno = %d", errno);
|
|
exit(1);
|
|
}
|
|
val = 1;
|
|
if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) {
|
|
log(LL_ERR, "could not setsockopt, errno = %d", errno);
|
|
exit(1);
|
|
}
|
|
memset(&sa, 0, sizeof sa);
|
|
sa.sin_len = sizeof sa;
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = htons(portno);
|
|
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
if (bind(remotesockfd, (struct sockaddr *)&sa, sizeof sa) == -1) {
|
|
log(LL_ERR, "could not bind remote monitor socket to port %d, errno = %d", portno, errno);
|
|
exit(1);
|
|
}
|
|
if (listen(remotesockfd, 0)) {
|
|
log(LL_ERR, "could not listen on monitor socket, errno = %d", errno);
|
|
exit(1);
|
|
}
|
|
|
|
return remotesockfd;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check if access rights for a local socket are specified and
|
|
* create this socket. Return -1 otherwise.
|
|
*/
|
|
int monitor_create_local_socket()
|
|
{
|
|
int s;
|
|
struct sockaddr_un sa;
|
|
|
|
/* check for a local entry */
|
|
if (!VARA_VALID(rights, local_rights))
|
|
return -1;
|
|
|
|
/* create and setup socket */
|
|
s = socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
if (s == -1) {
|
|
log(LL_ERR, "could not create local monitor socket, errno = %d", errno);
|
|
exit(1);
|
|
}
|
|
unlink(VARA_AT(rights, local_rights).name);
|
|
memset(&sa, 0, sizeof sa);
|
|
sa.sun_len = sizeof sa;
|
|
sa.sun_family = AF_LOCAL;
|
|
strcpy(sa.sun_path, VARA_AT(rights, local_rights).name);
|
|
if (bind(s, (struct sockaddr *)&sa, sizeof sa)) {
|
|
log(LL_ERR, "could not bind local monitor socket [%s], errno = %d", VARA_AT(rights, local_rights).name, errno);
|
|
exit(1);
|
|
}
|
|
if (listen(s, 0)) {
|
|
log(LL_ERR, "could not listen on local monitor socket, errno = %d", errno);
|
|
exit(1);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Prepare a fd_set for a select call. Add all our local
|
|
* filedescriptors to the set, increment max_fd if appropriate.
|
|
*/
|
|
void monitor_prepselect(fd_set *selset, int *max_fd)
|
|
{
|
|
int i;
|
|
|
|
VARA_FOREACH(connections, i) {
|
|
int fd = VARA_AT(connections, i).sock;
|
|
if (fd > *max_fd)
|
|
*max_fd = fd;
|
|
FD_SET(fd, selset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the result from a select call indicates something
|
|
* to do for us.
|
|
*/
|
|
void monitor_handle_input(fd_set *selset)
|
|
{
|
|
int i;
|
|
|
|
VARA_FOREACH(connections, i) {
|
|
int fd = VARA_AT(connections, i).sock;
|
|
if (FD_ISSET(fd, selset)) {
|
|
/* handle command from this client */
|
|
if (monitor_command(i, fd, VARA_AT(connections, i).rights) != 0) {
|
|
/* broken or closed connection */
|
|
log(LL_DBG, "monitor connection #%d closed", i);
|
|
VARA_REMOVEAT(connections, i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* all connections gone? */
|
|
if (VARA_NUM(connections) == 0)
|
|
accepted = 0;
|
|
}
|
|
|
|
/*
|
|
* Try new incoming connection on the given socket.
|
|
* Setup client descriptor and send initial data.
|
|
*/
|
|
void monitor_handle_connect(int sockfd, int is_local)
|
|
{
|
|
struct monitor_connection *con;
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
struct sockaddr_in ia;
|
|
u_int32_t ha = 0;
|
|
#endif
|
|
struct sockaddr_un ua;
|
|
BYTE idata[I4B_MON_IDATA_SIZE];
|
|
int fd = -1, s, i, r_mask;
|
|
char source[FILENAME_MAX];
|
|
|
|
/* accept the connection */
|
|
if (is_local) {
|
|
s = sizeof ua;
|
|
fd = accept(sockfd, (struct sockaddr *)&ua, &s);
|
|
strcpy(source, "local connection");
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
} else {
|
|
s = sizeof ia;
|
|
fd = accept(sockfd, (struct sockaddr *)&ia, &s);
|
|
snprintf(source, sizeof source, "tcp/ip connection from %s\n",
|
|
inet_ntoa(ia.sin_addr));
|
|
memcpy(&ha, &ia.sin_addr.s_addr, sizeof ha);
|
|
ha = ntohl(ha);
|
|
#endif
|
|
}
|
|
|
|
/* check the access rights of this connection */
|
|
r_mask = 0;
|
|
VARA_FOREACH(rights, i) {
|
|
struct monitor_rights *r = &VARA_AT(rights, i);
|
|
if (r->local) {
|
|
if (is_local) {
|
|
r_mask = r->rights;
|
|
break;
|
|
}
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
} else {
|
|
if ((ha & r->mask) == r->net) {
|
|
r_mask = r->rights;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (r_mask == 0) {
|
|
/* no rights - go away */
|
|
log(LL_DBG, "monitor access denied: %s", source);
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
accepted = 1;
|
|
i = VARA_NUM(connections);
|
|
VARA_ADD_AT(connections, i, struct monitor_connection, INITIAL_CONNECTIONS_ALLOC);
|
|
con = &VARA_AT(connections, i);
|
|
memset(con, 0, sizeof *con);
|
|
con->sock = fd;
|
|
con->rights = r_mask;
|
|
log(LL_DBG, "monitor access granted, rights = %x, #%d, %s",
|
|
r_mask, i, source);
|
|
|
|
/* send initial data */
|
|
I4B_PREP_CMD(idata, I4B_MON_IDATA_CODE);
|
|
I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMAJOR, MPROT_VERSION);
|
|
I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMINOR, MPROT_REL);
|
|
I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, ncontroller);
|
|
I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask);
|
|
write(fd, idata, sizeof idata);
|
|
|
|
for (i = 0; i < ncontroller; i++) {
|
|
BYTE ictrl[I4B_MON_ICTRL_SIZE];
|
|
I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE);
|
|
I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, name_of_controller(isdn_ctrl_tab[i].ctrl_type, isdn_ctrl_tab[i].card_type));
|
|
I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, 0);
|
|
I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0);
|
|
I4B_PUT_4B(ictrl, I4B_MON_ICTRL_NCHAN, 2);
|
|
write(fd, ictrl, sizeof ictrl);
|
|
}
|
|
}
|
|
|
|
/* dump all monitor rights */
|
|
static void cmd_dump_rights(int fd, int r_mask, BYTE *cmd)
|
|
{
|
|
int i;
|
|
BYTE drini[I4B_MON_DRINI_SIZE];
|
|
BYTE dr[I4B_MON_DR_SIZE];
|
|
|
|
I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE);
|
|
I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, VARA_NUM(rights));
|
|
write(fd, drini, sizeof drini);
|
|
|
|
VARA_FOREACH(rights, i) {
|
|
I4B_PREP_EVNT(dr, I4B_MON_DR_CODE);
|
|
I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, VARA_AT(rights, i).rights);
|
|
I4B_PUT_4B(dr, I4B_MON_DR_NET, VARA_AT(rights, i).net);
|
|
I4B_PUT_4B(dr, I4B_MON_DR_MASK, VARA_AT(rights, i).mask);
|
|
I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, VARA_AT(rights, i).local);
|
|
write(fd, dr, sizeof dr);
|
|
}
|
|
}
|
|
|
|
/* rescan config file */
|
|
static void cmd_reread_cfg(int fd, int rights, BYTE *cmd)
|
|
{
|
|
rereadconfig(42);
|
|
}
|
|
|
|
/* drop one connection */
|
|
static void cmd_hangup(int fd, int rights, BYTE *cmd)
|
|
{
|
|
int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
|
|
hangup_channel(channel);
|
|
}
|
|
|
|
/* dump all active monitor connections */
|
|
static void cmd_dump_mcons(int fd, int rights, BYTE *cmd)
|
|
{
|
|
int i;
|
|
BYTE dcini[I4B_MON_DCINI_SIZE];
|
|
|
|
I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE);
|
|
I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, VARA_NUM(connections));
|
|
write(fd, dcini, sizeof dcini);
|
|
|
|
VARA_FOREACH(connections, i) {
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
int namelen;
|
|
struct sockaddr_in name;
|
|
#endif
|
|
BYTE dc[I4B_MON_DC_SIZE];
|
|
|
|
I4B_PREP_EVNT(dc, I4B_MON_DC_CODE);
|
|
I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, VARA_AT(connections, i).rights);
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
namelen = sizeof name;
|
|
if (getpeername(VARA_AT(connections, i).sock, (struct sockaddr*)&name, &namelen) == 0)
|
|
memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr);
|
|
#endif
|
|
write(fd, dc, sizeof dc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle a command from the given socket. The client
|
|
* has rights as specified in the rights parameter.
|
|
* Return non-zero if connection is closed.
|
|
*/
|
|
static int monitor_command(int con_index, int fd, int rights)
|
|
{
|
|
char cmd[I4B_MAX_MON_CLIENT_CMD];
|
|
u_int code;
|
|
/* command dispatch table */
|
|
typedef void (*cmd_func_t)(int fd, int rights, BYTE *cmd);
|
|
static struct {
|
|
cmd_func_t call; /* function to execute */
|
|
u_int rights; /* necessary rights */
|
|
} cmd_tab[] =
|
|
{
|
|
/* 0 */ { NULL, 0 },
|
|
/* 1 */ { cmd_dump_rights, I4B_CA_COMMAND_FULL },
|
|
/* 2 */ { cmd_dump_mcons, I4B_CA_COMMAND_FULL },
|
|
/* 3 */ { cmd_reread_cfg, I4B_CA_COMMAND_FULL },
|
|
/* 4 */ { cmd_hangup, I4B_CA_COMMAND_FULL },
|
|
};
|
|
#define NUMCMD (sizeof cmd_tab / sizeof cmd_tab[0])
|
|
|
|
u_long u;
|
|
int bytes;
|
|
|
|
/* Network transfer may deliver two or more packets concatenated.
|
|
* Peek at the header and read only one event at a time... */
|
|
ioctl(fd, FIONREAD, &u);
|
|
if (u < I4B_MON_CMD_HDR) {
|
|
if (u == 0) {
|
|
/* socket closed by peer */
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
return 0; /* not enough data there yet */
|
|
}
|
|
bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK);
|
|
if (bytes < I4B_MON_CMD_HDR)
|
|
return 0; /* errh? something must be wrong... */
|
|
bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
|
|
if (bytes >= sizeof cmd) {
|
|
close(fd);
|
|
log(LL_ERR, "garbage on monitor connection #%d, closing it", con_index);
|
|
return 1;
|
|
}
|
|
/* now we know the size, it fits, so lets read it! */
|
|
if (read(fd, cmd, bytes) <= 0) {
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
|
|
/* decode command */
|
|
code = I4B_GET_2B(cmd, I4B_MON_CMD);
|
|
|
|
/* special case: may modify our connection descriptor, is
|
|
* beyound all rights checks */
|
|
if (code == I4B_MON_CCMD_SETMASK) {
|
|
/*
|
|
u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR);
|
|
u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR);
|
|
*/
|
|
int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS);
|
|
VARA_AT(connections, con_index).events = events & rights;
|
|
return 0;
|
|
}
|
|
|
|
if (code < 0 || code >= NUMCMD) {
|
|
log(LL_ERR, "illegal command from client #%d: code = %d\n",
|
|
con_index, code);
|
|
return 0;
|
|
}
|
|
if (cmd_tab[code].call == NULL)
|
|
return 0;
|
|
if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights)
|
|
cmd_tab[code].call(fd, rights, cmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if somebody would receive an event with this mask.
|
|
* We are lazy and try to avoid assembling unneccesary packets.
|
|
* Return 0 if no one interested, nonzero otherwise.
|
|
*/
|
|
static int anybody(int mask)
|
|
{
|
|
int i;
|
|
|
|
VARA_FOREACH(connections, i)
|
|
if ((VARA_AT(connections, i).events & mask) == mask)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send an event to every connection interested in this kind of
|
|
* event
|
|
*/
|
|
static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes)
|
|
{
|
|
int i;
|
|
|
|
VARA_FOREACH(connections, i) {
|
|
if ((VARA_AT(connections, i).events & mask) == mask) {
|
|
int fd = VARA_AT(connections, i).sock;
|
|
write(fd, pkt, bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Post a logfile event
|
|
*/
|
|
void monitor_evnt_log(int prio, const char * what, const char * msg)
|
|
{
|
|
BYTE evnt[I4B_MON_LOGEVNT_SIZE];
|
|
time_t now;
|
|
|
|
if (!anybody(I4B_CA_EVNT_I4B)) return;
|
|
|
|
time(&now);
|
|
I4B_PREP_EVNT(evnt, I4B_MON_LOGEVNT_CODE);
|
|
I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_TSTAMP, (long)now);
|
|
I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_PRIO, prio);
|
|
I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_WHAT, what);
|
|
I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_MSG, msg);
|
|
|
|
monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
|
|
}
|
|
|
|
/*
|
|
* Post a charging event on the connection described
|
|
* by the given config entry.
|
|
*/
|
|
void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate)
|
|
{
|
|
int chno = CHNO(cep);
|
|
int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
|
|
time_t now;
|
|
BYTE evnt[I4B_MON_CHRG_SIZE];
|
|
|
|
if (!anybody(mask)) return;
|
|
|
|
time(&now);
|
|
I4B_PREP_EVNT(evnt, I4B_MON_CHRG_CODE);
|
|
I4B_PUT_4B(evnt, I4B_MON_CHRG_TSTAMP, (long)now);
|
|
I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, chno);
|
|
I4B_PUT_4B(evnt, I4B_MON_CHRG_UNITS, units);
|
|
I4B_PUT_4B(evnt, I4B_MON_CHRG_ESTIMATED, estimate ? 1 : 0);
|
|
|
|
monitor_broadcast(mask, evnt, sizeof evnt);
|
|
}
|
|
|
|
/*
|
|
* Post a connection event
|
|
*/
|
|
void monitor_evnt_connect(cfg_entry_t *cep)
|
|
{
|
|
BYTE evnt[I4B_MON_CONNECT_SIZE];
|
|
char devname[I4B_MAX_MON_STRING];
|
|
int chno = CHNO(cep);
|
|
int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
|
|
time_t now;
|
|
|
|
if (!anybody(mask)) return;
|
|
|
|
time(&now);
|
|
snprintf(devname, sizeof devname, "%s%d", bdrivername(cep->usrdevicename), cep->usrdeviceunit);
|
|
I4B_PREP_EVNT(evnt, I4B_MON_CONNECT_CODE);
|
|
I4B_PUT_4B(evnt, I4B_MON_CONNECT_TSTAMP, (long)now);
|
|
I4B_PUT_4B(evnt, I4B_MON_CONNECT_DIR, cep->direction == DIR_OUT ? 1 : 0);
|
|
I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, chno);
|
|
I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name);
|
|
I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname);
|
|
I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming);
|
|
I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->remote_phone_dialout);
|
|
|
|
monitor_broadcast(mask, evnt, sizeof evnt);
|
|
}
|
|
|
|
/*
|
|
* Post a disconnect event
|
|
*/
|
|
void monitor_evnt_disconnect(cfg_entry_t *cep)
|
|
{
|
|
BYTE evnt[I4B_MON_DISCONNECT_SIZE];
|
|
int chno = CHNO(cep);
|
|
int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
|
|
time_t now;
|
|
|
|
if (!anybody(mask)) return;
|
|
|
|
time(&now);
|
|
I4B_PREP_EVNT(evnt, I4B_MON_DISCONNECT_CODE);
|
|
I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_TSTAMP, (long)now);
|
|
I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, chno);
|
|
|
|
monitor_broadcast(mask, evnt, sizeof evnt);
|
|
}
|
|
|
|
/*
|
|
* Post an up/down event
|
|
*/
|
|
void monitor_evnt_updown(cfg_entry_t *cep, int up)
|
|
{
|
|
BYTE evnt[I4B_MON_UPDOWN_SIZE];
|
|
int chno = CHNO(cep);
|
|
int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
|
|
time_t now;
|
|
|
|
if (!anybody(mask)) return;
|
|
|
|
time(&now);
|
|
I4B_PREP_EVNT(evnt, I4B_MON_UPDOWN_CODE);
|
|
I4B_PUT_4B(evnt, I4B_MON_UPDOWN_TSTAMP, (long)now);
|
|
I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, chno);
|
|
I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up);
|
|
|
|
monitor_broadcast(mask, evnt, sizeof evnt);
|
|
}
|
|
|
|
void hangup_channel(int channel)
|
|
{
|
|
int i;
|
|
cfg_entry_t * cep = NULL;
|
|
|
|
for (i = 0; i < ncontroller; i++)
|
|
{
|
|
if(isdn_ctrl_tab[i].state != CTRL_UP)
|
|
continue;
|
|
if(isdn_ctrl_tab[i].stateb1 != CHAN_IDLE)
|
|
{
|
|
cep = get_cep_by_cc(i, 0);
|
|
if (cep != NULL && CHNO(cep) == channel)
|
|
goto found;
|
|
}
|
|
if(isdn_ctrl_tab[i].stateb2 != CHAN_IDLE)
|
|
{
|
|
cep = get_cep_by_cc(i, 1);
|
|
if (cep != NULL && CHNO(cep) == channel)
|
|
goto found;
|
|
}
|
|
}
|
|
/* not found */
|
|
return;
|
|
|
|
found:
|
|
cep->hangup = 1;
|
|
return;
|
|
}
|
|
|
|
#endif /* I4B_EXTERNAL_MONITOR */
|