diff options
author | David Cantrell <dcantrell@redhat.com> | 2007-05-29 18:22:55 +0000 |
---|---|---|
committer | David Cantrell <dcantrell@redhat.com> | 2007-05-29 18:22:55 +0000 |
commit | 15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06 (patch) | |
tree | 83674ee223d0002d3459eca6a188d9121be2d860 /loader2 | |
parent | 07647fb5b545d3dcc7c1bc1436f1f1e8c6237937 (diff) | |
download | anaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.tar.gz anaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.tar.xz anaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.zip |
piece of shit
Diffstat (limited to 'loader2')
-rw-r--r-- | loader2/Makefile | 2 | ||||
-rw-r--r-- | loader2/loader.c | 6 | ||||
-rw-r--r-- | loader2/loader.h | 2 | ||||
-rw-r--r-- | loader2/telnet.c | 272 | ||||
-rw-r--r-- | loader2/telnet.h | 40 | ||||
-rw-r--r-- | loader2/telnetd.c | 250 | ||||
-rw-r--r-- | loader2/telnetd.h | 8 |
7 files changed, 579 insertions, 1 deletions
diff --git a/loader2/Makefile b/loader2/Makefile index 3eeec8762..0b59a2e91 100644 --- a/loader2/Makefile +++ b/loader2/Makefile @@ -44,7 +44,7 @@ OBJS = log.o moduleinfo.o loadermisc.o modules.o moduledeps.o windows.o \ getparts.o dirbrowser.o \ $(HWOBJS) $(METHOBJS) LOADEROBJS = loader.o loader-pcmcia.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 8e59c4369..99841c489 100644 --- a/loader2/loader.c +++ b/loader2/loader.c @@ -82,6 +82,7 @@ #include "urlinstall.h" #include "net.h" +#include "telnetd.h" #include <selinux/selinux.h> #include "selinux.h" @@ -622,6 +623,8 @@ static void parseCmdLineFlags(struct loaderData_s * loaderData, flags |= LOADER_FLAGS_NOUSB; else if (!strcasecmp(argv[i], "ub")) flags |= LOADER_FLAGS_UB; + 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], "nonet")) @@ -1620,6 +1623,9 @@ int main(int argc, char ** argv) { } } + if (FL_TELNETD(flags)) + startTelnetd(&loaderData, modInfo, modLoaded, modDeps); + url = doLoaderMain("/mnt/source", &loaderData, modInfo, modLoaded, &modDeps); if (!FL_TESTING(flags)) { diff --git a/loader2/loader.h b/loader2/loader.h index 5775eea92..9b9b012b4 100644 --- a/loader2/loader.h +++ b/loader2/loader.h @@ -27,6 +27,7 @@ #define LOADER_FLAGS_NOUSB (1 << 16) #define LOADER_FLAGS_NOSHELL (1 << 17) #define LOADER_FLAGS_NOPCMCIA (1 << 18) +#define LOADER_FLAGS_TELNETD (1 << 19) #define LOADER_FLAGS_NOPASS (1 << 20) #define LOADER_FLAGS_UB (1 << 21) #define LOADER_FLAGS_MEDIACHECK (1 << 22) @@ -58,6 +59,7 @@ #define FL_NOFB(a) ((a) & LOADER_FLAGS_NOFB) #define FL_NOPCMCIA(a) ((a) & LOADER_FLAGS_NOPCMCIA) #define FL_RESCUE_NOMOUNT(a) ((a) & LOADER_FLAGS_RESCUE_NOMOUNT) +#define FL_TELNETD(a) ((a) & LOADER_FLAGS_TELNETD) #define FL_NOPASS(a) ((a) & LOADER_FLAGS_NOPASS) #define FL_MEDIACHECK(a) ((a) & LOADER_FLAGS_MEDIACHECK) #define FL_NOUSBSTORAGE(a) ((a) & LOADER_FLAGS_NOUSBSTORAGE) diff --git a/loader2/telnet.c b/loader2/telnet.c new file mode 100644 index 000000000..4b8cf09aa --- /dev/null +++ b/loader2/telnet.c @@ -0,0 +1,272 @@ +/* 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 + ; + int ret; + + ret = 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 { + ret = read(socket, &ch, 1); + if (ch != '\xff') { + abort(); + } + + ret = read(socket, &ch, 1); /* command */ + + if (ch != '\xfa') { + ret = read(socket, &ch, 1); /* verb */ + continue; + } + + ret = read(socket, &ch, 1); /* suboption */ + if (ch == '\x18') { + state = ST_TERMTYPE; + ret = read(socket, &ch, 1); /* should be 0x0! */ + done = 1; + } else if (ch == '\x1f') { + state = ST_WINDOWSIZE; + } else { + state = ST_NONE;; + } + + ret = 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) < (int)sizeof(sizeBuf)) + *sizePtr++ = ch; + } + + ret = read(socket, &ch, 1); /* data */ + } + + ret = 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(WARNING, "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; + int ret; + + 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... */ + ret = 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..75bce5222 --- /dev/null +++ b/loader2/telnetd.c @@ -0,0 +1,250 @@ +/* + * 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" + +#ifndef IPPORT_TELNET +#define IPPORT_TELNET 23 +#endif + +/* boot flags */ +extern int flags; + +/* Forks, keeping the loader as our child (so we know when it dies). */ +int beTelnet(void) { + int sock; + int conn; + socklen_t addrLength; + pid_t child; + int i; + int masterFd, ttyFd; + 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(ERROR, "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_TELNET + printf("got term type %s\n", termType); +#endif + + masterFd = open("/dev/ptmx", O_RDWR); + if (masterFd < 0) { + logMessage(CRITICAL, "cannot open /dev/ptmx"); + close(conn); + return -1; + } + + if (height != -1 && width != -1) { +#ifdef DEBUG_TELNET + 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_TELNET + startNewt(); + 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_TELNET + { + 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) { + int ret; + i = read(conn, buf, sizeof(buf)); + + /* connection went away */ + if (!i) + break; + + i = telnet_process_input(&ts, buf, i); + ret = write(masterFd, buf, i); + +#ifdef DEBUG_TELNET + { + int j; + + printf("got:"); + for (j = 0; j < i; j++) + printf(" 0x%x", (unsigned char) buf[j]); + printf("\n"); + } +#endif + + } + } + + + if (i < 0) { + logMessage(ERROR, "poll: %s", strerror(errno)); + } + +#ifndef DEBUG_TELNET + stopNewt(); +#endif + + kill(child, SIGTERM); + close(conn); + + exit(0); + } + + unlockpt(masterFd); + grantpt(masterFd); + ttyFd = open(ptsname(masterFd), O_RDWR); + close(masterFd); + setsid(); + close(0); + close(1); + close(2); + + if (ttyFd != 0) { + dup2(ttyFd, 0); + close(ttyFd); + } + dup2(0, 1); + dup2(0, 2); + + /* brand new tty! */ + setenv("TERM", termType, 1); + + startNewt(); + + return 0; +} + +void startTelnetd(struct loaderData_s * loaderData, + moduleInfoSet modInfo, moduleList modLoaded, + moduleDeps modDeps) { + char ret[47]; + struct networkDeviceConfig netCfg; + ip_addr_t *tip; + + if (kickstartNetworkUp(loaderData, &netCfg)) { + logMessage(ERROR, "unable to bring up network"); + return; + } + + tip = &(netCfg.dev.ip); + inet_ntop(tip->sa_family, IP_ADDR(tip), ret, IP_STRLEN(tip)); + logMessage(INFO, "going to beTelnet for %s", ret); + if (!beTelnet()) + flags |= LOADER_FLAGS_TEXT | LOADER_FLAGS_NOSHELL; + + return; +} diff --git a/loader2/telnetd.h b/loader2/telnetd.h new file mode 100644 index 000000000..145c55f21 --- /dev/null +++ b/loader2/telnetd.h @@ -0,0 +1,8 @@ +#ifndef TELNETD_H +#define TELNETD_H + +void startTelnetd(struct loaderData_s * loaderData, + moduleInfoSet modInfo, moduleList modLoaded, + moduleDeps modDeps); + +#endif |