mars-nwe/ncpserv.c
2011-11-13 00:38:58 +01:00

761 lines
22 KiB
C

/* ncpserv.c 29-Jul-97 */
/* (C)opyright (C) 1993,1996 Martin Stover, Marburg, Germany
*
* 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
* (at your option) 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "net.h"
static int ncp_fd = -1;
static uint8 ipx_in_data[IPX_MAX_DATA];
static NCPREQUEST *ncprequest = (NCPREQUEST*)&ipx_in_data;
static struct t_unitdata ud;
static int in_len=0;
static uint8 ipx_pack_typ=17;
static ipxAddr_t from_addr; /* aktuelle Anforderungsaddresse */
static ipxAddr_t my_addr;
static int rcv_flags = 0;
static char my_nwname[50];
static time_t akttime;
static int server_goes_down=0;
static int ipx_out_fd=-1;
static int sock_nwbind=-1;
static int sock_echo =-1;
static int highest_fd = 10;
static int station_restrictions=0;
static int max_connections=MAX_CONNECTIONS;
static void set_highest_fd(int fd)
{
if (fd > highest_fd)
highest_fd=fd;
}
static int get_ini(int full)
{
FILE *f = open_nw_ini();
if (f){
uint8 buff[256];
int what;
while (0 != (what =get_ini_entry(f, 0, buff, sizeof(buff)))) {
if (60 == what && full) { /* max_connections */
max_connections=atoi((char*)buff);
if (max_connections < 1)
max_connections=MAX_CONNECTIONS;
} else if (400 == what) { /* station file */
new_str(station_fn, buff);
} else if (402 == what) { /* station connect restrictions */
station_restrictions=atoi((char*)buff);
}
} /* while */
fclose(f);
}
return(0);
}
/* next should be '1', is for testing only */
#define USE_PERMANENT_OUT_SOCKET 1
static void write_to_nwserv(int what, int connection, int mode,
char *data, int size)
{
switch (what) {
case 0x2222 : /* insert wdog connection */
write(FD_NWSERV, &what, sizeof(int));
write(FD_NWSERV, &connection, sizeof(int));
write(FD_NWSERV, &size, sizeof(int));
write(FD_NWSERV, data, size); /* ipxAddr_t + socknr */
break;
case 0x4444 : /* tell the wdog there's no need to look 0 */
/* activate wdogs to free connection 1 */
/* the connection ist closed 99 */
write(FD_NWSERV, &what, sizeof(int));
write(FD_NWSERV, &connection, sizeof(int));
write(FD_NWSERV, &mode, sizeof(int));
break;
case 0x5555 : /* close connection */
case 0x6666 : /* send to client that server holds message */
write(FD_NWSERV, &what, sizeof(int));
write(FD_NWSERV, &connection, sizeof(int));
break;
case 0xffff : /* tell nwserv to down the server */
write(FD_NWSERV, &what, sizeof(int));
write(FD_NWSERV, &what, sizeof(int));
break;
default : break;
}
}
#define nwserv_insert_conn(connection, adr, size) \
write_to_nwserv(0x2222, (connection), 0, (adr), (size))
#define nwserv_activate_wdogs() \
write_to_nwserv(0x4444, 0, 1, NULL, 0)
#define nwserv_reset_wdog(connection) \
write_to_nwserv(0x4444, (connection), 0, NULL, 0)
#define nwserv_close_conn(connection) \
write_to_nwserv(0x5555, (connection), 0, NULL, 0)
#define nwserv_handle_msg(connection) \
write_to_nwserv(0x6666, (connection), 0, NULL, 0)
#define nwserv_down_server() \
write_to_nwserv(0xffff, 0, 0, NULL, 0)
static int open_ipx_sockets(void)
{
ncp_fd = open_ipx_socket(&my_addr, SOCK_NCP);
if (ncp_fd < 0)
return(-1);
#if USE_PERMANENT_OUT_SOCKET
ipx_out_fd = open_ipx_socket(NULL, 0);
#endif
return(0);
}
typedef struct {
int fd; /* writepipe */
/* or if CALL_NWCONN_OVER_SOCKET */
/* then sock_nr of nwconn */
int pid; /* pid from son */
ipxAddr_t client_adr; /* address client */
int sequence; /* previous sequence */
int retry; /* one reply being serviced is sent */
time_t last_access; /* time of last 0x2222 request */
} CONNECTION;
static CONNECTION *connections=NULL;
static int count_connections=0; /* actual count connections */
#define TEST_HIGH_CONN 0
static int new_conn_nr(void)
{
int j = -1;
if (!count_connections){ /* init all */
j = max_connections;
while (j--) {
connections[j].fd = -1;
connections[j].pid = -1;
}
count_connections++;
#if TEST_HIGH_CONN
return(count_connections=301); /* TESTS ONLY */
#endif
return(1);
}
j = -1;
#if TEST_HIGH_CONN
j=300-1; /* TESTS ONLY */
#endif
while (++j < max_connections) {
CONNECTION *c=&(connections[j]);
if (c->fd < 0 && c->pid < 0) {
if (++j > count_connections) count_connections=j;
return(j);
}
}
/* nothing free */
nwserv_activate_wdogs();
return(0); /* nothing free */
}
static int free_conn_nr(int nr)
{
if (nr && --nr < count_connections) {
connections[nr].fd = -1;
connections[nr].pid = -1;
return(0);
}
return(-1);
}
static int find_conn_nr(ipxAddr_t *addr)
{
int j = -1;
while (++j < count_connections) {
if (connections[j].fd > -1 &&
!memcmp((char*)&(connections[j].client_adr),
(char*)addr, sizeof(ipxAddr_t))) return(++j);
}
return(0);
}
static void clear_connection(int conn)
{
nwserv_close_conn(conn);
if (conn > 0 && --conn < count_connections) {
CONNECTION *c = &connections[conn];
if (c->fd > -1) {
#if !CALL_NWCONN_OVER_SOCKET
close(c->fd);
#endif
c->fd = -1;
if (c->pid > -1) kill(c->pid, SIGTERM); /* kill it */
}
}
}
static void kill_connections(void)
/* here the connections will really removed */
{
int conn;
int stat_loc;
int pid;
while ((pid=waitpid(-1, &stat_loc, WNOHANG)) > 0) {
conn = count_connections;
while (conn--) {
if (connections[conn].pid == pid) clear_connection(conn+1);
}
}
conn = count_connections;
while (conn--) {
CONNECTION *c = &connections[conn];
if (c->fd < 0 && c->pid > -1) {
kill(c->pid, SIGKILL); /* kill it */
c->pid = -1;
}
}
conn = count_connections;
while (conn--) {
CONNECTION *c = &connections[conn];
if (c->fd < 0 && c->pid < 0) count_connections--;
else break;
}
}
static int find_get_conn_nr(ipxAddr_t *addr)
{
int connection=find_conn_nr(addr);
if (!connection){
if ((connection = new_conn_nr()) > 0){
CONNECTION *c=&(connections[connection-1]);
#if !CALL_NWCONN_OVER_SOCKET
int fds[2];
int res=pipe(fds);
memcpy((char*) &(c->client_adr), (char *)addr, sizeof(ipxAddr_t));
if (res > -1) {
set_highest_fd(fds[0]);
set_highest_fd(fds[1]);
}
if (res < 0) {
errorp(0, "find_get_conn_nr, pipe", NULL);
free_conn_nr(connection);
return(0);
} else {
#else
int ipx_fd=-1;
struct t_bind bind;
memcpy((char*) &(c->client_adr), (char *)addr, sizeof(ipxAddr_t));
ipx_fd=t_open("/dev/ipx", O_RDWR, NULL);
if (ipx_fd > -1) {
U16_TO_BE16(SOCK_AUTO, my_addr.sock); /* actual write socket */
bind.addr.len = sizeof(ipxAddr_t);
bind.addr.maxlen = sizeof(ipxAddr_t);
bind.addr.buf = (char*)&my_addr;
bind.qlen = 0; /* allways */
if (t_bind(ipx_fd, &bind, &bind) < 0){
if (nw_debug) t_error("t_bind !OK");
t_close(ipx_fd);
ipx_fd = -1;
} else {
set_highest_fd(ipx_fd);
}
} else {
if (nw_debug) t_error("t_open !Ok");
}
if (ipx_fd < 0) {
errorp(0, "find_get_conn_nr, socket", NULL);
free_conn_nr(connection);
return(0);
} else {
#endif
int akt_pid = getpid();
int pid = fork();
if (pid < 0) {
errorp(0, "find_get_conn_nr, fork", NULL);
free_conn_nr(connection);
#if !CALL_NWCONN_OVER_SOCKET
close(fds[0]);
close(fds[1]);
#endif
return(0);
}
if (pid == 0) {
/* new process */
char *progname="nwconn";
char pathname[300];
char pidstr[20];
char connstr[20];
char addrstr[100];
char divstr[50];
int l;
int j = 3;
#if !CALL_NWCONN_OVER_SOCKET
close(fds[1]); /* no writing */
dup2(fds[0], 0); /* becomes stdin */
close(fds[0]); /* not needed */
#else
dup2(ipx_fd, 0); /* becomes stdin */
close(ipx_fd);
#endif
dup2(ncp_fd, 3); /* becomes 3 */
while (j++ < highest_fd) close(j); /* close all > 3 */
sprintf(pidstr, "%d", akt_pid);
sprintf(connstr, "%d", connection);
ipx_addr_to_adr(addrstr, addr);
l=sprintf(divstr, "()INIT-:%08x,%04x,%04x-",
akt_pid, sock_nwbind, sock_echo);
if (l < 48) {
memset(divstr+l, '-', 48-l);
*(divstr+48)='\0';
}
execl(get_exec_path(pathname, progname), progname,
connstr, addrstr, divstr, NULL);
exit(1); /* normaly not reached */
}
c->pid = pid;
#if CALL_NWCONN_OVER_SOCKET
c->fd = (int) GET_BE16(my_addr.sock);
close(ipx_fd);
#else
c->fd = fds[1];
close(fds[0]); /* no need to read */
#endif
XDPRINTF((5,0, "AFTER FORK new PROCESS =%d, connection=%d", pid, connection));
}
}
}
if (connection>0) {
uint8 buff[sizeof(ipxAddr_t)+sizeof(uint16)+sizeof(uint32)];
memcpy(buff, addr, sizeof(ipxAddr_t));
#if CALL_NWCONN_OVER_SOCKET
/* here i can use the nwconn socket */
U16_TO_BE16(connections[connection-1].fd, buff+sizeof(ipxAddr_t));
#else
/* and in this mode all must be go over ncpserv */
U16_TO_BE16(SOCK_NCP, buff+sizeof(ipxAddr_t));
#endif
U32_TO_BE32(connections[connection-1].pid,
buff+sizeof(ipxAddr_t)+sizeof(uint16));
nwserv_insert_conn(connection, (char*)buff, sizeof(buff));
}
return(connection);
}
#if CALL_NWCONN_OVER_SOCKET
static void send_to_nwconn(int nwconn_sock, char *data, int size)
{
ipxAddr_t nwconn_addr;
memcpy(&nwconn_addr, &my_addr, sizeof(ipxAddr_t));
U16_TO_BE16(nwconn_sock, nwconn_addr.sock);
send_ipx_data(ipx_out_fd, 17, size, data, &nwconn_addr, NULL);
}
#endif
static void ncp_response(int type, int sequence,
int connection, int task,
int completition, int connect_status,
int data_len)
{
IPX_DATA ipx_out_data;
NCPRESPONSE *ncpresponse=(NCPRESPONSE*)&ipx_out_data;
U16_TO_BE16(type, ncpresponse->type);
ncpresponse->sequence = (uint8) sequence;
ncpresponse->connection = (uint8) connection;
ncpresponse->task = (uint8) task;
ncpresponse->high_connection= (connection >> 8) & 0xff;
ncpresponse->completition = (uint8)completition;
ncpresponse->connect_status = (uint8)connect_status;
if (nw_debug){
char comment[80];
sprintf(comment, "NCP-RESP compl=0x%x ", completition);
send_ipx_data(ncp_fd, 17, sizeof(NCPRESPONSE) + data_len,
(char *) ncpresponse,
&from_addr, comment);
} else
send_ipx_data(ncp_fd, 17, sizeof(NCPRESPONSE) + data_len,
(char *) ncpresponse,
&from_addr, NULL);
}
static void close_all(void)
{
int k=0;
while (k++ < count_connections) clear_connection(k);
if (ncp_fd > -1) {
t_unbind(ncp_fd);
t_close(ncp_fd);
XDPRINTF((2,0, "LEAVE ncpserv"));
ncp_fd = -1;
if (ipx_out_fd > -1) {
t_unbind(ipx_out_fd);
t_close(ipx_out_fd);
ipx_out_fd = -1;
}
}
}
static int server_is_down=0;
static void sig_quit(int isig)
{
server_is_down++;
}
static int got_sig_child=0;
static void sig_child(int isig)
{
got_sig_child++;
}
static void set_sig(void)
{
signal(SIGQUIT, sig_quit);
signal(SIGTERM, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, sig_child);
signal(SIGHUP, SIG_IGN);
}
static int xread(IPX_DATA *ipxd, int *offs, uint8 *data, int size)
{
if (size > -1 && *offs + size > ipxd->owndata.d.size) {
errorp(1, "xread:", "prog error: *offs=%d + size=%d > d.size=%d",
*offs, size, ipxd->owndata.d.size);
size = -1;
}
if (size > -1) {
memcpy(data, ((uint8 *)&(ipxd->owndata.d.function)) + *offs, size);
*offs+=size;
} else {
errorp(1, "xread:", "readerror");
}
return(size);
}
static int handle_ctrl(void)
/* packets from nwserv */
{
int what;
int conn;
int result = 0;
int offs=0;
IPX_DATA *ipxd = (IPX_DATA*)&ipx_in_data;
int data_len = xread(ipxd, &offs, (uint8*)&(what), sizeof(int));
if (data_len == sizeof(int)) {
XDPRINTF((2, 0, "GOT CTRL what=0x%x, len=%d", what, ipxd->owndata.d.size));
#if 1
(void)send_own_reply(ipx_out_fd, 0, ipxd->owndata.h.sequence, &from_addr);
#endif
switch (what) {
case 0x5555 : /* clear_connection, from wdog process */
if (sizeof (int) ==
xread(ipxd, &offs, (uint8*)&(conn), sizeof(int)))
clear_connection(conn);
break;
case 0xeeee:
get_ini_debug(NCPSERV);
get_ini(0);
break;
case 0xffff : /* server down */
data_len = xread(ipxd, &offs, (uint8*)&conn, sizeof(int));
if (sizeof(int) == data_len && conn == what)
server_goes_down++;
break;
default : break;
} /* switch */
result++;
} else {
errorp(1, "handle_ctrl", "wrong data len=%d", data_len);
}
return(result);
}
static void handle_ncp_request(void)
{
if (t_rcvudata(ncp_fd, &ud, &rcv_flags) > -1){
int type;
int compl;
int cstat;
in_len = ud.udata.len;
time(&akttime);
XDPRINTF((20, 0, "NCPSERV-LOOP von %s", visable_ipx_adr(&from_addr)));
if ((type = GET_BE16(ncprequest->type)) == 0x2222
|| type == 0x5555) {
int connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
XDPRINTF((10,0, "GOT 0x%x in NCPSERV connection=%d", type, connection));
#if 0
ncp_response(0x9999, ncprequest->sequence,
connection, ncprequest->task,
0x0, 0, 0);
#endif
if ( connection > 0 && connection <= count_connections) {
CONNECTION *c = &(connections[connection-1]);
if (!memcmp(&from_addr, &(c->client_adr), sizeof(ipxAddr_t))) {
if (c->fd > -1){
if (type == 0x2222) {
int diff_time = akttime - c->last_access;
c->last_access = akttime;
if (diff_time > 50) /* after max. 50 seconds */
nwserv_reset_wdog(connection);
/* tell the wdog there's no need to look */
#if !CALL_NWCONN_OVER_SOCKET
if (ncprequest->sequence == c->sequence
&& !c->retry++) {
/* perhaps nwconn is busy */
ncp_response(0x9999, ncprequest->sequence,
connection, ncprequest->task,
0x0, 0, 0);
XDPRINTF((2, 0, "Send Request being serviced to connection:%d", connection));
return;
}
#endif
if (1) {
int anz=
#if CALL_NWCONN_OVER_SOCKET
in_len;
send_to_nwconn(c->fd, (char*)ncprequest, in_len);
#else
write(c->fd, (char*)ncprequest, in_len);
#endif
XDPRINTF((10,0, "write to %d, anz = %d", c->fd, anz));
}
c->sequence = ncprequest->sequence; /* save last sequence */
c->retry = 0;
return;
} else { /* 0x5555, close connection */
#if !CALL_NWCONN_OVER_SOCKET
if ( (uint8) (c->sequence+1) == (uint8) ncprequest->sequence)
#endif
{
clear_connection(connection);
ncp_response(0x3333,
ncprequest->sequence,
connection,
0, /* task here is 0 !? */
0x0, /* completition */
0, /* conn status */
0);
return;
}
}
}
}
/* here someting is wrong */
XDPRINTF((1,0, "Not ok:0x%x,%s,fd=%d,conn=%d of %d",
type,
visable_ipx_adr(&from_addr),
c->fd,
(int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8),
count_connections));
} else {
/* here the connection number is wrong */
XDPRINTF((1,0, "Not ok:0x%x conn=%d of %d conns",
type, ncprequest->connection,
count_connections));
}
if (type == 0x5555 || (type == 0x2222 && ncprequest->function == 0x19)) {
compl = 0;
cstat = 0;
} else {
compl = 0;
cstat = 1;
}
ncp_response(0x3333, ncprequest->sequence,
(int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8),
(type== 0x5555) ? 0 : ncprequest->task, /* task */
compl, /* completition */
cstat, /* conn status */
0);
#if ENABLE_BURSTMODE
} else if (type == 0x7777) { /* BURST-mode */
int connection = (int) GET_16(((BURSTPACKET*)ncprequest)->dest_conn);
if ( connection > 0 && connection <= count_connections) {
CONNECTION *c = &(connections[connection-1]);
#if CALL_NWCONN_OVER_SOCKET
send_to_nwconn(c->fd, (char*)ncprequest, in_len);
#else
write(c->fd, (char*)ncprequest, in_len);
#endif
}
#endif
#if !CALL_NWCONN_OVER_SOCKET
/* here comes a call from nwbind */
} else if (type == 0x3333
&& IPXCMPNODE(from_addr.node, my_addr.node)
&& IPXCMPNET (from_addr.net, my_addr.net)) {
int connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
XDPRINTF((6,0, "GOT 0x3333 in NCPSERV connection=%d", connection));
if ( connection > 0 && connection <= count_connections) {
CONNECTION *c = &(connections[connection-1]);
if (c->fd > -1) write(c->fd, (char*)ncprequest, in_len);
}
#endif
} else if (type == 0x1111) {
/* GIVE CONNECTION Nr connection */
int connection = 0;
if (!server_goes_down) {
if (!station_restrictions)
connection=find_get_conn_nr(&from_addr);
else {
int do_sent = (station_restrictions == 1) ? 1 : 0;
if (find_station_match(2, &from_addr))
do_sent = !do_sent;
if (do_sent)
connection=find_get_conn_nr(&from_addr);
}
}
XDPRINTF((2, 0, "GIVE CONNECTION NR=%d", connection));
if (connection) {
CONNECTION *c = &(connections[connection-1]);
int anz;
c->sequence = 0;
#if CALL_NWCONN_OVER_SOCKET
anz = in_len;
send_to_nwconn(c->fd, (char*)ncprequest, in_len);
#else
anz=write(c->fd, (char*)ncprequest, in_len);
#endif
XDPRINTF((10, 0, "write to oldconn %d, anz = %d", c->fd, anz));
} else /* no free connection */
ncp_response(0x3333, 0, 0, 0,
0xf9, /* completition */
0, /* conn status */
0);
} else if (type == 0xeeee
&& IPXCMPNODE(from_addr.node, my_addr.node)
&& IPXCMPNET (from_addr.net, my_addr.net)) {
/* comes from nwserv */
handle_ctrl();
#ifdef _MAR_TESTS_xx
} else if (type == 0xc000) {
/* rprinter */
int connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
int sequence = (int)ncprequest->sequence;
ncp_response(0x3333, sequence, connection, 1, 0x0, 0, 0);
#endif
} else {
int connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
int sequence = (int)ncprequest->sequence;
XDPRINTF((1,0, "Got UNKNOWN TYPE: 0x%x", type));
ncp_response(0x3333, sequence, connection, 1, 0xfb, 0, 0);
}
}
}
int main(int argc, char *argv[])
{
init_tools(NCPSERV, 0);
if (argc != 5) {
errorp(1, "Usage:", "%s: nwname address nwbindsock echosocket", argv[0]);
return(1);
}
get_ini(1);
if (max_connections < 1)
max_connections=1;
connections=(CONNECTION*)xcmalloc(max_connections*sizeof(CONNECTION));
strncpy(my_nwname, argv[1], 48);
my_nwname[47] = '\0';
adr_to_ipx_addr(&my_addr, argv[2]);
sscanf(argv[3], "%x", &sock_nwbind);
sscanf(argv[4], "%x", &sock_echo);
#ifdef LINUX
set_emu_tli();
#endif
if (open_ipx_sockets()) {
errorp(1, "open_ipx_sockets", NULL);
return(1);
}
XDPRINTF((1, 0, "USE_PERMANENT_OUT_SOCKET %s",
(ipx_out_fd > -1) ? "enabled" : "disabled"));
ud.opt.len = sizeof(ipx_pack_typ);
ud.opt.maxlen = sizeof(ipx_pack_typ);
ud.opt.buf = (char*)&ipx_pack_typ; /* gets actual Typ */
ud.addr.len = sizeof(ipxAddr_t);
ud.addr.maxlen = sizeof(ipxAddr_t);
ud.addr.buf = (char*)&from_addr;
ud.udata.len = IPX_MAX_DATA;
ud.udata.maxlen = IPX_MAX_DATA;
ud.udata.buf = (char*)&ipx_in_data;
set_sig();
while (!server_is_down) {
handle_ncp_request();
if (got_sig_child) {
XDPRINTF((2, 0, "Got SIGCHLD"));
kill_connections();
got_sig_child=0;
signal(SIGCHLD, sig_child);
}
}
close_all();
xfree(connections);
return(0);
}