/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ typedef struct Opt Opt; int debug;#define DPRINT if(debug)fprint enum{ /* control characters */ Se= 240, /* end subnegotiation */ NOP= 241, Mark= 242, /* data mark */ Break= 243, Interrupt= 244, Abort= 245, /* TENEX ^O */ AreYouThere= 246, Erasechar= 247, /* erase last character */ Eraseline= 248, /* erase line */ GoAhead= 249, /* half duplex clear to send */ Sb= 250, /* start subnegotiation */ Will= 251, Wont= 252, Do= 253, Dont= 254, Iac= 255, /* options */ Binary= 0, Echo, SGA, Stat, Timing, Det, Term, EOR, Uid, Outmark, Ttyloc, M3270, Padx3, Window, Speed, Flow, Line, Xloc, Extend,}; struct Opt{ char *name; int code; char noway; int (*change)(Biobuf*, int); /* routine for status change */ int (*sub)(Biobuf*, uchar*, int n); /* routine for subnegotiation */ char remote; /* remote value */ char local; /* local value */}; Opt opt[] ={[Binary] { "binary", 0, 0, },[Echo] { "echo", 1, 0, },[SGA] { "suppress Go Ahead", 3, 0, },[Stat] { "status", 5, 1, },[Timing] { "timing", 6, 1, },[Det] { "det", 20, 1, },[Term] { "terminal", 24, 0, },[EOR] { "end of record", 25, 1, },[Uid] { "uid", 26, 1, },[Outmark] { "outmark", 27, 1, },[Ttyloc] { "ttyloc", 28, 1, },[M3270] { "3270 mode", 29, 1, },[Padx3] { "pad x.3", 30, 1, },[Window] { "window size", 31, 1, },[Speed] { "speed", 32, 1, },[Flow] { "flow control", 33, 1, },[Line] { "line mode", 34, 1, },[Xloc] { "X display loc", 35, 0, },[Extend] { "Extended", 255, 1, },}; int control(Biobuf*, int);Opt* findopt(int);int will(Biobuf*);int wont(Biobuf*);int doit(Biobuf*);int dont(Biobuf*);int sub(Biobuf*);int send2(int, int, int);int send3(int, int, int, int);int sendnote(int, char*);void fatal(char*, void*, void*);char* syserr(void);int wasintr(void);long iread(int, void*, int);long iwrite(int, void*, int);void binit(Biobuf*, int);void berase(Biobuf*);void bkill(Biobuf*); /* * parse telnet control messages */intcontrol(Biobuf *bp, int c){ if(c < 0) return -1; switch(c){ case AreYouThere: fprint(Bfildes(bp), "Plan 9 telnet, version 1\r\n"); break; case Sb: return sub(bp); case Will: return will(bp); case Wont: return wont(bp); case Do: return doit(bp); case Dont: return dont(bp); case Se: fprint(2, "telnet: SE without an SB\n"); break; default: break; } return 0;} Opt*findopt(int c){ Opt *o; for(o = opt; o <= &opt[Extend]; o++) if(o->code == c) return o; return 0;} intwill(Biobuf *bp){ Opt *o; int c; int rv = 0; c = Bgetc(bp); if(c < 0) return -1; DPRINT(2, "will %d\n", c); o = findopt(c); if(o == 0){ send3(Bfildes(bp), Iac, Dont, c); return 0; } if(o->noway) send3(Bfildes(bp), Iac, Dont, c); else if(o->remote == 0) rv |= send3(Bfildes(bp), Iac, Do, c); if(o->remote == 0){ if(o->change) rv |= (*o->change)(bp, Will); } o->remote = 1; return rv;} intwont(Biobuf *bp){ Opt *o; int c; int rv = 0; c = Bgetc(bp); if(c < 0) return -1; DPRINT(2, "wont %d\n", c); o = findopt(c); if(o == 0) return 0; if(o->remote){ if(o->change) rv |= (*o->change)(bp, Wont); rv |= send3(Bfildes(bp), Iac, Dont, c); } o->remote = 0; return rv;} intdoit(Biobuf *bp){ Opt *o; int c; int rv = 0; c = Bgetc(bp); if(c < 0) return -1; DPRINT(2, "do %d\n", c); o = findopt(c); if(o == 0 || o->noway){ send3(Bfildes(bp), Iac, Wont, c); return 0; } if(o->noway) return 0; if(o->local == 0){ if(o->change) rv |= (*o->change)(bp, Do); rv |= send3(Bfildes(bp), Iac, Will, c); } o->local = 1; return rv;} intdont(Biobuf *bp){ Opt *o; int c; int rv = 0; c = Bgetc(bp); if(c < 0) return -1; DPRINT(2, "dont %d\n", c); o = findopt(c); if(o == 0) return 0; if(o->noway) return 0; if(o->local){ o->local = 0; if(o->change) rv |= (*o->change)(bp, Dont); rv |= send3(Bfildes(bp), Iac, Wont, c); } o->local = 0; return rv;} /* read in a subnegotiation message and pass it to a routine for that option */intsub(Biobuf *bp){ uchar subneg[128]; uchar *p; Opt *o; int c; p = subneg; for(;;){ c = Bgetc(bp); if(c == Iac){ c = Bgetc(bp); if(c == Se) break; if(p < &subneg[sizeof(subneg)]) *p++ = Iac; } if(c < 0) return -1; if(p < &subneg[sizeof(subneg)]) *p++ = c; } if(p == subneg) return 0; DPRINT(2, "sub %d %d n = %d\n", subneg[0], subneg[1], (int)(p - subneg - 1)); o = findopt(subneg[0]); if(o == 0 || o->sub == 0) return 0; return (*o->sub)(bp, subneg+1, p - subneg - 1);} voidsendd(int c0, int c1){ char *t = 0; switch(c0){ case Will: t = "Will"; break; case Wont: t = "Wont"; break; case Do: t = "Do"; break; case Dont: t = "Dont"; break; } if(t) DPRINT(2, "r %s %d\n", t, c1);} intsend2(int f, int c0, int c1){ uchar buf[2]; buf[0] = c0; buf[1] = c1; return iwrite(f, buf, 2) == 2 ? 0 : -1;} intsend3(int f, int c0, int c1, int c2){ uchar buf[3]; buf[0] = c0; buf[1] = c1; buf[2] = c2; sendd(c1, c2); return iwrite(f, buf, 3) == 3 ? 0 : -1;} intsendnote(int pid, char *msg){ int fd; char name[128]; sprint(name, "/proc/%d/note", pid); fd = open(name, OWRITE); if(fd < 0) return -1; if(write(fd, msg, strlen(msg))!=strlen(msg)) return -1; return close(fd);} voidfatal(char *fmt, void *a0, void *a1){ char buf[128]; sprint(buf, fmt, a0, a1); fprint(2, "%s: %s\n", argv0, buf); exits(buf);} char*syserr(void){ static char err[ERRMAX]; errstr(err, sizeof err); return err;} intwasintr(void){ return strcmp(syserr(), "interrupted") == 0;} longiread(int f, void *a, int n){ long m; for(;;){ m = read(f, a, n); if(m >= 0 || !wasintr()) break; } return m;} longiwrite(int f, void *a, int n){ long m; m = write(f, a, n); if(m < 0 && wasintr()) return n; return m;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
typedef struct Opt Opt;
int debug;
#define DPRINT if(debug)fprint
enum
{
/* control characters */
Se= 240, /* end subnegotiation */
NOP= 241,
Mark= 242, /* data mark */
Break= 243,
Interrupt= 244,
Abort= 245, /* TENEX ^O */
AreYouThere= 246,
Erasechar= 247, /* erase last character */
Eraseline= 248, /* erase line */
GoAhead= 249, /* half duplex clear to send */
Sb= 250, /* start subnegotiation */
Will= 251,
Wont= 252,
Do= 253,
Dont= 254,
Iac= 255,
/* options */
Binary= 0,
Echo,
SGA,
Stat,
Timing,
Det,
Term,
EOR,
Uid,
Outmark,
Ttyloc,
M3270,
Padx3,
Window,
Speed,
Flow,
Line,
Xloc,
Extend,
};
struct Opt
{
char *name;
int code;
char noway;
int (*change)(Biobuf*, int); /* routine for status change */
int (*sub)(Biobuf*, uchar*, int n); /* routine for subnegotiation */
char remote; /* remote value */
char local; /* local value */
};
Opt opt[] =
{
[Binary] { "binary", 0, 0, },
[Echo] { "echo", 1, 0, },
[SGA] { "suppress Go Ahead", 3, 0, },
[Stat] { "status", 5, 1, },
[Timing] { "timing", 6, 1, },
[Det] { "det", 20, 1, },
[Term] { "terminal", 24, 0, },
[EOR] { "end of record", 25, 1, },
[Uid] { "uid", 26, 1, },
[Outmark] { "outmark", 27, 1, },
[Ttyloc] { "ttyloc", 28, 1, },
[M3270] { "3270 mode", 29, 1, },
[Padx3] { "pad x.3", 30, 1, },
[Window] { "window size", 31, 1, },
[Speed] { "speed", 32, 1, },
[Flow] { "flow control", 33, 1, },
[Line] { "line mode", 34, 1, },
[Xloc] { "X display loc", 35, 0, },
[Extend] { "Extended", 255, 1, },
};
int control(Biobuf*, int);
Opt* findopt(int);
int will(Biobuf*);
int wont(Biobuf*);
int doit(Biobuf*);
int dont(Biobuf*);
int sub(Biobuf*);
int send2(int, int, int);
int send3(int, int, int, int);
int sendnote(int, char*);
void fatal(char*, void*, void*);
char* syserr(void);
int wasintr(void);
long iread(int, void*, int);
long iwrite(int, void*, int);
void binit(Biobuf*, int);
void berase(Biobuf*);
void bkill(Biobuf*);
/*
* parse telnet control messages
*/
int
control(Biobuf *bp, int c)
{
if(c < 0)
return -1;
switch(c){
case AreYouThere:
fprint(Bfildes(bp), "Plan 9 telnet, version 1\r\n");
break;
case Sb:
return sub(bp);
case Will:
return will(bp);
case Wont:
return wont(bp);
case Do:
return doit(bp);
case Dont:
return dont(bp);
case Se:
fprint(2, "telnet: SE without an SB\n");
break;
default:
break;
}
return 0;
}
Opt*
findopt(int c)
{
Opt *o;
for(o = opt; o <= &opt[Extend]; o++)
if(o->code == c)
return o;
return 0;
}
int
will(Biobuf *bp)
{
Opt *o;
int c;
int rv = 0;
c = Bgetc(bp);
if(c < 0)
return -1;
DPRINT(2, "will %d\n", c);
o = findopt(c);
if(o == 0){
send3(Bfildes(bp), Iac, Dont, c);
return 0;
}
if(o->noway)
send3(Bfildes(bp), Iac, Dont, c);
else if(o->remote == 0)
rv |= send3(Bfildes(bp), Iac, Do, c);
if(o->remote == 0){
if(o->change)
rv |= (*o->change)(bp, Will);
}
o->remote = 1;
return rv;
}
int
wont(Biobuf *bp)
{
Opt *o;
int c;
int rv = 0;
c = Bgetc(bp);
if(c < 0)
return -1;
DPRINT(2, "wont %d\n", c);
o = findopt(c);
if(o == 0)
return 0;
if(o->remote){
if(o->change)
rv |= (*o->change)(bp, Wont);
rv |= send3(Bfildes(bp), Iac, Dont, c);
}
o->remote = 0;
return rv;
}
int
doit(Biobuf *bp)
{
Opt *o;
int c;
int rv = 0;
c = Bgetc(bp);
if(c < 0)
return -1;
DPRINT(2, "do %d\n", c);
o = findopt(c);
if(o == 0 || o->noway){
send3(Bfildes(bp), Iac, Wont, c);
return 0;
}
if(o->noway)
return 0;
if(o->local == 0){
if(o->change)
rv |= (*o->change)(bp, Do);
rv |= send3(Bfildes(bp), Iac, Will, c);
}
o->local = 1;
return rv;
}
int
dont(Biobuf *bp)
{
Opt *o;
int c;
int rv = 0;
c = Bgetc(bp);
if(c < 0)
return -1;
DPRINT(2, "dont %d\n", c);
o = findopt(c);
if(o == 0)
return 0;
if(o->noway)
return 0;
if(o->local){
o->local = 0;
if(o->change)
rv |= (*o->change)(bp, Dont);
rv |= send3(Bfildes(bp), Iac, Wont, c);
}
o->local = 0;
return rv;
}
/* read in a subnegotiation message and pass it to a routine for that option */
int
sub(Biobuf *bp)
{
uchar subneg[128];
uchar *p;
Opt *o;
int c;
p = subneg;
for(;;){
c = Bgetc(bp);
if(c == Iac){
c = Bgetc(bp);
if(c == Se)
break;
if(p < &subneg[sizeof(subneg)])
*p++ = Iac;
}
if(c < 0)
return -1;
if(p < &subneg[sizeof(subneg)])
*p++ = c;
}
if(p == subneg)
return 0;
DPRINT(2, "sub %d %d n = %d\n", subneg[0], subneg[1], (int)(p - subneg - 1));
o = findopt(subneg[0]);
if(o == 0 || o->sub == 0)
return 0;
return (*o->sub)(bp, subneg+1, p - subneg - 1);
}
void
sendd(int c0, int c1)
{
char *t = 0;
switch(c0){
case Will:
t = "Will";
break;
case Wont:
t = "Wont";
break;
case Do:
t = "Do";
break;
case Dont:
t = "Dont";
break;
}
if(t)
DPRINT(2, "r %s %d\n", t, c1);
}
int
send2(int f, int c0, int c1)
{
uchar buf[2];
buf[0] = c0;
buf[1] = c1;
return iwrite(f, buf, 2) == 2 ? 0 : -1;
}
int
send3(int f, int c0, int c1, int c2)
{
uchar buf[3];
buf[0] = c0;
buf[1] = c1;
buf[2] = c2;
sendd(c1, c2);
return iwrite(f, buf, 3) == 3 ? 0 : -1;
}
int
sendnote(int pid, char *msg)
{
int fd;
char name[128];
sprint(name, "/proc/%d/note", pid);
fd = open(name, OWRITE);
if(fd < 0)
return -1;
if(write(fd, msg, strlen(msg))!=strlen(msg))
return -1;
return close(fd);
}
void
fatal(char *fmt, void *a0, void *a1)
{
char buf[128];
sprint(buf, fmt, a0, a1);
fprint(2, "%s: %s\n", argv0, buf);
exits(buf);
}
char*
syserr(void)
{
static char err[ERRMAX];
errstr(err, sizeof err);
return err;
}
int
wasintr(void)
{
return strcmp(syserr(), "interrupted") == 0;
}
long
iread(int f, void *a, int n)
{
long m;
for(;;){
m = read(f, a, n);
if(m >= 0 || !wasintr())
break;
}
return m;
}
long
iwrite(int f, void *a, int n)
{
long m;
m = write(f, a, n);
if(m < 0 && wasintr())
return n;
return m;
}