diff options
author | Jeremy Katz <katzj@redhat.com> | 2002-12-09 23:09:52 +0000 |
---|---|---|
committer | Jeremy Katz <katzj@redhat.com> | 2002-12-09 23:09:52 +0000 |
commit | 0923a61ba4a031a2e23128ca6475c3693676d940 (patch) | |
tree | 7c935dd1122335a0ac1374cbe7a45abc271c49d6 /loader2 | |
parent | 4026940b2d7ba91ccc934d2dcf3fa6c674586c9a (diff) | |
download | anaconda-0923a61ba4a031a2e23128ca6475c3693676d940.tar.gz anaconda-0923a61ba4a031a2e23128ca6475c3693676d940.tar.xz anaconda-0923a61ba4a031a2e23128ca6475c3693676d940.zip |
merge telnet mode back in
Diffstat (limited to 'loader2')
-rw-r--r-- | loader2/Makefile | 3 | ||||
-rw-r--r-- | loader2/loader.c | 7 | ||||
-rw-r--r-- | loader2/telnet.c | 269 | ||||
-rw-r--r-- | loader2/telnet.h | 40 | ||||
-rw-r--r-- | loader2/telnetd.c | 236 | ||||
-rw-r--r-- | loader2/telnetd.h | 8 |
6 files changed, 560 insertions, 3 deletions
diff --git a/loader2/Makefile b/loader2/Makefile index 05c9bcf98..a83f7d252 100644 --- a/loader2/Makefile +++ b/loader2/Makefile @@ -32,8 +32,7 @@ OBJS = log.o moduleinfo.o loadermisc.o modules.o moduledeps.o windows.o \ md5.o mediacheck.o kickstart.o \ $(HWOBJS) $(METHOBJS) LOADEROBJS = loader.o loader-pcmcia.o -NETOBJS = net.o urls.o telnet.o telnetd.o -NETOBJS = net.o urls.o ftp.o +NETOBJS = net.o urls.o ftp.o telnet.o telnetd.o PCMCIAOBJS = pcmcia.o $(NETOBJS) SOURCES = $(subst .o,.c,$(OBJS)) loader.c diff --git a/loader2/loader.c b/loader2/loader.c index 1a9857a5f..6e52c3004 100644 --- a/loader2/loader.c +++ b/loader2/loader.c @@ -69,6 +69,8 @@ #include "hdinstall.h" #include "urlinstall.h" +#include "telnetd.h" + #include "../isys/imount.h" #include "../isys/isys.h" #include "../isys/probe.h" @@ -352,6 +354,8 @@ static int parseCmdLineFlags(int flags, struct loaderData_s * loaderData, flags |= LOADER_FLAGS_NOUSBSTORAGE; else if (!strcasecmp(argv[i], "nousb")) flags |= LOADER_FLAGS_NOUSB; + else if (!strcasecmp(argv[i], "telnet")) + flags |= LOADER_FLAGS_TELNETD; else if (!strcasecmp(argv[i], "nofirewire")) flags |= LOADER_FLAGS_NOIEEE1394; else if (!strcasecmp(argv[i], "noprobe")) @@ -865,7 +869,8 @@ int main(int argc, char ** argv) { } } - /* JKFIXME: telnetd */ + if (FL_TELNETD(flags)) + startTelnetd(&kd, &loaderData, modInfo, modLoaded, modDeps, flags); url = doLoaderMain("/mnt/source", &loaderData, &kd, modInfo, modLoaded, &modDeps, flags); diff --git a/loader2/telnet.c b/loader2/telnet.c new file mode 100644 index 000000000..fda836639 --- /dev/null +++ b/loader2/telnet.c @@ -0,0 +1,269 @@ +/* telnet.c -- basic telnet protocol handling for ttywatch + * + * Copyright © 2001 Michael K. Johnson <johnsonm@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Shamelessly stolen from ttywatch -- oot */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "telnet.h" +#include "log.h" + +#define IAC "\xff" +#define DONT "\xfe" +#define WONT "\xfc" +#define WILL "\xfb" +#define DO "\xfd" +#define SB "\xfa" +#define SE "\xf0" +#define ECHO "\x01" +#define SUPPRESS_GO_AHEAD "\x03" +#define TERMINAL_TYPE "\x18" +#define NAWS "\x1f" +#define LINEMODE "\x22" +#define NEWENVIRON "\x27" +#define MODE "\x01" + +/* Make a request. Not intended to be RFC-compatible, just enough + * to convince telnet clients to do what we want... To do this + * right, we would have to honestly negotiate, not speak blind. + * + * For now, assume all responses will be favorable and stripped + * out in telnet_process_input()... Sending it all in a single + * write makes it more efficient because it will all go out in a + * single packet, and the responses are more likely to all come + * back in a single packet (and thus, practically, a single read) + * too. + */ +void +telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr, + int * widthPtr) { + char ch; + int done = 0; + char * termType = NULL; + int termLength = 0, termAlloced = 0; + enum { ST_NONE, ST_TERMTYPE, ST_WINDOWSIZE } state; + char sizeBuf[4]; + int height = -1, width = -1; + char * sizePtr = sizeBuf; + char request[]= + IAC DONT ECHO + IAC WILL ECHO + IAC WILL NAWS + IAC WILL SUPPRESS_GO_AHEAD + IAC DO SUPPRESS_GO_AHEAD + IAC DONT NEWENVIRON + IAC WONT NEWENVIRON + IAC WONT LINEMODE + IAC DO NAWS + IAC SB TERMINAL_TYPE "\x01" IAC SE + ; + + write(socket, request, sizeof(request)-1); + + /* Read from the terminal until we get the terminal type. This will + do bad things if the client doesn't send the terminal type, but + those clients have existed for aeons (right?) */ + + do { + read(socket, &ch, 1); + if (ch != '\xff') { + abort(); + } + + read(socket, &ch, 1); /* command */ + + if (ch != '\xfa') { + read(socket, &ch, 1); /* verb */ + continue; + } + + read(socket, &ch, 1); /* suboption */ + if (ch == '\x18') { + state = ST_TERMTYPE; + read(socket, &ch, 1); /* should be 0x0! */ + done = 1; + } else if (ch == '\x1f') { + state = ST_WINDOWSIZE; + } else { + state = ST_NONE;; + } + + read(socket, &ch, 1); /* data */ + while (ch != '\xff') { + if (state == ST_TERMTYPE) { + if (termAlloced == termLength) { + termAlloced += 10; + termType = realloc(termType, termAlloced + 1); + } + + termType[termLength++] = tolower(ch); + } else if (state == ST_WINDOWSIZE) { + if ((sizePtr - sizeBuf) < sizeof(sizeBuf)) + *sizePtr++ = ch; + } + + read(socket, &ch, 1); /* data */ + } + + read(socket, &ch, 1); /* should be a SE */ + + } while (!done); + + termType[termLength] = '\0'; + + if (sizePtr - sizeBuf == sizeof(sizeBuf)) { + width = (sizeBuf[0] << 8) + sizeBuf[1]; + height = (sizeBuf[2] << 8) + sizeBuf[3]; + } + + if (heightPtr) *heightPtr = height; + if (widthPtr) *widthPtr = width; + + if (term_type_ptr) *term_type_ptr = termType; +} + +int +telnet_process_input(telnet_state * ts, char *data, int len) { + char *s, *d; /* source, destination */ + +# if DEBUG_TELNET + printf("\nprinting packet:"); + for (s=data; s<data+len; s++) { + if (!((s-data)%10)) + printf("\n %03d: ", s-data); + printf("%02x ", *s & 0x000000FF); + } + printf("\n"); +# endif /* DEBUG_TELNET */ + + for (s=data, d=data; s<data+len; s++) { + switch (*ts) { + case TS_DATA: + if (*s == '\xff') { /* IAC */ + *ts = TS_IAC; + continue; + } +#if DEBUG_TELNET + printf("copying data element '%c'\n", *s); +#endif /* DEBUG_TELNET */ + if (s>d) { + *(d++) = *s; + } else { + d++; + } + break; + + case TS_IAC: + if (*s == '\xfa') { /* SB */ + *ts = TS_SB; + continue; + } + /* if not SB, skip IAC verb object */ +# if DEBUG_TELNET + printf("skipping verb/object (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + s += 1; + *ts = TS_DATA; + break; + + case TS_SB: +# if DEBUG_TELNET + printf("skipping SB (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + while (s < (data+(len-1))) { + if (*s == '\xff') { + break; /* fall through to TS_SB_IAC setting below */ + } else { + s++; + } + } + if (*s == '\xff') { + *ts = TS_SB_IAC; + } + break; + + case TS_SB_IAC: + if (*s == '\xf0') { /* SE */ +# if DEBUG_TELNET + printf("SE ends SB (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + *ts = TS_DATA; + } else { +# if DEBUG_TELNET + printf("IAC without SE in SB (offset %d)\n", s-data-1); +# endif /* DEBUG_TELNET */ + *ts = TS_SB; + } + break; + + default: + logMessage("unknown telnet state %d for data element %c", *ts, *s); + *ts = TS_DATA; + break; + } + } + + /* calculate new length after copying data around */ + len = d - data; +#if DEBUG_TELNET + printf("returning len: %d of packet:", len); + for (s=data; s<data+len; s++) { + if (!((s-data)%10)) + printf("\n %03d: ", s-data); + printf("%02x ", *s & 0x000000FF); + } + printf("\n"); +#endif /* DEBUG_TELNET */ + + return len; +} + +/* The telnet protocol requires CR/NL instead of just NL + * We normally deal with Unix, which just uses NL, so we need to translate. + * + * It would be easy to go through line-by-line and write each line, but + * that would create more packet overhead by sending out one packet + * per line, and over things like slow PPP connections, that is painful. + * Therefore, instead, we create a modified copy of the data and write + * the whole modified copy at once. + */ +void +telnet_send_output(int sock, char *data, int len) { + char *s, *d; /* source, destination */ + char *buf; + + buf = alloca((len*2)+1); /* max necessary size */ + + /* just may need to add CR before NL (but do not double existing CRs) */ + for (s=data, d=buf; d-buf<len; s++, d++) { + if ((*s == '\n') && (s == data || (*(s-1) != '\r'))) { + /* NL without preceding CR */ + *(d++) = '\r'; + len++; + } + *d = *s; + } + + /* now send it... */ + write(sock, buf, len); +} diff --git a/loader2/telnet.h b/loader2/telnet.h new file mode 100644 index 000000000..58ea5ba0a --- /dev/null +++ b/loader2/telnet.h @@ -0,0 +1,40 @@ +/* telnet.h -- basic telnet protocol handling for ttywatch + * + * Copyright © 2001 Michael K. Johnson <johnsonm@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __TELNET_H__ +#define __TELNET_H__ + +typedef enum { + TS_DATA = 0, + TS_IAC, + TS_SB, + TS_SB_IAC, +} telnet_state; + +void +telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr, + int * widthPtr); +int +telnet_process_input(telnet_state * ts, char *data, int len); +void +telnet_send_output(int sock, char *data, int len); + +#endif /* __TELNET_H__ */ diff --git a/loader2/telnetd.c b/loader2/telnetd.c new file mode 100644 index 000000000..796322523 --- /dev/null +++ b/loader2/telnetd.c @@ -0,0 +1,236 @@ +/* + * telnetd.c - glue to tie telnet.c from ttywatch to the loader + * + * Erik Troan <ewt@redhat.com> + * Jeremy Katz <katzj@redhat.com> + * + * Copyright 2002 Red Hat, Inc. + * + * This software may be freely redistributed under the terms of the GNU + * General Public License. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <arpa/inet.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <newt.h> +#include <pty.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lang.h" +#include "loader.h" +#include "log.h" +#include "modules.h" +#include "net.h" +#include "telnet.h" +#include "windows.h" + +#include "../isys/probe.h" + +#ifndef IPPORT_TELNET +#define IPPORT_TELNET 23 +#endif + +/* Forks, keeping the loader as our child (so we know when it dies). */ +int beTelnet(int flags) { + int sock; + int conn; + int addrLength; + pid_t child; + int i; + int masterFd; + struct sockaddr_in address; + char buf[4096]; + struct pollfd fds[3]; + telnet_state ts = TS_DATA; + char * termType; + int height, width; + struct winsize ws; + + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + logMessage("socket: %s", strerror(errno)); + return -1; + } + + address.sin_family = AF_INET; + address.sin_port = htons(IPPORT_TELNET); + memset(&address.sin_addr, 0, sizeof(address.sin_addr)); + addrLength = sizeof(address); + + /* Let the kernel reuse the socket address. This lets us run + twice in a row, without waiting for the (ip, port) tuple + to time out. Makes testing much easier*/ + conn = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &conn, sizeof(conn)); + + bind(sock, (struct sockaddr *) &address, sizeof(address)); + listen(sock, 5); + + winStatus(45, 3, _("Telnet"), _("Waiting for telnet connection...")); + + if ((conn = accept(sock, (struct sockaddr *) &address, + &addrLength)) < 0) { + newtWinMessage(_("Error"), _("OK"), "accept failed: %s", + strerror(errno)); + close(sock); + return -1; + } + + stopNewt(); + + close(sock); + + telnet_negotiate(conn, &termType, &height, &width); + +#ifdef DEBUG + printf("got term type %s\n", termType); +#endif + + masterFd = open("/dev/ptyp0", O_RDWR); + if (masterFd < 0) { + logMessage("cannot open /dev/ttyp0"); + close(conn); + return -1; + } + + if (height != -1 && width != -1) { +#ifdef DEBUF + printf("setting window size to %d x %d\n", width, height); +#endif + ws.ws_row = height; + ws.ws_col = width; + ioctl(masterFd, TIOCSWINSZ, &ws); + } + + + child = fork(); + + if (child) { +#ifndef DEBUG + startNewt(flags); + winStatus(45, 3, _("Telnet"), _("Running anaconda via telnet...")); +#endif + + fds[0].events = POLLIN; + fds[0].fd = masterFd; + + fds[1].events = POLLIN; + fds[1].fd = conn; + + while ((i = poll(fds, 2, -1)) > 0) { + if (fds[0].revents) { + i = read(masterFd, buf, sizeof(buf)); + +#ifdef DEBUG + { + int j; + int row; + + for (row = 0; row < (i / 12) + 1; row++) { + printf("wrote:"); + for (j = (row * 12); j < i && j < ((row + 1) * 12); j++) + printf(" 0x%2x", (unsigned char) buf[j]); + printf("\n"); + printf("wrote:"); + for (j = (row * 12); j < i && j < ((row + 1) * 12); j++) + { + if (isprint(buf[j])) + printf(" %c ", buf[j]); + else + printf(" "); + } + printf("\n"); + } + } +#endif + /* child died */ + if (i < 0) + break; + + telnet_send_output(conn, buf, i); + } + + if (fds[1].revents) { + i = read(conn, buf, sizeof(buf)); + + /* connection went away */ + if (!i) + break; + + i = telnet_process_input(&ts, buf, i); + write(masterFd, buf, i); + +#ifdef DEBUG + { + int j; + + printf("got:"); + for (j = 0; j < i; j++) + printf(" 0x%x", (unsigned char) buf[j]); + printf("\n"); + } +#endif + + } + } + + + if (i < 0) { + logMessage("poll: %s", strerror(errno)); + } + +#ifndef DEBUG + stopNewt(); +#endif + + kill(child, SIGTERM); + close(conn); + + exit(0); + } + + close(masterFd); + setsid(); + close(0); + close(1); + close(2); + + open("/dev/ttyp0", O_RDWR); + dup(0); + dup(0); + + /* brand new tty! */ + setenv("TERM", termType, 1); + + startNewt(flags); + + return 0; +} + +void startTelnetd(struct knownDevices * kd, struct loaderData_s * loaderData, + moduleInfoSet modInfo, moduleList modLoaded, + moduleDeps modDeps, int flags) { + if (kickstartNetworkUp(kd, loaderData, flags)) { + logMessage("unable to bring up network"); + return; + } + + logMessage("going to beTelnet"); + if (!beTelnet(flags)) + flags |= LOADER_FLAGS_TEXT | LOADER_FLAGS_NOSHELL; + + return; +} diff --git a/loader2/telnetd.h b/loader2/telnetd.h new file mode 100644 index 000000000..d9a2a6cd4 --- /dev/null +++ b/loader2/telnetd.h @@ -0,0 +1,8 @@ +#ifndef TELNETD_H +#define TELNETD_H + +void startTelnetd(struct knownDevices * kd, struct loaderData_s * loaderData, + moduleInfoSet modInfo, moduleList modLoaded, + moduleDeps modDeps, int flags); + +#endif |