#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "machdep.h" #include "mbuf.h" #include "timer.h" #include "internet.h" #include "icmp.h" #include "netuser.h" #include "tcp.h" #include "telnet.h" #include "session.h" #include "inetdev.h" #include "inetlib.h" #define DEBUG struct Process *mytask; APTR oldwindowptr; struct IntuitionBase *IntuitionBase; char banner[80] = "telnet window"; static struct NewWindow nw = { 0, 0, 640, 200, /* left, top, (max) width, (max) height */ 0, 1, /* detail pen, block pen */ 0, /* IDCMP flags */ SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING | SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH, /* window flags */ NULL, NULL, /* gadget, checkmark */ (UBYTE *)&banner[0], /* title of window */ NULL, NULL, /* screen, bitmap */ 200, 50, -1, -1, /* sizing limits */ WBENCHSCREEN, /* on the workbench */ }; struct Window *win; struct MsgPort *keyboard, *consinp, *consoutp, *tcpinp, *tcpoutp; struct IOStdReq consin, consout; char InputCharacter; int deviceopened = 0; struct IOINETReq tnreq, tninreq, tnoutreq; char recv[512], snd[512], recvbuf[512], tcpinbuf[512]; struct telnet *tn; #ifdef LATTICE extern struct { short error; char *msg; } os_errlist[]; extern int _OSERR, os_nerr; #endif static /* * wait for something to happen */ eihalt() { register struct IntuiMessage *msg; static ULONG mask = 0; if (mask == 0L) mask = 1L << consinp->mp_SigBit | 1L << tcpinp->mp_SigBit; (void) Wait(mask); } clean(why) char *why; { int i; if (win) CloseWindow(win); if (consinp) DeletePort(consinp); if (consoutp) DeletePort(consoutp); if (tcpinp) DeletePort(tcpinp); if (tcpoutp) DeletePort(tcpoutp); if (deviceopened) CloseDevice(&tnreq); mytask->pr_WindowPtr = oldwindowptr; if (why) { myoserr(why); } exit(0); } printlist(l) struct List *l; { printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, l->lh_TailPred); } myoserr(why) char *why; { int i; fprintf(stderr, "%s: ", why); #ifdef LATTICE fprintf(stderr, "%d: ", _OSERR); for(i = 0; os_errlist[i].error < os_nerr; i++) if (os_errlist[i].error == _OSERR) fprintf(stderr, os_errlist[i].msg); #endif fprintf(stderr, "\r\n"); } /* Called at startup time to set up console I/O, memory heap */ ioinit() { struct Screen *scr; mytask = (struct Process *) FindTask((char *) NULL); oldwindowptr = mytask->pr_WindowPtr; mytask->pr_WindowPtr = (APTR) -1; /* disable DOS requestors */ if ((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 33L)) == NULL) clean("No intuition: Version 1.2 of Amiga Systems Software required"); /* * Try to determine the size of the workbench screen */ scr = malloc(sizeof(struct Screen)); if (scr==NULL) clean("Can't alloc screen"); if (GetScreenData(scr, (ULONG) sizeof(struct Screen), WBENCHSCREEN, NULL) == TRUE) { nw.Width = scr->Width; nw.Height = scr->Height-20; nw.TopEdge = 19; } else fprintf(stderr, "Can't GetScreenData()\n"); free((char *)scr); if ((win = OpenWindow(&nw)) == NULL) clean("Can't open window"); if ((consinp = CreatePort("telnet:console in", 0L)) == NULL) clean("Can't create console port"); if ((tcpinp = CreatePort("telnet:tcp in", 0L)) == NULL) clean("Can't create telnet tcp input port"); if ((tcpoutp = CreatePort("telnet:tcp out", 0L)) == NULL) clean("Can't create telnet tcp output port"); consin.io_Data = (APTR) win; consin.io_Length = sizeof(struct Window); _OSERR = OpenDevice("console.device", 0L, &consin, 0L); if (_OSERR != 0L){ printf("opendevice returned %d\n", _OSERR); myoserr("could not get console"); clean("Can't open console device"); } consout = consin; consin.io_Message.mn_ReplyPort = consinp; consin.io_Length = 512; consin.io_Data = (APTR) recvbuf; consin.io_Command = CMD_READ; SendIO(&consin); consout.io_Message.mn_ReplyPort = consoutp; consout.io_Command = CMD_WRITE; } /* Read characters from the keyboard, translating them to "real" ASCII * If none are ready, return the -1 from kbraw() */ kbread() { char c; int amount; if (CheckIO(&consin)) { WaitIO(&consin); amount = consin.io_Actual; if (amount > 512) amount = 511; strncpy(recv, recvbuf, amount); SendIO(&consin); return (amount); } return -1; /* nuthin here */ } extern char nospace[]; int refuse_echo = 0; int unix_line_mode = 0; /* if true turn to when in line mode */ #ifdef DEBUG char *t_options[] = { "Transmit Binary", "Echo", "", "Suppress Go Ahead", "", "Status", "Timing Mark" }; #endif /* Telnet receiver upcall routine */ void rcv_char() { tel_input(tn,tninreq.io_Data, tninreq.io_Actual); fflush(stdout); } brk() { clean("ok i iwll quit\n"); } /* TCP connection states */ char *tcpstates[] = { "Closed", "Listen", "SYN sent", "SYN received", "Established", "FIN wait 1", "FIN wait 2", "Close wait", "Closing", "Last ACK", "Time wait" }; /* TCP segment header flags */ char *tcpflags[] = { "FIN", /* 0x01 */ "SYN", /* 0x02 */ "RST", /* 0x04 */ "PSH", /* 0x08 */ "ACK", /* 0x10 */ "URG" /* 0x20 */ }; /* TCP closing reasons */ char *reasons[] = { "Normal", "Reset", "Timeout", "ICMP" }; char old = LISTEN; int done = 0; char *hostname=""; int hostport=0; char *bannerfmt = "telnet %10s %4d %10s"; void showstate(old, new) char old, new; { /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ sprintf(banner, bannerfmt, hostname, hostport, tcpstates[new]); SetWindowTitles(win, banner, -1); switch(new){ case CLOSE_WAIT: done = 1; break; case CLOSED: /* court adjourned */ done = 1; break; default: break; } fflush(stdout); } /* Execute user telnet command */ main(argc,argv) int argc; char *argv[]; { extern int _OSERR; struct InternetBase *InternetBase; int send_tel(); int unix_send_tel(); struct session *s; int amount; struct socket lsocket,fsocket; ioinit(); hostname = argv[1]; old = LISTEN; showstate(old, LISTEN); tnreq.io_fsocket.address = aton(argv[1]); if(argc < 3) tnreq.io_fsocket.port = TELNET_PORT; else tnreq.io_fsocket.port = atoi(argv[2]); tnreq.io_Device = NULL; tnreq.io_Unit = NULL; tnreq.io_Flags = 0; tnreq.io_Error = 0; InternetBase = (struct InternetBase *) OpenDevice("internet.device", (long) INET_UNIT_TCP, &tnreq, 0L); if (InternetBase != 0L){ printf("it did not open %d\n",InternetBase); clean("i quit"); } hostport = tnreq.io_lsocket.port; deviceopened = 1; tninreq = tnreq; /* possible lettuce bug ?*/ tnoutreq = tnreq; tninreq.io_Length = 512; tnoutreq.io_Length = 1; tninreq.io_Data = tcpinbuf; tnoutreq.io_Data = snd; tnoutreq.io_Length = 0; tninreq.io_Command = CMD_READ; tnoutreq.io_Command = CMD_WRITE; tninreq.io_Message.mn_ReplyPort = tcpinp; tnoutreq.io_Message.mn_ReplyPort = tcpoutp; /* Create and initialize a Telnet protocol descriptor */ if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){ myoserr("calloc faiuled\n"); goto done; } tn->session = s; /* Upward pointer */ tn->state = TS_DATA; SendIO(&tninreq); onbreak(&brk); InputCharacter = ' '; while (! done) { eihalt(); if ((amount = kbread()) >= 0){ unix_send_tel(recv, amount);} if (CheckIO(&tninreq)) { chkabort(); WaitIO(&tninreq); rcv_char(); if (tninreq.io_State != old) { showstate(old, tninreq.io_State); old = tninreq.io_State; } SendIO(&tninreq); } } done: clean("All done"); } /* Process typed characters */ int unix_send_tel(buf,n) char *buf; int16 n; { int i; for (i=0; (istate == TS_DATA){ while(bp != NULLBUF && memchr(bp->data,IAC,bp->cnt) == NULLCHAR){ fflush(stdout); write(1,bp->data,bp->cnt); bp = free_mbuf(bp); } } #endif while(len--){ c = *bp++; ci = c & 0xff; switch(tn->state){ case TS_DATA: if(ci == IAC){ tn->state = TS_IAC; } else { if(!tn->remote[TN_TRANSMIT_BINARY]) c &= 0x7f; putchar(c); } break; case TS_IAC: switch(ci){ case WILL: tn->state = TS_WILL; break; case WONT: tn->state = TS_WONT; break; case DO: tn->state = TS_DO; break; case DONT: tn->state = TS_DONT; break; case IAC: putchar(c); tn->state = TS_DATA; break; default: tn->state = TS_DATA; break; } break; case TS_WILL: willopt(tn,ci); tn->state = TS_DATA; break; case TS_WONT: wontopt(tn,ci); tn->state = TS_DATA; break; case TS_DO: doopt(tn,ci); tn->state = TS_DATA; break; case TS_DONT: dontopt(tn,ci); tn->state = TS_DATA; break; } } } #ifdef NOTDEF /* State change upcall routine */ void t_state(tcb,old,new) register struct tcb *tcb; char old,new; { struct telnet *tn; char notify = 0; extern char *tcpstates[]; extern char *reasons[]; extern char *unreach[]; extern char *exceed[]; /* Can't add a check for unknown connection here, it would loop * on a close upcall! We're just careful later on. */ tn = (struct telnet *)tcb->user; if(current != NULLSESSION && current->type == TELNET && current->cb.telnet == tn) notify = 1; switch(new){ case CLOSE_WAIT: if(notify) printf("%s\r\n",tcpstates[new]); close_tcp(tcb); break; case CLOSED: /* court adjourned */ if(notify){ printf("%s (%s",tcpstates[new],reasons[tcb->reason]); if(tcb->reason == NETWORK){ switch(tcb->type){ case DEST_UNREACH: printf(": %s unreachable",unreach[tcb->code]); break; case TIME_EXCEED: printf(": %s time exceeded",exceed[tcb->code]); break; } } printf(")\r\n"); cmdmode(); } del_tcp(tcb); if(tn != NULLTN) free_telnet(tn); break; default: if(notify) printf("%s\r\n",tcpstates[new]); break; } fflush(stdout); } #endif /* Delete telnet structure */ static free_telnet(tn) struct telnet *tn; { if(tn != NULLTN) free((char *)tn); } /* The guts of the actual Telnet protocol: negotiating options */ static void willopt(tn,opt) struct telnet *tn; int opt; { int ack; void answer(); #ifdef DEBUG printf("recv: will "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif switch(opt){ case TN_TRANSMIT_BINARY: case TN_ECHO: case TN_SUPPRESS_GA: if(tn->remote[opt] == 1) return; /* Already set, ignore to prevent loop */ if(opt == TN_ECHO){ if(refuse_echo){ /* User doesn't want to accept */ ack = DONT; break; } else raw(); /* Put tty into raw mode */ } tn->remote[opt] = 1; ack = DO; break; default: ack = DONT; /* We don't know what he's offering; refuse */ } answer(tn,ack,opt); } static void wontopt(tn,opt) struct telnet *tn; int opt; { void answer(); #ifdef DEBUG printf("recv: wont "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif if(opt <= NOPTIONS){ if(tn->remote[opt] == 0) return; /* Already clear, ignore to prevent loop */ tn->remote[opt] = 0; if(opt == TN_ECHO) cooked(); /* Put tty into cooked mode */ } answer(tn,DONT,opt); /* Must always accept */ } static void doopt(tn,opt) struct telnet *tn; int opt; { void answer(); int ack; #ifdef DEBUG printf("recv: do "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif switch(opt){ #ifdef FUTURE /* Use when local options are implemented */ if(tn->local[opt] == 1) return; /* Already set, ignore to prevent loop */ tn->local[opt] = 1; ack = WILL; break; #endif default: ack = WONT; /* Don't know what it is */ } answer(tn,ack,opt); } static void dontopt(tn,opt) struct telnet *tn; int opt; { void answer(); #ifdef DEBUG printf("recv: dont "); if(opt <= NOPTIONS) printf("%s\r\n",t_options[opt]); else printf("%u\r\n",opt); #endif if(opt <= NOPTIONS){ if(tn->local[opt] == 0){ /* Already clear, ignore to prevent loop */ return; } tn->local[opt] = 0; } answer(tn,WONT,opt); } static void answer(tn,r1,r2) struct telnet *tn; int r1,r2; { struct mbuf *bp,*qdata(); char s[3]; #ifdef DEBUG switch(r1){ case WILL: printf("sent: will "); break; case WONT: printf("sent: wont "); break; case DO: printf("sent: do "); break; case DONT: printf("sent: dont "); break; } if(r2 <= 6) printf("%s\r\n",t_options[r2]); else printf("%u\r\n",r2); #endif s[0] = IAC; s[1] = r1; s[2] = r2; tnoutreq.io_Data = s; tnoutreq.io_Length = 3; DoIO(&tnoutreq); /* bp = qdata(s,(int16)3); send_tcp(tn->tcb,bp); */ } #define BUFMAXCNT 150 static char conbuf[BUFMAXCNT]; static int concnt = 0; int amigaputchar(c) char c; { conbuf[concnt++] = c; if ((c == '\n') || (concnt == BUFMAXCNT)) amigaflush(); return c; } amigaflush() { if (concnt == 0) return; consout.io_Data = (APTR) conbuf; consout.io_Length = concnt; consout.io_Command = CMD_WRITE; DoIO(&consout); concnt = 0; } /* * Begin terrible, horrible hack. All output should be printed upon (into?) * the window we opened before. Here goes nothing... */ printf(a, b, c, d, e, f, g, h, i, j, k) char *a; int b, c, d, e, f, g, h, i, j, k; { if (concnt) amigaflush(); sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k); consout.io_Data = (APTR) conbuf; consout.io_Length = strlen(conbuf); consout.io_Command = CMD_WRITE; DoIO(&consout); /* no use in doing this async */ }