/* * Copyright (c) 2004 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. * * $Id: evilserver.c,v 1.7 2004/09/30 20:24:37 wpaul Exp wpaul $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint static const char rcsid[] = "@(#) $Id: evilserver.c,v 1.7 2004/09/30 20:24:37 wpaul Exp wpaul $"; #endif /* * This program is part of the EvilLink software for the Danger Hiptop. * Its purpose is to pipe traffic between the EvilLink utility running * on the hiptop and a pseudo-network interface on the local machine. * In the case of FreeBSD, we use a tun(4) device. * * The program will create a listening socket and connect to an * available tun(4) interface on the host. When a connection is received, * all traffic received from the tun(4) device will be sent over the * the socket in the form of EvilPackets. The only special thing about * an EvilPacket is that it's prefixed with a small EvilHeader of * the form EVILxxxx, where xxxx is 4 ASCII digits that describe the * length of the data in the EvilPacket. Data received over the socket * will also be in the form of EvilPackets, which will have their * EvilHeaders stripped before being written to the tun(4) device. * * When a client connects, the server will send out one special * EvilPacket which contains the local and remote IP addresses * assigned to the tun(4) device as well as the netmask. This is * packet is intercepted by the EvilLink program so that the * information can be displayed to the user on the Hiptop. */ #define EVIL_PACKET_LEN 1508 #define EVIL_HEADER_LEN 8 static int evilsock = -1; static int evilsocksend (int, int); static int eviltunsend (int, int); static int evilsockcreate (int); static int eviltuncreate (int *, char *, char *, char*); static int evilaccept (int, int *); static int evilinfosend (int, int); static int evilinfoget (int, char **, char **, char **); static char *eviliface (int); static void evilloop (int, int); static void usage (char *); static int evilsocksend(sock, tun) int sock; int tun; { char buf[EVIL_PACKET_LEN]; char header[EVIL_HEADER_LEN + 1]; int r; r = read(tun, buf, sizeof(buf)); if (r == -1) { printf ("read from tunnel device failed\n"); return(errno); } if (r > EVIL_PACKET_LEN || r == 0) { printf ("invalid tunnel packet length: %d\n", r); return(errno); } sprintf(header, "EVIL%.4d\n", r); if (write(sock, header, EVIL_HEADER_LEN) == -1 || write(sock, buf, r) == -1) { printf ("write to socket failed\n"); return(errno); } return(0); } static int eviltunsend(sock, tun) int sock; int tun; { char buf[EVIL_PACKET_LEN]; char header[EVIL_HEADER_LEN + 1]; int r, len; bzero((char *)&header, sizeof(header)); r = read(sock, header, EVIL_HEADER_LEN); if (r == -1) { printf ("read from socket failed\n"); return(errno); } if (r == 0) return(EINVAL); /* discard non-evil packets */ if (strncmp(header, "EVIL", 4)) { printf ("not an evil packet: %s\n", header); return(0); } len = atoi((char *)&header[4]); if (len > EVIL_PACKET_LEN) { printf ("invalid tunnel packet length: %d\n", r); return(errno); } /* * An EvilPacket with length 0 has special meaning: it means * the peer on the other end is requesting an EvilInfo packet, * which contains the network configuration info (local IP, * remote IP, etc...) for this session. */ if (len == 0) return(evilinfosend(sock, tun)); r = read(sock, buf, len); if (r == -1) { printf ("reading packet failed\n"); return(errno); } if (write(tun, buf, len) == -1) { printf ("write to tun failed\n"); return(errno); } return(0); } static int evilsockcreate(port) int port; { struct sockaddr_in s; evilsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (evilsock == -1) return(errno); bzero((char *)&s, sizeof(s)); s.sin_family = AF_INET; s.sin_port = htons(port); s.sin_addr.s_addr = INADDR_ANY; if (bind(evilsock, (struct sockaddr *)&s, sizeof(s)) == -1) { close(evilsock); return(errno); } if (listen(evilsock, 5) == -1) { close(evilsock); return(errno); } return (0); } static int evilaccept(port, sock) int port; int *sock; { int n, fromlen; struct sockaddr_in from; printf ("Listening on port %d\n", port); fromlen = sizeof(from); n = accept(evilsock, (struct sockaddr *)&from, &fromlen); if (n == -1) { close(evilsock); return(errno); } *sock = n; printf ("Got connection, peer: %s\n", inet_ntoa(from.sin_addr)); return(0); } static int evilinfosend(sock, tun) int sock; int tun; { char *laddr; char *raddr; char *mask; char buf[EVIL_PACKET_LEN]; char header[EVIL_HEADER_LEN + 1]; int len; if (evilinfoget(tun, &laddr, &raddr, &mask)) return(EINVAL); sprintf(buf, "[%s][%s][%s]", laddr, raddr, mask); len = strlen(buf); sprintf(header, "EVIL%.4d\n", len); if (write(sock, header, EVIL_HEADER_LEN) == -1 || write(sock, buf, len) == -1) return(errno); printf ("Sent info packet.\n"); return(0); } static char * eviliface(tun) int tun; { struct stat st, tmp; static char dname[FILENAME_MAX]; int i; if (fstat(tun, &st) == -1) { close(tun); return(NULL); } for (i = 0; i < 255; i++) { sprintf(dname, "/dev/tun%d", i); if (stat(dname, &tmp) == -1) return(NULL); if (tmp.st_dev == st.st_dev) break; } if (i == 255) return(NULL); sprintf(dname, "tun%d", i); return((char *)&dname); } static int eviltuncreate(tun, laddr, raddr, mask) int *tun; char *laddr; char *raddr; char *mask; { int i, t; char *dname; struct ifreq ifr; struct in_aliasreq ifra; t = open("/dev/tun", O_RDWR); if (t == -1) { perror("open of /dev/tun failed"); return(errno); } *tun = t; dname = eviliface(t); if (dname == NULL) { close(t); return(errno); } i = socket(AF_INET, SOCK_DGRAM, 0); if (i == -1) { close(t); return(errno); } if (laddr != NULL) { /* First, delete any existing addresses. */ bzero((char *)&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, dname); ioctl(i, SIOCDIFADDR, &ifr); /* Then set the new local and remote addresses. */ bzero((char *)&ifra, sizeof(ifra)); strcpy(ifra.ifra_name, dname); ifra.ifra_addr.sin_addr.s_addr = inet_addr(laddr); ifra.ifra_addr.sin_family = AF_INET; ifra.ifra_addr.sin_len = sizeof(struct sockaddr_in); ifra.ifra_dstaddr.sin_addr.s_addr = inet_addr(raddr); ifra.ifra_dstaddr.sin_family = AF_INET; ifra.ifra_dstaddr.sin_len = sizeof(struct sockaddr_in); ifra.ifra_mask.sin_addr.s_addr = inet_addr(mask); ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); if (ioctl(i, SIOCAIFADDR, &ifra) == -1) err(1, "ioctl failed"); } printf ("Listening on interface %s\n", dname); return(0); } static int evilinfoget(tun, laddr, raddr, mask) int tun; char **laddr; char **raddr; char **mask; { static char l[MAXHOSTNAMELEN]; static char r[MAXHOSTNAMELEN]; static char m[MAXHOSTNAMELEN]; struct ifreq ifr; int i; struct sockaddr_in *s; char *tmp; char *dname; i = socket(AF_INET, SOCK_DGRAM, 0); if (i == -1) return(errno); dname = eviliface(tun); if (dname == NULL) return(EINVAL); bzero((char *)&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, dname); if (ioctl(i, SIOCGIFADDR, &ifr) == -1) { close(i); return(errno); } s = (struct sockaddr_in *)&ifr.ifr_addr; tmp = inet_ntoa(s->sin_addr); strcpy(l, tmp); bzero((char *)&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, dname); if (ioctl(i, SIOCGIFDSTADDR, &ifr) == -1) { close(i); return(errno); } s = (struct sockaddr_in *)&ifr.ifr_dstaddr; tmp = inet_ntoa(s->sin_addr); strcpy(r, tmp); bzero((char *)&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, dname); if (ioctl(i, SIOCGIFNETMASK, &ifr) == -1) { close(i); return(errno); } s = (struct sockaddr_in *)&ifr.ifr_dstaddr; tmp = inet_ntoa(s->sin_addr); strcpy(m, tmp); *laddr = (char *)&l; *raddr = (char *)&r; *mask = (char *)&m; return(0); } void evilloop(sock, tun) int sock; int tun; { fd_set rd_fdset; int r; while(1) { FD_ZERO(&rd_fdset); FD_SET(sock, &rd_fdset); FD_SET(tun, &rd_fdset); r = select(FD_SETSIZE, &rd_fdset, NULL, NULL, NULL); switch (r) { case 1: case 2: if (FD_ISSET(tun, &rd_fdset)) { if (evilsocksend(sock, tun)) goto error; } else if (FD_ISSET(sock, &rd_fdset)) { if (eviltunsend(sock, tun)) goto error; } else { printf("no descriptor set?!\n"); goto error; } break; case 0: case -1: default: printf ("error from select\n"); goto error; break; } } error: return; } static void usage(pname) char *pname; { fprintf(stderr, "%s: -p [-l " "-r -m ]\n", pname); return; } int main(int argc, char *argv[]) { int port = 0; int s, t, ch; char *laddr = NULL; char *raddr = NULL; char *mask = NULL; char *pname = argv[0]; while((ch = getopt(argc, argv, "p:l:r:m:h")) != -1) { switch (ch) { case 'p': port = atoi(optarg); break; case 'l': laddr = optarg; break; case 'r': raddr = optarg; break; case 'm': mask = optarg; break; case 'h': default: usage(pname); exit(-1); } } if (port == 0) err(1, "port number must be specified"); if (port > 65535) err(1, "invalid port number: %d", port); if (laddr != NULL && (raddr == NULL || mask == NULL)) err(1, "must provide local address, remote address and" "netmask together"); if (raddr != NULL && (laddr == NULL || mask == NULL)) err(1, "must provide local address, remote address and" "netmask together"); if (mask != NULL && (laddr == NULL || raddr == NULL)) err(1, "must provide local address, remote address and" "netmask together"); printf("EvilServer Copyright (c) 2004 Bill Paul\n"); printf("$Revision: 1.7 $ $Date: 2004/09/30 20:24:37 $\n"); printf("\nEvilServer starting.\n"); /* Create main socket. */ if (evilsockcreate(port)) { printf ("socket create failed\n"); exit(-1); } while (1) { /* Open tun(4) device and set it up */ if (eviltuncreate(&t, laddr, raddr, mask)) { printf ("eviltun failed\n"); exit(-1); } /* Wait for a client to connect. */ if (evilaccept(port, &s)) { printf ("evilaccept failed\n"); close(t); close(evilsock); exit(-1); } #ifdef notdef /* Send info packet. */ if (evilinfosend(s, t)) { close(t); close(s); printf ("Failed to send info packet, respawning.\n"); continue; } #endif printf ("Starting traffic forwarding.\n"); evilloop(s, t); close(s); close(t); printf ("Connection closed, respawning.\n"); } close(evilsock); exit(0); }