From: Tsuyoshi SAKAMOTO Date: Wed, 29 Aug 2012 03:49:45 +0000 (+0900) Subject: Adding all files as MASTER X-Git-Url: http://git.sourceforge.jp/view?p=icmpsh%2Ficmpsh.git;a=commitdiff_plain;h=e70225a8af23b619b7e61de17f161f00e8da867e Adding all files as MASTER --- e70225a8af23b619b7e61de17f161f00e8da867e diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d81200f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +*.log diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..b253f43 --- /dev/null +++ b/icmpsh.c @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2006 Tsuyoshi SAKAMOTO , + * 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 +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#ifdef SOLARIS + #include + #include +#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 index 0000000..cea8281 --- /dev/null +++ b/icmpsh.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2006 Tsuyoshi SAKAMOTO , + * 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 +#include +#include /* FreeBSD */ +#include +#include +#include +#include +#include +#include + + + +/******************************************** + * 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 index 0000000..a375806 --- /dev/null +++ b/icmpshd.c @@ -0,0 +1,1283 @@ +/* + * Copyright (c) 2006 Tsuyoshi SAKAMOTO , + * 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 +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include + + +#include +#ifdef SOLARIS + #include + #include + #include + #include +#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 index 0000000..dc62714 --- /dev/null +++ b/openpty.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2006 Tsuyoshi SAKAMOTO , + * 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 +#include +#include +#include +#include + +#ifdef SOLARIS + #include + #include + #include + #include +#else /* BSD & Mac */ + #include +#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 index 0000000..8d842c0 --- /dev/null +++ b/utils.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2006 Tsuyoshi SAKAMOTO , + * 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 +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + + +/******************************************** + * 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 */