OSDN Git Service

Adding all files as MASTER master
authorTsuyoshi SAKAMOTO <skmt.japan@gmail.com>
Wed, 29 Aug 2012 03:49:45 +0000 (12:49 +0900)
committerTsuyoshi SAKAMOTO <skmt.japan@gmail.com>
Wed, 29 Aug 2012 03:49:45 +0000 (12:49 +0900)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
icmpsh.c [new file with mode: 0644]
icmpsh.h [new file with mode: 0644]
icmpshd.c [new file with mode: 0644]
openpty.c [new file with mode: 0644]
utils.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d81200f
--- /dev/null
@@ -0,0 +1,3 @@
+*.o
+*.a
+*.log
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f8a56f3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,82 @@
+#
+# icmpsh's Makefile
+#
+
+#======================================================
+# chose your os type
+#======================================================
+OS=-DMACOSX
+#OS=-DFREEBSD 
+#OS=-DSOLARIS
+
+
+
+#--------------------------------------------------------------------------
+# compile option
+#--------------------------------------------------------------------------
+CC     = gcc
+TAGS   = ctags # for vi
+OPTIM  = -O -pipe
+CFLAGS = $(OS) -pedantic -g -Wall $(OPTIM)
+LDFLAGS        = # nothing
+
+#--------------------------------------------------------------------------
+# include and library
+#--------------------------------------------------------------------------
+INCDIRS        = -I./
+
+#--------------------------------------------------------------------------
+# target and dependency
+#--------------------------------------------------------------------------
+COMMON_SRC = openpty.c utils.c
+CLIENT_SRC = icmpsh.c $(COMMON_SRC)
+SERVER_SRC = icmpshd.c $(COMMON_SRC)
+COMMON_OBJ = openpty.o utils.o
+CLIENT_OBJ = $(COMMON_OBJ) icmpsh.o
+SERVER_OBJ = $(COMMON_OBJ) icmpshd.o
+
+CLIENT_TARGET = icmpsh
+SERVER_TARGET = icmpshd
+TARGET = $(CLIENT_TARGET) $(SERVER_TARGET)
+
+
+#--------------------------------------------------------------------------
+# linked library handling
+#--------------------------------------------------------------------------
+ifneq ($(findstring -DSOLARIS,$(OS)),)
+ SYSLIBS= -lsocket -lnsl
+endif
+ifneq ($(findstring -DFREEBSD,$(OS)),)
+# SYSLIBS= -lutil
+ SYSLIBS=
+endif
+ifneq ($(findstring -DMACOSX,$(OS)),)
+ SYSLIBS=
+endif
+
+
+#--------------------------------------------------------------------------
+# rule
+#--------------------------------------------------------------------------
+
+all: $(TARGET)
+
+$(CLIENT_TARGET): $(CLIENT_OBJ)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(INCDIRS) $(SYSLIBS)
+
+$(SERVER_TARGET): $(SERVER_OBJ)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(INCDIRS) $(SYSLIBS)
+
+clean:
+       rm -f $(TARGET) *.exe *.o core.* tags .gdb*
+
+ctags:
+       ctags *.c *.h
+
+etags:
+       etags *.c *.h
+
+.c.o:
+       $(CC) -c -o $@ $(CFLAGS) $(INCDIRS) $^
+
+# end of makefile
diff --git a/icmpsh.c b/icmpsh.c
new file mode 100644 (file)
index 0000000..b253f43
--- /dev/null
+++ b/icmpsh.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 2006  Tsuyoshi SAKAMOTO <skmt.free@gmail.com>,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+*/
+
+
+/********************************************
+ * include file
+ ********************************************
+*/
+#include "icmpsh.h"
+
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <strings.h>
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <ctype.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <stdlib.h>
+
+#include <syslog.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <time.h>
+
+#include <stdarg.h>
+
+#include <sys/ioctl.h>
+#ifdef SOLARIS
+ #include <stropts.h>
+ #include <sys/filio.h>
+#endif
+
+
+/********************************************
+ * macro
+ ********************************************
+*/
+#define DEFAULT_IP             "127.0.0.1"
+
+
+
+/********************************************
+ * type definition
+ ********************************************
+*/
+
+/*
+ * for short-cut
+*/
+typedef struct sockaddr SA;
+typedef struct sockaddr_in SA_IN;
+typedef struct in_addr ADDR;
+
+
+/*
+ * options
+*/
+typedef struct _opt {
+       int echo;
+       char *psrc;
+       char *pdst;
+       SA *src;
+       socklen_t srclen;
+       SA *dst;
+       socklen_t dstlen;
+       pid_t pid;
+       int timeout;
+} OPT;
+
+
+
+/********************************************
+ * global variable
+ ********************************************
+*/
+int debug = 0;
+
+static char *program   = NULL;
+
+/* option */
+static OPT *opt;
+
+/* raw socket */
+static int sockin  = -1;
+static int sockout = -1;
+
+/* network and tty buffer */
+static bufset netbuf;
+static bufset termbuf;
+
+
+
+/********************************************
+ * proto type
+ ********************************************
+*/
+
+
+/*============================================================================
+ * program section
+ *============================================================================
+*/
+/*-------------------------------------------------------------------------
+ * print usage and die
+ *-------------------------------------------------------------------------
+*/
+void
+ic_print_usage(void) {
+       fprintf(stdout, "usage: %s [option(s)] target\n"
+       "\t[-d]         print debug information \n"
+       "\t[-i host]    binding interface [not implemented] \n"
+       "\t[-q]         use icmp-echo and reply (reply only by default) \n"
+       "\t[-t sec]     session timeout \n",
+       program);
+
+       exit(1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * set option
+ *-------------------------------------------------------------------------
+*/
+void
+ic_option(int argc, char **argv) {
+       int ch;
+       struct addrinfo dst_hints, src_hints;
+       struct addrinfo *dst, *src;
+       int out;
+
+       opt = xmalloc(sizeof(OPT));
+
+       program       = xstrdup(basename(argv[0]));
+       opt->psrc     = DEFAULT_IP;
+       opt->pdst     = DEFAULT_IP;
+       opt->pid      = getpid();
+       opt->timeout  = IC_TIMEOUT;
+
+       while ((ch = getopt(argc, argv, "dhiqt:")) != -1) {
+               switch(ch) {
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'h':
+                       ic_print_usage();
+                       break;
+               case 'i':
+                       opt->psrc = xstrdup(optarg);
+                       break;
+               case 'q':
+                       opt->echo = 1;
+                       break;
+               case 't':
+                       if ((out = atoi(optarg)) > 0 && out < IC_TIMEOUT)
+                               opt->timeout = out;
+                       break;
+               default:
+                       ic_print_usage();
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       if (argc)
+               opt->pdst = xstrdup(*argv);
+
+       /*
+        * get host info
+       */
+       memset(&dst_hints, 0, sizeof(dst_hints));
+       dst_hints.ai_flags    = AI_CANONNAME;
+       dst_hints.ai_family   = AF_INET;
+       dst_hints.ai_socktype = SOCK_RAW;
+       if (getaddrinfo(opt->pdst, (char *)NULL, &dst_hints, &dst) != 0) {
+               ic_log("getaddrinfo() failure, destination");
+               exit (1);
+       }
+       opt->dst = dst->ai_addr;
+       opt->dstlen = dst->ai_addrlen;
+
+       memset(&src_hints, 0, sizeof(src_hints));
+       src_hints.ai_flags    = AI_CANONNAME;
+       src_hints.ai_family   = AF_INET;
+       src_hints.ai_socktype = SOCK_RAW;
+       if (getaddrinfo(opt->psrc, (char *)NULL, &src_hints, &src) != 0) {
+               ic_log("getaddrinfo() failure, source");
+               exit (1);
+       }
+       opt->src = src->ai_addr;
+       opt->srclen = src->ai_addrlen;
+
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * write to network
+ *
+ *-------------------------------------------------------------------------
+*/
+int
+ic_write_network(bufset *nbuf) {
+       int iclen;
+       ic_data dg;
+       icmp_echo *picmph = &(dg.icmph);
+       int send;
+       u_short pid = opt->pid;
+
+       /*
+        * if size of netbuf is bigger than IC_REALPAYLOAD(ic_data's
+        * payload), it has to split a netbuf in two (or more).
+       */
+       while (nbuf->len > 0) {
+               if (nbuf->len > IC_REALPAYLOAD) {
+                       iclen = IC_REALPAYLOAD;
+                       nbuf->len -= IC_REALPAYLOAD;
+               }
+               else {
+                       iclen = nbuf->len;
+                       nbuf->len = 0;
+               }
+               memset(&dg, 0, sizeof(dg));
+               ic_set_data(&dg, IC_REQ, IC_NORMAL, iclen, nbuf->buf, iclen);
+               ic_set_header(&dg, ICMP_REQ, 0, pid, IC_TAG);
+               for (;;) {
+                       send = sendto(sockout, (icmp_echo *)picmph, IC_DATASIZE, 0, opt->dst, opt->dstlen);
+                       if (send < 0) {
+                               if (errno == ENOBUFS)
+                                       continue;
+                               ic_log("-> can not send packet (%s)", strerror(errno));
+                               return (-1);
+                       }
+                       else if (send == IC_DATASIZE) {
+                               break;
+                       }
+                       ic_log("-> invalid data size sent, retry to send");
+               }
+       }
+
+       return (0);
+}
+
+
+/*-------------------------------------------------------------------------
+ * write to tty
+ *
+ *-------------------------------------------------------------------------
+*/
+int
+ic_write_tty(bufset *tbuf) {
+       (void) xwrite(STDOUT_FILENO, tbuf->buf, tbuf->len);
+       tbuf->len = 0;
+       return (0);
+}
+
+/*-------------------------------------------------------------------------
+ * read from tty
+ *
+ *-------------------------------------------------------------------------
+*/
+int
+ic_read_tty(bufset *nbuf) {
+       int cc;
+       u_char tempbuff[BUFSIZ];
+
+       for (;;) {
+               cc = read(STDIN_FILENO, tempbuff, nbuf->bufsz);
+               if (cc < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       else
+                               return (0);
+               }
+               else
+                       break;
+       }
+
+       if (cc > 0 && tempbuff[0] == ESCAPE)
+               return (-1); /* quit immediately */
+       else {
+               if ((nbuf->len + cc) > nbuf->bufsz) {
+                       nbuf->bufsz *= 2;
+                       xrealloc(nbuf->buf, nbuf->bufsz);
+                       ic_log("-> expand netbuf (%d)", nbuf->bufsz);
+               }
+               memcpy(nbuf->buf + nbuf->len, tempbuff, cc);
+               nbuf->len += cc;
+       }
+               
+       return (cc);
+}
+
+/*-------------------------------------------------------------------------
+ # read from network
+ *
+ *-------------------------------------------------------------------------
+*/
+u_char
+ic_is_icmpsh_client(ic_data *p, u_short pid) {
+       u_char type;
+
+       if (p->iph.ip_hl != IC_IPHLWRS)
+               return (IC_RESERVE);
+
+       if (p->icmph.type  == ICMP_ECHOREPLY &&
+           p->icmph.id    == pid            &&
+           p->icmph.seq   == IC_TAG)
+               type = p->data.ich.type;
+       else
+               return (IC_RESERVE);
+
+       ic_log("dg.iph.ip_src    (%s)", inet_ntoa(p->iph.ip_src));
+       ic_log("dg.iph.ip_dst    (%s)", inet_ntoa(p->iph.ip_dst));
+       ic_log("dg.icmph.type    (%d)", p->icmph.type);
+       ic_log("dg.icmph.code    (%d)", p->icmph.code);
+       ic_log("dg.icmph.id      (%d)", p->icmph.id);
+       ic_log("dg.icmph.seq     (%d)", p->icmph.seq);
+       ic_log("dg.data.ich.type (%d)", p->data.ich.type);
+
+       if (type == IC_REPLY || type == IC_EOT)
+               return (type);
+
+       return (IC_RESERVE);
+}
+
+int
+ic_read_network(bufset *tbuf) {
+       ic_data dg;
+       ic_header *pich   = &(dg.data.ich);
+       u_char *ppayload  = (u_char *)&(dg.data.payload[0]) + sizeof(ic_header);
+       int recv;   /* length of ip datagram */
+       u_short pid = opt->pid;
+       u_char type;
+       SA_IN src;
+       socklen_t srclen;
+
+       for (;;) {
+               memset(&dg, 0, sizeof(dg));
+               recv = recvfrom(sockin, (ic_data *)&dg, sizeof(dg), 0, (SA *)&src, &srclen);
+               if (recv == IC_IPSIZE) {
+                       ic_recv_ntohs(&dg);
+                       if ((type = ic_is_icmpsh_client(&dg, pid)) == IC_REPLY) {
+                               if ((tbuf->len + pich->length) > tbuf->bufsz) {
+                                       tbuf->bufsz *= 2;
+                                       xrealloc(tbuf->buf, tbuf->bufsz);
+                                       ic_log("-> expand termbuf (%d)", tbuf->bufsz);
+                               }
+                               ic_log("-> accepted data (%d)", pich->length);
+                               strncpy((char *)(tbuf->buf + tbuf->len), (char *)ppayload, pich->length);
+                               tbuf->len += pich->length;
+                               return (0);
+                       }
+                       else if (type == IC_EOT) {
+                               ic_log("quit, due to receiving IC_EOT");
+                               return (-1);
+                       }
+                       ic_log("mismatch icmpsh packet, ignored");
+                       continue;
+               }
+               else if (recv < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       ic_log("-> error occurd, ignore incoming packet");
+                       break;
+               }
+               ic_log("-> non-icmpsh data received, ignored");
+               break;
+       }
+
+       return (0);
+}
+
+
+/*-------------------------------------------------------------------------
+ * icmpsh start and finish handshake
+ *
+ *-------------------------------------------------------------------------
+*/
+
+/*
+ * interface of sending, receiving handshake packet
+ *    success, return value 0
+ *    fail, return value -1
+ *    recoverable fail, return value 1
+*/
+int
+ic_handshake_sendto(icmp_echo *p) {
+       int cc;
+       int retry;
+
+       for (retry = 0; retry < 5; ++retry) {
+               cc = sendto(sockout, p, IC_DATASIZE, 0, opt->dst, opt->dstlen);
+               if (cc < 0) {
+                       ic_log("-> handshake_1 failure (%s)", strerror(errno));
+                       if (errno == ENOBUFS)
+                               sleep (3);
+                       else
+                               return (-1);
+               }
+               else if (cc == IC_DATASIZE) {
+                       return (0); /* success */
+               }
+               ic_log("-> invalid data sent, retry to send");
+       }
+
+       /* fail */
+       return (1);
+}
+
+int
+ic_handshake_recvfrom(ic_data *p, u_char type) {
+       int recv;
+       int retry;
+       u_short pid = opt->pid;
+       SA_IN src;
+       socklen_t srclen;
+       ic_data dg;
+
+       for (retry = 0; retry < 5; ++retry) {
+               if (ic_select(sockin, IC_TIMEOUT) <= 0)
+                       break;
+               memset(&dg, 0, sizeof(dg));
+               recv = recvfrom(sockin, (ic_data *)&dg, sizeof(dg), 0, (SA *)&src, &srclen);
+               if (recv < 0) {
+                       ic_log("-> handshake_2 failure(%s)", strerror(errno));
+                       if (errno == EINTR)
+                               sleep (3);
+                       else
+                               return (-1);
+               }
+               else if (recv == IC_IPSIZE) {
+                       ic_recv_ntohs(&dg);
+                       if (dg.icmph.type     == ICMP_ECHOREPLY &&
+                           dg.icmph.id       == pid &&
+                           dg.icmph.seq      == IC_TAG &&
+                           dg.data.ich.type  == type) {
+                               ic_log("-> received ack from (%s)", inet_ntoa(dg.iph.ip_src));
+                               *p = dg;
+                               return (0);
+                       }
+               }
+               ic_log("-> invalid data received, retry to receive");
+       }
+
+       /* fail */
+       return (1);
+}
+
+int
+ic_start(struct winsize *pw) {
+       ic_data sdg, rdg;
+       icmp_echo *psdg = (icmp_echo *)&(sdg.icmph);
+       u_short pid = opt->pid;
+       int retry;
+
+       ic_log("-> starting icmsh, opened socket (in:%d, out:%d)", sockin, sockout);
+
+       memset(&sdg, 0, sizeof(sdg));
+       ic_set_data(&sdg, IC_START, IC_NORMAL, 0, NULL, 0);
+       if (pw) {
+               sdg.data.ich.tmflag |= IC_WNCHANG;
+               ic_set_winsz(&(sdg.data.ich.opt.nwinsz), pw);
+       }
+       ic_set_header(&sdg, ICMP_REQ, 0, pid, IC_TAG);
+
+       for (retry = 0; retry < 5; ++retry) {
+               int sc, rc;
+               if ((sc = ic_handshake_sendto(psdg)) == -1)
+                       break;
+               else if (sc == 1)
+                       continue;
+
+               memset(&rdg, 0, sizeof(rdg));
+               if ((rc = ic_handshake_recvfrom(&rdg, IC_START_ACK)) == -1)
+                       break;
+               else if (rc == 0)
+                       return (0);
+               ic_log("-> retry to send starting packet [%d]", retry);
+       }
+
+       return (-1);
+}
+
+int
+ic_finish(void) {
+       ic_data sdg, rdg;
+       icmp_echo *psdg = (icmp_echo *)&(sdg.icmph);
+       u_short pid = opt->pid;
+       int retry;
+
+       ic_log("-> finishing icmsh");
+
+       memset(&sdg, 0, sizeof(sdg));
+       ic_set_data(&sdg, IC_QUIT, IC_NORMAL, 0, NULL, 0);
+       ic_set_header(&sdg, ICMP_REQ, 0, pid, IC_TAG);
+
+       for (retry = 0; retry < 5; ++retry) {
+               int sc, rc;
+               if ((sc = ic_handshake_sendto(psdg)) == -1)
+                       break;
+               else if (sc == 1)
+                       continue;
+
+               memset(&rdg, 0, sizeof(rdg));
+               if ((rc = ic_handshake_recvfrom(&rdg, IC_QUIT_ACK)) == -1 )
+                       break;
+               else if (rc == 0)
+                       return (0);
+               ic_log("-> retry to send finishing packet [%d]", retry);
+       }
+
+       return (-1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * signal hander
+ *-------------------------------------------------------------------------
+*/
+
+/*
+ * send sigquit, CTRL-\
+*/
+void
+ic_sendquit(int signo) {
+       static u_char payload[BUFSIZ] = { IAC, ABORT, 0 };
+       bufset urgebuf = { payload, sizeof(payload), 2 };
+       (void) ic_write_network(&urgebuf);
+       return;
+}
+
+void
+ic_catch_sigquit(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_sendquit;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGQUIT, &act, &oact) < 0)
+               ic_log("can not set signal hander SIGQUIT");
+
+       return;
+}
+
+/*
+ * send sigint, CTRL-C
+*/
+void
+ic_sendint(int signo) {
+       static u_char payload[BUFSIZ] = { IAC, IP, 0 };
+       bufset urgebuf = { payload, sizeof(payload), 2 };
+       (void) ic_write_network(&urgebuf);
+       return;
+}
+
+void
+ic_catch_sigint(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_sendint;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGINT, &act, &oact) < 0)
+               ic_log("can not set signal hander SIGINT");
+
+       return;
+}
+
+/*
+ * send sigtstp, CTRL-Z
+*/
+void
+ic_sendtstp(int signo) {
+       static u_char payload[BUFSIZ] = { IAC, SUSP, 0 };
+       bufset urgebuf = { payload, sizeof(payload), 2 };
+       (void) ic_write_network(&urgebuf);
+       return;
+}
+
+void
+ic_catch_sigtstp(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_sendtstp;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGTSTP, &act, &oact) < 0)
+               ic_log("can not set signal hander SIGTSTP");
+
+       return;
+}
+
+
+void
+ic_signal() {
+       ic_catch_sigquit();
+       ic_catch_sigint();
+       ic_catch_sigtstp();
+
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * main
+ *-------------------------------------------------------------------------
+*/
+void
+ic_original_termios(struct termios *p) {
+       (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, p);
+       return;
+}
+
+int
+ic_init_terminal(struct termios tt) {
+       struct termios rtt = tt;
+       int on = 1;
+
+       rtt.c_lflag &= ~(ECHO|ICANON);
+       rtt.c_cc[VMIN] = 1;
+       rtt.c_cc[VTIME] = 0;
+       if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt) == -1)
+               ic_log("tcsetattr error (%s)", strerror(errno));
+
+       if (ioctl(STDIN_FILENO, FIONBIO, (char *)&on) == -1) {
+               ic_log("ioctl error stdin (%s)", strerror(errno));
+               return (-1);
+       }
+       if (ioctl(STDOUT_FILENO, FIONBIO, (char *)&on) == -1) {
+               ic_log("ioctl error stdout (%s)", strerror(errno));
+               return (-1);
+       }
+       if (ioctl(sockin, FIONBIO, (char *)&on) == -1) {
+               ic_log("ioctl error sockin (%s)", strerror(errno));
+               return (-1);
+       }
+       if (ioctl(sockout, FIONBIO, (char *)&on) == -1) {
+               ic_log("ioctl error sockout (%s)", strerror(errno));
+               return (-1);
+       }
+
+       return (0);
+}
+
+void
+ic_make_buf() {
+       netbuf.buf = xmalloc(IC_REALPAYLOAD);
+       netbuf.bufsz = IC_REALPAYLOAD;
+       netbuf.len = 0;
+       termbuf.buf = xmalloc(IC_REALPAYLOAD);
+       termbuf.bufsz = IC_REALPAYLOAD;
+       termbuf.len = 0;
+
+       return;
+}
+
+int
+main(int argc, char **argv) {
+       int uid;
+       int maxfd;
+       fd_set rfd, wfd;
+       struct termios tt;      /* original termios */
+       struct winsize win;
+
+       ic_option(argc, argv);
+
+       if ((sockin  = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
+               ic_log("socket failure in(%d)", sockin);
+       if ((sockout = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
+               ic_log("socket failure out(%d)", sockout);
+
+       /* renounce privilege */
+       if ((uid = getuid()) > 0)
+               setuid(uid);
+
+       if (isatty(STDIN_FILENO)) {
+               if (tcgetattr(STDIN_FILENO, &tt) == -1)
+                       return (1);
+               if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1)
+                       return (1);
+               if (ic_init_terminal(tt) == -1)
+                       return (1);
+       }
+       else
+               return (1);
+
+       /*
+        * main loop
+       */
+       if (ic_start(&win) < 0) {
+               ic_original_termios(&tt);
+               return (1);
+       }
+
+       fprintf(stderr, "Start icmpsh, escape sequence is ^]\n");
+       ic_signal();
+       ic_make_buf();
+
+       maxfd = (sockin > sockout) ? sockin : sockout;
+       FD_ZERO(&rfd);
+       FD_ZERO(&wfd);
+       for (;;) {
+               int nfd;
+               FD_ZERO(&wfd);
+               if (netbuf.len > 0)
+                       FD_SET(sockout, &wfd);
+               if (termbuf.len > 0)
+                       FD_SET(STDOUT_FILENO, &wfd);
+               FD_SET(STDIN_FILENO, &rfd);
+               FD_SET(sockin, &rfd);
+               ic_log("-> waiting (%d,%d)", sockin, sockout);
+               nfd = select(maxfd + 1, &rfd, &wfd, 0, 0);
+               ic_log("-> return select() -> %d", nfd);
+               if (nfd < 0 && errno != EINTR)
+                       break;
+               if (nfd > 0 && netbuf.len > 0 && FD_ISSET(sockout, &wfd))
+                       (void) ic_write_network(&netbuf);
+               if (nfd > 0 && termbuf.len > 0 && FD_ISSET(STDOUT_FILENO, &wfd))
+                       (void) ic_write_tty(&termbuf);
+               if (nfd > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
+                       if (ic_read_tty(&netbuf) < 0)
+                               break;
+               }
+               if (nfd > 0 && FD_ISSET(sockin, &rfd)) {
+                       if (ic_read_network(&termbuf) < 0)
+                               break;
+               }
+       }
+
+       if (ic_finish() < 0) {
+               fprintf(stderr, "\nAbnormal finish icmpsh\n");
+               return (1);
+       }
+
+       ic_original_termios(&tt);
+
+       fprintf(stderr, "\nFinish icmpsh, bye ;-)\n");
+       return (0);
+}
+
+/* end of source */
diff --git a/icmpsh.h b/icmpsh.h
new file mode 100644 (file)
index 0000000..cea8281
--- /dev/null
+++ b/icmpsh.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2006  Tsuyoshi SAKAMOTO <skmt.free@gmail.com>,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+*/
+
+
+/********************************************
+ * include file
+ ********************************************
+*/
+
+/* include */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>                /* FreeBSD */
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <arpa/telnet.h>
+
+
+
+/********************************************
+ * global type definition
+ ********************************************
+*/
+
+/*
+ * netbuf & termbuf
+*/
+typedef struct _bufset {
+       u_char *buf;
+       int bufsz;
+       int len;
+} bufset;
+
+
+
+/*
+ * icmpsh data format
+*/
+
+typedef struct _net_termios {  /* struct termios */
+       u_long c_iflag;
+       u_long c_oflag;
+       u_long c_cflag;
+       u_long c_lflag;
+       u_char c_cc[48];        /* referencing NCCS in termios.h */
+       u_long c_ispeed;
+       u_long c_ospeed;
+} net_termios;
+
+typedef struct _net_winsz {    /* struct winsize */
+       u_short ws_row;
+       u_short ws_col;
+       u_short ws_xpixel;
+       u_short ws_ypixel;
+} net_winsz;
+
+typedef struct _icmp_echo {
+       u_char type;
+       u_char code;
+       u_short cksum;
+       u_short id;     /* client process id */
+       u_short seq;    /* IC_TAG field */
+} icmp_echo;
+
+#define IC_IPHLWRS     5       /* number of ip header words (20 bytes) */
+
+#define IC_BUFSIZE     512                               /* ic */
+#define IC_REALPAYLOAD (IC_BUFSIZE - sizeof(ic_header))  /* payload-ich */
+#define IC_DATASIZE    (sizeof(icmp_echo) + IC_BUFSIZE)  /* icmp+ic */
+#define IC_IPSIZE      (IC_DATASIZE + (IC_IPHLWRS * 4))  /* ip+icmp+ic */
+
+
+/* icmpsh header */
+typedef struct _ic_header_option {
+       net_termios ntermios;
+       net_winsz nwinsz;
+} ic_header_opt;
+
+typedef struct _ic_header {
+       u_char type;
+       u_char flag;
+       u_char tmflag;
+       u_char reserve;
+       ic_header_opt opt;
+       u_short length;
+} ic_header;
+
+typedef struct _ic_data {
+       struct ip iph;
+       icmp_echo icmph;
+       union {
+               ic_header ich;
+               u_char payload[IC_BUFSIZE];
+       } data;
+} ic_data;
+
+
+/* tag */
+#define IC_TAG         0x1122
+
+/* type */
+#define IC_RESERVE     0x00 /* error handle */
+#define IC_START       0x01 /* start session, make co-proc(shell) */
+#define IC_START_ACK   0x02 /* ack of IC_START */
+#define IC_REQ         0xa1 /* client command request */
+#define IC_REPLY       0xb1 /* server command response */
+#define IC_QUIT                0xe1 /* end session, kill co-proc(shell) */
+#define IC_QUIT_ACK    0xe2 /* ack of IC_QUIT */
+#define IC_EOT         0xf1 /* not implement */
+
+/* flag */
+#define IC_NORMAL      0x01
+#define IC_CONT                0x02 /* not implement */
+#define IC_EOD         0x04 /* not implement */
+
+/* termios */
+#define IC_TMNOTCH     0x01 /* not changed */
+#define IC_TMCHANG     0x02 /* changed */
+#define IC_WNNOTCH     0x04 /* not changed */
+#define IC_WNCHANG     0x08 /* changed */
+
+/* misc */
+#define IC_TIMEOUT     600  /* 600 seconds */
+
+
+
+/********************************************
+ * constant number and string
+ ********************************************
+*/
+#define ESCAPE 29      /* escape character for icmpsh */
+
+
+
+/********************************************
+ * misc macro
+ ********************************************
+*/
+
+/*
+ * short cut
+*/
+#define ICMP_REQ       ((opt->echo) ? ICMP_ECHO : ICMP_ECHOREPLY)
+
+
+/*
+ * syslog's level for ic_plog()
+*/
+#define SEMR   0
+#define SALT   1
+#define SCRT   2
+#define SERR   3
+#define SWAR   4
+#define SNOT   5
+#define SINF   6
+#define SDEB   7
+#define SNOP   8
+
+
+
+/********************************************
+ * interface type definition
+ ********************************************
+*/
+
+
+/********************************************
+* function
+********************************************
+*/
+
+/*
+ * openpty.c
+*/
+extern int ptym_open(char *);
+extern int ptys_open(int, char *);
+
+
+/*
+ * utils.c
+*/
+extern void ic_plog(int, const char *, ...);
+extern void ic_log(const char *, ...);
+extern int ic_select(int, long);
+
+#ifdef SOLARIS
+extern void cfmakeraw(struct termios *);
+#endif
+
+extern void ic_recv_ntohs(ic_data *);
+extern void ic_set_termios(net_termios *, struct termios *);
+extern void ic_set_winsz(net_winsz *, struct winsize *);
+extern void ic_get_termios(struct termios *, net_termios *);
+extern void ic_get_winsz(struct winsize *, net_winsz *);
+extern void ic_set_data(ic_data *, u_char, u_char, u_short, u_char *, int);
+extern void ic_set_header(ic_data *, u_char, u_char, u_short, u_short);
+
+extern int ic_kill(char *, pid_t, int);
+extern pid_t ic_waitpid(char *, pid_t, int *, int);
+
+extern ssize_t xread(int, void *, size_t);
+extern ssize_t xwrite(int, void *, size_t);
+
+extern void vsys_err(int, const char *, ...);
+extern void vsys(int, const char *, ...);
+
+extern unsigned short xchecksum(unsigned short *, int);
+
+extern pid_t xfork(void);
+
+extern void *xmalloc(size_t);
+extern void *xrealloc(void *, size_t);
+extern char *xstrdup(char *);
+
+/* end of header */
diff --git a/icmpshd.c b/icmpshd.c
new file mode 100644 (file)
index 0000000..a375806
--- /dev/null
+++ b/icmpshd.c
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2006  Tsuyoshi SAKAMOTO <skmt.free@gmail.com>,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+*/
+
+
+/********************************************
+ * include file
+ ********************************************
+*/
+#include "icmpsh.h"
+
+#include <stdio.h>
+#include <libgen.h>
+#include <string.h>
+#include <strings.h>
+
+#include <errno.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <ctype.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <stdlib.h>
+
+#include <syslog.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <time.h>
+
+#include <stdarg.h>
+
+
+#include <sys/ioctl.h>
+#ifdef SOLARIS
+ #include <stropts.h>
+ #include <sys/filio.h>
+ #include <sac.h>
+ #include <utmpx.h>
+#endif
+
+
+
+/********************************************
+ * macro
+ ********************************************
+*/
+#define MAXHOSTLEN             256
+#define MAXFD                  256
+#define DEFAULT_DIR            "/var/tmp"
+#define DEFAULT_PIDFILE                "icmpshd.pid"
+#define DEFAULT_IP             "127.0.0.1"
+#define DEFAULT_INTERVAL       1800
+
+#define MAXFD3(max, a, b, c)   \
+{ \
+       max = (a > b) ? a : b; \
+       max = (max > c) ? max : c; \
+}
+
+
+/********************************************
+ * type definition
+ ********************************************
+*/
+
+/*
+ * for short-cut
+*/
+typedef struct sockaddr SA;
+typedef struct sockaddr_in SA_IN;
+typedef struct in_addr ADDR;
+
+
+/*
+ * options
+*/
+typedef struct _opt {
+       char *dir;
+       char *ip;
+       SA *src;
+       socklen_t srclen;
+       char *user;
+       int fac;
+       time_t timeout;
+       struct termios origtt;
+       char *pidfile;
+} OPT;
+
+
+/*
+ * process database
+*/
+typedef struct _proc_db {
+       struct _proc_db *next;
+       ADDR client;    /* key(1): client ip address */
+       u_short id;     /* key(2): process id of remote host */
+       pid_t shelldrv; /* key(3): child's pid (ic_shell_driver) */
+       char clientname[MAXHOSTLEN];
+       ADDR local;
+       int fd;
+       time_t create;  /* create time */
+       time_t last;    /* last access time */
+} proc_db;
+
+
+
+/********************************************
+ * global variable
+ ********************************************
+*/
+int debug = 0;
+
+static char *program   = NULL;
+
+static int sockin  = -1;
+static int sockout = -1;
+
+
+/*
+ * syslog facility and level
+*/
+const int facility[] = {
+       LOG_LOCAL0,
+       LOG_LOCAL1,
+       LOG_LOCAL2,
+       LOG_LOCAL3,
+       LOG_LOCAL4,
+       LOG_LOCAL5,
+       LOG_LOCAL6,
+       LOG_LOCAL7
+};
+
+
+
+/********************************************
+ * proto type
+ ********************************************
+*/
+/* misc */
+static void ic_print_usage(void);
+static OPT *ic_option(int, char **);
+static void ic_go_daemon(OPT *);
+
+/* signal handler for listener process */
+static void ic_ignore_sighup(void);
+static void ic_sig_chld(int);
+static void ic_catch_status(void);
+
+/* process database utils */
+static proc_db * ic_db_make_list(void);
+static void ic_db_lock(void);
+static void ic_db_unlock(void);
+static void ic_db_cleanup(void);
+static int ic_db_cleanup_old(time_t, time_t);
+static void ic_db_make(int);
+static int ic_db_nrecord(void);
+static proc_db * ic_db_insert(ic_data *, int);
+static proc_db * ic_db_remove(ic_data *);
+static proc_db * ic_db_find(ic_data *, int);
+static proc_db * ic_db_find_pid(pid_t);
+
+/* request handler */
+static int ic_reply(u_char, proc_db *);
+static int ic_req_start(ic_data *, char **, OPT *);
+static int ic_req_req(ic_data *);
+static int ic_req_quit(ic_data *);
+
+
+/*============================================================================
+ * program section
+ *============================================================================
+*/
+
+/*-------------------------------------------------------------------------
+ * print usage and die
+ *-------------------------------------------------------------------------
+*/
+void
+ic_print_usage(void) {
+       fprintf(stdout, "usage: %s [option(s)] target\n"
+       "\t[-d]        print debug information, not daemon mode \n"
+       "\t[-f fac]    syslog facility \n"
+       "\t[-i ip]     bind ip address \n"
+       "\t[-p pid]    change pid file \n"
+       "\t[-r dir]    change root directory \n"
+       "\t[-t sec]    internal database clean-up interval (for debug) \n",
+       program);
+
+       exit(1);
+}
+
+
+/*-------------------------------------------------------------------------
+ * set option
+ *-------------------------------------------------------------------------
+*/
+OPT *
+ic_option(int argc, char **argv) {
+       OPT *opt;
+       int ch;
+       int i;
+       struct addrinfo hints;
+       struct addrinfo *src;
+
+       opt = xmalloc(sizeof(OPT));
+
+       program       = xstrdup(basename(argv[0]));
+       opt->dir      = DEFAULT_DIR;
+       opt->ip       = DEFAULT_IP;
+       opt->fac      = facility[0];
+       opt->timeout  = DEFAULT_INTERVAL;
+       opt->pidfile  = DEFAULT_PIDFILE;
+
+       if (tcgetattr(STDIN_FILENO, &(opt->origtt)) == -1)
+               ic_plog(SDEB, "(main) tcgetattr error (%s)", strerror(errno));
+
+       while ((ch = getopt(argc, argv, "df:i:r:t:")) != -1) {
+               switch(ch) {
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'f':
+                       if (strlen(optarg) != 1 || !isdigit((int)optarg[0]))
+                               ic_print_usage();
+                       i = atoi(optarg);
+                       if (i < 0 || i > 7)
+                               i = 0;
+                       opt->fac = facility[i];
+                       break;
+               case 'h':
+                       ic_print_usage();
+                       break;
+               case 'i':
+                       opt->ip = xstrdup(optarg);
+                       break;
+               case 'p':
+                       opt->pidfile = xstrdup(optarg);
+                       break;
+               case 'r':
+                       opt->dir = xstrdup(optarg);
+                       break;
+               case 't':
+                       opt->timeout = (time_t)atoi(optarg);
+                       if (opt->timeout > (time_t)DEFAULT_INTERVAL)
+                               opt->timeout = DEFAULT_INTERVAL;
+                       break;
+               default:
+                       ic_print_usage();
+                       break;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       if (argc)
+               opt->ip = xstrdup(*argv);
+
+       /*
+        * get interface address
+       */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_flags     = AI_CANONNAME;
+       hints.ai_family    = AF_INET;
+       hints.ai_socktype  = SOCK_RAW;
+       if (getaddrinfo(opt->ip, (char *)NULL, &hints, &src) != 0)
+               ic_print_usage();
+       opt->src    = src->ai_addr;
+       opt->srclen = src->ai_addrlen;
+
+       return opt;
+}
+
+
+/*-------------------------------------------------------------------------
+ * put pid file
+ *-------------------------------------------------------------------------
+*/
+void
+ic_put_pidfile(OPT *opt) {
+       FILE *fp;
+       char buf[BUFSIZ];
+
+       if (debug)
+               return;
+
+       if ((fp = fopen(opt->pidfile, "w")) == NULL)
+               return;
+
+#ifdef SOLARIS
+       snprintf(buf, sizeof(buf), "%lu\n", getpid());
+#else
+       snprintf(buf, sizeof(buf), "%d\n", getpid());
+#endif
+       fputs(buf, fp);
+       fclose(fp);
+
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * signal handler
+ *-------------------------------------------------------------------------
+*/
+void
+ic_ignore_sighup(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = SIG_IGN;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGHUP, &act, &oact) < 0)
+               ic_plog(SDEB, "(main) can not set signal handler");
+
+       return;
+}
+
+void
+ic_sig_chld(int signo) {
+       pid_t tmp_pid = -1;
+       pid_t pid = -1;
+       int status;
+       proc_db *ptr;
+
+       while ((tmp_pid = waitpid(-1, &status, WNOHANG)) > 0) {
+               pid = tmp_pid;
+               ic_plog(SDEB, "(main) child co-process %d terminated", pid);
+       }
+
+       /*
+        * reset session immediately
+       */
+       if (pid > 0 && (ptr = ic_db_find_pid(pid)) != NULL)
+               ic_reply(IC_EOT, ptr);
+
+       return;
+}
+
+void
+ic_catch_status(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_sig_chld;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGCHLD, &act, &oact) < 0)
+               ic_plog(SDEB, "(main) can not set signal handler");
+
+       return;
+}
+
+/*-------------------------------------------------------------------------
+ * switch daemon mode
+ *-------------------------------------------------------------------------
+*/
+void
+ic_go_daemon(OPT *opt) {
+       int i;
+       pid_t pid;
+
+       if ((pid = xfork()) > 0) {
+               exit(0);        /* first parent exit */
+       }
+
+       setsid();               /* get control terminal */
+       ic_ignore_sighup();     /* ignore hang-up signal */
+       ic_catch_status();      /* catch child process's status */
+
+       if ((pid = xfork()) > 0) {
+               exit(0);        /* second parent exit */
+       }
+
+       if (chdir(opt->dir) < 0) {
+               exit(1);
+       }
+
+       for (i = 0; i < MAXFD; ++i) {
+               close(i);       /* force to close all opened descriptors */
+       }
+
+       return;
+}
+
+/*-------------------------------------------------------------------------
+ * database interface
+ *
+ *-------------------------------------------------------------------------
+*/
+static proc_db *Proc_db = NULL; /* base element */
+static int nrecord = 0; /* total number of element */
+
+/* insert flag */
+#define IC_DB_REMOVE   0x01
+#define IC_DB_UPDATE   0x02
+/* not needed
+#define IC_DB_INSERT   0x04
+*/
+
+
+/* local */
+proc_db *
+ic_db_make_list(void) {
+       return xmalloc(sizeof(proc_db));
+}
+
+void
+ic_db_lock(void) {
+       return;
+}
+
+void
+ic_db_unlock(void) {
+       return;
+}
+
+/* public */
+void
+ic_db_cleanup(void) {
+       proc_db *ptr, *tmptr;
+       if (Proc_db == NULL) 
+               return;
+
+       ptr = Proc_db;
+       do {
+               tmptr = ptr->next;
+               free(ptr);
+               ptr = tmptr;
+       } while (ptr);
+
+       Proc_db = NULL;
+       nrecord = 0;
+       return;
+}
+
+int
+ic_db_cleanup_old(time_t now, time_t sec) {
+       int nfree;
+       proc_db *ptr, *prev;
+       if (Proc_db == NULL) 
+               return (-1);
+
+       nfree = 0;
+       prev = Proc_db;
+       ptr = prev->next;
+       while (ptr) {
+               if ((now - ptr->last) > sec) {
+                       proc_db *tmptr = ptr;
+                       ic_plog(SDEB, "(ic_db_cleanup_old) remove pid %d", ptr->id);
+                       ic_kill("ic_db_cleanup_old", ptr->shelldrv, SIGINT);
+                       prev->next = ptr->next;
+                       ptr = ptr->next;
+                       free(tmptr);
+                       ++nfree;
+                       --nrecord;
+               }
+               else {
+                       prev = ptr;
+                       ptr = ptr->next;
+               }
+       }
+
+       return (nfree);
+}
+
+void
+ic_db_make(int rebuild) {
+       if (rebuild)
+               ic_db_cleanup();
+       else if (Proc_db != NULL)
+               return;
+
+       Proc_db = xmalloc(sizeof(proc_db));
+
+       return;
+}
+
+int
+ic_db_nrecord(void) {
+       return (nrecord);
+}
+
+proc_db *
+ic_db_insert(ic_data *dg, int masterfd) {
+       proc_db *new, *tmptr;
+       ic_db_lock();
+       new          = ic_db_make_list();
+       new->client  = dg->iph.ip_src;
+       new->local   = dg->iph.ip_dst;
+       new->fd      = masterfd;
+       new->id      = dg->icmph.id;
+       new->create  = time(NULL);
+       new->last    = time(NULL);
+       strcpy(new->clientname, inet_ntoa(dg->iph.ip_src));
+
+       tmptr = Proc_db->next;
+       Proc_db->next = new;
+       new->next = tmptr;
+
+       ic_plog(SDEB, "(ic_db_insert) add entry pid %d", new->id);
+       ++nrecord;
+       ic_db_unlock();
+       return (new);
+}
+
+proc_db *
+ic_db_remove(ic_data *dg) {
+       proc_db *ptr, *prev;
+       ic_db_lock();
+       prev = Proc_db;
+       for (ptr = Proc_db->next; ptr != NULL; ptr = ptr->next) {
+               if (ptr->client.s_addr == dg->iph.ip_src.s_addr &&
+                   ptr->id            == dg->icmph.id) {
+                       prev->next = ptr->next;
+                       free(ptr);
+                       --nrecord;
+                       break;
+               }
+               prev = ptr;
+       }
+
+       ic_db_unlock();
+       return (NULL);
+}
+
+proc_db *
+ic_db_find(ic_data *dg, int flag) {
+       proc_db *ptr;
+
+       if (Proc_db->next == NULL)
+               return (NULL);
+
+       for (ptr = Proc_db->next; ptr != NULL; ptr = ptr->next) {
+               if (ptr->client.s_addr == dg->iph.ip_src.s_addr &&
+                   ptr->id            == dg->icmph.id) {
+                       if (flag & IC_DB_REMOVE)
+                               /* need to improve for performance */
+                               return (ic_db_remove(dg));
+                       else {
+                               if (flag & IC_DB_UPDATE) {
+                                       ic_db_lock();
+                                       ptr->last = time(NULL);
+                                       ic_db_unlock();
+                               }
+                               return (ptr);
+                       }
+               }
+       }
+
+       return (NULL);
+}
+
+proc_db *
+ic_db_find_pid(pid_t shelldrv_pid) {
+       proc_db *ptr;
+
+       if (Proc_db->next == NULL)
+               return (NULL);
+
+       for (ptr = Proc_db->next; ptr != NULL; ptr = ptr->next) {
+               if (ptr->shelldrv == shelldrv_pid)
+                       return (ptr);
+       }
+
+       return (NULL);
+}
+
+
+/*-------------------------------------------------------------------------
+ * shell driver
+ *
+ *-------------------------------------------------------------------------
+*/
+
+/*
+ * ic_shell_driver has to terminate real shell, so that send SIGTERM to it
+ * when SIGINT is sent me by the parent.
+*/
+
+static char *pdrv = "ic_shell_driver";
+static char *psh = "shell";
+
+static pid_t ic_shelldrv_sigint     = 1; /* to terminate myself */
+static pid_t ic_shelldrv_shell      = 0; /* real shell's pid */
+
+void
+ic_shelldrv_kill_child(void) {
+       if (ic_shelldrv_shell == 0)
+               return;
+       ic_kill(pdrv, ic_shelldrv_shell, SIGTERM);
+       ic_plog(SDEB, "(%s) sent signal to shell (%d)", pdrv, ic_shelldrv_shell);
+
+       return;
+}
+
+void
+ic_shelldrv_set_sigint(int signo) {
+       ic_shelldrv_sigint = 0;
+       return;
+}
+
+void
+ic_shelldrv_catch_sigint(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_shelldrv_set_sigint;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGINT, &act, &oact) < 0)
+               ic_plog(SDEB, "(%s) can not set signal handler", pdrv);
+
+       return;
+}
+
+void
+ic_shelldrv_chld_stat(int signo) {
+       pid_t pid;
+       int status;
+
+       while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+               ic_plog(SDEB, "(%s) child process %d terminated", pdrv, pid);
+       }
+
+       if (pid == ic_shelldrv_shell)
+               ic_shelldrv_shell = 0;
+
+       /* exit main loop in ic_shell_driver() */
+       ic_shelldrv_set_sigint(0);
+
+       return;
+}
+
+void
+ic_shelldrv_catch_sigchld(void) {
+       struct sigaction act, oact;
+
+       act.sa_handler = ic_shelldrv_chld_stat;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+
+       if (sigaction(SIGCHLD, &act, &oact) < 0)
+               ic_plog(SDEB, "(%s) can not set signal handler", pdrv);
+
+       return;
+}
+
+/*
+ * ic_shell_driver main
+*/
+
+void
+ic_set_icmp_echoreply(ic_data *pic, proc_db *pproc, u_char *ibuf, ssize_t sz) {
+       u_short pid = pproc->id;
+       u_short len = sz;
+
+       ic_set_data(pic, IC_REPLY, IC_NORMAL, len, ibuf, sz);
+       ic_set_header(pic, ICMP_ECHOREPLY, 0, pid, IC_TAG);
+
+       return;
+}
+
+int
+ic_responser(bufset *nbuf, proc_db *pproc) {
+       ic_data dg;
+       icmp_echo *picmph = &(dg.icmph);
+       SA_IN addr;
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = pproc->client.s_addr;
+
+       while (nbuf->len > 0) {
+               int iclen, send, retry;
+               if (nbuf->len > IC_REALPAYLOAD) {
+                       iclen = IC_REALPAYLOAD;
+                       nbuf->len -= IC_REALPAYLOAD;
+               }
+               else {
+                       iclen = nbuf->len;
+                       nbuf->len = 0;
+               }
+               memset(&dg, 0, sizeof(dg));
+               ic_set_icmp_echoreply(&dg, pproc, nbuf->buf, iclen);
+               for (retry = 5; retry > 0; --retry) {
+                       send = sendto(sockout, (icmp_echo *)picmph, IC_DATASIZE, 0, (SA *)&addr, sizeof(addr));
+                       if (send < 0) {
+                               if (errno == ENOBUFS)
+                                       continue;
+                               ic_plog(SINF, "(%s) unrecoverable error occured (%s)", pdrv, strerror(errno));
+                               return (-1);
+                       }
+                       else if (send == IC_DATASIZE)
+                               break;
+               }
+       }
+
+       return (0);
+}
+
+int
+ic_write_terminal(int shmaster, bufset *nbuf, pid_t shell) {
+       switch (nbuf->buf[0]) {
+       case IAC:
+               switch (nbuf->buf[1]) {
+#ifdef SOLARIS
+               case IP:
+                       (void) ioctl(shmaster, TIOCSIGNAL, SIGINT);
+                       break;
+               case SUSP:
+                       (void) ioctl(shmaster, TIOCSIGNAL, SIGTSTP);
+                       break;
+               case ABORT:
+                       (void) ioctl(shmaster, TIOCSIGNAL, SIGQUIT);
+                       break;
+               default:
+                       break;
+               }
+#else
+               case IP:
+                       (void) ioctl(shmaster, TIOCSIG, (char *)SIGINT);
+                       break;
+               case SUSP:
+                       (void) ioctl(shmaster, TIOCSIG, (char *)SIGTSTP);
+                       break;
+               case ABORT:
+                       (void) ioctl(shmaster, TIOCSIG, (char *)SIGQUIT);
+                       break;
+               default:
+                       break;
+               }
+#endif
+               break;
+       default:
+               xwrite(shmaster, nbuf->buf, nbuf->len);
+               break;
+       }
+       nbuf->len = 0;
+
+       return (0);
+}
+
+int
+ic_read_tty(int fd, bufset *nbuf) {
+       int retry;
+       int cc;
+       u_char tempbuff[BUFSIZ];
+
+       for (retry = 5; retry > 0; --retry) {
+               cc = read(fd, tempbuff, nbuf->bufsz);
+               if (cc < 0)
+                       if (errno == EINTR)
+                               continue;
+                       else
+                               return (0);
+               else
+                       break;
+       }
+
+       if (cc > 0) {
+               if ((nbuf->len + cc) > nbuf->bufsz) {
+                       nbuf->bufsz *= 2;
+                       xrealloc(nbuf->buf, nbuf->bufsz);
+                       ic_plog(SDEB, "(%s) expand bufset (%d)", pdrv, nbuf->bufsz);
+               }
+               memcpy(nbuf->buf + nbuf->len, tempbuff, cc);
+               nbuf->len += cc;
+       }
+
+       return (cc);
+}
+
+
+void
+ic_make_buf(bufset *term, bufset *net) {
+       term->bufsz = net->bufsz = IC_REALPAYLOAD;
+       term->len = net->len = 0;
+       term->buf = xmalloc(term->bufsz);
+       net->buf = xmalloc(net->bufsz);
+       return;
+}
+
+char *
+ic_login_path() {
+#ifdef SOLARIS
+       static char *login = "/bin/login";
+#else
+       static char *login = "/usr/bin/login";
+#endif
+       return (login);
+}
+
+#ifdef SOLARIS
+void
+ic_make_utmpx(char *pts) {
+       struct utmpx ut;
+
+       (void) memset((char *)&ut, 0, sizeof(ut));
+       (void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
+       (void) strncpy(ut.ut_line, pts, sizeof(ut.ut_line));
+       ut.ut_pid = getpid();
+       ut.ut_id[0] = 't';
+       ut.ut_id[1] = (char)SC_WILDC;
+       ut.ut_id[2] = (char)SC_WILDC;
+       ut.ut_id[3] = (char)SC_WILDC;
+       ut.ut_type = LOGIN_PROCESS;
+       ut.ut_exit.e_termination = 0;
+       ut.ut_exit.e_exit = 0;
+       (void) time(&ut.ut_tv.tv_sec);
+       if (makeutx(&ut) == NULL)
+               ic_plog(SDEB, "(%s) makeutx fail", pdrv);
+
+       return;
+}
+#endif
+
+void
+ic_shell_driver(int master, char *pts, char **envp, struct termios tt, struct winsize ws, proc_db proc) {
+       const char *login = ic_login_path();
+       int slave, shmaster, maxfd;
+       int on = 1;
+       struct termios rawtt;
+       pid_t shellpid;
+       char pts_shell[64]; /* save pts name, /dev/ptyXY */
+       bufset termbuf, netbuf;
+       fd_set rfd, wfd;
+
+       /*
+        * get `pts' to communicate between icmpshd and ic_shell_driver,
+        * and close master
+       */
+       if ((slave = ptys_open(master, pts)) < 0) {
+               ic_plog(SDEB, "(%s) ptys_open failure", pdrv);
+               return;
+       }
+       rawtt = tt;
+       cfmakeraw(&rawtt);
+       if (tcsetattr(slave, TCSANOW, &rawtt) < 0)
+               ic_plog(SDEB, "(%s) tcsetattr failure (%s)", pdrv, strerror(errno));
+
+       if (setsid() < 0) {
+               ic_plog(SDEB, "(%s) setsid failure", pdrv);
+               return;
+       }
+
+       if ((shmaster = ptym_open(pts_shell)) < 0) {
+               ic_plog(SDEB, "(%s) ptym_open failure", pdrv);
+               return;
+       }
+       (void) ioctl(shmaster, FIONBIO, (char *)&on);
+
+       if ((shellpid = xfork()) == 0) {
+               /* start real shell */
+               int shslave;
+               if (setsid() < 0)
+                       return;
+               if ((shslave = ptys_open(shmaster, pts_shell)) < 0)
+                       return;
+#ifndef SOLARIS
+               /* ioctl(tty) not needed on SVR4
+                * slave becomes control terminal when process opens ptys
+               */
+               if (ioctl(shslave, TIOCSCTTY, (char *)NULL) < 0)
+                       ic_plog(SDEB, "(%s) ioctl(tty) failure (%s)", psh, strerror(errno));
+#endif
+               tt.c_lflag |= ECHO;
+               if (tcsetattr(shslave, TCSANOW, &tt) < 0)
+                       return;
+               if (ioctl(shslave, TIOCSWINSZ, &ws) < 0)
+                       return;
+               ic_plog(SDEB, "(%s) forked(%d)", psh, getpid());
+               dup2(shslave, 0);
+               dup2(shslave, 1);
+               dup2(shslave, 2);
+#ifdef SOLARIS
+               ic_make_utmpx(pts_shell+6);
+               execl(login, "login", "-p", "-h", proc.clientname, "-d", pts_shell, "--", getenv("USER"), 0);
+#else
+               execle(login, "login", "-h", proc.clientname, "-p", (char *)NULL, envp);
+#endif
+               ic_plog(SDEB, "(%s) fork login failure", psh);
+               return;
+       }
+       else if (shellpid < 0) {
+               ic_plog(SDEB, "(%s) fork failure", pdrv);
+               return;
+       }
+               
+       /*----------------------------------------------------
+        * ic_shell_driver main loop
+        *----------------------------------------------------
+       */
+
+       ic_shelldrv_shell = shellpid;
+       ic_shelldrv_catch_sigint();
+       ic_shelldrv_catch_sigchld();
+
+       ic_make_buf(&termbuf, &netbuf);
+       MAXFD3(maxfd, shmaster, slave, sockout);
+       FD_ZERO(&rfd);
+       FD_ZERO(&wfd);
+
+       while (ic_shelldrv_sigint) {
+               int nfd;
+               FD_ZERO(&wfd);
+               if (termbuf.len)
+                       FD_SET(shmaster, &wfd);
+               if (netbuf.len)
+                       FD_SET(sockout, &wfd);
+               FD_SET(shmaster, &rfd);
+               FD_SET(slave, &rfd);
+               nfd = select(maxfd + 1, &rfd, &wfd, NULL, NULL);
+               if (nfd < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       ic_plog(SDEB, "(%s) select failure", pdrv);
+                       break;
+               }
+               /*
+                * response to client (write to network)
+               */
+               if (nfd > 0 && netbuf.len > 0 && FD_ISSET(sockout, &wfd))
+                       (void) ic_responser(&netbuf, &proc);
+               /*
+                * write to real-shell
+               */
+               if (nfd > 0 && termbuf.len > 0 && FD_ISSET(shmaster, &wfd))
+                       (void) ic_write_terminal(shmaster, &termbuf, shellpid);
+               /*
+                * read from shell
+               */
+               if (nfd > 0 && FD_ISSET(shmaster, &rfd))
+                       (void) ic_read_tty(shmaster, &netbuf);
+               /*
+                * read from network and store in buffer
+               */
+               if (nfd > 0 && FD_ISSET(slave, &rfd))
+                       (void) ic_read_tty(slave, &termbuf);
+       }
+
+       ic_shelldrv_kill_child();
+
+       ic_plog(SDEB, "(%s) exit", pdrv);
+       exit (0);
+}
+
+
+/*-------------------------------------------------------------------------
+ * request handler
+ *    ic_reply()     common interface for ic_req_start & ic_req_quit
+ *    ic_req_start() startup procedure
+ *    ic_req_quit()  remove child process and clean up database
+ *-------------------------------------------------------------------------
+*/
+
+int
+ic_reply(u_char code, proc_db *proc) {
+       int send;
+       ic_data dg;
+       icmp_echo *picmph = &(dg.icmph);
+       SA_IN dst;
+
+       memset(&dg, 0, sizeof(dg));
+       ic_set_data(&dg, code, IC_NORMAL, 0, NULL, 0);
+       ic_set_header(&dg, ICMP_ECHOREPLY, 0, proc->id, IC_TAG);
+
+       dst.sin_family = AF_INET;
+       dst.sin_addr.s_addr = proc->client.s_addr;
+       for (;;) {
+               send = sendto(sockout, (icmp_echo *)picmph, IC_DATASIZE, 0, (SA *)&dst, sizeof(dst));
+               if (send == IC_DATASIZE)
+                       break;
+               if (send < 0 && errno == ENOBUFS) {
+                       continue;
+               }
+               ic_plog(SDEB, "(ic_reply) sendto() failure");
+               return (-1);
+       }
+
+       return (0);
+}
+
+
+/* IC_START */
+
+int
+ic_req_start(ic_data *pdg, char **envp, OPT *opt) {
+       int master;
+       int on = 1;
+       char pts[64]; /* save pts name, /dev/ptyXY */
+       proc_db *proc;
+       pid_t shelldrv;
+
+       if ((master = ptym_open(pts)) < 0) {
+               ic_plog(SDEB, "(main) ptym_open failure");
+               return (-1);
+       }
+       (void) ioctl(master, FIONBIO, (char *)&on);
+
+       proc = ic_db_insert(pdg, master);
+
+       if ((shelldrv = xfork()) == 0) {
+               /* ic_shell_driver start */
+               struct winsize ws;
+               ic_get_winsz(&ws, &(pdg->data.ich.opt.nwinsz));
+               ic_shell_driver(master, pts, envp, opt->origtt, ws, *proc);
+       }
+       else if (shelldrv < 0) {
+               ic_db_remove(pdg);
+               return (-1);
+       }
+
+       proc->shelldrv = shelldrv;
+       ic_plog(SDEB, "(main) open master (%d) for shelldrv pid (%d)", master, shelldrv);
+       ic_reply(IC_START_ACK, proc);
+
+       return (0);
+}
+
+
+/* IC_REQ */
+int
+ic_req_req(ic_data *pdg) {
+       u_char *ppay = (u_char *)&(pdg->data.payload[0]) + sizeof(ic_header);
+       proc_db *proc;
+
+       if ((proc = ic_db_find(pdg, IC_DB_UPDATE)) == NULL) {
+               ic_plog(SDEB, "(main) no entry in proc_db");
+               return (-1);
+       }
+
+       xwrite(proc->fd, ppay, pdg->data.ich.length);
+       ic_plog(SDEB, "(main) sent through fd(%d), %d byte", proc->fd, pdg->data.ich.length);
+       
+       return (0);
+}
+
+
+/* IC_QUIT */
+int
+ic_req_quit(ic_data *pdg) {
+       proc_db *proc;
+       
+       if ((proc = ic_db_find(pdg, 0)) == NULL) {
+               ic_plog(SDEB, "(main) no entry in proc_db");
+               return (-1);
+       }
+
+       /*
+        * terminate ic_shell_driver and its child, see ic_shell_driver()
+        * in detail.
+       */
+       ic_kill("main", proc->shelldrv, SIGINT);
+       ic_reply(IC_QUIT_ACK, proc);
+
+       /*
+        * remove entry from proc_db
+       */
+       if ((proc = ic_db_remove(pdg)) == NULL)
+               ic_plog(SDEB, "(main) removed entry");
+       else
+               ic_plog(SDEB, "(main) can not remove entry");
+
+       return (0);
+}
+
+
+/*-------------------------------------------------------------------------
+ * main
+ *-------------------------------------------------------------------------
+*/
+
+/*
+ * make new environment
+*/
+void
+ic_env_make(void *envp) {
+       extern char **environ;
+       int i;
+       int envn = 16;
+       char **newenvp;
+
+       newenvp = xmalloc(envn * sizeof(char *));
+       for (i = 0; environ[i] != NULL; ++i) {
+               if (i == envn) {
+                       envn *= 2;
+                       newenvp = xrealloc(newenvp, envn * sizeof(char *));
+               }
+               newenvp[i] = xstrdup(environ[i]);
+       }
+       if (getenv("TERM") == NULL) {
+               newenvp[i] = xstrdup("TERM=vt100");
+               ++i;
+       }
+
+       if (i >= envn)
+               newenvp = xrealloc(newenvp, (i + 1) * sizeof(char *));
+       newenvp[i] = NULL;
+       *((char **)envp) = (char *)newenvp;
+
+       return;
+}
+
+
+/*
+ * finger out whether incoming packet is icmpsh's format or not,
+ * then returning with value "1" is true, "0" is false.
+*/
+int
+ic_is_icmpsh_server(ic_data *pdg) {
+       if (pdg->iph.ip_hl != IC_IPHLWRS)
+               return (0);
+
+       if (pdg->icmph.seq   == IC_TAG &&
+          (pdg->icmph.type  == ICMP_ECHO ||
+           pdg->icmph.type  == ICMP_ECHOREPLY))
+               ic_plog(SDEB, "(main) accept icmpsh packet");
+       else
+               return (0);
+
+       ic_plog(SDEB, "(main)        src(%s), ",  inet_ntoa(pdg->iph.ip_src));
+       ic_plog(SDEB, "(main)        dst(%s), ",  inet_ntoa(pdg->iph.ip_dst));
+       ic_plog(SDEB, "(main)        type(%d), ", pdg->icmph.type);
+       ic_plog(SDEB, "(main)        code(%d), ", pdg->icmph.code);
+       ic_plog(SDEB, "(main)        id(%d), ",   pdg->icmph.id);
+       ic_plog(SDEB, "(main)        seq(%d), ",  pdg->icmph.seq);
+       ic_plog(SDEB, "(main)        type(%d)",   pdg->data.ich.type);
+
+       return (1);
+}
+
+/*
+ * open and configure socket
+*/
+int
+ic_init_server(OPT *opt) {
+       int on = 1;
+
+       if ((sockin  = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+               ic_plog(SDEB, "(main) socket failure in(%d)", sockin);
+               return (-1);
+       }
+       if ((sockout = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+               ic_plog(SDEB, "(main) socket failure out(%d)", sockout);
+               return (-1);
+       }
+
+       if (ioctl(sockout, FIONBIO, (char *)&on) == -1) {
+               ic_plog(SDEB, "(main) ioctl error sockout (%s)", strerror(errno));
+               return (-1);
+       }
+       if (opt->src) {
+               if (bind(sockin, opt->src, opt->srclen) != 0) {
+                       ic_plog(SDEB, "(main) bind error (%s)", strerror(errno));
+                       return (-1);
+               }
+       }
+
+       return (0);
+}
+
+
+int
+main(int argc, char **argv) {
+       OPT *opt; /* command option */
+       char **envp;
+       
+       opt = ic_option(argc, argv);
+       openlog(program, LOG_PID, opt->fac);
+
+       /* preparation */
+       if (debug)
+               ic_catch_status();
+       else
+               ic_go_daemon(opt);
+
+       ic_put_pidfile(opt);
+       ic_db_make(0);
+       ic_env_make(&envp);
+
+       /* socket */
+       if (ic_init_server(opt) < 0)
+               exit (1);
+
+       /*
+        * main loop
+       */
+       ic_plog(SINF, "(main) starting up");
+
+       for(;;) {
+               int nfd;
+               ic_data dg; /* ip datagram */
+               ssize_t n;
+               SA_IN src;
+               socklen_t srclen;
+
+               ic_plog(SDEB, "(main) waiting(pid:%d)", getpid());
+
+               if ((nfd = ic_select(sockin, opt->timeout)) == 0) {
+                       /* timeout */
+                       ic_plog(SINF, "(main) proc database -> %d", ic_db_nrecord());
+                       ic_db_cleanup_old(time(NULL), opt->timeout);
+                       continue;
+               }
+               memset(&dg, 0, sizeof(dg));
+               n = recvfrom(sockin, (ic_data *)&dg, sizeof(dg), 0, (SA *)&src, &srclen);
+               if (n < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       else {
+                               ic_plog(SDEB, "(main) error occued while reading, quit immediately");
+                               exit (1);
+                       }
+               }
+               else if (n == 0) {
+                       ic_plog(SDEB, "(main) no data read");
+                       continue;
+               }
+               else if (n != IC_IPSIZE) {
+                       ic_plog(SDEB, "(main) mismatch packet size");
+                       continue;
+               }
+
+               /* Is it icmpsh's packet ?? */
+               ic_plog(SDEB, "(main) received packet (%d)", n);
+               ic_recv_ntohs(&dg);
+               if (ic_is_icmpsh_server(&dg) == 0)
+                       continue;
+                       
+               /* execute command */
+               switch (dg.data.ich.type) {
+               case (IC_START):
+                       ic_plog(SDEB, "(main) req: IC_START");
+                       (void) ic_req_start(&dg, envp, opt);
+                       break;
+               case (IC_REQ):
+                       ic_plog(SDEB, "(main) req: IC_REQ");
+                       (void) ic_req_req(&dg);
+                       break;
+               case (IC_QUIT):
+                       ic_plog(SDEB, "(main) req: IC_QUIT");
+                       (void) ic_req_quit(&dg);
+                       break;
+               default:
+                       ic_plog(SDEB, "(main) request not implemented");
+               }
+       }
+
+       exit(0);
+}
+
diff --git a/openpty.c b/openpty.c
new file mode 100644 (file)
index 0000000..dc62714
--- /dev/null
+++ b/openpty.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2006  Tsuyoshi SAKAMOTO <skmt.free@gmail.com>,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+*/
+
+
+
+/********************************************
+ * include file
+ ********************************************
+*/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef SOLARIS
+ #include <stdlib.h>
+ #include <stropts.h>
+ #include <termios.h>
+ #include <sys/ioctl.h>
+#else /* BSD & Mac */
+ #include <grp.h>
+#endif
+
+
+/********************************************
+ * macro
+ ********************************************
+*/
+
+
+/********************************************
+ * type definition
+ ********************************************
+*/
+
+
+/********************************************
+ * global variable
+ ********************************************
+*/
+
+
+/********************************************
+ * prototype
+ ********************************************
+*/
+/* public */
+int ptym_open(char *);
+int ptys_open(int, char *);
+
+
+/********************************************
+ * open master and slave
+ ********************************************
+*/
+#ifdef SOLARIS
+int
+ptym_open(char *pts) {
+       char ptym[] = "/dev/ptmx";
+       char *p;
+       int master;
+
+       if (pts == NULL)
+               return (-1);
+
+       if ((master = open(ptym, O_RDWR)) < 0)
+               return (-1);
+
+       if (grantpt(master) < 0)
+               goto ptym_open_error;
+
+       if (unlockpt(master) < 0)
+               goto ptym_open_error;
+
+       if ((p = ptsname(master)) == NULL)
+               goto ptym_open_error;
+
+       strcpy(pts, p);
+       return (master);
+
+ptym_open_error:
+       close(master);
+       return (-1);
+}
+
+int
+ptys_open(int master, char *pts_name) {
+       int slave;
+
+       if ((slave = open(pts_name, O_RDWR)) < 0) {
+               close(master);
+               return (-1);
+       }
+
+       if (ioctl(slave, I_PUSH, "ptem") < 0)
+               goto ptys_open_error;
+
+       if (ioctl(slave, I_PUSH, "ldterm") < 0)
+               goto ptys_open_error;
+
+       if (ioctl(slave, I_PUSH, "ttcompat") < 0)
+               goto ptys_open_error;
+
+       return (slave);
+
+ptys_open_error:
+       close(master);
+       close(slave);
+       return (-1);
+}
+#else /* BSD & Mac */
+int
+ptym_open(char *pts_name) {
+       int master;
+       char *ptr1, *ptr2;
+
+       if (pts_name == NULL)
+               return (-1);
+
+       strcpy(pts_name, "/dev/ptyXY");
+       for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ++ptr1) {
+               pts_name[8] = *ptr1;
+               for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ++ptr2) {
+                       pts_name[9] = *ptr2;
+                       if ((master = open(pts_name, O_RDWR)) < 0) {
+                               if (errno == ENOENT)
+                                       return (-1);
+                               else
+                                       continue;
+                       }
+                       pts_name[5] = 't';
+                       return (master);
+               }
+       }
+
+       return (-1);
+}
+
+int
+ptys_open(int master, char *pts_name) {
+       struct group *grptr;
+       int gid, slave;
+
+       if (master < 0 || pts_name == NULL)
+               return (-1);
+
+       if ((grptr = getgrnam("tty")) != NULL)
+               gid = grptr->gr_gid;
+       else
+               gid = -1;
+
+       chown(pts_name, getuid(), gid);
+       chmod(pts_name, S_IRUSR|S_IWUSR|S_IWGRP);
+
+       if ((slave = open(pts_name, O_RDWR)) < 0) {
+               close(master);
+               return (-1);
+       }
+
+       return (slave);
+}
+#endif
+
+/* end of source */
diff --git a/utils.c b/utils.c
new file mode 100644 (file)
index 0000000..8d842c0
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2006  Tsuyoshi SAKAMOTO <skmt.free@gmail.com>,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+*/
+
+
+/********************************************
+ * include file
+ ********************************************
+*/
+#include "icmpsh.h"
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <errno.h>
+
+#include <syslog.h>
+#include <stdarg.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+
+/********************************************
+ * macro
+ ********************************************
+*/
+
+
+/********************************************
+ * type definition
+ ********************************************
+*/
+
+
+/********************************************
+ * global variable
+ ********************************************
+*/
+extern int debug;
+
+
+/* syslog */
+const int level[] = {
+       LOG_EMERG,
+       LOG_ALERT,
+       LOG_CRIT,
+       LOG_ERR,
+       LOG_WARNING,
+       LOG_NOTICE,
+       LOG_INFO,
+       LOG_DEBUG,
+       0       /* syslog off */
+};
+
+
+/********************************************
+ * proto type
+ ********************************************
+*/
+
+/* public */
+
+#ifdef SOLARIS
+void cfmakeraw(struct termios *);
+#endif
+
+int ic_select(int, long);
+void ic_plog(int, const char *, ...);
+void ic_log(const char *, ...);
+void ic_recv_ntohs(ic_data *);
+void ic_set_termios(net_termios *, struct termios *);
+void ic_set_winsz(net_winsz *, struct winsize *);
+void ic_get_termios(struct termios *, net_termios *);
+void ic_get_winsz(struct winsize *, net_winsz *);
+void ic_set_data(ic_data *, u_char, u_char, u_short, u_char *, int);
+void ic_set_header(ic_data *, u_char, u_char, u_short, u_short);
+
+ssize_t xread(int, void *, size_t);
+ssize_t xwrite(int, void *, size_t);
+
+void vsys_err(int, const char *, ...);
+void vsys(int, const char *, ...);
+
+u_short xchecksum(u_short *, int);
+
+pid_t xfork(void);
+
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+
+char *xstrdup(char *);
+
+
+/* local */
+static int xselect(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+
+
+/*============================================================================
+ * program section
+ *============================================================================
+*/
+
+#ifdef SOLARIS
+/*-------------------------------------------------------------------------
+ * cfmakeraw for Solaris
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+cfmakeraw(struct termios *t)
+{
+       memset(t, 0, sizeof(struct termios));
+
+       t->c_iflag |= IGNBRK;
+       t->c_cflag |= CS8|CREAD;
+       t->c_cc[VMIN] = 1;
+       t->c_cc[VTIME] = 0;
+
+       return;
+}
+#endif
+
+/*-------------------------------------------------------------------------
+ * termios and window size
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+ic_set_termios(net_termios *pn, struct termios *pt) {
+        int i;
+       pn->c_iflag   = htonl(pt->c_iflag);
+       pn->c_oflag   = htonl(pt->c_oflag);
+       pn->c_cflag   = htonl(pt->c_cflag);
+       pn->c_lflag   = htonl(pt->c_lflag);
+#ifndef SOLARIS
+       pn->c_ispeed  = htonl(pt->c_ispeed);
+       pn->c_ospeed  = htonl(pt->c_ospeed);
+#endif
+       for (i = 0; i < NCCS; ++i) {
+               pn->c_cc[i] = pt->c_cc[i];
+       }
+       return;
+}
+
+void
+ic_set_winsz(net_winsz *pn, struct winsize *pt) {
+       pn->ws_row     = htons(pt->ws_row);
+       pn->ws_col     = htons(pt->ws_col);
+       pn->ws_xpixel  = htons(pt->ws_xpixel);
+       pn->ws_ypixel  = htons(pt->ws_ypixel);
+       return;
+}
+
+void
+ic_get_termios(struct termios *pt, net_termios *pn) {
+       int i;
+       pt->c_iflag   = ntohl(pn->c_iflag);
+       pt->c_oflag   = ntohl(pn->c_oflag);
+       pt->c_cflag   = ntohl(pn->c_cflag);
+       pt->c_lflag   = ntohl(pn->c_lflag);
+#ifndef SOLARIS
+       pt->c_ispeed  = ntohl(pn->c_ispeed);
+       pt->c_ospeed  = ntohl(pn->c_ospeed);
+#endif
+       for (i = 0; i < NCCS; ++i) { 
+               pt->c_cc[i] = pn->c_cc[i];              
+       }       
+       return;         
+} 
+
+void
+ic_get_winsz(struct winsize *pt, net_winsz *pn) {
+       pt->ws_row     = ntohs(pn->ws_row);
+       pt->ws_col     = ntohs(pn->ws_col);
+       pt->ws_xpixel  = ntohs(pn->ws_xpixel);
+       pt->ws_ypixel  = ntohs(pn->ws_ypixel);
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * debug log handler
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+ic_plog(int lvl, const char *format, ...) {
+       va_list ap;
+       va_start(ap, format);
+
+       if (debug) {
+               vfprintf(stderr, format, ap);
+               fprintf(stderr, "\n");
+       }
+       else {
+               if (level[lvl])
+                       vsyslog(level[lvl], format, ap);
+       }
+
+       va_end(ap);
+
+       return;
+}
+
+void
+ic_log(const char *format, ...) {
+       va_list ap;
+       va_start(ap, format);
+
+       if (debug) {
+               vfprintf(stderr, format, ap);
+               fprintf(stderr, "\n");
+       }
+
+       va_end(ap);
+
+       return;
+}
+
+/*-------------------------------------------------------------------------
+ * convert net to host byte order
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+ic_recv_ntohs(ic_data *pic) {
+       char *p;
+       struct ip *piph;
+       icmp_echo *picmph;
+       ic_header *pich;
+
+       p       = (char *)pic;
+       piph    = (struct ip *)p;
+       picmph  = (icmp_echo *)(p + (IC_IPHLWRS * 4));
+       pich    = (ic_header *)(p + (IC_IPHLWRS * 4) + sizeof(icmp_echo));
+
+       picmph->id    = ntohs(picmph->id);
+       picmph->seq   = ntohs(picmph->seq);
+       pich->length  = ntohs(pich->length);
+
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * select with error check
+ *    arg: follow select()'s interface
+ *    ret: >=0(success), -1(failure)
+ *-------------------------------------------------------------------------
+*/
+int
+xselect(int nfd, fd_set *rfd, fd_set *wfd, fd_set *ofd, struct timeval *pt) {
+       int n;
+
+       for (;;) {
+               if ((n = select(nfd, rfd, wfd, ofd, pt)) < 0) {
+                       if (errno == EINTR)
+                               continue;
+               }
+               else
+                       break;
+       }
+
+       return (n);
+}
+
+/*
+ * common select interface for icmsh
+*/
+int
+ic_select(int nfd, long timeout) {
+       int n;
+       struct timeval tv, *ptv;
+       fd_set rfd;
+
+       memset(&rfd, 0, sizeof(rfd));
+       tv.tv_sec  = timeout;
+       tv.tv_usec = 0;
+
+       ptv = &tv;
+       for (;;) {
+               FD_ZERO(&rfd);
+               FD_SET(nfd, &rfd);
+               if ((n = xselect(nfd + 1, &rfd, NULL, NULL, ptv)) < 0) {
+                       break;
+               }
+               else if (n == 0) {
+                       break;
+               }
+               if (FD_ISSET(nfd, &rfd))
+                       break;
+       }
+
+       return (n);
+}
+
+
+/*-------------------------------------------------------------------------
+ * ic_data common interface
+ *
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+ic_set_data(ic_data *p, u_char type, u_char flag, u_short length, u_char *buf, int bufsz) {
+       u_char *ppay = (u_char *)&(p->data.payload[0]) + sizeof(ic_header);
+       p->data.ich.type    = type;
+       p->data.ich.flag    = flag;
+       p->data.ich.length  = htons(length);
+       if (buf)
+               strncpy((char *)ppay, (char *)buf, bufsz);
+       return;
+}
+
+void
+ic_set_header(ic_data *p, u_char type, u_char code, u_short id, u_short seq) {
+       p->icmph.type   = type;
+       p->icmph.code   = code;
+       p->icmph.id     = htons(id);
+       p->icmph.seq    = htons(seq);
+       p->icmph.cksum  = xchecksum((u_short *)&(p->icmph), IC_DATASIZE);
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * send / wait signal
+ *
+ *-------------------------------------------------------------------------
+*/
+int
+ic_kill(char *proc, pid_t p, int sig) {
+       int rc;
+       if ((rc = kill(p, sig)) != 0)
+               ic_plog(SINF, "(%s) kill failure (%s)", proc, strerror(errno));
+
+       return (rc);
+}
+
+pid_t
+ic_waitpid(char *proc, pid_t p, int *status, int options) {
+       pid_t wpid;
+       if ((wpid = waitpid(p, status, options)) < 0)
+               ic_plog(SINF, "(%s) waitpid failure (%s)", proc, strerror(errno));
+
+       return (wpid);
+}
+
+
+/*-------------------------------------------------------------------------
+ * read with error check
+ *
+ *-------------------------------------------------------------------------
+*/
+ssize_t
+xread(int fd, void *buff, size_t size)
+{
+       size_t left;
+       ssize_t nread;
+
+       void *p;
+
+       p = buff;
+       left = size;
+
+       while (left > 0) {
+               if ((nread = read(fd, p, left)) < 0) {
+                       if (errno == EINTR)
+                               nread = 0;
+                       else
+                               return (-1);
+               }
+               else if (nread == 0) {
+                       break;
+               }
+               left -= nread;
+               p = (char *)p + nread;
+       }
+
+       return (size - left);
+}
+
+
+/*-------------------------------------------------------------------------
+ * write with error check
+ *
+ *-------------------------------------------------------------------------
+*/
+ssize_t
+xwrite(int fd, void *buff, size_t size)
+{
+       size_t left;
+       ssize_t nwrite;
+
+       void *p;
+
+       p = buff;
+       left = size;
+
+       while (left > 0) {
+               if ((nwrite = write(fd, p, left)) < 0) {
+                       if (errno == EINTR)
+                               nwrite = 0;
+                       else
+                               return (-1);
+               }
+               left -= nwrite;
+               p = (char *)p + nwrite;
+       }
+
+       return size;
+}
+
+
+/*-------------------------------------------------------------------------
+ * syslog interface
+ *
+ *-------------------------------------------------------------------------
+*/
+void
+vsys_err(int priority, const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       vsyslog(priority, format, ap);
+
+       va_end(ap);
+
+       exit(1);
+}
+
+void
+vsys(int priority, const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       vsyslog(priority, format, ap);
+
+       va_end(ap);
+
+       return;
+}
+
+
+/*-------------------------------------------------------------------------
+ * internet checksum
+ *
+ *-------------------------------------------------------------------------
+*/
+u_short
+xchecksum(u_short *addr, int len) {
+       u_short answer;
+       u_short *w;
+       int nleft, sum;
+
+       nleft = len;
+       sum = 0;
+       w = addr;
+       while (nleft > 1) {
+               sum += *(w++);
+               nleft -= 2;
+       }
+       if (nleft == 1)
+               sum += *(u_char *) w;
+
+       sum = (sum & 0xffff) + (sum >> 16);
+       sum += (sum >> 16);
+       answer = ~sum;
+
+       return (answer);
+}
+
+
+/*-------------------------------------------------------------------------
+ * fork with error check
+ *
+ *-------------------------------------------------------------------------
+*/
+pid_t
+xfork(void)
+{
+       pid_t pid;
+
+       if ((pid = fork()) < 0)
+               exit(1);
+
+       return (pid);
+}
+
+
+/*-------------------------------------------------------------------------
+ * malloc family with error check
+ *
+ *-------------------------------------------------------------------------
+*/
+void *
+xmalloc(size_t size)
+{
+       void *tmp;
+
+       if (!size)
+               exit (1);
+
+       if ((tmp = malloc(size)) == NULL)
+               exit (1);
+       else
+               memset(tmp, 0, size);
+
+       return (tmp);
+}
+
+void *
+xrealloc(void *src, size_t size)
+{
+       void *dst;
+
+       if (!size)
+               exit (1);
+
+       if ((dst = realloc(src, size)) == NULL)
+               exit (1);
+
+       return (dst);
+}
+
+
+/*-------------------------------------------------------------------------
+ * strdup with error check
+ *
+ *-------------------------------------------------------------------------
+*/
+char *
+xstrdup(char *ptr)
+{
+       char *tmp;
+
+       if (!ptr)
+               exit (1);
+
+       if ((tmp = strdup(ptr)) == NULL)
+               exit (1);
+
+       return (tmp);
+}
+
+
+/* end of source */