diff --git a/contrib/telnet/telnet/commands.c b/contrib/telnet/telnet/commands.c index f6bece2aa32a..dd83669d1d9b 100644 --- a/contrib/telnet/telnet/commands.c +++ b/contrib/telnet/telnet/commands.c @@ -1,3038 +1,3164 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static const char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; #endif /* not lint */ #if defined(unix) #include #if defined(CRAY) || defined(sysV88) #include #endif #include #else #include #endif /* defined(unix) */ #include #include #ifdef CRAY #include #endif /* CRAY */ #include #include #include #include #include #include #include #include #include #include "general.h" #include "ring.h" #include "externs.h" #include "defines.h" #include "types.h" #if defined(AUTHENTICATION) #include #endif #if defined(ENCRYPTION) #include #endif #if !defined(CRAY) && !defined(sysV88) #include # if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix) # include # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include +#include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif MAXHOSTNAMELEN #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ char *hostname; static char _hostname[MAXHOSTNAMELEN]; extern char *getenv(); extern int isprefix(); extern char **genget(); extern int Ambiguous(); static int help(int argc, char *argv[]); static int call(); static void cmdrc(char *m1, char *m2); int quit(void); typedef struct { char *name; /* command name */ char *help; /* help string (NULL for no help) */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ } Command; static char line[256]; static char saveline[256]; static int margc; static char *margv[20]; #if defined(SKEY) #include #define PATH_SKEY "/usr/bin/key" int skey_calc(argc, argv) int argc; char **argv; { int status; if(argc != 3) { printf("%s sequence challenge\n", argv[0]); return; } switch(fork()) { case 0: execv(PATH_SKEY, argv); exit (1); case -1: perror("fork"); break; default: (void) wait(&status); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (0); } } #endif static void makeargv() { register char *cp, *cp2, c; register char **argp = margv; margc = 0; cp = line; if (*cp == '!') { /* Special case shell escape */ strcpy(saveline, line); /* save for shell command */ *argp++ = "!"; /* No room in string to get this */ margc++; cp++; } while ((c = *cp)) { register int inquote = 0; while (isspace(c)) c = *++cp; if (c == '\0') break; *argp++ = cp; margc += 1; for (cp2 = cp; c != '\0'; c = *++cp) { if (inquote) { if (c == inquote) { inquote = 0; continue; } } else { if (c == '\\') { if ((c = *++cp) == '\0') break; } else if (c == '"') { inquote = '"'; continue; } else if (c == '\'') { inquote = '\''; continue; } else if (isspace(c)) break; } *cp2++ = c; } *cp2 = '\0'; if (c == '\0') break; cp++; } *argp++ = 0; } /* * Make a character string into a number. * * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). */ static int special(s) register char *s; { register char c; char b; switch (*s) { case '^': b = *++s; if (b == '?') { c = b | 0x40; /* DEL */ } else { c = b & 0x1f; } break; default: c = *s; break; } return c; } /* * Construct a control character sequence * for a special character. */ static char * control(c) register cc_t c; { static char buf[5]; /* * The only way I could get the Sun 3.5 compiler * to shut up about * if ((unsigned int)c >= 0x80) * was to assign "c" to an unsigned int variable... * Arggg.... */ register unsigned int uic = (unsigned int)c; if (uic == 0x7f) return ("^?"); if (c == (cc_t)_POSIX_VDISABLE) { return "off"; } if (uic >= 0x80) { buf[0] = '\\'; buf[1] = ((c>>6)&07) + '0'; buf[2] = ((c>>3)&07) + '0'; buf[3] = (c&07) + '0'; buf[4] = 0; } else if (uic >= 0x20) { buf[0] = c; buf[1] = 0; } else { buf[0] = '^'; buf[1] = '@'+c; buf[2] = 0; } return (buf); } /* * The following are data structures and routines for * the "send" command. * */ struct sendlist { char *name; /* How user refers to it (case independent) */ char *help; /* Help information (0 ==> no help) */ int needconnect; /* Need to be connected */ int narg; /* Number of arguments */ int (*handler)(); /* Routine to perform (for special ops) */ int nbyte; /* Number of bytes to send this command */ int what; /* Character to be sent (<0 ==> special) */ }; static int send_esc P((void)), send_help P((void)), send_docmd P((char *)), send_dontcmd P((char *)), send_willcmd P((char *)), send_wontcmd P((char *)); static struct sendlist Sendlist[] = { { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, { "break", 0, 1, 0, 0, 2, BREAK }, { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, { "intp", 0, 1, 0, 0, 2, IP }, { "interrupt", 0, 1, 0, 0, 2, IP }, { "intr", 0, 1, 0, 0, 2, IP }, { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, { "?", "Display send options", 0, 0, send_help, 0, 0 }, { "help", 0, 0, 0, send_help, 0, 0 }, { "do", 0, 0, 1, send_docmd, 3, 0 }, { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, { "will", 0, 0, 1, send_willcmd, 3, 0 }, { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, { 0 } }; #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ sizeof(struct sendlist))) static int sendcmd(argc, argv) int argc; char **argv; { int count; /* how many bytes we are going to need to send */ int i; struct sendlist *s; /* pointer to current command */ int success = 0; int needconnect = 0; if (argc < 2) { printf("need at least one argument for 'send' command\n"); printf("'send ?' for help\n"); return 0; } /* * First, validate all the send arguments. * In addition, we see how much space we are going to need, and * whether or not we will be doing a "SYNCH" operation (which * flushes the network queue). */ count = 0; for (i = 1; i < argc; i++) { s = GETSEND(argv[i]); if (s == 0) { printf("Unknown send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } else if (Ambiguous(s)) { printf("Ambiguous send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } if (i + s->narg >= argc) { fprintf(stderr, "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n", s->narg, s->narg == 1 ? "" : "s", s->name, s->name); return 0; } count += s->nbyte; if (s->handler == send_help) { send_help(); return 0; } i += s->narg; needconnect += s->needconnect; } if (!connected && needconnect) { printf("?Need to be connected first.\n"); printf("'send ?' for help\n"); return 0; } /* Now, do we have enough room? */ if (NETROOM() < count) { printf("There is not enough room in the buffer TO the network\n"); printf("to process your request. Nothing will be done.\n"); printf("('send synch' will throw away most data in the network\n"); printf("buffer, if this might help.)\n"); return 0; } /* OK, they are all OK, now go through again and actually send */ count = 0; for (i = 1; i < argc; i++) { if ((s = GETSEND(argv[i])) == 0) { fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); (void) quit(); /*NOTREACHED*/ } if (s->handler) { count++; success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0, (s->narg > 1) ? argv[i+2] : 0); i += s->narg; } else { NET2ADD(IAC, s->what); printoption("SENT", IAC, s->what); } } return (count == success); } static int send_esc() { NETADD(escape); return 1; } static int send_docmd(name) char *name; { return(send_tncmd(send_do, "do", name)); } static int send_dontcmd(name) char *name; { return(send_tncmd(send_dont, "dont", name)); } static int send_willcmd(name) char *name; { return(send_tncmd(send_will, "will", name)); } static int send_wontcmd(name) char *name; { return(send_tncmd(send_wont, "wont", name)); } int send_tncmd(func, cmd, name) void (*func)(); char *cmd, *name; { char **cpp; extern char *telopts[]; register int val = 0; if (isprefix(name, "help") || isprefix(name, "?")) { register int col, len; printf("Usage: send %s \n", cmd); printf("\"value\" must be from 0 to 255\n"); printf("Valid options are:\n\t"); col = 8; for (cpp = telopts; *cpp; cpp++) { len = strlen(*cpp) + 3; if (col + len > 65) { printf("\n\t"); col = 8; } printf(" \"%s\"", *cpp); col += len; } printf("\n"); return 0; } cpp = (char **)genget(name, telopts, sizeof(char *)); if (Ambiguous(cpp)) { fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n", name, cmd); return 0; } if (cpp) { val = cpp - telopts; } else { register char *cp = name; while (*cp >= '0' && *cp <= '9') { val *= 10; val += *cp - '0'; cp++; } if (*cp != 0) { fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n", name, cmd); return 0; } else if (val < 0 || val > 255) { fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n", name, cmd); return 0; } } if (!connected) { printf("?Need to be connected first.\n"); return 0; } (*func)(val, 1); return 1; } static int send_help() { struct sendlist *s; /* pointer to current command */ for (s = Sendlist; s->name; s++) { if (s->help) printf("%-15s %s\n", s->name, s->help); } return(0); } /* * The following are the routines and data structures referred * to by the arguments to the "toggle" command. */ static int lclchars() { donelclchars = 1; return 1; } static int togdebug() { #ifndef NOT43 if (net > 0 && (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { perror("setsockopt (SO_DEBUG)"); } #else /* NOT43 */ if (debug) { if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) perror("setsockopt (SO_DEBUG)"); } else printf("Cannot turn off socket debugging\n"); #endif /* NOT43 */ return 1; } static int togcrlf() { if (crlf) { printf("Will send carriage returns as telnet .\n"); } else { printf("Will send carriage returns as telnet .\n"); } return 1; } int binmode; static int togbinary(val) int val; { donebinarytoggle = 1; if (val >= 0) { binmode = val; } else { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { binmode = 1; } else if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { binmode = 0; } val = binmode ? 0 : 1; } if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { printf("Already operating in binary mode with remote host.\n"); } else { printf("Negotiating binary mode with remote host.\n"); tel_enter_binary(3); } } else { if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { printf("Already in network ascii mode with remote host.\n"); } else { printf("Negotiating network ascii mode with remote host.\n"); tel_leave_binary(3); } } return 1; } static int togrbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_do(TELOPT_BINARY)) { printf("Already receiving in binary mode.\n"); } else { printf("Negotiating binary mode on input.\n"); tel_enter_binary(1); } } else { if (my_want_state_is_dont(TELOPT_BINARY)) { printf("Already receiving in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on input.\n"); tel_leave_binary(1); } } return 1; } static int togxbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY)) { printf("Already transmitting in binary mode.\n"); } else { printf("Negotiating binary mode on output.\n"); tel_enter_binary(2); } } else { if (my_want_state_is_wont(TELOPT_BINARY)) { printf("Already transmitting in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on output.\n"); tel_leave_binary(2); } } return 1; } static int togglehelp P((void)); #if defined(AUTHENTICATION) extern int auth_togdebug P((int)); #endif #ifdef ENCRYPTION extern int EncryptAutoEnc P((int)); extern int EncryptAutoDec P((int)); extern int EncryptDebug P((int)); extern int EncryptVerbose P((int)); #endif /* ENCRYPTION */ struct togglelist { char *name; /* name of toggle */ char *help; /* help message */ int (*handler)(); /* routine to do actual setting */ int *variable; char *actionexplanation; }; static struct togglelist Togglelist[] = { { "autoflush", "flushing of output when sending interrupt characters", 0, &autoflush, "flush output when sending interrupt characters" }, { "autosynch", "automatic sending of interrupt characters in urgent mode", 0, &autosynch, "send interrupt characters in urgent mode" }, #if defined(AUTHENTICATION) { "autologin", "automatic sending of login and/or authentication info", 0, &autologin, "send login name and/or authentication information" }, { "authdebug", "Toggle authentication debugging", auth_togdebug, 0, "print authentication debugging information" }, #endif #ifdef ENCRYPTION { "autoencrypt", "automatic encryption of data stream", EncryptAutoEnc, 0, "automatically encrypt output" }, { "autodecrypt", "automatic decryption of data stream", EncryptAutoDec, 0, "automatically decrypt input" }, { "verbose_encrypt", "Toggle verbose encryption output", EncryptVerbose, 0, "print verbose encryption output" }, { "encdebug", "Toggle encryption debugging", EncryptDebug, 0, "print encryption debugging information" }, #endif /* ENCRYPTION */ { "skiprc", "don't read ~/.telnetrc file", 0, &skiprc, "skip reading of ~/.telnetrc file" }, { "binary", "sending and receiving of binary data", togbinary, 0, 0 }, { "inbinary", "receiving of binary data", togrbinary, 0, 0 }, { "outbinary", "sending of binary data", togxbinary, 0, 0 }, { "crlf", "sending carriage returns as telnet ", togcrlf, &crlf, 0 }, { "crmod", "mapping of received carriage returns", 0, &crmod, "map carriage return on output" }, { "localchars", "local recognition of certain control characters", lclchars, &localchars, "recognize certain control characters" }, { " ", "", 0 }, /* empty line */ #if defined(unix) && defined(TN3270) { "apitrace", "(debugging) toggle tracing of API transactions", 0, &apitrace, "trace API transactions" }, { "cursesdata", "(debugging) toggle printing of hexadecimal curses data", 0, &cursesdata, "print hexadecimal representation of curses data" }, #endif /* defined(unix) && defined(TN3270) */ { "debug", "debugging", togdebug, &debug, "turn on socket level debugging" }, { "netdata", "printing of hexadecimal network data (debugging)", 0, &netdata, "print hexadecimal representation of network traffic" }, { "prettydump", "output of \"netdata\" to user readable format (debugging)", 0, &prettydump, "print user readable output for \"netdata\"" }, { "options", "viewing of options processing (debugging)", 0, &showoptions, "show option processing" }, #if defined(unix) { "termdata", "(debugging) toggle printing of hexadecimal terminal data", 0, &termdata, "print hexadecimal representation of terminal traffic" }, #endif /* defined(unix) */ { "?", 0, togglehelp }, { "help", 0, togglehelp }, { 0 } }; static int togglehelp() { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s toggle %s\n", c->name, c->help); else printf("\n"); } } printf("\n"); printf("%-15s %s\n", "?", "display help information"); return 0; } static void settogglehelp(set) int set; { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s %s\n", c->name, set ? "enable" : "disable", c->help); else printf("\n"); } } } #define GETTOGGLE(name) (struct togglelist *) \ genget(name, (char **) Togglelist, sizeof(struct togglelist)) static int toggle(argc, argv) int argc; char *argv[]; { int retval = 1; char *name; struct togglelist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); return 0; } argc--; argv++; while (argc--) { name = *argv++; c = GETTOGGLE(name); if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", name); return 0; } else if (c == 0) { fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", name); return 0; } else { if (c->variable) { *c->variable = !*c->variable; /* invert it */ if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) { retval &= (*c->handler)(-1); } } } return retval; } /* * The following perform the "set" command. */ #ifdef USE_TERMIO struct termio new_tc = { 0 }; #endif struct setlist { char *name; /* name */ char *help; /* help information */ void (*handler)(); cc_t *charp; /* where it is located at */ }; static struct setlist Setlist[] = { #ifdef KLUDGELINEMODE { "echo", "character to toggle local echoing on/off", 0, &echoc }, #endif { "escape", "character to escape back to telnet command mode", 0, &escape }, { "rlogin", "rlogin escape character", 0, &rlogin }, { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, { " ", "" }, { " ", "The following need 'localchars' to be toggled true", 0, 0 }, { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp }, { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp }, { "quit", "character to cause an Abort process", 0, termQuitCharp }, { "eof", "character to cause an EOF ", 0, termEofCharp }, { " ", "" }, { " ", "The following are for local editing in linemode", 0, 0 }, { "erase", "character to use to erase a character", 0, termEraseCharp }, { "kill", "character to use to erase a line", 0, termKillCharp }, { "lnext", "character to use for literal next", 0, termLiteralNextCharp }, { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, { "reprint", "character to use for line reprint", 0, termRprntCharp }, { "worderase", "character to use to erase a word", 0, termWerasCharp }, { "start", "character to use for XON", 0, termStartCharp }, { "stop", "character to use for XOFF", 0, termStopCharp }, { "forw1", "alternate end of line character", 0, termForw1Charp }, { "forw2", "alternate end of line character", 0, termForw2Charp }, { "ayt", "alternate AYT character", 0, termAytCharp }, { 0 } }; #if defined(CRAY) && !defined(__STDC__) /* Work around compiler bug in pcc 4.1.5 */ void _setlist_init() { #ifndef KLUDGELINEMODE #define N 5 #else #define N 6 #endif Setlist[N+0].charp = &termFlushChar; Setlist[N+1].charp = &termIntChar; Setlist[N+2].charp = &termQuitChar; Setlist[N+3].charp = &termEofChar; Setlist[N+6].charp = &termEraseChar; Setlist[N+7].charp = &termKillChar; Setlist[N+8].charp = &termLiteralNextChar; Setlist[N+9].charp = &termSuspChar; Setlist[N+10].charp = &termRprntChar; Setlist[N+11].charp = &termWerasChar; Setlist[N+12].charp = &termStartChar; Setlist[N+13].charp = &termStopChar; Setlist[N+14].charp = &termForw1Char; Setlist[N+15].charp = &termForw2Char; Setlist[N+16].charp = &termAytChar; #undef N } #endif /* defined(CRAY) && !defined(__STDC__) */ static struct setlist * getset(name) char *name; { return (struct setlist *) genget(name, (char **) Setlist, sizeof(struct setlist)); } void set_escape_char(s) char *s; { if (rlogin != _POSIX_VDISABLE) { rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet rlogin escape character is '%s'.\n", control(rlogin)); } else { escape = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet escape character is '%s'.\n", control(escape)); } } static int setcmd(argc, argv) int argc; char *argv[]; { int value; struct setlist *ct; struct togglelist *c; if (argc < 2 || argc > 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(1); printf("%-15s %s\n", "?", "display help information"); return 0; } ct = getset(argv[1]); if (ct == 0) { c = GETTOGGLE(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", argv[1]); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } if (c->variable) { if ((argc == 2) || (strcmp("on", argv[2]) == 0)) *c->variable = 1; else if (strcmp("off", argv[2]) == 0) *c->variable = 0; else { printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n"); return 0; } if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(1); } else if (argc != 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } else if (ct->handler) { (*ct->handler)(argv[2]); printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp); } else { if (strcmp("off", argv[2])) { value = special(argv[2]); } else { value = _POSIX_VDISABLE; } *(ct->charp) = (cc_t)value; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } slc_check(); return 1; } static int unsetcmd(argc, argv) int argc; char *argv[]; { struct setlist *ct; struct togglelist *c; register char *name; if (argc < 2) { fprintf(stderr, "Need an argument to 'unset' command. 'unset ?' for help.\n"); return 0; } if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(0); printf("%-15s %s\n", "?", "display help information"); return 0; } argc--; argv++; while (argc--) { name = *argv++; ct = getset(name); if (ct == 0) { c = GETTOGGLE(name); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n", name); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } if (c->variable) { *c->variable = 0; if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(0); } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } else if (ct->handler) { (*ct->handler)(0); printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp); } else { *(ct->charp) = _POSIX_VDISABLE; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } } return 1; } /* * The following are the data structures and routines for the * 'mode' command. */ #ifdef KLUDGELINEMODE extern int kludgelinemode; static int dokludgemode() { kludgelinemode = 1; send_wont(TELOPT_LINEMODE, 1); send_dont(TELOPT_SGA, 1); send_dont(TELOPT_ECHO, 1); return 1; } #endif static int dolinemode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_dont(TELOPT_SGA, 1); #endif send_will(TELOPT_LINEMODE, 1); send_dont(TELOPT_ECHO, 1); return 1; } static int docharmode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_do(TELOPT_SGA, 1); else #endif send_wont(TELOPT_LINEMODE, 1); send_do(TELOPT_ECHO, 1); return 1; } static int dolmmode(bit, on) int bit, on; { unsigned char c; extern int linemode; if (my_want_state_is_wont(TELOPT_LINEMODE)) { printf("?Need to have LINEMODE option enabled first.\n"); printf("'mode ?' for help.\n"); return 0; } if (on) c = (linemode | bit); else c = (linemode & ~bit); lm_mode(&c, 1, 1); return 1; } int setmod(bit) { return dolmmode(bit, 1); } int clearmode(bit) { return dolmmode(bit, 0); } struct modelist { char *name; /* command name */ char *help; /* help string */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ int arg1; }; extern int modehelp(); static struct modelist ModeList[] = { { "character", "Disable LINEMODE option", docharmode, 1 }, #ifdef KLUDGELINEMODE { "", "(or disable obsolete line-by-line mode)", 0 }, #endif { "line", "Enable LINEMODE option", dolinemode, 1 }, #ifdef KLUDGELINEMODE { "", "(or enable obsolete line-by-line mode)", 0 }, #endif { "", "", 0 }, { "", "These require the LINEMODE option to be enabled", 0 }, { "isig", "Enable signal trapping", setmod, 1, MODE_TRAPSIG }, { "+isig", 0, setmod, 1, MODE_TRAPSIG }, { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG }, { "edit", "Enable character editing", setmod, 1, MODE_EDIT }, { "+edit", 0, setmod, 1, MODE_EDIT }, { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT }, { "softtabs", "Enable tab expansion", setmod, 1, MODE_SOFT_TAB }, { "+softtabs", 0, setmod, 1, MODE_SOFT_TAB }, { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB }, { "litecho", "Enable literal character echo", setmod, 1, MODE_LIT_ECHO }, { "+litecho", 0, setmod, 1, MODE_LIT_ECHO }, { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO }, { "help", 0, modehelp, 0 }, #ifdef KLUDGELINEMODE { "kludgeline", 0, dokludgemode, 1 }, #endif { "", "", 0 }, { "?", "Print help information", modehelp, 0 }, { 0 }, }; int modehelp() { struct modelist *mt; printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); for (mt = ModeList; mt->name; mt++) { if (mt->help) { if (*mt->help) printf("%-15s %s\n", mt->name, mt->help); else printf("\n"); } } return 0; } #define GETMODECMD(name) (struct modelist *) \ genget(name, (char **) ModeList, sizeof(struct modelist)) static int modecmd(argc, argv) int argc; char *argv[]; { struct modelist *mt; if (argc != 2) { printf("'mode' command requires an argument\n"); printf("'mode ?' for help.\n"); } else if ((mt = GETMODECMD(argv[1])) == 0) { fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); } else if (Ambiguous(mt)) { fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); } else if (mt->needconnect && !connected) { printf("?Need to be connected first.\n"); printf("'mode ?' for help.\n"); } else if (mt->handler) { return (*mt->handler)(mt->arg1); } return 0; } /* * The following data structures and routines implement the * "display" command. */ static int display(argc, argv) int argc; char *argv[]; { struct togglelist *tl; struct setlist *sl; #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ if (*tl->variable) { \ printf("will"); \ } else { \ printf("won't"); \ } \ printf(" %s.\n", tl->actionexplanation); \ } #define doset(sl) if (sl->name && *sl->name != ' ') { \ if (sl->handler == 0) \ printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \ else \ printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \ } if (argc == 1) { for (tl = Togglelist; tl->name; tl++) { dotog(tl); } printf("\n"); for (sl = Setlist; sl->name; sl++) { doset(sl); } } else { int i; for (i = 1; i < argc; i++) { sl = getset(argv[i]); tl = GETTOGGLE(argv[i]); if (Ambiguous(sl) || Ambiguous(tl)) { printf("?Ambiguous argument '%s'.\n", argv[i]); return 0; } else if (!sl && !tl) { printf("?Unknown argument '%s'.\n", argv[i]); return 0; } else { if (tl) { dotog(tl); } if (sl) { doset(sl); } } } } /*@*/optionstatus(); #ifdef ENCRYPTION EncryptStatus(); #endif /* ENCRYPTION */ return 1; #undef doset #undef dotog } /* * The following are the data structures, and many of the routines, * relating to command processing. */ /* * Set the escape character. */ static int setescape(argc, argv) int argc; char *argv[]; { register char *arg; char buf[50]; printf( "Deprecated usage - please use 'set escape%s%s' in the future.\n", (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); if (argc > 2) arg = argv[1]; else { printf("new escape character: "); (void) fgets(buf, sizeof(buf), stdin); arg = buf; } if (arg[0] != '\0') escape = arg[0]; if (!In3270) { printf("Escape character is '%s'.\n", control(escape)); } (void) fflush(stdout); return 1; } /*VARARGS*/ static int togcrmod() { crmod = !crmod; printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); (void) fflush(stdout); return 1; } /*VARARGS*/ int suspend() { #ifdef SIGTSTP setcommandmode(); { long oldrows, oldcols, newrows, newcols, err; err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; (void) kill(0, SIGTSTP); /* * If we didn't get the window size before the SUSPEND, but we * can get them now (?), then send the NAWS to make sure that * we are set up for the right window size. */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } } /* reget parameters in case they were changed */ TerminalSaveState(); setconnmode(0); #else printf("Suspend is not supported. Try the '!' command instead\n"); #endif return 1; } #if !defined(TN3270) /*ARGSUSED*/ int shell(argc, argv) int argc; char *argv[]; { long oldrows, oldcols, newrows, newcols, err; setcommandmode(); err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; switch(vfork()) { case -1: perror("Fork failed\n"); break; case 0: { /* * Fire up the shell in the child. */ register char *shellp, *shellname; extern char *strrchr(); shellp = getenv("SHELL"); if (shellp == NULL) shellp = "/bin/sh"; if ((shellname = strrchr(shellp, '/')) == 0) shellname = shellp; else shellname++; if (argc > 1) execl(shellp, shellname, "-c", &saveline[1], 0); else execl(shellp, shellname, 0); perror("Execl"); _exit(1); } default: (void)wait((int *)0); /* Wait for the shell to complete */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } break; } return 1; } #else /* !defined(TN3270) */ extern int shell(); #endif /* !defined(TN3270) */ /*VARARGS*/ static int bye(argc, argv) int argc; /* Number of arguments */ char *argv[]; /* arguments */ { extern int resettermname; if (connected) { (void) shutdown(net, 2); printf("Connection closed.\n"); (void) NetClose(net); connected = 0; resettermname = 1; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ /* reset options */ tninit(); #if defined(TN3270) SetIn3270(); /* Get out of 3270 mode */ #endif /* defined(TN3270) */ } if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { longjmp(toplevel, 1); /* NOTREACHED */ } return 1; /* Keep lint, etc., happy */ } /*VARARGS*/ int quit() { (void) call(bye, "bye", "fromquit", 0); Exit(0); /*NOTREACHED*/ } /*VARARGS*/ int logout() { send_do(TELOPT_LOGOUT, 1); (void) netflush(); return 1; } /* * The SLC command. */ struct slclist { char *name; char *help; void (*handler)(); int arg; }; static void slc_help(); struct slclist SlcList[] = { { "export", "Use local special character definitions", slc_mode_export, 0 }, { "import", "Use remote special character definitions", slc_mode_import, 1 }, { "check", "Verify remote special character definitions", slc_mode_import, 0 }, { "help", 0, slc_help, 0 }, { "?", "Print help information", slc_help, 0 }, { 0 }, }; static void slc_help() { struct slclist *c; for (c = SlcList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct slclist * getslc(name) char *name; { return (struct slclist *) genget(name, (char **) SlcList, sizeof(struct slclist)); } static int slccmd(argc, argv) int argc; char *argv[]; { struct slclist *c; if (argc != 2) { fprintf(stderr, "Need an argument to 'slc' command. 'slc ?' for help.\n"); return 0; } c = getslc(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]); return 0; } (*c->handler)(c->arg); slcstate(); return 1; } /* * The ENVIRON command. */ struct envlist { char *name; char *help; void (*handler)(); int narg; }; extern struct env_lst * env_define P((unsigned char *, unsigned char *)); extern void env_undefine P((unsigned char *)), env_export P((unsigned char *)), env_unexport P((unsigned char *)), env_send P((unsigned char *)), #if defined(OLD_ENVIRON) && defined(ENV_HACK) env_varval P((unsigned char *)), #endif env_list P((void)); static void env_help P((void)); struct envlist EnvList[] = { { "define", "Define an environment variable", (void (*)())env_define, 2 }, { "undefine", "Undefine an environment variable", env_undefine, 1 }, { "export", "Mark an environment variable for automatic export", env_export, 1 }, { "unexport", "Don't mark an environment variable for automatic export", env_unexport, 1 }, { "send", "Send an environment variable", env_send, 1 }, { "list", "List the current environment variables", env_list, 0 }, #if defined(OLD_ENVIRON) && defined(ENV_HACK) { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", env_varval, 1 }, #endif { "help", 0, env_help, 0 }, { "?", "Print help information", env_help, 0 }, { 0 }, }; static void env_help() { struct envlist *c; for (c = EnvList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct envlist * getenvcmd(name) char *name; { return (struct envlist *) genget(name, (char **) EnvList, sizeof(struct envlist)); } int env_cmd(argc, argv) int argc; char *argv[]; { struct envlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'environ' command. 'environ ?' for help.\n"); return 0; } c = getenvcmd(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } (*c->handler)(argv[2], argv[3]); return 1; } struct env_lst { struct env_lst *next; /* pointer to next structure */ struct env_lst *prev; /* pointer to previous structure */ unsigned char *var; /* pointer to variable name */ unsigned char *value; /* pointer to variable value */ int export; /* 1 -> export with default list of variables */ int welldefined; /* A well defined variable */ }; struct env_lst envlisthead; struct env_lst * env_find(var) unsigned char *var; { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { if (strcmp((char *)ep->var, (char *)var) == 0) return(ep); } return(NULL); } void env_init() { extern char **environ; register char **epp, *cp; register struct env_lst *ep; extern char *strchr(); for (epp = environ; *epp; epp++) { if ((cp = strchr(*epp, '='))) { *cp = '\0'; ep = env_define((unsigned char *)*epp, (unsigned char *)cp+1); ep->export = 0; *cp = '='; } } /* * Special case for DISPLAY variable. If it is ":0.0" or * "unix:0.0", we have to get rid of "unix" and insert our * hostname. */ if ((ep = env_find("DISPLAY")) && ((*ep->value == ':') || (strncmp((char *)ep->value, "unix:", 5) == 0))) { char hbuf[256+1]; char *cp2 = strchr((char *)ep->value, ':'); gethostname(hbuf, 256); hbuf[256] = '\0'; cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1); sprintf((char *)cp, "%s%s", hbuf, cp2); free(ep->value); ep->value = (unsigned char *)cp; } /* * If USER is not defined, but LOGNAME is, then add * USER with the value from LOGNAME. By default, we * don't export the USER variable. */ if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) { env_define((unsigned char *)"USER", ep->value); env_unexport((unsigned char *)"USER"); } env_export((unsigned char *)"DISPLAY"); env_export((unsigned char *)"PRINTER"); } struct env_lst * env_define(var, value) unsigned char *var, *value; { register struct env_lst *ep; if ((ep = env_find(var))) { if (ep->var) free(ep->var); if (ep->value) free(ep->value); } else { ep = (struct env_lst *)malloc(sizeof(struct env_lst)); ep->next = envlisthead.next; envlisthead.next = ep; ep->prev = &envlisthead; if (ep->next) ep->next->prev = ep; } ep->welldefined = opt_welldefined(var); ep->export = 1; ep->var = (unsigned char *)strdup((char *)var); ep->value = (unsigned char *)strdup((char *)value); return(ep); } void env_undefine(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) { ep->prev->next = ep->next; if (ep->next) ep->next->prev = ep->prev; if (ep->var) free(ep->var); if (ep->value) free(ep->value); free(ep); } } void env_export(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) ep->export = 1; } void env_unexport(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) ep->export = 0; } void env_send(var) unsigned char *var; { register struct env_lst *ep; if (my_state_is_wont(TELOPT_NEW_ENVIRON) #ifdef OLD_ENVIRON && my_state_is_wont(TELOPT_OLD_ENVIRON) #endif ) { fprintf(stderr, "Cannot send '%s': Telnet ENVIRON option not enabled\n", var); return; } ep = env_find(var); if (ep == 0) { fprintf(stderr, "Cannot send '%s': variable not defined\n", var); return; } env_opt_start_info(); env_opt_add(ep->var); env_opt_end(0); } void env_list() { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { printf("%c %-20s %s\n", ep->export ? '*' : ' ', ep->var, ep->value); } } unsigned char * env_default(init, welldefined) int init; { static struct env_lst *nep = NULL; if (init) { nep = &envlisthead; return(NULL); } if (nep) { while ((nep = nep->next)) { if (nep->export && (nep->welldefined == welldefined)) return(nep->var); } } return(NULL); } unsigned char * env_getvalue(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) return(ep->value); return(NULL); } #if defined(OLD_ENVIRON) && defined(ENV_HACK) void env_varval(what) unsigned char *what; { extern int old_env_var, old_env_value, env_auto; int len = strlen((char *)what); if (len == 0) goto unknown; if (strncasecmp((char *)what, "status", len) == 0) { if (env_auto) printf("%s%s", "VAR and VALUE are/will be ", "determined automatically\n"); if (old_env_var == OLD_ENV_VAR) printf("VAR and VALUE set to correct definitions\n"); else printf("VAR and VALUE definitions are reversed\n"); } else if (strncasecmp((char *)what, "auto", len) == 0) { env_auto = 1; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else if (strncasecmp((char *)what, "right", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VAR; old_env_value = OLD_ENV_VALUE; } else if (strncasecmp((char *)what, "wrong", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else { unknown: printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n"); } } #endif #if defined(AUTHENTICATION) /* * The AUTHENTICATE command. */ struct authlist { char *name; char *help; int (*handler)(); int narg; }; extern int auth_enable P((char *)), auth_disable P((char *)), auth_status P((void)); static int auth_help P((void)); struct authlist AuthList[] = { { "status", "Display current status of authentication information", auth_status, 0 }, { "disable", "Disable an authentication type ('auth disable ?' for more)", auth_disable, 1 }, { "enable", "Enable an authentication type ('auth enable ?' for more)", auth_enable, 1 }, { "help", 0, auth_help, 0 }, { "?", "Print help information", auth_help, 0 }, { 0 }, }; static int auth_help() { struct authlist *c; for (c = AuthList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } int auth_cmd(argc, argv) int argc; char *argv[]; { struct authlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'auth' command. 'auth ?' for help.\n"); return 0; } c = (struct authlist *) genget(argv[1], (char **) AuthList, sizeof(struct authlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } return((*c->handler)(argv[2], argv[3])); } #endif #ifdef ENCRYPTION /* * The ENCRYPT command. */ struct encryptlist { char *name; char *help; int (*handler)(); int needconnect; int minarg; int maxarg; }; extern int EncryptEnable P((char *, char *)), EncryptDisable P((char *, char *)), EncryptType P((char *, char *)), EncryptStart P((char *)), EncryptStartInput P((void)), EncryptStartOutput P((void)), EncryptStop P((char *)), EncryptStopInput P((void)), EncryptStopOutput P((void)), EncryptStatus P((void)); static int EncryptHelp P((void)); struct encryptlist EncryptList[] = { { "enable", "Enable encryption. ('encrypt enable ?' for more)", EncryptEnable, 1, 1, 2 }, { "disable", "Disable encryption. ('encrypt enable ?' for more)", EncryptDisable, 0, 1, 2 }, { "type", "Set encryption type. ('encrypt type ?' for more)", EncryptType, 0, 1, 1 }, { "start", "Start encryption. ('encrypt start ?' for more)", EncryptStart, 1, 0, 1 }, { "stop", "Stop encryption. ('encrypt stop ?' for more)", EncryptStop, 1, 0, 1 }, { "input", "Start encrypting the input stream", EncryptStartInput, 1, 0, 0 }, { "-input", "Stop encrypting the input stream", EncryptStopInput, 1, 0, 0 }, { "output", "Start encrypting the output stream", EncryptStartOutput, 1, 0, 0 }, { "-output", "Stop encrypting the output stream", EncryptStopOutput, 1, 0, 0 }, { "status", "Display current status of authentication information", EncryptStatus, 0, 0, 0 }, { "help", 0, EncryptHelp, 0, 0, 0 }, { "?", "Print help information", EncryptHelp, 0, 0, 0 }, { 0 }, }; static int EncryptHelp() { struct encryptlist *c; for (c = EncryptList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } int encrypt_cmd(argc, argv) int argc; char *argv[]; { struct encryptlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'encrypt' command. 'encrypt ?' for help.\n"); return 0; } c = (struct encryptlist *) genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('encrypt ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('encrypt ?' for help).\n", argv[1]); return 0; } argc -= 2; if (argc < c->minarg || argc > c->maxarg) { if (c->minarg == c->maxarg) { fprintf(stderr, "Need %s%d argument%s ", c->minarg < argc ? "only " : "", c->minarg, c->minarg == 1 ? "" : "s"); } else { fprintf(stderr, "Need %s%d-%d arguments ", c->maxarg < argc ? "only " : "", c->minarg, c->maxarg); } fprintf(stderr, "to 'encrypt %s' command. 'encrypt ?' for help.\n", c->name); return 0; } if (c->needconnect && !connected) { if (!(argc && (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) { printf("?Need to be connected first.\n"); return 0; } } return ((*c->handler)(argc > 0 ? argv[2] : 0, argc > 1 ? argv[3] : 0, argc > 2 ? argv[4] : 0)); } #endif /* ENCRYPTION */ #if defined(unix) && defined(TN3270) static void filestuff(fd) int fd; { int res; #ifdef F_GETOWN setconnmode(0); res = fcntl(fd, F_GETOWN, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } printf("\tOwner is %d.\n", res); #endif setconnmode(0); res = fcntl(fd, F_GETFL, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } #ifdef notdef printf("\tFlags are 0x%x: %s\n", res, decodeflags(res)); #endif } #endif /* defined(unix) && defined(TN3270) */ /* * Print status about the connection. */ /*ARGSUSED*/ static int status(argc, argv) int argc; char *argv[]; { if (connected) { printf("Connected to %s.\n", hostname); if ((argc < 2) || strcmp(argv[1], "notmuch")) { int mode = getconnmode(); if (my_want_state_is_will(TELOPT_LINEMODE)) { printf("Operating with LINEMODE option\n"); printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No"); printf("%s catching of signals\n", (mode&MODE_TRAPSIG) ? "Local" : "No"); slcstate(); #ifdef KLUDGELINEMODE } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) { printf("Operating in obsolete linemode\n"); #endif } else { printf("Operating in single character mode\n"); if (localchars) printf("Catching signals locally\n"); } printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote"); if (my_want_state_is_will(TELOPT_LFLOW)) printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No"); #ifdef ENCRYPTION encrypt_display(); #endif /* ENCRYPTION */ } } else { printf("No connection.\n"); } # if !defined(TN3270) printf("Escape character is '%s'.\n", control(escape)); (void) fflush(stdout); # else /* !defined(TN3270) */ if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { printf("Escape character is '%s'.\n", control(escape)); } # if defined(unix) if ((argc >= 2) && !strcmp(argv[1], "everything")) { printf("SIGIO received %d time%s.\n", sigiocount, (sigiocount == 1)? "":"s"); if (In3270) { printf("Process ID %d, process group %d.\n", getpid(), getpgrp(getpid())); printf("Terminal input:\n"); filestuff(tin); printf("Terminal output:\n"); filestuff(tout); printf("Network socket:\n"); filestuff(net); } } if (In3270 && transcom) { printf("Transparent mode command is '%s'.\n", transcom); } # endif /* defined(unix) */ (void) fflush(stdout); if (In3270) { return 0; } # endif /* defined(TN3270) */ return 1; } #ifdef SIGINFO /* * Function that gets called when SIGINFO is received. */ void ayt_status() { (void) call(status, "status", "notmuch", 0); } #endif -unsigned long inet_addr(); +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; - extern char *inet_ntoa(); -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - memset((char *)&sin, 0, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); setuid(getuid()); return 0; } if (argc < 2) { (void) strcpy(line, "open "); printf("(to) "); (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin); makeargv(); argc = margc; argv = margv; } cmd = *argv; --argc; ++argv; while (argc) { if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?")) goto usage; if (strcmp(*argv, "-l") == 0) { --argc; ++argv; if (argc == 0) goto usage; user = *argv++; --argc; continue; } if (strcmp(*argv, "-a") == 0) { --argc; ++argv; autologin = 1; continue; } if (strcmp(*argv, "-s") == 0) { --argc; ++argv; if (argc == 0) goto usage; src_addr = *argv++; --argc; continue; } if (hostp == 0) { hostp = *argv++; --argc; continue; } if (portp == 0) { portp = *argv++; --argc; continue; } usage: printf("usage: %s [-l user] [-a] [-s src_addr] host-name [port]\n", cmd); setuid(getuid()); return 0; } if (hostp == 0) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); - return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); + setuid(getuid()); + return 0; + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { setuid(getuid()); + freeaddrinfo(res); return 0; - } else if (temp == -1) { + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; } - telnetport = 1; } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(net, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) && (errno != ENOPROTOOPT)) perror("telnet: setsockopt (IP_TOS) (ignored)"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { perror("setsockopt (SO_DEBUG)"); } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } connected++; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; user = getenv("USER"); if (user == NULL || ((pw = getpwnam(user)) && pw->pw_uid != getuid())) { if ((pw = getpwuid(getuid()))) user = pw->pw_name; else user = NULL; } } if (user) { env_define((unsigned char *)"USER", (unsigned char *)user); env_export((unsigned char *)"USER"); } (void) call(status, "status", "notmuch", 0); if (setjmp(peerdied) == 0) telnet(user); (void) NetClose(net); ExitString("Connection closed by foreign host.\n",1); /*NOTREACHED*/ } #define HELPINDENT (sizeof ("connect")) static char openhelp[] = "connect to a site", closehelp[] = "close current connection", logouthelp[] = "forcibly logout remote user and close the connection", quithelp[] = "exit telnet", statushelp[] = "print status information", helphelp[] = "print help information", sendhelp[] = "transmit special characters ('send ?' for more)", sethelp[] = "set operating parameters ('set ?' for more)", unsethelp[] = "unset operating parameters ('unset ?' for more)", togglestring[] ="toggle operating parameters ('toggle ?' for more)", slchelp[] = "change state of special charaters ('slc ?' for more)", displayhelp[] = "display operating parameters", #if defined(TN3270) && defined(unix) transcomhelp[] = "specify Unix command for transparent mode pipe", #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) authhelp[] = "turn on (off) authentication ('auth ?' for more)", #endif #ifdef ENCRYPTION encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)", #endif /* ENCRYPTION */ #if defined(unix) zhelp[] = "suspend telnet", #endif /* defined(unix) */ #if defined(SKEY) skeyhelp[] = "compute response to s/key challenge", #endif shellhelp[] = "invoke a subshell", envhelp[] = "change environment variables ('environ ?' for more)", modestring[] = "try to enter line or character mode ('mode ?' for more)"; static Command cmdtab[] = { { "close", closehelp, bye, 1 }, { "logout", logouthelp, logout, 1 }, { "display", displayhelp, display, 0 }, { "mode", modestring, modecmd, 0 }, { "open", openhelp, tn, 0 }, { "quit", quithelp, quit, 0 }, { "send", sendhelp, sendcmd, 0 }, { "set", sethelp, setcmd, 0 }, { "unset", unsethelp, unsetcmd, 0 }, { "status", statushelp, status, 0 }, { "toggle", togglestring, toggle, 0 }, { "slc", slchelp, slccmd, 0 }, #if defined(TN3270) && defined(unix) { "transcom", transcomhelp, settranscom, 0 }, #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) { "auth", authhelp, auth_cmd, 0 }, #endif #ifdef ENCRYPTION { "encrypt", encrypthelp, encrypt_cmd, 0 }, #endif /* ENCRYPTION */ #if defined(unix) { "z", zhelp, suspend, 0 }, #endif /* defined(unix) */ #if defined(TN3270) { "!", shellhelp, shell, 1 }, #else { "!", shellhelp, shell, 0 }, #endif { "environ", envhelp, env_cmd, 0 }, { "?", helphelp, help, 0 }, #if defined(SKEY) { "skey", skeyhelp, skey_calc, 0 }, #endif { 0, 0, 0, 0 } }; static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; static char escapehelp[] = "deprecated command -- use 'set escape' instead"; static Command cmdtab2[] = { { "help", 0, help, 0 }, { "escape", escapehelp, setescape, 0 }, { "crmod", crmodhelp, togcrmod, 0 }, { 0, 0, 0, 0 } }; /* * Call routine with argc, argv set from args (terminated by 0). */ /*VARARGS1*/ static int call(va_alist) va_dcl { va_list ap; typedef int (*intrtn_t)(); intrtn_t routine; char *args[100]; int argno = 0; va_start(ap); routine = (va_arg(ap, intrtn_t)); while ((args[argno++] = va_arg(ap, char *)) != 0) { ; } va_end(ap); return (*routine)(argno-1, args); } static Command * getcmd(name) char *name; { Command *cm; if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command)))) return cm; return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); } void command(top, tbuf, cnt) int top; char *tbuf; int cnt; { register Command *c; setcommandmode(); if (!top) { putchar('\n'); #if defined(unix) } else { (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); #endif /* defined(unix) */ } for (;;) { if (rlogin == _POSIX_VDISABLE) printf("%s> ", prompt); if (tbuf) { register char *cp; cp = line; while (cnt > 0 && (*cp++ = *tbuf++) != '\n') cnt--; tbuf = 0; if (cp == line || *--cp != '\n' || cp == line) goto getline; *cp = '\0'; if (rlogin == _POSIX_VDISABLE) printf("%s\n", line); } else { getline: if (rlogin != _POSIX_VDISABLE) printf("%s> ", prompt); if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin) || ferror(stdin)) { (void) quit(); /*NOTREACHED*/ } break; } } if (line[0] == 0) break; makeargv(); if (margv[0] == 0) { break; } c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command\n"); continue; } if (c == 0) { printf("?Invalid command\n"); continue; } if (c->needconnect && !connected) { printf("?Need to be connected first.\n"); continue; } if ((*c->handler)(margc, margv)) { break; } } if (!top) { if (!connected) { longjmp(toplevel, 1); /*NOTREACHED*/ } #if defined(TN3270) if (shell_active == 0) { setconnmode(0); } #else /* defined(TN3270) */ setconnmode(0); #endif /* defined(TN3270) */ } } /* * Help command. */ static int help(argc, argv) int argc; char *argv[]; { register Command *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) if (c->help) { printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); } } else while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (Ambiguous(c)) printf("?Ambiguous help command %s\n", arg); else if (c == (Command *)0) printf("?Invalid help command %s\n", arg); else printf("%s\n", c->help); } return(0); } static char *rcname = 0; static char rcbuf[128]; void cmdrc(m1, m2) char *m1, *m2; { register Command *c; FILE *rcfile; int gotmachine = 0; int l1 = strlen(m1); int l2 = strlen(m2); char m1save[64]; if (skiprc) return; strcpy(m1save, m1); m1 = m1save; if (rcname == 0) { rcname = getenv("HOME"); if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf)) strcpy(rcbuf, rcname); else rcbuf[0] = '\0'; strcat(rcbuf, "/.telnetrc"); rcname = rcbuf; } if ((rcfile = fopen(rcname, "r")) == 0) { return; } for (;;) { if (fgets(line, sizeof(line), rcfile) == NULL) break; if (line[0] == 0) break; if (line[0] == '#') continue; if (gotmachine) { if (!isspace(line[0])) gotmachine = 0; } if (gotmachine == 0) { if (isspace(line[0])) continue; if (strncasecmp(line, m1, l1) == 0) strncpy(line, &line[l1], sizeof(line) - l1); else if (strncasecmp(line, m2, l2) == 0) strncpy(line, &line[l2], sizeof(line) - l2); else if (strncasecmp(line, "DEFAULT", 7) == 0) strncpy(line, &line[7], sizeof(line) - 7); else continue; if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n') continue; gotmachine = 1; } makeargv(); if (margv[0] == 0) continue; c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command: %s\n", margv[0]); continue; } if (c == 0) { printf("?Invalid command: %s\n", margv[0]); continue; } /* * This should never happen... */ if (c->needconnect && !connected) { printf("?Need to be connected first for %s.\n", margv[0]); continue; } (*c->handler)(margc, margv); } fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst * If the leading ! is present, it is a * strict source route, otherwise it is * assmed to be a loose source route. * * We fill in the source route option as * hop1,hop2,hop3...dest * and return a pointer to hop1, which will * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a * pointer to a pointer to a character array * that should be filled in with the option. * * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the * path is unknown, and *cpp is set to point to the bad * hostname. * * *cpp: If *cpp was equal to NULL, it will be filled * in with a pointer to our static area that has * the option filled in. This will be 32bit aligned. * * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* * Verify the arguments, and make sure we have * at least 7 bytes for the option. */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in * the begining of the option. */ #ifndef sysV88 if (*cp == '!') { cp++; *lsrp++ = IPOPT_SSRR; } else *lsrp++ = IPOPT_LSRR; #else if (*cp == '!') { cp++; ipopt.io_type = IPOPT_SSRR; } else ipopt.io_type = IPOPT_LSRR; #endif if (*cp != '@') return((unsigned long)-1); #ifndef sysV88 lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; (c = *cp2); cp2++) { if (c == ',') { *cp2++ = '\0'; if (*cp2 == '@') cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; break; } if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if ((host = gethostbyname(cp))) { -#if defined(h_addr) - memmove((caddr_t)&sin_addr, - host->h_addr_list[0], host->h_length); -#else - memmove((caddr_t)&sin_addr, host->h_addr, host->h_length); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memmove(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else break; /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) + return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ *lenp = lsrp - *cpp; #else ipopt.io_len = lsrp - *cpp; if (ipopt.io_len <= 5) { /* Is 3 better ? */ *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif + + + diff --git a/contrib/telnet/telnet/externs.h b/contrib/telnet/telnet/externs.h index 0c6894f53d4e..46253d9a6048 100644 --- a/contrib/telnet/telnet/externs.h +++ b/contrib/telnet/telnet/externs.h @@ -1,492 +1,502 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)externs.h 8.3 (Berkeley) 5/30/95 + * $FreeBSD$ */ #ifndef BSD # define BSD 43 #endif /* * ucb stdio.h defines BSD as something wierd */ #if defined(sun) && defined(__svr4__) #define BSD 43 #endif #ifndef USE_TERMIO # if BSD > 43 || defined(SYSV_TERMIO) # define USE_TERMIO # endif #endif #include #include #if defined(CRAY) && !defined(NO_BSD_SETJMP) #include #endif #ifndef FILIO_H #include #else #include #endif #ifdef CRAY # include #endif /* CRAY */ #ifdef USE_TERMIO # ifndef VINTR # ifdef SYSV_TERMIO # include # else # include # define termio termios # endif # endif #endif #if defined(NO_CC_T) || !defined(USE_TERMIO) # if !defined(USE_TERMIO) typedef char cc_t; # else typedef unsigned char cc_t; # endif #endif #ifndef NO_STRING_H #include #else #include #endif +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ # endif # ifdef VDISABLE # define _POSIX_VDISABLE VDISABLE # else # define _POSIX_VDISABLE ((cc_t)'\377') # endif #endif #define SUBBUFSIZE 256 #ifndef CRAY extern int errno; /* outside this world */ #endif /* !CRAY */ #if !defined(P) # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ In3270, /* Are we in 3270 mode? */ telnetport, /* Are we connected to the telnet port? */ localflow, /* Flow control handled locally */ restartany, /* If flow control, restart output on any character */ localchars, /* we recognize interrupt/quit */ donelclchars, /* the user has set "localchars" */ showoptions, net, /* Network file descriptor */ tin, /* Terminal input file descriptor */ tout, /* Terminal output file descriptor */ crlf, /* Should '\r' be mapped to (or )? */ autoflush, /* flush output when interrupting? */ autosynch, /* send interrupt characters with SYNCH? */ SYNCHing, /* Is the stream in telnet SYNCH mode? */ donebinarytoggle, /* the user has put us in binary */ dontlecho, /* do we suppress local echoing right now? */ crmod, netdata, /* Print out network data flow */ prettydump, /* Print "netdata" output in user readable format */ #if defined(unix) #if defined(TN3270) cursesdata, /* Print out curses data flow */ apitrace, /* Trace API transactions */ #endif /* defined(TN3270) */ termdata, /* Print out terminal data flow */ #endif /* defined(unix) */ debug, /* Debug level */ doaddrlookup, /* do a reverse lookup? */ clienteof; /* Client received EOF */ extern cc_t escape; /* Escape to command mode */ extern cc_t rlogin; /* Rlogin mode escape character */ #ifdef KLUDGELINEMODE extern cc_t echoc; /* Toggle local echoing */ #endif extern char *prompt; /* Prompt for command. */ extern char doopt[], dont[], will[], wont[], options[], /* All the little options */ *hostname; /* Who are we connected to? */ #ifdef ENCRYPTION extern void (*encrypt_output) P((unsigned char *, int)); extern int (*decrypt_input) P((int)); #endif /* ENCRYPTION */ /* * We keep track of each side of the option negotiation. */ #define MY_STATE_WILL 0x01 #define MY_WANT_STATE_WILL 0x02 #define MY_STATE_DO 0x04 #define MY_WANT_STATE_DO 0x08 /* * Macros to check the current state of things */ #define my_state_is_do(opt) (options[opt]&MY_STATE_DO) #define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) #define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) #define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) #define my_state_is_dont(opt) (!my_state_is_do(opt)) #define my_state_is_wont(opt) (!my_state_is_will(opt)) #define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) #define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) #define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;} #define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;} #define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;} #define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;} #define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;} #define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;} #define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;} #define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;} /* * Make everything symetrical */ #define HIS_STATE_WILL MY_STATE_DO #define HIS_WANT_STATE_WILL MY_WANT_STATE_DO #define HIS_STATE_DO MY_STATE_WILL #define HIS_WANT_STATE_DO MY_WANT_STATE_WILL #define his_state_is_do my_state_is_will #define his_state_is_will my_state_is_do #define his_want_state_is_do my_want_state_is_will #define his_want_state_is_will my_want_state_is_do #define his_state_is_dont my_state_is_wont #define his_state_is_wont my_state_is_dont #define his_want_state_is_dont my_want_state_is_wont #define his_want_state_is_wont my_want_state_is_dont #define set_his_state_do set_my_state_will #define set_his_state_will set_my_state_do #define set_his_want_state_do set_my_want_state_will #define set_his_want_state_will set_my_want_state_do #define set_his_state_dont set_my_state_wont #define set_his_state_wont set_my_state_dont #define set_his_want_state_dont set_my_want_state_wont #define set_his_want_state_wont set_my_want_state_dont extern FILE *NetTrace; /* Where debugging output goes */ extern unsigned char NetTraceFile[]; /* Name of file where debugging output goes */ extern void SetNetTrace P((char *)); /* Function to change where debugging goes */ extern jmp_buf peerdied, toplevel; /* For error conditions. */ extern void command P((int, char *, int)), Dump P((int, unsigned char *, int)), ExitString P((char *, int)), init_3270 P((void)), printoption P((char *, int, int)), printsub P((int, unsigned char *, int)), sendnaws P((void)), setconnmode P((int)), setcommandmode P((void)), setneturg P((void)), sys_telnet_init P((void)), telnet P((char *)), tel_enter_binary P((int)), TerminalFlushOutput P((void)), TerminalNewMode P((int)), TerminalRestoreState P((void)), TerminalSaveState P((void)), tninit P((void)), upcase P((char *)), willoption P((int)), wontoption P((int)); extern void send_do P((int, int)), send_dont P((int, int)), send_will P((int, int)), send_wont P((int, int)); extern void lm_will P((unsigned char *, int)), lm_wont P((unsigned char *, int)), lm_do P((unsigned char *, int)), lm_dont P((unsigned char *, int)), lm_mode P((unsigned char *, int, int)); extern void slc_init P((void)), slcstate P((void)), slc_mode_export P((void)), slc_mode_import P((int)), slc_import P((int)), slc_export P((void)), slc P((unsigned char *, int)), slc_check P((void)), slc_start_reply P((void)), slc_add_reply P((int, int, int)), slc_end_reply P((void)); extern int NetClose P((int)), netflush P((void)), SetSockOpt P((int, int, int, int)), slc_update P((void)), telrcv P((void)), TerminalWrite P((char *, int)), TerminalAutoFlush P((void)), ttyflush P((int)); extern void env_opt P((unsigned char *, int)), env_opt_start P((void)), env_opt_start_info P((void)), env_opt_add P((unsigned char *)), env_opt_end P((int)); extern unsigned char *env_default P((int, int)), *env_getvalue P((unsigned char *)); extern int get_status P((void)), dosynch P((void)); extern cc_t *tcval P((int)); #ifndef USE_TERMIO extern struct tchars ntc; extern struct ltchars nltc; extern struct sgttyb nttyb; # define termEofChar ntc.t_eofc # define termEraseChar nttyb.sg_erase # define termFlushChar nltc.t_flushc # define termIntChar ntc.t_intrc # define termKillChar nttyb.sg_kill # define termLiteralNextChar nltc.t_lnextc # define termQuitChar ntc.t_quitc # define termSuspChar nltc.t_suspc # define termRprntChar nltc.t_rprntc # define termWerasChar nltc.t_werasc # define termStartChar ntc.t_startc # define termStopChar ntc.t_stopc # define termForw1Char ntc.t_brkc extern cc_t termForw2Char; extern cc_t termAytChar; # define termEofCharp (cc_t *)&ntc.t_eofc # define termEraseCharp (cc_t *)&nttyb.sg_erase # define termFlushCharp (cc_t *)&nltc.t_flushc # define termIntCharp (cc_t *)&ntc.t_intrc # define termKillCharp (cc_t *)&nttyb.sg_kill # define termLiteralNextCharp (cc_t *)&nltc.t_lnextc # define termQuitCharp (cc_t *)&ntc.t_quitc # define termSuspCharp (cc_t *)&nltc.t_suspc # define termRprntCharp (cc_t *)&nltc.t_rprntc # define termWerasCharp (cc_t *)&nltc.t_werasc # define termStartCharp (cc_t *)&ntc.t_startc # define termStopCharp (cc_t *)&ntc.t_stopc # define termForw1Charp (cc_t *)&ntc.t_brkc # define termForw2Charp (cc_t *)&termForw2Char # define termAytCharp (cc_t *)&termAytChar # else extern struct termio new_tc; # define termEofChar new_tc.c_cc[VEOF] # define termEraseChar new_tc.c_cc[VERASE] # define termIntChar new_tc.c_cc[VINTR] # define termKillChar new_tc.c_cc[VKILL] # define termQuitChar new_tc.c_cc[VQUIT] # ifndef VSUSP extern cc_t termSuspChar; # else # define termSuspChar new_tc.c_cc[VSUSP] # endif # if defined(VFLUSHO) && !defined(VDISCARD) # define VDISCARD VFLUSHO # endif # ifndef VDISCARD extern cc_t termFlushChar; # else # define termFlushChar new_tc.c_cc[VDISCARD] # endif # ifndef VWERASE extern cc_t termWerasChar; # else # define termWerasChar new_tc.c_cc[VWERASE] # endif # ifndef VREPRINT extern cc_t termRprntChar; # else # define termRprntChar new_tc.c_cc[VREPRINT] # endif # ifndef VLNEXT extern cc_t termLiteralNextChar; # else # define termLiteralNextChar new_tc.c_cc[VLNEXT] # endif # ifndef VSTART extern cc_t termStartChar; # else # define termStartChar new_tc.c_cc[VSTART] # endif # ifndef VSTOP extern cc_t termStopChar; # else # define termStopChar new_tc.c_cc[VSTOP] # endif # ifndef VEOL extern cc_t termForw1Char; # else # define termForw1Char new_tc.c_cc[VEOL] # endif # ifndef VEOL2 extern cc_t termForw2Char; # else # define termForw2Char new_tc.c_cc[VEOL] # endif # ifndef VSTATUS extern cc_t termAytChar; #else # define termAytChar new_tc.c_cc[VSTATUS] #endif # if !defined(CRAY) || defined(__STDC__) # define termEofCharp &termEofChar # define termEraseCharp &termEraseChar # define termIntCharp &termIntChar # define termKillCharp &termKillChar # define termQuitCharp &termQuitChar # define termSuspCharp &termSuspChar # define termFlushCharp &termFlushChar # define termWerasCharp &termWerasChar # define termRprntCharp &termRprntChar # define termLiteralNextCharp &termLiteralNextChar # define termStartCharp &termStartChar # define termStopCharp &termStopChar # define termForw1Charp &termForw1Char # define termForw2Charp &termForw2Char # define termAytCharp &termAytChar # else /* Work around a compiler bug */ # define termEofCharp 0 # define termEraseCharp 0 # define termIntCharp 0 # define termKillCharp 0 # define termQuitCharp 0 # define termSuspCharp 0 # define termFlushCharp 0 # define termWerasCharp 0 # define termRprntCharp 0 # define termLiteralNextCharp 0 # define termStartCharp 0 # define termStopCharp 0 # define termForw1Charp 0 # define termForw2Charp 0 # define termAytCharp 0 # endif #endif /* Ring buffer structures which are shared */ extern Ring netoring, netiring, ttyoring, ttyiring; /* Tn3270 section */ #if defined(TN3270) extern int HaveInput, /* Whether an asynchronous I/O indication came in */ noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */ noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */ sigiocount, /* Count of SIGIO receptions */ shell_active; /* Subshell is active */ extern char *Ibackp, /* Oldest byte of 3270 data */ Ibuf[], /* 3270 buffer */ *Ifrontp, /* Where next 3270 byte goes */ tline[], *transcom; /* Transparent command */ extern int settranscom P((int, char**)); extern void inputAvailable P((int)); #endif /* defined(TN3270) */ diff --git a/contrib/telnet/telnet/main.c b/contrib/telnet/telnet/main.c index 2dc05fd73eb0..c3d0f5d74ba9 100644 --- a/contrib/telnet/telnet/main.c +++ b/contrib/telnet/telnet/main.c @@ -1,350 +1,389 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static const char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; #endif /* not lint */ #include +#include #include #include "ring.h" #include "externs.h" #include "defines.h" #if defined(AUTHENTICATION) #include #endif #if defined(ENCRYPTION) #include #endif /* These values need to be the same as defined in libtelnet/kerberos5.c */ /* Either define them in both places, or put in some common header file. */ #define OPTS_FORWARD_CREDS 0x00000002 #define OPTS_FORWARDABLE_CREDS 0x00000001 #if 0 #define FORWARD #endif void init_terminal(void); void init_network(void); void init_telnet(void); void init_sys(void); void init_3270(void); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ void tninit() { init_terminal(); init_network(); init_telnet(); init_sys(); #if defined(TN3270) init_3270(); #endif } void usage() { fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) # ifdef AUTHENTICATION "[-noasynch] [-noasynctty]\n\t" "[-noasyncnet] [-r] [-s src_addr] [-t transcom] ", # else "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t" "[-s src_addr] [-t transcom]", # endif #else "[-r] [-s src_addr] ", #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" +#endif #ifdef ENCRYPTION "[-x] [host-name [port]]" #else /* ENCRYPTION */ "[host-name [port]]" #endif /* ENCRYPTION */ ); exit(1); } /* * main. Parse arguments, invoke the protocol or command parser. */ int main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int ch; char *user, *strrchr(); char *src_addr = NULL; #ifdef FORWARD extern int forward_flags; #endif /* FORWARD */ tninit(); /* Clear out things */ #if defined(CRAY) && !defined(__STDC__) _setlist_init(); /* Work around compiler bug */ #endif TerminalSaveState(); if ((prompt = strrchr(argv[0], '/'))) ++prompt; else prompt = argv[0]; user = NULL; rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != EOF) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; case 'E': rlogin = escape = _POSIX_VDISABLE; break; case 'K': #ifdef AUTHENTICATION autologin = 0; #endif break; case 'L': eight |= 2; /* binary output only */ break; case 'N': doaddrlookup = 0; break; case 'S': { #ifdef HAS_GETTOS extern int tos; if ((tos = parsetos(optarg, "tcp")) < 0) fprintf(stderr, "%s%s%s%s\n", prompt, ": Bad TOS argument '", optarg, "; will try to use default TOS"); #else fprintf(stderr, "%s: Warning: -S ignored, no parsetos() support.\n", prompt); #endif } break; case 'X': #ifdef AUTHENTICATION auth_disable_name(optarg); #endif break; case 'a': autologin = 1; break; case 'c': skiprc = 1; break; case 'd': debug = 1; break; case 'e': set_escape_char(optarg); break; case 'f': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; #else fprintf(stderr, "%s: Warning: -f ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'F': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; forward_flags |= OPTS_FORWARDABLE_CREDS; #else fprintf(stderr, "%s: Warning: -F ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'k': #if defined(AUTHENTICATION) && defined(KRB4) { extern char *dest_realm, dst_realm_buf[], dst_realm_sz; dest_realm = dst_realm_buf; (void)strncpy(dest_realm, optarg, dst_realm_sz); } #else fprintf(stderr, "%s: Warning: -k ignored, no Kerberos V4 support.\n", prompt); #endif break; case 'l': autologin = 1; user = optarg; break; case 'n': #if defined(TN3270) && defined(unix) /* distinguish between "-n oasynch" and "-noasynch" */ if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == 'n' && argv[optind - 1][2] == 'o') { if (!strcmp(optarg, "oasynch")) { noasynchtty = 1; noasynchnet = 1; } else if (!strcmp(optarg, "oasynchtty")) noasynchtty = 1; else if (!strcmp(optarg, "oasynchnet")) noasynchnet = 1; } else #endif /* defined(TN3270) && defined(unix) */ SetNetTrace(optarg); break; case 'r': rlogin = '~'; break; case 's': src_addr = optarg; break; case 't': #if defined(TN3270) && defined(unix) transcom = tline; (void)strcpy(transcom, optarg); #else fprintf(stderr, "%s: Warning: -t ignored, no TN3270 support.\n", prompt); #endif break; case 'x': #ifdef ENCRYPTION encrypt_auto(1); decrypt_auto(1); #else /* ENCRYPTION */ fprintf(stderr, "%s: Warning: -x ignored, no ENCRYPT support.\n", prompt); #endif /* ENCRYPTION */ break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); /* NOTREACHED */ } } if (autologin == -1) autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1; argc -= optind; argv += optind; if (argc) { char *args[9], **argp = args; if (argc > 2) usage(); *argp++ = prompt; if (user) { *argp++ = "-l"; *argp++ = user; } if (src_addr) { *argp++ = "-s"; *argp++ = src_addr; } *argp++ = argv[0]; /* host */ if (argc > 1) *argp++ = argv[1]; /* port */ *argp = 0; if (setjmp(toplevel) != 0) Exit(0); if (tn(argp - args, args) == 1) return (0); else return (1); } (void)setjmp(toplevel); for (;;) { #ifdef TN3270 if (shell_active) shell_continue(); else #endif command(1, 0, 0); } return 0; } diff --git a/contrib/telnet/telnet/telnet.1 b/contrib/telnet/telnet/telnet.1 index 984146232215..3378d3a4ad92 100644 --- a/contrib/telnet/telnet/telnet.1 +++ b/contrib/telnet/telnet/telnet.1 @@ -1,1380 +1,1384 @@ .\" Copyright (c) 1983, 1990, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)telnet.1 8.6 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME .Nm telnet .Nd user interface to the .Tn TELNET protocol .Sh SYNOPSIS .Nm telnet .Op Fl 8EFKLNacdfrx .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl e Ar escapechar .Op Fl k Ar realm .Op Fl l Ar user .Op Fl n Ar tracefile .Op Fl s Ar src_addr .Oo .Ar host .Op port .Oc .Sh DESCRIPTION The .Nm telnet command is used to communicate with another host using the .Tn TELNET protocol. If .Nm telnet is invoked without the .Ar host argument, it enters command mode, indicated by its prompt .Pq Nm telnet\&> . In this mode, it accepts and executes the commands listed below. If it is invoked with arguments, it performs an .Ic open command with those arguments. .Pp Options: .Bl -tag -width indent .It Fl 8 Specifies an 8-bit data path. This causes an attempt to negotiate the .Dv TELNET BINARY option on both input and output. .It Fl E Stops any character from being recognized as an escape character. .It Fl F If Kerberos V5 authentication is being used, the .Fl F option allows the local credentials to be forwarded to the remote system, including any credentials that have already been forwarded into the local environment. .It Fl K Specifies no automatic login to the remote system. .It Fl L Specifies an 8-bit data path on output. This causes the BINARY option to be negotiated on output. .It Fl N Prevents IP address to name lookup when destination host is given as an IP address. .It Fl S Ar tos Sets the IP type-of-service (TOS) option for the telnet connection to the value .Ar tos, which can be a numeric TOS value or, on systems that support it, a symbolic TOS name found in the /etc/iptos file. .It Fl X Ar atype Disables the .Ar atype type of authentication. .It Fl a Attempt automatic login. Currently, this sends the user name via the .Ev USER variable of the .Ev ENVIRON option if supported by the remote system. The name used is that of the current user as returned by .Xr getlogin 2 if it agrees with the current user ID, otherwise it is the name associated with the user ID. .It Fl c Disables the reading of the user's .Pa \&.telnetrc file. (See the .Ic toggle skiprc command on this man page.) .It Fl d Sets the initial value of the .Ic debug toggle to .Dv TRUE .It Fl e Ar escape char Sets the initial .Nm .Nm telnet escape character to .Ar escape char. If .Ar escape char is omitted, then there will be no escape character. .It Fl f If Kerberos V5 authentication is being used, the .Fl f option allows the local credentials to be forwarded to the remote system. .ne 1i .It Fl k Ar realm If Kerberos authentication is being used, the .Fl k option requests that telnet obtain tickets for the remote host in realm realm instead of the remote host's realm, as determined by .Xr krb_realmofhost 3 . .It Fl l Ar user When connecting to the remote system, if the remote system understands the .Ev ENVIRON option, then .Ar user will be sent to the remote system as the value for the variable USER. This option implies the .Fl a option. This option may also be used with the .Ic open command. .It Fl n Ar tracefile Opens .Ar tracefile for recording trace information. See the .Ic set tracefile command below. .It Fl r Specifies a user interface similar to .Xr rlogin 1 . In this mode, the escape character is set to the tilde (~) character, unless modified by the -e option. .It Fl s Ar src_addr Set the source IP address for the .Nm connection to .Ar src_addr , which can be an IP address or a host name. .It Fl x Turns on encryption of the data stream if possible. This option is not available outside of the United States and Canada. .It Ar host Indicates the official name, an alias, or the Internet address of a remote host. .It Ar port Indicates a port number (address of an application). If a number is not specified, the default .Nm telnet port is used. .El .Pp When in rlogin mode, a line of the form ~. disconnects from the remote host; ~ is the telnet escape character. Similarly, the line ~^Z suspends the telnet session. The line ~^] escapes to the normal telnet escape prompt. .Pp Once a connection has been opened, .Nm telnet will attempt to enable the .Dv TELNET LINEMODE option. If this fails, then .Nm telnet will revert to one of two input modes: either \*(Lqcharacter at a time\*(Rq or \*(Lqold line by line\*(Rq depending on what the remote system supports. .Pp When .Dv LINEMODE is enabled, character processing is done on the local system, under the control of the remote system. When input editing or character echoing is to be disabled, the remote system will relay that information. The remote system will also relay changes to any special characters that happen on the remote system, so that they can take effect on the local system. .Pp In \*(Lqcharacter at a time\*(Rq mode, most text typed is immediately sent to the remote host for processing. .Pp In \*(Lqold line by line\*(Rq mode, all text is echoed locally, and (normally) only completed lines are sent to the remote host. The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used to turn off and on the local echo (this would mostly be used to enter passwords without the password being echoed). .Pp If the .Dv LINEMODE option is enabled, or if the .Ic localchars toggle is .Dv TRUE (the default for \*(Lqold line by line\*(Lq; see below), the user's .Ic quit , .Ic intr , and .Ic flush characters are trapped locally, and sent as .Tn TELNET protocol sequences to the remote side. If .Dv LINEMODE has ever been enabled, then the user's .Ic susp and .Ic eof are also sent as .Tn TELNET protocol sequences, and .Ic quit is sent as a .Dv TELNET ABORT instead of .Dv BREAK There are options (see .Ic toggle .Ic autoflush and .Ic toggle .Ic autosynch below) which cause this action to flush subsequent output to the terminal (until the remote host acknowledges the .Tn TELNET sequence) and flush previous terminal input (in the case of .Ic quit and .Ic intr ) . .Pp While connected to a remote host, .Nm telnet command mode may be entered by typing the .Nm telnet \*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq). When in command mode, the normal terminal editing conventions are available. .Pp The following .Nm telnet commands are available. Only enough of each command to uniquely identify it need be typed (this is also true for arguments to the .Ic mode , .Ic set , .Ic toggle , .Ic unset , .Ic slc , .Ic environ , and .Ic display commands). .Pp .Bl -tag -width "mode type" .It Ic auth Ar argument ... The auth command manipulates the information sent through the .Dv TELNET AUTHENTICATE option. Valid arguments for the auth command are as follows: .Bl -tag -width "disable type" .It Ic disable Ar type Disables the specified type of authentication. To obtain a list of available types, use the .Ic auth disable \&? command. .It Ic enable Ar type Enables the specified type of authentication. To obtain a list of available types, use the .Ic auth enable \&? command. .It Ic status Lists the current status of the various types of authentication. .El .It Ic close Close a .Tn TELNET session and return to command mode. .It Ic display Ar argument ... Displays all, or some, of the .Ic set and .Ic toggle values (see below). .It Ic encrypt Ar argument ... The encrypt command manipulates the information sent through the .Dv TELNET ENCRYPT option. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside of the United States and Canada. .Pp Valid arguments for the encrypt command are as follows: .Bl -tag -width Ar .It Ic disable Ar type Ic [input|output] Disables the specified type of encryption. If you omit the input and output, both input and output are disabled. To obtain a list of available types, use the .Ic encrypt disable \&? command. .It Ic enable Ar type Ic [input|output] Enables the specified type of encryption. If you omit input and output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic input This is the same as the .Ic encrypt start input command. .It Ic -input This is the same as the .Ic encrypt stop input command. .It Ic output This is the same as the .Ic encrypt start output command. .It Ic -output This is the same as the .Ic encrypt stop output command. .It Ic start Ic [input|output] Attempts to start encryption. If you omit .Ic input and .Ic output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic status Lists the current status of encryption. .It Ic stop Ic [input|output] Stops encryption. If you omit input and output, encryption is on both input and output. .It Ic type Ar type Sets the default type of encryption to be used with later .Ic encrypt start or .Ic encrypt stop commands. .El .It Ic environ Ar arguments... The .Ic environ command is used to manipulate the the variables that my be sent through the .Dv TELNET ENVIRON option. The initial set of variables is taken from the users environment, with only the .Ev DISPLAY and .Ev PRINTER variables being exported by default. The .Ev USER variable is also exported if the .Fl a or .Fl l options are used. .br Valid arguments for the .Ic environ command are: .Bl -tag -width Fl .It Ic define Ar variable value Define the variable .Ar variable to have a value of .Ar value. Any variables defined by this command are automatically exported. The .Ar value may be enclosed in single or double quotes so that tabs and spaces may be included. .It Ic undefine Ar variable Remove .Ar variable from the list of environment variables. .It Ic export Ar variable Mark the variable .Ar variable to be exported to the remote side. .It Ic unexport Ar variable Mark the variable .Ar variable to not be exported unless explicitly asked for by the remote side. .It Ic list List the current set of environment variables. Those marked with a .Cm * will be sent automatically, other variables will only be sent if explicitly requested. .It Ic \&? Prints out help information for the .Ic environ command. .El .It Ic logout Sends the .Dv TELNET LOGOUT option to the remote side. This command is similar to a .Ic close command; however, if the remote side does not support the .Dv LOGOUT option, nothing happens. If, however, the remote side does support the .Dv LOGOUT option, this command should cause the remote side to close the .Tn TELNET connection. If the remote side also supports the concept of suspending a user's session for later reattachment, the logout argument indicates that you should terminate the session immediately. .It Ic mode Ar type .Ar Type is one of several options, depending on the state of the .Tn TELNET session. The remote host is asked for permission to go into the requested mode. If the remote host is capable of entering that mode, the requested mode will be entered. .Bl -tag -width Ar .It Ic character Disable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then enter \*(Lqcharacter at a time\*(Lq mode. .It Ic line Enable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then attempt to enter \*(Lqold-line-by-line\*(Lq mode. .It Ic isig Pq Ic \-isig Attempt to enable (disable) the .Dv TRAPSIG mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic edit Pq Ic \-edit Attempt to enable (disable) the .Dv EDIT mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic softtabs Pq Ic \-softtabs Attempt to enable (disable) the .Dv SOFT_TAB mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .ne 1i .It Ic litecho Pq Ic \-litecho Attempt to enable (disable) the .Dv LIT_ECHO mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic \&? Prints out help information for the .Ic mode command. .El .It Xo .Ic open Ar host .Oo Op Fl l .Ar user .Oc Ns Oo Fl .Ar port Oc .Xc Open a connection to the named host. If no port number is specified, .Nm telnet will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Op Fl l option may be used to specify the user name to be passed to the remote system via the .Ev ENVIRON option. When connecting to a non-standard port, .Nm telnet omits any automatic initiation of .Tn TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done. After establishing a connection, the file .Pa \&.telnetrc in the users home directory is opened. Lines beginning with a # are comment lines. Blank lines are ignored. Lines that begin without white space are the start of a machine entry. The first thing on the line is the name of the machine that is being connected to. The rest of the line, and successive lines that begin with white space are assumed to be .Nm telnet commands and are processed as if they had been typed in manually to the .Nm telnet command prompt. .It Ic quit Close any open .Tn TELNET session and exit .Nm telnet . An end of file (in command mode) will also close a session and exit. .It Ic send Ar arguments Sends one or more special character sequences to the remote host. The following are the arguments which may be specified (more than one argument may be specified at a time): .Pp .Bl -tag -width escape .It Ic abort Sends the .Dv TELNET ABORT (Abort processes) sequence. .It Ic ao Sends the .Dv TELNET AO (Abort Output) sequence, which should cause the remote system to flush all output .Em from the remote system .Em to the user's terminal. .It Ic ayt Sends the .Dv TELNET AYT (Are You There) sequence, to which the remote system may or may not choose to respond. .It Ic brk Sends the .Dv TELNET BRK (Break) sequence, which may have significance to the remote system. .It Ic ec Sends the .Dv TELNET EC (Erase Character) sequence, which should cause the remote system to erase the last character entered. .It Ic el Sends the .Dv TELNET EL (Erase Line) sequence, which should cause the remote system to erase the line currently being entered. .It Ic eof Sends the .Dv TELNET EOF (End Of File) sequence. .It Ic eor Sends the .Dv TELNET EOR (End of Record) sequence. .It Ic escape Sends the current .Nm telnet escape character (initially \*(Lq^\*(Rq). .It Ic ga Sends the .Dv TELNET GA (Go Ahead) sequence, which likely has no significance to the remote system. .It Ic getstatus If the remote side supports the .Dv TELNET STATUS command, .Ic getstatus will send the subnegotiation to request that the server send its current option status. .ne 1i .It Ic ip Sends the .Dv TELNET IP (Interrupt Process) sequence, which should cause the remote system to abort the currently running process. .It Ic nop Sends the .Dv TELNET NOP (No OPeration) sequence. .It Ic susp Sends the .Dv TELNET SUSP (SUSPend process) sequence. .It Ic synch Sends the .Dv TELNET SYNCH sequence. This sequence causes the remote system to discard all previously typed (but not yet read) input. This sequence is sent as .Tn TCP urgent data (and may not work if the remote system is a .Bx 4.2 system -- if it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal). .It Ic do Ar cmd .It Ic dont Ar cmd .It Ic will Ar cmd .It Ic wont Ar cmd Sends the .Dv TELNET DO .Ar cmd sequence. .Ar Cmd can be either a decimal number between 0 and 255, or a symbolic name for a specific .Dv TELNET command. .Ar Cmd can also be either .Ic help or .Ic \&? to print out help information, including a list of known symbolic names. .It Ic \&? Prints out help information for the .Ic send command. .El .It Ic set Ar argument value .It Ic unset Ar argument value The .Ic set command will set any one of a number of .Nm telnet variables to a specific value or to .Dv TRUE . The special value .Ic off turns off the function associated with the variable, this is equivalent to using the .Ic unset command. The .Ic unset command will disable or set to .Dv FALSE any of the specified functions. The values of variables may be interrogated with the .Ic display command. The variables which may be set or unset, but not toggled, are listed here. In addition, any of the variables for the .Ic toggle command may be explicitly set or unset using the .Ic set and .Ic unset commands. .Bl -tag -width escape .It Ic ayt If .Tn TELNET is in localchars mode, or .Dv LINEMODE is enabled, and the status character is typed, a .Dv TELNET AYT sequence (see .Ic send ayt preceding) is sent to the remote host. The initial value for the "Are You There" character is the terminal's status character. .It Ic echo This is the value (initially \*(Lq^E\*(Rq) which, when in \*(Lqline by line\*(Rq mode, toggles between doing local echoing of entered characters (for normal processing), and suppressing echoing of entered characters (for entering, say, a password). .It Ic eof If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, entering this character as the first character on a line will cause this character to be sent to the remote system. The initial value of the eof character is taken to be the terminal's .Ic eof character. .It Ic erase If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Sy and if .Nm telnet is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EC sequence (see .Ic send .Ic ec above) is sent to the remote system. The initial value for the erase character is taken to be the terminal's .Ic erase character. .It Ic escape This is the .Nm telnet escape character (initially \*(Lq^[\*(Rq) which causes entry into .Nm telnet command mode (when connected to a remote system). .It Ic flushoutput If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic flushoutput character is typed, a .Dv TELNET AO sequence (see .Ic send .Ic ao above) is sent to the remote host. The initial value for the flush character is taken to be the terminal's .Ic flush character. .It Ic forw1 .It Ic forw2 If .Tn TELNET is operating in .Dv LINEMODE , these are the characters that, when typed, cause partial lines to be forwarded to the remote system. The initial value for the forwarding characters are taken from the terminal's eol and eol2 characters. .It Ic interrupt If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic interrupt character is typed, a .Dv TELNET IP sequence (see .Ic send .Ic ip above) is sent to the remote host. The initial value for the interrupt character is taken to be the terminal's .Ic intr character. .It Ic kill If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Ic and if .Nm telnet is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EL sequence (see .Ic send .Ic el above) is sent to the remote system. The initial value for the kill character is taken to be the terminal's .Ic kill character. .It Ic lnext If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic lnext character. The initial value for the lnext character is taken to be the terminal's .Ic lnext character. .It Ic quit If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic quit character is typed, a .Dv TELNET BRK sequence (see .Ic send .Ic brk above) is sent to the remote host. The initial value for the quit character is taken to be the terminal's .Ic quit character. .It Ic reprint If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic reprint character. The initial value for the reprint character is taken to be the terminal's .Ic reprint character. .It Ic rlogin This is the rlogin escape character. If set, the normal .Tn TELNET escape character is ignored unless it is preceded by this character at the beginning of a line. This character, at the beginning of a line followed by a "." closes the connection; when followed by a ^Z it suspends the telnet command. The initial state is to disable the rlogin escape character. .It Ic start If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic start character. The initial value for the kill character is taken to be the terminal's .Ic start character. .It Ic stop If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic stop character. The initial value for the kill character is taken to be the terminal's .Ic stop character. .It Ic susp If .Nm telnet is in .Ic localchars mode, or .Dv LINEMODE is enabled, and the .Ic suspend character is typed, a .Dv TELNET SUSP sequence (see .Ic send .Ic susp above) is sent to the remote host. The initial value for the suspend character is taken to be the terminal's .Ic suspend character. .ne 1i .It Ic tracefile This is the file to which the output, caused by .Ic netdata or .Ic option tracing being .Dv TRUE , will be written. If it is set to .Dq Fl , then tracing information will be written to standard output (the default). .It Ic worderase If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic worderase character. The initial value for the worderase character is taken to be the terminal's .Ic worderase character. .It Ic \&? Displays the legal .Ic set .Pq Ic unset commands. .El .It Ic skey Ar sequence challenge The .Ic skey command computes a response to the S/Key challenge. .It Ic slc Ar state The .Ic slc command (Set Local Characters) is used to set or change the state of the the special characters when the .Dv TELNET LINEMODE option has been enabled. Special characters are characters that get mapped to .Tn TELNET commands sequences (like .Ic ip or .Ic quit ) or line editing characters (like .Ic erase and .Ic kill ) . By default, the local special characters are exported. .Bl -tag -width Fl .It Ic check Verify the current settings for the current special characters. The remote side is requested to send all the current special character settings, and if there are any discrepancies with the local side, the local side will switch to the remote value. .It Ic export Switch to the local defaults for the special characters. The local default characters are those of the local terminal at the time when .Nm telnet was started. .It Ic import Switch to the remote defaults for the special characters. The remote default characters are those of the remote system at the time when the .Tn TELNET connection was established. .It Ic \&? Prints out help information for the .Ic slc command. .El .It Ic status Show the current status of .Nm telnet . This includes the peer one is connected to, as well as the current mode. .It Ic toggle Ar arguments ... Toggle (between .Dv TRUE and .Dv FALSE ) various flags that control how .Nm telnet responds to events. These flags may be set explicitly to .Dv TRUE or .Dv FALSE using the .Ic set and .Ic unset commands listed above. More than one argument may be specified. The state of these flags may be interrogated with the .Ic display command. Valid arguments are: .Bl -tag -width Ar .It Ic authdebug Turns on debugging information for the authentication code. .It Ic autoflush If .Ic autoflush and .Ic localchars are both .Dv TRUE , then when the .Ic ao , or .Ic quit characters are recognized (and transformed into .Tn TELNET sequences; see .Ic set above for details), .Nm telnet refuses to display any data on the user's terminal until the remote system acknowledges (via a .Dv TELNET TIMING MARK option) that it has processed those .Tn TELNET sequences. The initial value for this toggle is .Dv TRUE if the terminal user had not done an "stty noflsh", otherwise .Dv FALSE (see .Xr stty 1 ) . .It Ic autodecrypt When the .Dv TELNET ENCRYPT option is negotiated, by default the actual encryption (decryption) of the data stream does not start automatically. The autoencrypt (autodecrypt) command states that encryption of the output (input) stream should be enabled as soon as possible. .sp .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside the United States and Canada. .It Ic autologin If the remote side supports the .Dv TELNET AUTHENTICATION option .Tn TELNET attempts to use it to perform automatic authentication. If the .Dv AUTHENTICATION option is not supported, the user's login name are propagated through the .Dv TELNET ENVIRON option. This command is the same as specifying .Ar a option on the .Ic open command. .It Ic autosynch If .Ic autosynch and .Ic localchars are both .Dv TRUE , then when either the .Ic intr or .Ic quit characters is typed (see .Ic set above for descriptions of the .Ic intr and .Ic quit characters), the resulting .Tn TELNET sequence sent is followed by the .Dv TELNET SYNCH sequence. This procedure .Ic should cause the remote system to begin throwing away all previously typed input until both of the .Tn TELNET sequences have been read and acted upon. The initial value of this toggle is .Dv FALSE . .It Ic binary Enable or disable the .Dv TELNET BINARY option on both input and output. .It Ic inbinary Enable or disable the .Dv TELNET BINARY option on input. .It Ic outbinary Enable or disable the .Dv TELNET BINARY option on output. .It Ic crlf If this is .Dv TRUE , then carriage returns will be sent as .Li . If this is .Dv FALSE , then carriage returns will be send as .Li . The initial value for this toggle is .Dv FALSE . .It Ic crmod Toggle carriage return mode. When this mode is enabled, most carriage return characters received from the remote host will be mapped into a carriage return followed by a line feed. This mode does not affect those characters typed by the user, only those received from the remote host. This mode is not very useful unless the remote host only sends carriage return, but never line feed. The initial value for this toggle is .Dv FALSE . .It Ic debug Toggles socket level debugging (useful only to the .Ic super user ) . The initial value for this toggle is .Dv FALSE . .It Ic encdebug Turns on debugging information for the encryption code. .It Ic localchars If this is .Dv TRUE , then the .Ic flush , .Ic interrupt , .Ic quit , .Ic erase , and .Ic kill characters (see .Ic set above) are recognized locally, and transformed into (hopefully) appropriate .Tn TELNET control sequences (respectively .Ic ao , .Ic ip , .Ic brk , .Ic ec , and .Ic el ; see .Ic send above). The initial value for this toggle is .Dv TRUE in \*(Lqold line by line\*(Rq mode, and .Dv FALSE in \*(Lqcharacter at a time\*(Rq mode. When the .Dv LINEMODE option is enabled, the value of .Ic localchars is ignored, and assumed to always be .Dv TRUE . If .Dv LINEMODE has ever been enabled, then .Ic quit is sent as .Ic abort , and .Ic eof and .B suspend are sent as .Ic eof and .Ic susp , see .Ic send above). .It Ic netdata Toggles the display of all network data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic options Toggles the display of some internal .Nm telnet protocol processing (having to do with .Tn TELNET options). The initial value for this toggle is .Dv FALSE . .ne 1i .It Ic prettydump When the .Ic netdata toggle is enabled, if .Ic prettydump is enabled the output from the .Ic netdata command will be formatted in a more user readable format. Spaces are put between each character in the output, and the beginning of any .Tn TELNET escape sequence is preceded by a '*' to aid in locating them. .It Ic skiprc When the skiprc toggle is .Dv TRUE , .Tn TELNET skips the reading of the .Pa \&.telnetrc file in the users home directory when connections are opened. The initial value for this toggle is .Dv FALSE. .It Ic termdata Toggles the display of all terminal data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic verbose_encrypt When the .Ic verbose_encrypt toggle is .Dv TRUE , .Tn TELNET prints out a message each time encryption is enabled or disabled. The initial value for this toggle is .Dv FALSE. Note: Because of export controls, data encryption is not supported outside of the United States and Canada. .It Ic \&? Displays the legal .Ic toggle commands. .El .It Ic z Suspend .Nm telnet . This command only works when the user is using the .Xr csh 1 . .It Ic \&! Op Ar command Execute a single command in a subshell on the local system. If .Ic command is omitted, then an interactive subshell is invoked. .It Ic \&? Op Ar command Get help. With no arguments, .Nm telnet prints a help summary. If a command is specified, .Nm telnet will print the help information for just that command. .El .Sh ENVIRONMENT .Nm Telnet uses at least the .Ev HOME , .Ev SHELL , .Ev DISPLAY , and .Ev TERM environment variables. Other environment variables may be propagated to the other side via the .Dv TELNET ENVIRON option. .Sh FILES .Bl -tag -width ~/.telnetrc -compact .It Pa ~/.telnetrc user customized telnet startup values .El .Sh HISTORY The .Nm Telnet command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in \*(Lqold line by line\*(Rq mode. .Pp In \*(Lqold line by line\*(Rq mode or .Dv LINEMODE the terminal's .Ic eof character is only recognized (and sent to the remote system) when it is the first character on a line. diff --git a/contrib/telnet/telnetd/telnetd.8 b/contrib/telnet/telnetd/telnetd.8 index e03f290daaa9..4e004a4edede 100644 --- a/contrib/telnet/telnetd/telnetd.8 +++ b/contrib/telnet/telnetd/telnetd.8 @@ -1,612 +1,615 @@ .\" Copyright (c) 1983, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)telnetd.8 8.4 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNETD 8 .Os BSD 4.2 .Sh NAME .Nm telnetd .Nd DARPA .Tn TELNET protocol server .Sh SYNOPSIS .Nm /usr/libexec/telnetd .Op Fl BUhlkns .Op Fl D Ar debugmode .Op Fl I Ns Ar initid .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl a Ar authmode .Op Fl edebug .Op Fl p Ar loginprog .Op Fl r Ns Ar lowpty-highpty .Op Fl u Ar len .Op Fl debug Op Ar port .Sh DESCRIPTION The .Nm command is a server which supports the .Tn DARPA standard .Tn TELNET virtual terminal protocol. .Nm Telnetd is normally invoked by the internet server (see .Xr inetd 8 ) for requests to connect to the .Tn TELNET port as indicated by the .Pa /etc/services file (see .Xr services 5 ). The .Fl debug option may be used to start up .Nm manually, instead of through .Xr inetd 8 . If started up this way, .Ar port may be specified to run .Nm on an alternate .Tn TCP port number. .Pp The .Nm command accepts the following options: .Bl -tag -width indent .It Fl a Ar authmode This option may be used for specifying what mode should be used for authentication. Note that this option is only useful if .Nm has been compiled with support for the .Dv AUTHENTICATION option. There are several valid values for .Ar authmode : .Bl -tag -width debug .It Cm debug Turn on authentication debugging code. .It Cm user Only allow connections when the remote user can provide valid authentication information to identify the remote user, and is allowed access to the specified account without providing a password. .It Cm valid Only allow connections when the remote user can provide valid authentication information to identify the remote user. The .Xr login 1 command will provide any additional user verification needed if the remote user is not allowed automatic access to the specified account. .It Cm other Only allow connections that supply some authentication information. This option is currently not supported by any of the existing authentication mechanisms, and is thus the same as specifying .Fl a .Cm valid . .It Cm none This is the default state. Authentication information is not required. If no or insufficient authentication information is provided, then the .Xr login 1 program will provide the necessary user verification. .It Cm off Disable the authentication code. All user verification will happen through the .Xr login 1 program. .El .It Fl B Specify bftp server mode. In this mode, .Nm causes login to start a .Xr bftp 1 session rather than the user's normal shell. In bftp daemon mode normal logins are not supported, and it must be used on a port other than the normal .Tn TELNET port. .It Fl D Ar debugmode This option may be used for debugging purposes. This allows .Nm to print out debugging information to the connection, allowing the user to see what .Nm is doing. There are several possible values for .Ar debugmode : .Bl -tag -width exercise .It Cm options Print information about the negotiation of .Tn TELNET options. .It Cm report Print the .Cm options information, plus some additional information about what processing is going on. .It Cm netdata Display the data stream received by .Nm Ns . .It Cm ptydata Display data written to the pty. .It Cm exercise Has not been implemented yet. .El .It Fl debug Enable debugging on each socket created by .Nm (see .Dv SO_DEBUG in .Xr socket 2 ) . .It Fl edebug If .Nm has been compiled with support for data encryption, then the .Fl edebug option may be used to enable encryption debugging code. .It Fl p Ar loginprog Specify an alternate .Xr login 1 command to run to complete the login. The alternate command must understand the same command arguments as the standard login. .It Fl h Disable the printing of host-specific information before login has been completed. .It Fl I Ar initid This option is only applicable to .Tn UNICOS systems prior to 7.0. It specifies the .Dv ID from .Pa /etc/inittab to use when init starts login sessions. The default .Dv ID is .Dv fe. .It Fl k This option is only useful if .Nm has been compiled with both linemode and kludge linemode support. If the .Fl k option is specified, then if the remote client does not support the .Dv LINEMODE option, then .Nm will operate in character at a time mode. It will still support kludge linemode, but will only go into kludge linemode if the remote client requests it. (This is done by the client sending .Dv DONT SUPPRESS-GO-AHEAD and .Dv DONT ECHO . ) The .Fl k option is most useful when there are remote clients that do not support kludge linemode, but pass the heuristic (if they respond with .Dv WILL TIMING-MARK in response to a .Dv DO TIMING-MARK) for kludge linemode support. .It Fl l Specify line mode. Try to force clients to use line- at-a-time mode. If the .Dv LINEMODE option is not supported, it will go into kludge linemode. .It Fl n Disable .Dv TCP keep-alives. Normally .Nm enables the .Tn TCP keep-alive mechanism to probe connections that have been idle for some period of time to determine if the client is still there, so that idle connections from machines that have crashed or can no longer be reached may be cleaned up. .It Fl r Ar lowpty-highpty This option is only enabled when .Nm is compiled for .Dv UNICOS. It specifies an inclusive range of pseudo-terminal devices to use. If the system has sysconf variable .Dv _SC_CRAY_NPTY configured, the default pty search range is 0 to .Dv _SC_CRAY_NPTY; otherwise, the default range is 0 to 128. Either .Ar lowpty or .Ar highpty may be omitted to allow changing either end of the search range. If .Ar lowpty is omitted, the - character is still required so that .Nm can differentiate .Ar highpty from .Ar lowpty . .It Fl s This option is only enabled if .Nm is compiled with support for .Tn SecurID cards. It causes the .Fl s option to be passed on to .Xr login 1 , and thus is only useful if .Xr login 1 supports the .Fl s flag to indicate that only .Tn SecurID validated logins are allowed, and is usually useful for controlling remote logins from outside of a firewall. .It Fl S Ar tos .It Fl u Ar len This option is used to specify the size of the field in the .Dv utmp structure that holds the remote host name. If the resolved host name is longer than .Ar len , the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying .Fl u0 indicates that only dotted decimal addresses should be put into the .Pa utmp file. .ne 1i .It Fl U This option causes .Nm to refuse connections from addresses that cannot be mapped back into a symbolic name via the .Xr gethostbyaddr 3 routine. .It Fl X Ar authtype This option is only valid if .Nm has been built with support for the authentication option. It disables the use of .Ar authtype authentication, and can be used to temporarily disable a specific authentication type without having to recompile .Nm Ns . .El .Pp .Nm Telnetd operates by allocating a pseudo-terminal device (see .Xr pty 4 ) for a client, then creating a login process which has the slave side of the pseudo-terminal as .Dv stdin , .Dv stdout and .Dv stderr . .Nm Telnetd manipulates the master side of the pseudo-terminal, implementing the .Tn TELNET protocol and passing characters between the remote client and the login process. .Pp When a .Tn TELNET session is started up, .Nm sends .Tn TELNET options to the client side indicating a willingness to do the following .Tn TELNET options, which are described in more detail below: .Bd -literal -offset indent DO AUTHENTICATION WILL ENCRYPT DO TERMINAL TYPE DO TSPEED DO XDISPLOC DO NEW-ENVIRON DO ENVIRON WILL SUPPRESS GO AHEAD DO ECHO DO LINEMODE DO NAWS WILL STATUS DO LFLOW DO TIMING-MARK .Ed .Pp The pseudo-terminal allocated to the client is configured to operate in \*(lqcooked\*(rq mode, and with .Dv XTABS and .Dv CRMOD enabled (see .Xr tty 4 ) . .Pp .Nm Telnetd has support for enabling locally the following .Tn TELNET options: .Bl -tag -width "DO AUTHENTICATION" .It "WILL ECHO" When the .Dv LINEMODE option is enabled, a .Dv WILL ECHO or .Dv WONT ECHO will be sent to the client to indicate the current state of terminal echoing. When terminal echo is not desired, a .Dv WILL ECHO is sent to indicate that .Nm will take care of echoing any data that needs to be echoed to the terminal, and then nothing is echoed. When terminal echo is desired, a .Dv WONT ECHO is sent to indicate that .Nm will not be doing any terminal echoing, so the client should do any terminal echoing that is needed. .It "WILL BINARY" Indicate that the client is willing to send a 8 bits of data, rather than the normal 7 bits of the Network Virtual Terminal. .It "WILL SGA" Indicate that it will not be sending .Dv IAC GA, go ahead, commands. .It "WILL STATUS" Indicate a willingness to send the client, upon request, of the current status of all .Tn TELNET options. .It "WILL TIMING-MARK" Whenever a .Dv DO TIMING-MARK command is received, it is always responded to with a .Dv WILL TIMING-MARK .ne 1i .It "WILL LOGOUT" When a .Dv DO LOGOUT is received, a .Dv WILL LOGOUT is sent in response, and the .Tn TELNET session is shut down. .It "WILL ENCRYPT" Only sent if .Nm is compiled with support for data encryption, and indicates a willingness to decrypt the data stream. .El .Pp .Nm Telnetd has support for enabling remotely the following .Tn TELNET options: .Bl -tag -width "DO AUTHENTICATION" .It "DO BINARY" Sent to indicate that .Nm is willing to receive an 8 bit data stream. .It "DO LFLOW" Requests that the client handle flow control characters remotely. .It "DO ECHO" This is not really supported, but is sent to identify a 4.2BSD .Xr telnet 1 client, which will improperly respond with .Dv WILL ECHO. If a .Dv WILL ECHO is received, a .Dv DONT ECHO will be sent in response. .It "DO TERMINAL-TYPE" Indicate a desire to be able to request the name of the type of terminal that is attached to the client side of the connection. .It "DO SGA" Indicate that it does not need to receive .Dv IAC GA, the go ahead command. .It "DO NAWS" Requests that the client inform the server when the window (display) size changes. .It "DO TERMINAL-SPEED" Indicate a desire to be able to request information about the speed of the serial line to which the client is attached. .It "DO XDISPLOC" Indicate a desire to be able to request the name of the X Window System display that is associated with the telnet client. .It "DO NEW-ENVIRON" Indicate a desire to be able to request environment variable information, as described in RFC 1572. .It "DO ENVIRON" Indicate a desire to be able to request environment variable information, as described in RFC 1408. .It "DO LINEMODE" Only sent if .Nm is compiled with support for linemode, and requests that the client do line by line processing. .It "DO TIMING-MARK" Only sent if .Nm is compiled with support for both linemode and kludge linemode, and the client responded with .Dv WONT LINEMODE. If the client responds with .Dv WILL TM, the it is assumed that the client supports kludge linemode. Note that the .Op Fl k option can be used to disable this. .It "DO AUTHENTICATION" Only sent if .Nm is compiled with support for authentication, and indicates a willingness to receive authentication information for automatic login. .It "DO ENCRYPT" Only sent if .Nm is compiled with support for data encryption, and indicates a willingness to decrypt the data stream. .Sh ENVIRONMENT .Sh FILES .Bl -tag -width /usr/ucb/bftp -compact .It Pa /etc/services .It Pa /etc/inittab (UNICOS systems only) .It Pa /etc/iptos (if supported) .It Pa /usr/ucb/bftp (if supported) .El .Sh "SEE ALSO" .Xr bftp 1 , .Xr login 1 , .Xr telnet 1 (if supported) .Sh STANDARDS .Bl -tag -compact -width RFC-1572 .It Cm RFC-854 .Tn TELNET PROTOCOL SPECIFICATION .It Cm RFC-855 TELNET OPTION SPECIFICATIONS .It Cm RFC-856 TELNET BINARY TRANSMISSION .It Cm RFC-857 TELNET ECHO OPTION .It Cm RFC-858 TELNET SUPPRESS GO AHEAD OPTION .It Cm RFC-859 TELNET STATUS OPTION .It Cm RFC-860 TELNET TIMING MARK OPTION .It Cm RFC-861 TELNET EXTENDED OPTIONS - LIST OPTION .It Cm RFC-885 TELNET END OF RECORD OPTION .It Cm RFC-1073 Telnet Window Size Option .It Cm RFC-1079 Telnet Terminal Speed Option .It Cm RFC-1091 Telnet Terminal-Type Option .It Cm RFC-1096 Telnet X Display Location Option .It Cm RFC-1123 Requirements for Internet Hosts -- Application and Support .It Cm RFC-1184 Telnet Linemode Option .It Cm RFC-1372 Telnet Remote Flow Control Option .It Cm RFC-1416 Telnet Authentication Option .It Cm RFC-1411 Telnet Authentication: Kerberos Version 4 .It Cm RFC-1412 Telnet Authentication: SPX .It Cm RFC-1571 Telnet Environment Option Interoperability Issues .It Cm RFC-1572 Telnet Environment Option .Sh BUGS Some .Tn TELNET commands are only partially implemented. .Pp Because of bugs in the original 4.2 BSD .Xr telnet 1 , .Nm performs some dubious protocol exchanges to try to discover if the remote client is, in fact, a 4.2 BSD .Xr telnet 1 . .Pp Binary mode has no common interpretation except between similar operating systems (Unix in this case). .Pp The terminal type name received from the remote client is converted to lower case. .Pp .Nm Telnetd never sends .Tn TELNET .Dv IAC GA (go ahead) commands. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. diff --git a/contrib/telnet/telnetd/telnetd.c b/contrib/telnet/telnetd/telnetd.c index 57e6ed2a7a79..6d7bcde5fc4f 100644 --- a/contrib/telnet/telnetd/telnetd.c +++ b/contrib/telnet/telnetd/telnetd.c @@ -1,1614 +1,1634 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include "telnetd.h" #include "pathnames.h" #if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) /* * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can * use it to tell us to turn off all the socket security code, * since that is only used in UNICOS 7.0 and later. */ # undef _SC_CRAY_SECURE_SYS #endif #include #include #include #include #if defined(_SC_CRAY_SECURE_SYS) #include #include # ifdef SO_SEC_MULTI /* 8.0 code */ #include #include # endif /* SO_SEC_MULTI */ + +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int secflag; char tty_dev[16]; struct secdev dv; struct sysv sysv; # ifdef SO_SEC_MULTI /* 8.0 code */ struct socksec ss; # else /* SO_SEC_MULTI */ /* 7.0 code */ struct socket_security ss; # endif /* SO_SEC_MULTI */ #endif /* _SC_CRAY_SECURE_SYS */ #if defined(AUTHENTICATION) #include int auth_level = 0; #endif #if defined(ENCRYPTION) #include #endif #include #if defined(SecurID) int require_SecurID = 0; #endif char remote_hostname[MAXHOSTNAMELEN]; int utmp_len = sizeof(remote_hostname) - 1; int registerd_host_only = 0; #ifdef STREAMSPTY # include # include /* make sure we don't get the bsd version */ # include "/usr/include/sys/tty.h" # include /* * Because of the way ptyibuf is used with streams messages, we need * ptyibuf+1 to be on a full-word boundary. The following wierdness * is simply to make that happen. */ long ptyibufbuf[BUFSIZ/sizeof(long)+1]; char *ptyibuf = ((char *)&ptyibufbuf[1])-1; char *ptyip = ((char *)&ptyibufbuf[1])-1; char ptyibuf2[BUFSIZ]; unsigned char ctlbuf[BUFSIZ]; struct strbuf strbufc, strbufd; #else /* ! STREAMPTY */ /* * I/O data buffers, * pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; char ptyibuf2[BUFSIZ]; # include int readstream(int p, char *ibuf, int bufsize); -void doit(struct sockaddr_in *who); +void doit(struct sockaddr *who); int terminaltypeok(char *s); void startslave(char *host, int autologin, char *autoname); #endif /* ! STREAMPTY */ int hostinfo = 1; /* do we print login banner? */ #ifdef CRAY extern int newmap; /* nonzero if \n maps to ^M^J */ int lowpty = 0, highpty; /* low, high pty numbers */ #endif /* CRAY */ int debug = 0; int keepalive = 1; char *altlogin; -void doit __P((struct sockaddr_in *)); +void doit __P((struct sockaddr *)); int terminaltypeok __P((char *)); void startslave __P((char *, int, char *)); extern void usage P((void)); /* * The string to pass to getopt(). We do it this way so * that only the actual options that we support will be * passed off to getopt(). */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', + '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif #ifdef BFTPDAEMON 'B', #endif #ifdef DIAGNOSTICS 'D', ':', #endif #ifdef ENCRYPTION 'e', ':', #endif #if defined(CRAY) && defined(NEWINIT) 'I', ':', #endif #ifdef LINEMODE 'l', #endif #ifdef CRAY 'r', ':', #endif #ifdef SecurID 's', #endif '\0' }; - int +int family = AF_INET; + +int main(argc, argv) char *argv[]; { - struct sockaddr_in from; + struct sockaddr_storage from; int on = 1, fromlen; register int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif pfrontp = pbackp = ptyobuf; netip = netibuf; nfrontp = nbackp = netobuf; #ifdef ENCRYPTION nclearto = 0; #endif /* ENCRYPTION */ /* * This initialization causes linemode to default to a configuration * that works on all telnet clients, including the FreeBSD client. * This is not quite the same as the telnet client issuing a "mode * character" command, but has most of the same benefits, and is * preferable since some clients (like usofts) don't have the * mode character command anyway and linemode breaks things. * The most notable symptom of fix is that csh "set filec" operations * like (filename completion) and ^D (choices) keys now work * in telnet sessions and can be used more than once on the same line. * CR/LF handling is also corrected in some termio modes. This * change resolves problem reports bin/771 and bin/1037. */ linemode=1; /*Default to mode that works on bulk of clients*/ #ifdef CRAY /* * Get number of pty's before trying to process options, * which may include changing pty range. */ highpty = getnpty(); #endif /* CRAY */ while ((ch = getopt(argc, argv, valid_opts)) != -1) { switch(ch) { #ifdef AUTHENTICATION case 'a': /* * Check for required authentication level */ if (strcmp(optarg, "debug") == 0) { extern int auth_debug_mode; auth_debug_mode = 1; } else if (strcasecmp(optarg, "none") == 0) { auth_level = 0; } else if (strcasecmp(optarg, "other") == 0) { auth_level = AUTH_OTHER; } else if (strcasecmp(optarg, "user") == 0) { auth_level = AUTH_USER; } else if (strcasecmp(optarg, "valid") == 0) { auth_level = AUTH_VALID; } else if (strcasecmp(optarg, "off") == 0) { /* * This hack turns off authentication */ auth_level = -1; } else { warnx("unknown authorization level for -a"); } break; #endif /* AUTHENTICATION */ #ifdef BFTPDAEMON case 'B': bftpd++; break; #endif /* BFTPDAEMON */ case 'd': if (strcmp(optarg, "ebug") == 0) { debug++; break; } usage(); /* NOTREACHED */ break; #ifdef DIAGNOSTICS case 'D': /* * Check for desired diagnostics capabilities. */ if (!strcmp(optarg, "report")) { diagnostic |= TD_REPORT|TD_OPTIONS; } else if (!strcmp(optarg, "exercise")) { diagnostic |= TD_EXERCISE; } else if (!strcmp(optarg, "netdata")) { diagnostic |= TD_NETDATA; } else if (!strcmp(optarg, "ptydata")) { diagnostic |= TD_PTYDATA; } else if (!strcmp(optarg, "options")) { diagnostic |= TD_OPTIONS; } else { usage(); /* NOT REACHED */ } break; #endif /* DIAGNOSTICS */ #ifdef ENCRYPTION case 'e': if (strcmp(optarg, "debug") == 0) { extern int encrypt_debug_mode; encrypt_debug_mode = 1; break; } usage(); /* NOTREACHED */ break; #endif /* ENCRYPTION */ case 'h': hostinfo = 0; break; #if defined(CRAY) && defined(NEWINIT) case 'I': { extern char *gen_id; gen_id = optarg; break; } #endif /* defined(CRAY) && defined(NEWINIT) */ #ifdef LINEMODE case 'l': alwayslinemode = 1; break; #endif /* LINEMODE */ case 'k': #if defined(LINEMODE) && defined(KLUDGELINEMODE) lmodetype = NO_AUTOKLUDGE; #else /* ignore -k option if built without kludge linemode */ #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ break; case 'n': keepalive = 0; break; case 'p': altlogin = optarg; break; #ifdef CRAY case 'r': { char *strchr(); char *c; /* * Allow the specification of alterations * to the pty search range. It is legal to * specify only one, and not change the * other from its default. */ c = strchr(optarg, '-'); if (c) { *c++ = '\0'; highpty = atoi(c); } if (*optarg != '\0') lowpty = atoi(optarg); if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { usage(); /* NOT REACHED */ } break; } #endif /* CRAY */ #ifdef SecurID case 's': /* SecurID required */ require_SecurID = 1; break; #endif /* SecurID */ case 'S': #ifdef HAS_GETTOS if ((tos = parsetos(optarg, "tcp")) < 0) warnx("%s%s%s", "bad TOS argument '", optarg, "'; will try to use default TOS"); #else warnx("TOS option unavailable; -S flag not supported"); #endif break; case 'u': utmp_len = atoi(optarg); if (utmp_len < 0) utmp_len = -utmp_len; if (utmp_len >= sizeof(remote_hostname)) utmp_len = sizeof(remote_hostname) - 1; break; case 'U': registerd_host_only = 1; break; #ifdef AUTHENTICATION case 'X': /* * Check for invalid authentication types */ auth_disable_name(optarg); break; #endif /* AUTHENTICATION */ + case '4': + family = AF_INET; + break; + +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif + default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ case '?': usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (debug) { - int s, ns, foo; - struct servent *sp; - static struct sockaddr_in sin = { AF_INET }; + int s, ns, foo, error; + char *service = "telnet"; + struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ - } else if (argc == 1) { - if ((sp = getservbyname(*argv, "tcp"))) { - sin.sin_port = sp->s_port; - } else { - sin.sin_port = atoi(*argv); - if ((int)sin.sin_port <= 0) { - warnx("%s: bad port #", *argv); - usage(); - /* NOT REACHED */ - } - sin.sin_port = htons((u_short)sin.sin_port); - } - } else { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) - errx(1, "tcp/telnet: unknown service"); - sin.sin_port = sp->s_port; + } else if (argc == 1) + service = *argv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + + if (error) { + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "tcp/%s: %s\n", service, strerror(errno)); + usage(); } - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); - foo = sizeof sin; - ns = accept(s, (struct sockaddr *)&sin, &foo); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) dup2(ns, 0); (void) close(ns); (void) close(s); #ifdef convex } else if (argc == 1) { ; /* VOID*/ /* Just ignore the host/port name */ #endif } else if (argc > 0) { usage(); /* NOT REACHED */ } #if defined(_SC_CRAY_SECURE_SYS) secflag = sysconf(_SC_CRAY_SECURE_SYS); /* * Get socket's security label */ if (secflag) { int szss = sizeof(ss); #ifdef SO_SEC_MULTI /* 8.0 code */ int sock_multi; int szi = sizeof(int); #endif /* SO_SEC_MULTI */ memset((char *)&dv, 0, sizeof(dv)); if (getsysv(&sysv, sizeof(struct sysv)) != 0) err(1, "getsysv"); /* * Get socket security label and set device values * {security label to be set on ttyp device} */ #ifdef SO_SEC_MULTI /* 8.0 code */ if ((getsockopt(0, SOL_SOCKET, SO_SECURITY, (char *)&ss, &szss) < 0) || (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI, (char *)&sock_multi, &szi) < 0)) { err(1, "getsockopt"); } else { dv.dv_actlvl = ss.ss_actlabel.lt_level; dv.dv_actcmp = ss.ss_actlabel.lt_compart; if (!sock_multi) { dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl; dv.dv_valcmp = dv.dv_actcmp; } else { dv.dv_minlvl = ss.ss_minlabel.lt_level; dv.dv_maxlvl = ss.ss_maxlabel.lt_level; dv.dv_valcmp = ss.ss_maxlabel.lt_compart; } dv.dv_devflg = 0; } #else /* SO_SEC_MULTI */ /* 7.0 code */ if (getsockopt(0, SOL_SOCKET, SO_SECURITY, (char *)&ss, &szss) >= 0) { dv.dv_actlvl = ss.ss_slevel; dv.dv_actcmp = ss.ss_compart; dv.dv_minlvl = ss.ss_minlvl; dv.dv_maxlvl = ss.ss_maxlvl; dv.dv_valcmp = ss.ss_maxcmp; } #endif /* SO_SEC_MULTI */ } #endif /* _SC_CRAY_SECURE_SYS */ openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { warn("getpeername"); _exit(1); } if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) < 0) && (errno != ENOPROTOOPT) ) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; - doit(&from); + doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ void usage() { fprintf(stderr, "usage: telnetd"); #ifdef AUTHENTICATION fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t"); #endif #ifdef BFTPDAEMON fprintf(stderr, " [-B]"); #endif fprintf(stderr, " [-debug]"); #ifdef DIAGNOSTICS fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-edebug]"); #endif fprintf(stderr, " [-h]"); #if defined(CRAY) && defined(NEWINIT) fprintf(stderr, " [-Iinitid]"); #endif #if defined(LINEMODE) && defined(KLUDGELINEMODE) fprintf(stderr, " [-k]"); #endif #ifdef LINEMODE fprintf(stderr, " [-l]"); #endif fprintf(stderr, " [-n]"); #ifdef CRAY fprintf(stderr, " [-r[lowpty]-[highpty]]"); #endif fprintf(stderr, "\n\t"); #ifdef SecurID fprintf(stderr, " [-s]"); #endif #ifdef HAS_GETTOS fprintf(stderr, " [-S tos]"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-X auth-type]"); #endif fprintf(stderr, " [-u utmp_hostname_length] [-U]"); fprintf(stderr, " [port]\n"); exit(1); } /* * getterminaltype * * Ask the other end to send along its terminal type and speed. * Output is the variable terminaltype filled in. */ static unsigned char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; int getterminaltype(name) char *name; { int retval = -1; void _gettermname(); settimer(baseline); #if defined(AUTHENTICATION) /* * Handle the Authentication option before we do anything else. */ send_do(TELOPT_AUTHENTICATION, 1); while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) ttloop(); if (his_state_is_will(TELOPT_AUTHENTICATION)) { retval = auth_wait(name); } #endif #ifdef ENCRYPTION send_will(TELOPT_ENCRYPT, 1); #endif /* ENCRYPTION */ send_do(TELOPT_TTYPE, 1); send_do(TELOPT_TSPEED, 1); send_do(TELOPT_XDISPLOC, 1); send_do(TELOPT_NEW_ENVIRON, 1); send_do(TELOPT_OLD_ENVIRON, 1); while ( #ifdef ENCRYPTION his_do_dont_is_changing(TELOPT_ENCRYPT) || #endif /* ENCRYPTION */ his_will_wont_is_changing(TELOPT_TTYPE) || his_will_wont_is_changing(TELOPT_TSPEED) || his_will_wont_is_changing(TELOPT_XDISPLOC) || his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { ttloop(); } #ifdef ENCRYPTION /* * Wait for the negotiation of what type of encryption we can * send with. If autoencrypt is not set, this will just return. */ if (his_state_is_will(TELOPT_ENCRYPT)) { encrypt_wait(); } #endif /* ENCRYPTION */ if (his_state_is_will(TELOPT_TSPEED)) { static unsigned char sb[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_XDISPLOC)) { static unsigned char sb[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_TTYPE)) { memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); } if (his_state_is_will(TELOPT_TSPEED)) { while (sequenceIs(tspeedsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_XDISPLOC)) { while (sequenceIs(xdisplocsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { while (sequenceIs(environsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_OLD_ENVIRON)) { while (sequenceIs(oenvironsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_TTYPE)) { char first[256], last[256]; while (sequenceIs(ttypesubopt, baseline)) ttloop(); /* * If the other side has already disabled the option, then * we have to just go with what we (might) have already gotten. */ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { (void) strncpy(first, terminaltype, sizeof(first)-1); first[sizeof(first)-1] = '\0'; for(;;) { /* * Save the unknown name, and request the next name. */ (void) strncpy(last, terminaltype, sizeof(last)-1); last[sizeof(last)-1] = '\0'; _gettermname(); if (terminaltypeok(terminaltype)) break; if ((strncmp(last, terminaltype, sizeof(last)) == 0) || his_state_is_wont(TELOPT_TTYPE)) { /* * We've hit the end. If this is the same as * the first name, just go with it. */ if (strncmp(first, terminaltype, sizeof(first)) == 0) break; /* * Get the terminal name one more time, so that * RFC1091 compliant telnets will cycle back to * the start of the list. */ _gettermname(); if (strncmp(first, terminaltype, sizeof(first)) != 0) { (void) strncpy(terminaltype, first, sizeof(terminaltype)-1); terminaltype[sizeof(terminaltype)-1] = '\0'; } break; } } } } return(retval); } /* end of getterminaltype */ void _gettermname() { /* * If the client turned off the option, * we can't send another request, so we * just return. */ if (his_state_is_wont(TELOPT_TTYPE)) return; settimer(baseline); memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); while (sequenceIs(ttypesubopt, baseline)) ttloop(); } int terminaltypeok(s) char *s; { char buf[1024]; if (terminaltype == NULL) return(1); /* * tgetent() will return 1 if the type is known, and * 0 if it is not known. If it returns -1, it couldn't * open the database. But if we can't open the database, * it won't help to say we failed, because we won't be * able to verify anything else. So, we treat -1 like 1. */ if (tgetent(buf, s) == 0) return(0); return(1); } #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ char *hostname; char host_name[MAXHOSTNAMELEN]; extern void telnet P((int, int, char *)); int level; char user_name[256]; /* * Get a pty, scan input lines. */ void doit(who) - struct sockaddr_in *who; + struct sockaddr *who; { + int err; int ptynum; /* * Find an available pty to use. */ #ifndef convex pty = getpty(&ptynum); if (pty < 0) fatal(net, "All network ports in use"); #else for (;;) { char *lp; extern char *line, *getpty(); if ((lp = getpty()) == NULL) fatal(net, "Out of ptys"); if ((pty = open(lp, 2)) >= 0) { strcpy(line,lp); line[5] = 't'; break; } } #endif #if defined(_SC_CRAY_SECURE_SYS) /* * set ttyp line security label */ if (secflag) { char slave_dev[16]; sprintf(tty_dev, "/dev/pty/%03d", ptynum); if (setdevs(tty_dev, &dv) < 0) fatal(net, "cannot set pty security"); sprintf(slave_dev, "/dev/ttyp%03d", ptynum); if (setdevs(slave_dev, &dv) < 0) fatal(net, "cannot set tty security"); } #endif /* _SC_CRAY_SECURE_SYS */ /* get name of connected client */ - if (realhostname(remote_hostname, sizeof(remote_hostname) - 1, - &who->sin_addr) == HOSTNAME_INVALIDADDR && registerd_host_only) + if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, + who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; trimdomain(remote_hostname, UT_HOSTSIZE); if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) - strncpy(remote_hostname, inet_ntoa(who->sin_addr), - sizeof(remote_hostname) - 1); + err = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; hostname = host_name; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); #endif init_env(); /* * get terminal type. */ *user_name = 0; level = getterminaltype(user_name); setenv("TERM", terminaltype ? terminaltype : "network", 1); #if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (setulvl(dv.dv_actlvl) < 0) fatal(net,"cannot setulvl()"); if (setucmp(dv.dv_actcmp) < 0) fatal(net, "cannot setucmp()"); } #endif /* _SC_CRAY_SECURE_SYS */ telnet(net, pty, remote_hostname); /* begin server process */ /*NOTREACHED*/ } /* end of doit */ #if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) int Xterm_output(ibufp, obuf, icountp, ocount) char **ibufp, *obuf; int *icountp, ocount; { int ret; ret = term_output(*ibufp, obuf, *icountp, ocount); *ibufp += *icountp; *icountp = 0; return(ret); } #define term_output Xterm_output #endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */ /* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */ void telnet(f, p, host) int f, p; char *host; { int on = 1; #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char defstrs[TABBUFSIZ]; #undef TABBUFSIZ char *HE; char *HN; char *IM; void netflush(); int nfd; /* * Initialize the slc mapping table. */ get_slc_defaults(); /* * Do some tests where it is desireable to wait for a response. * Rather than doing them slowly, one at a time, do them all * at once. */ if (my_state_is_wont(TELOPT_SGA)) send_will(TELOPT_SGA, 1); /* * Is the client side a 4.2 (NOT 4.3) system? We need to know this * because 4.2 clients are unable to deal with TCP urgent data. * * To find out, we send out a "DO ECHO". If the remote system * answers "WILL ECHO" it is probably a 4.2 client, and we note * that fact ("WILL ECHO" ==> that the client will echo what * WE, the server, sends it; it does NOT mean that the client will * echo the terminal input). */ send_do(TELOPT_ECHO, 1); #ifdef LINEMODE if (his_state_is_wont(TELOPT_LINEMODE)) { /* Query the peer for linemode support by trying to negotiate * the linemode option. */ linemode = 0; editmode = 0; send_do(TELOPT_LINEMODE, 1); /* send do linemode */ } #endif /* LINEMODE */ /* * Send along a couple of other options that we wish to negotiate. */ send_do(TELOPT_NAWS, 1); send_will(TELOPT_STATUS, 1); flowmode = 1; /* default flow control state */ restartany = -1; /* uninitialized... */ send_do(TELOPT_LFLOW, 1); /* * Spin, waiting for a response from the DO ECHO. However, * some REALLY DUMB telnets out there might not respond * to the DO ECHO. So, we spin looking for NAWS, (most dumb * telnets so far seem to respond with WONT for a DO that * they don't understand...) because by the time we get the * response, it will already have processed the DO ECHO. * Kludge upon kludge. */ while (his_will_wont_is_changing(TELOPT_NAWS)) ttloop(); /* * But... * The client might have sent a WILL NAWS as part of its * startup code; if so, we'll be here before we get the * response to the DO ECHO. We'll make the assumption * that any implementation that understands about NAWS * is a modern enough implementation that it will respond * to our DO ECHO request; hence we'll do another spin * waiting for the ECHO option to settle down, which is * what we wanted to do in the first place... */ if (his_want_state_is_will(TELOPT_ECHO) && his_state_is_will(TELOPT_NAWS)) { while (his_will_wont_is_changing(TELOPT_ECHO)) ttloop(); } /* * On the off chance that the telnet client is broken and does not * respond to the DO ECHO we sent, (after all, we did send the * DO NAWS negotiation after the DO ECHO, and we won't get here * until a response to the DO NAWS comes back) simulate the * receipt of a will echo. This will also send a WONT ECHO * to the client, since we assume that the client failed to * respond because it believes that it is already in DO ECHO * mode, which we do not want. */ if (his_want_state_is_will(TELOPT_ECHO)) { DIAG(TD_OPTIONS, {sprintf(nfrontp, "td: simulating recv\r\n"); nfrontp += strlen(nfrontp);}); willoption(TELOPT_ECHO); } /* * Finally, to clean things up, we turn on our echo. This * will break stupid 4.2 telnets out of local terminal echo. */ if (my_state_is_wont(TELOPT_ECHO)) send_will(TELOPT_ECHO, 1); #ifndef STREAMSPTY /* * Turn on packet mode */ (void) ioctl(p, TIOCPKT, (char *)&on); #endif #if defined(LINEMODE) && defined(KLUDGELINEMODE) /* * Continuing line mode support. If client does not support * real linemode, attempt to negotiate kludge linemode by sending * the do timing mark sequence. */ if (lmodetype < REAL_LINEMODE) send_do(TELOPT_TM, 1); #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ /* * Call telrcv() once to pick up anything received during * terminal type negotiation, 4.2/4.3 determination, and * linemode negotiation. */ telrcv(); (void) ioctl(f, FIONBIO, (char *)&on); (void) ioctl(p, FIONBIO, (char *)&on); #if defined(CRAY2) && defined(UNICOS5) init_termdriver(f, p, interrupt, sendbrk); #endif #if defined(SO_OOBINLINE) (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof on); #endif /* defined(SO_OOBINLINE) */ #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); #endif #ifdef SIGTTOU /* * Ignoring SIGTTOU keeps the kernel from blocking us * in ttioct() in /sys/tty.c. */ (void) signal(SIGTTOU, SIG_IGN); #endif (void) signal(SIGCHLD, cleanup); #if defined(CRAY2) && defined(UNICOS5) /* * Cray-2 will send a signal when pty modes are changed by slave * side. Set up signal handler now. */ if ((int)signal(SIGUSR1, termstat) < 0) warn("signal"); else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) warn("ioctl:TCSIGME"); /* * Make processing loop check terminal characteristics early on. */ termstat(); #endif #ifdef TIOCNOTTY { register int t; t = open(_PATH_TTY, O_RDWR); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } } #endif #if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY) (void) setsid(); ioctl(p, TIOCSCTTY, 0); #endif /* * Show banner that getty never gave. * * We put the banner in the pty input buffer. This way, it * gets carriage return null processing, etc., just like all * other pty --> client data. */ #if !defined(CRAY) || !defined(NEWINIT) if (getenv("USER")) hostinfo = 0; #endif if (getent(defent, "default") == 1) { char *Getstr(); char *cp=defstrs; HE = Getstr("he", &cp); HN = Getstr("hn", &cp); IM = Getstr("im", &cp); if (HN && *HN) (void) strcpy(host_name, HN); if (IM == 0) IM = ""; } else { IM = DEFAULT_IM; HE = 0; } edithost(HE, host_name); if (hostinfo && *IM) putf(IM, ptyibuf2); if (pcc) (void) strncat(ptyibuf2, ptyip, pcc+1); ptyip = ptyibuf2; pcc = strlen(ptyip); #ifdef LINEMODE /* * Last check to make sure all our states are correct. */ init_termbuf(); localstat(); #endif /* LINEMODE */ DIAG(TD_REPORT, {sprintf(nfrontp, "td: Entering processing loop\r\n"); nfrontp += strlen(nfrontp);}); /* * Startup the login process on the slave side of the terminal * now. We delay this until here to insure option negotiation * is complete. */ startslave(host, level, user_name); nfd = ((f > p) ? f : p) + 1; for (;;) { fd_set ibits, obits, xbits; register int c; if (ncc < 0 && pcc < 0) break; #if defined(CRAY2) && defined(UNICOS5) if (needtermstat) _termstat(); #endif /* defined(CRAY2) && defined(UNICOS5) */ FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); /* * Never look for input if there's still * stuff in the corresponding output buffer */ if (nfrontp - nbackp || pcc > 0) { FD_SET(f, &obits); } else { FD_SET(p, &ibits); } if (pfrontp - pbackp || ncc > 0) { FD_SET(p, &obits); } else { FD_SET(f, &ibits); } if (!SYNCHing) { FD_SET(f, &xbits); } if ((c = select(nfd, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep(5); continue; } /* * Any urgent data? */ if (FD_ISSET(net, &xbits)) { SYNCHing = 1; } /* * Something to read from the network... */ if (FD_ISSET(net, &ibits)) { #if !defined(SO_OOBINLINE) /* * In 4.2 (and 4.3 beta) systems, the * OOB indication and data handling in the kernel * is such that if two separate TCP Urgent requests * come in, one byte of TCP data will be overlaid. * This is fatal for Telnet, but we try to live * with it. * * In addition, in 4.2 (and...), a special protocol * is needed to pick up the TCP Urgent data in * the correct sequence. * * What we do is: if we think we are in urgent * mode, we look to see if we are "at the mark". * If we are, we do an OOB receive. If we run * this twice, we will do the OOB receive twice, * but the second will fail, since the second * time we were "at the mark", but there wasn't * any data there (the kernel doesn't reset * "at the mark" until we do a normal read). * Once we've read the OOB data, we go ahead * and do normal reads. * * There is also another problem, which is that * since the OOB byte we read doesn't put us * out of OOB state, and since that byte is most * likely the TELNET DM (data mark), we would * stay in the TELNET SYNCH (SYNCHing) state. * So, clocks to the rescue. If we've "just" * received a DM, then we test for the * presence of OOB data when the receive OOB * fails (and AFTER we did the normal mode read * to clear "at the mark"). */ if (SYNCHing) { int atmark; (void) ioctl(net, SIOCATMARK, (char *)&atmark); if (atmark) { ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { ncc = read(net, netibuf, sizeof (netibuf)); if (sequenceIs(didnetreceive, gotDM)) { SYNCHing = stilloob(net); } } } else { ncc = read(net, netibuf, sizeof (netibuf)); } } else { ncc = read(net, netibuf, sizeof (netibuf)); } settimer(didnetreceive); #else /* !defined(SO_OOBINLINE)) */ ncc = read(net, netibuf, sizeof (netibuf)); #endif /* !defined(SO_OOBINLINE)) */ if (ncc < 0 && errno == EWOULDBLOCK) ncc = 0; else { if (ncc <= 0) { break; } netip = netibuf; } DIAG((TD_REPORT | TD_NETDATA), {sprintf(nfrontp, "td: netread %d chars\r\n", ncc); nfrontp += strlen(nfrontp);}); DIAG(TD_NETDATA, printdata("nd", netip, ncc)); } /* * Something to read from the pty... */ if (FD_ISSET(p, &ibits)) { #ifndef STREAMSPTY pcc = read(p, ptyibuf, BUFSIZ); #else pcc = readstream(p, ptyibuf, BUFSIZ); #endif /* * On some systems, if we try to read something * off the master side before the slave side is * opened, we get EIO. */ if (pcc < 0 && (errno == EWOULDBLOCK || #ifdef EAGAIN errno == EAGAIN || #endif errno == EIO)) { pcc = 0; } else { if (pcc <= 0) break; #if !defined(CRAY2) || !defined(UNICOS5) #ifdef LINEMODE /* * If ioctl from pty, pass it through net */ if (ptyibuf[0] & TIOCPKT_IOCTL) { copy_termbuf(ptyibuf+1, pcc-1); localstat(); pcc = 1; } #endif /* LINEMODE */ if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { netclear(); /* clear buffer back */ #ifndef NO_URGENT /* * There are client telnets on some * operating systems get screwed up * royally if we send them urgent * mode data. */ *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp-1; /* off by one XXX */ DIAG(TD_OPTIONS, printoption("td: send IAC", DM)); #endif } if (his_state_is_will(TELOPT_LFLOW) && (ptyibuf[0] & (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { int newflow = ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; if (newflow != flowmode) { flowmode = newflow; (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB, TELOPT_LFLOW, flowmode ? LFLOW_ON : LFLOW_OFF, IAC, SE); nfrontp += 6; DIAG(TD_OPTIONS, printsub('>', (unsigned char *)nfrontp-4, 4);); } } pcc--; ptyip = ptyibuf+1; #else /* defined(CRAY2) && defined(UNICOS5) */ if (!uselinemode) { unpcc = pcc; unptyip = ptyibuf; pcc = term_output(&unptyip, ptyibuf2, &unpcc, BUFSIZ); ptyip = ptyibuf2; } else ptyip = ptyibuf; #endif /* defined(CRAY2) && defined(UNICOS5) */ } } while (pcc > 0) { if ((&netobuf[BUFSIZ] - nfrontp) < 2) break; c = *ptyip++ & 0377, pcc--; if (c == IAC) *nfrontp++ = c; #if defined(CRAY2) && defined(UNICOS5) else if (c == '\n' && my_state_is_wont(TELOPT_BINARY) && newmap) *nfrontp++ = '\r'; #endif /* defined(CRAY2) && defined(UNICOS5) */ *nfrontp++ = c; if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { *nfrontp++ = *ptyip++ & 0377; pcc--; } else *nfrontp++ = '\0'; } } #if defined(CRAY2) && defined(UNICOS5) /* * If chars were left over from the terminal driver, * note their existence. */ if (!uselinemode && unpcc) { pcc = unpcc; unpcc = 0; ptyip = unptyip; } #endif /* defined(CRAY2) && defined(UNICOS5) */ if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) netflush(); if (ncc > 0) telrcv(); if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) ptyflush(); } cleanup(0); } /* end of telnet */ #ifndef TCSIG # ifdef TIOCSIG # define TCSIG TIOCSIG # endif #endif #ifdef STREAMSPTY int flowison = -1; /* current state of flow: -1 is unknown */ int readstream(p, ibuf, bufsize) int p; char *ibuf; int bufsize; { int flags = 0; int ret = 0; struct termios *tsp; struct termio *tp; struct iocblk *ip; char vstop, vstart; int ixon; int newflow; strbufc.maxlen = BUFSIZ; strbufc.buf = (char *)ctlbuf; strbufd.maxlen = bufsize-1; strbufd.len = 0; strbufd.buf = ibuf+1; ibuf[0] = 0; ret = getmsg(p, &strbufc, &strbufd, &flags); if (ret < 0) /* error of some sort -- probably EAGAIN */ return(-1); if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) { /* data message */ if (strbufd.len > 0) { /* real data */ return(strbufd.len + 1); /* count header char */ } else { /* nothing there */ errno = EAGAIN; return(-1); } } /* * It's a control message. Return 1, to look at the flag we set */ switch (ctlbuf[0]) { case M_FLUSH: if (ibuf[1] & FLUSHW) ibuf[0] = TIOCPKT_FLUSHWRITE; return(1); case M_IOCTL: ip = (struct iocblk *) (ibuf+1); switch (ip->ioc_cmd) { case TCSETS: case TCSETSW: case TCSETSF: tsp = (struct termios *) (ibuf+1 + sizeof(struct iocblk)); vstop = tsp->c_cc[VSTOP]; vstart = tsp->c_cc[VSTART]; ixon = tsp->c_iflag & IXON; break; case TCSETA: case TCSETAW: case TCSETAF: tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk)); vstop = tp->c_cc[VSTOP]; vstart = tp->c_cc[VSTART]; ixon = tp->c_iflag & IXON; break; default: errno = EAGAIN; return(-1); } newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0; if (newflow != flowison) { /* it's a change */ flowison = newflow; ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP; return(1); } } /* nothing worth doing anything about */ errno = EAGAIN; return(-1); } #endif /* STREAMSPTY */ /* * Send interrupt to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write intr char. */ void interrupt() { ptyflush(); /* half-hearted */ #if defined(STREAMSPTY) && defined(TIOCSIGNAL) /* Streams PTY style ioctl to post a signal */ { int sig = SIGINT; (void) ioctl(pty, TIOCSIGNAL, &sig); (void) ioctl(pty, I_FLUSH, FLUSHR); } #else #ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGINT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_IP].sptr ? (unsigned char)*slctab[SLC_IP].sptr : '\177'; #endif /* TCSIG */ #endif } /* * Send quit to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write quit char. */ void sendbrk() { ptyflush(); /* half-hearted */ #ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGQUIT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_ABORT].sptr ? (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; #endif /* TCSIG */ } void sendsusp() { #ifdef SIGTSTP ptyflush(); /* half-hearted */ # ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGTSTP); # else /* TCSIG */ *pfrontp++ = slctab[SLC_SUSP].sptr ? (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; # endif /* TCSIG */ #endif /* SIGTSTP */ } /* * When we get an AYT, if ^T is enabled, use that. Otherwise, * just send back "[Yes]". */ void recv_ayt() { #if defined(SIGINFO) && defined(TCSIG) if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { (void) ioctl(pty, TCSIG, (char *)SIGINFO); return; } #endif (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); nfrontp += 9; } void doeof() { init_termbuf(); #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) if (!tty_isediting()) { extern char oldeofc; *pfrontp++ = oldeofc; return; } #endif *pfrontp++ = slctab[SLC_EOF].sptr ? (unsigned char)*slctab[SLC_EOF].sptr : '\004'; } diff --git a/crypto/telnet/telnet/commands.c b/crypto/telnet/telnet/commands.c index f6bece2aa32a..dd83669d1d9b 100644 --- a/crypto/telnet/telnet/commands.c +++ b/crypto/telnet/telnet/commands.c @@ -1,3038 +1,3164 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static const char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; #endif /* not lint */ #if defined(unix) #include #if defined(CRAY) || defined(sysV88) #include #endif #include #else #include #endif /* defined(unix) */ #include #include #ifdef CRAY #include #endif /* CRAY */ #include #include #include #include #include #include #include #include #include #include "general.h" #include "ring.h" #include "externs.h" #include "defines.h" #include "types.h" #if defined(AUTHENTICATION) #include #endif #if defined(ENCRYPTION) #include #endif #if !defined(CRAY) && !defined(sysV88) #include # if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix) # include # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include +#include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif MAXHOSTNAMELEN #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ char *hostname; static char _hostname[MAXHOSTNAMELEN]; extern char *getenv(); extern int isprefix(); extern char **genget(); extern int Ambiguous(); static int help(int argc, char *argv[]); static int call(); static void cmdrc(char *m1, char *m2); int quit(void); typedef struct { char *name; /* command name */ char *help; /* help string (NULL for no help) */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ } Command; static char line[256]; static char saveline[256]; static int margc; static char *margv[20]; #if defined(SKEY) #include #define PATH_SKEY "/usr/bin/key" int skey_calc(argc, argv) int argc; char **argv; { int status; if(argc != 3) { printf("%s sequence challenge\n", argv[0]); return; } switch(fork()) { case 0: execv(PATH_SKEY, argv); exit (1); case -1: perror("fork"); break; default: (void) wait(&status); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (0); } } #endif static void makeargv() { register char *cp, *cp2, c; register char **argp = margv; margc = 0; cp = line; if (*cp == '!') { /* Special case shell escape */ strcpy(saveline, line); /* save for shell command */ *argp++ = "!"; /* No room in string to get this */ margc++; cp++; } while ((c = *cp)) { register int inquote = 0; while (isspace(c)) c = *++cp; if (c == '\0') break; *argp++ = cp; margc += 1; for (cp2 = cp; c != '\0'; c = *++cp) { if (inquote) { if (c == inquote) { inquote = 0; continue; } } else { if (c == '\\') { if ((c = *++cp) == '\0') break; } else if (c == '"') { inquote = '"'; continue; } else if (c == '\'') { inquote = '\''; continue; } else if (isspace(c)) break; } *cp2++ = c; } *cp2 = '\0'; if (c == '\0') break; cp++; } *argp++ = 0; } /* * Make a character string into a number. * * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). */ static int special(s) register char *s; { register char c; char b; switch (*s) { case '^': b = *++s; if (b == '?') { c = b | 0x40; /* DEL */ } else { c = b & 0x1f; } break; default: c = *s; break; } return c; } /* * Construct a control character sequence * for a special character. */ static char * control(c) register cc_t c; { static char buf[5]; /* * The only way I could get the Sun 3.5 compiler * to shut up about * if ((unsigned int)c >= 0x80) * was to assign "c" to an unsigned int variable... * Arggg.... */ register unsigned int uic = (unsigned int)c; if (uic == 0x7f) return ("^?"); if (c == (cc_t)_POSIX_VDISABLE) { return "off"; } if (uic >= 0x80) { buf[0] = '\\'; buf[1] = ((c>>6)&07) + '0'; buf[2] = ((c>>3)&07) + '0'; buf[3] = (c&07) + '0'; buf[4] = 0; } else if (uic >= 0x20) { buf[0] = c; buf[1] = 0; } else { buf[0] = '^'; buf[1] = '@'+c; buf[2] = 0; } return (buf); } /* * The following are data structures and routines for * the "send" command. * */ struct sendlist { char *name; /* How user refers to it (case independent) */ char *help; /* Help information (0 ==> no help) */ int needconnect; /* Need to be connected */ int narg; /* Number of arguments */ int (*handler)(); /* Routine to perform (for special ops) */ int nbyte; /* Number of bytes to send this command */ int what; /* Character to be sent (<0 ==> special) */ }; static int send_esc P((void)), send_help P((void)), send_docmd P((char *)), send_dontcmd P((char *)), send_willcmd P((char *)), send_wontcmd P((char *)); static struct sendlist Sendlist[] = { { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, { "break", 0, 1, 0, 0, 2, BREAK }, { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, { "intp", 0, 1, 0, 0, 2, IP }, { "interrupt", 0, 1, 0, 0, 2, IP }, { "intr", 0, 1, 0, 0, 2, IP }, { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, { "?", "Display send options", 0, 0, send_help, 0, 0 }, { "help", 0, 0, 0, send_help, 0, 0 }, { "do", 0, 0, 1, send_docmd, 3, 0 }, { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, { "will", 0, 0, 1, send_willcmd, 3, 0 }, { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, { 0 } }; #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ sizeof(struct sendlist))) static int sendcmd(argc, argv) int argc; char **argv; { int count; /* how many bytes we are going to need to send */ int i; struct sendlist *s; /* pointer to current command */ int success = 0; int needconnect = 0; if (argc < 2) { printf("need at least one argument for 'send' command\n"); printf("'send ?' for help\n"); return 0; } /* * First, validate all the send arguments. * In addition, we see how much space we are going to need, and * whether or not we will be doing a "SYNCH" operation (which * flushes the network queue). */ count = 0; for (i = 1; i < argc; i++) { s = GETSEND(argv[i]); if (s == 0) { printf("Unknown send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } else if (Ambiguous(s)) { printf("Ambiguous send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } if (i + s->narg >= argc) { fprintf(stderr, "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n", s->narg, s->narg == 1 ? "" : "s", s->name, s->name); return 0; } count += s->nbyte; if (s->handler == send_help) { send_help(); return 0; } i += s->narg; needconnect += s->needconnect; } if (!connected && needconnect) { printf("?Need to be connected first.\n"); printf("'send ?' for help\n"); return 0; } /* Now, do we have enough room? */ if (NETROOM() < count) { printf("There is not enough room in the buffer TO the network\n"); printf("to process your request. Nothing will be done.\n"); printf("('send synch' will throw away most data in the network\n"); printf("buffer, if this might help.)\n"); return 0; } /* OK, they are all OK, now go through again and actually send */ count = 0; for (i = 1; i < argc; i++) { if ((s = GETSEND(argv[i])) == 0) { fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); (void) quit(); /*NOTREACHED*/ } if (s->handler) { count++; success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0, (s->narg > 1) ? argv[i+2] : 0); i += s->narg; } else { NET2ADD(IAC, s->what); printoption("SENT", IAC, s->what); } } return (count == success); } static int send_esc() { NETADD(escape); return 1; } static int send_docmd(name) char *name; { return(send_tncmd(send_do, "do", name)); } static int send_dontcmd(name) char *name; { return(send_tncmd(send_dont, "dont", name)); } static int send_willcmd(name) char *name; { return(send_tncmd(send_will, "will", name)); } static int send_wontcmd(name) char *name; { return(send_tncmd(send_wont, "wont", name)); } int send_tncmd(func, cmd, name) void (*func)(); char *cmd, *name; { char **cpp; extern char *telopts[]; register int val = 0; if (isprefix(name, "help") || isprefix(name, "?")) { register int col, len; printf("Usage: send %s \n", cmd); printf("\"value\" must be from 0 to 255\n"); printf("Valid options are:\n\t"); col = 8; for (cpp = telopts; *cpp; cpp++) { len = strlen(*cpp) + 3; if (col + len > 65) { printf("\n\t"); col = 8; } printf(" \"%s\"", *cpp); col += len; } printf("\n"); return 0; } cpp = (char **)genget(name, telopts, sizeof(char *)); if (Ambiguous(cpp)) { fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n", name, cmd); return 0; } if (cpp) { val = cpp - telopts; } else { register char *cp = name; while (*cp >= '0' && *cp <= '9') { val *= 10; val += *cp - '0'; cp++; } if (*cp != 0) { fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n", name, cmd); return 0; } else if (val < 0 || val > 255) { fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n", name, cmd); return 0; } } if (!connected) { printf("?Need to be connected first.\n"); return 0; } (*func)(val, 1); return 1; } static int send_help() { struct sendlist *s; /* pointer to current command */ for (s = Sendlist; s->name; s++) { if (s->help) printf("%-15s %s\n", s->name, s->help); } return(0); } /* * The following are the routines and data structures referred * to by the arguments to the "toggle" command. */ static int lclchars() { donelclchars = 1; return 1; } static int togdebug() { #ifndef NOT43 if (net > 0 && (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { perror("setsockopt (SO_DEBUG)"); } #else /* NOT43 */ if (debug) { if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) perror("setsockopt (SO_DEBUG)"); } else printf("Cannot turn off socket debugging\n"); #endif /* NOT43 */ return 1; } static int togcrlf() { if (crlf) { printf("Will send carriage returns as telnet .\n"); } else { printf("Will send carriage returns as telnet .\n"); } return 1; } int binmode; static int togbinary(val) int val; { donebinarytoggle = 1; if (val >= 0) { binmode = val; } else { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { binmode = 1; } else if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { binmode = 0; } val = binmode ? 0 : 1; } if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { printf("Already operating in binary mode with remote host.\n"); } else { printf("Negotiating binary mode with remote host.\n"); tel_enter_binary(3); } } else { if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { printf("Already in network ascii mode with remote host.\n"); } else { printf("Negotiating network ascii mode with remote host.\n"); tel_leave_binary(3); } } return 1; } static int togrbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_do(TELOPT_BINARY)) { printf("Already receiving in binary mode.\n"); } else { printf("Negotiating binary mode on input.\n"); tel_enter_binary(1); } } else { if (my_want_state_is_dont(TELOPT_BINARY)) { printf("Already receiving in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on input.\n"); tel_leave_binary(1); } } return 1; } static int togxbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY)) { printf("Already transmitting in binary mode.\n"); } else { printf("Negotiating binary mode on output.\n"); tel_enter_binary(2); } } else { if (my_want_state_is_wont(TELOPT_BINARY)) { printf("Already transmitting in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on output.\n"); tel_leave_binary(2); } } return 1; } static int togglehelp P((void)); #if defined(AUTHENTICATION) extern int auth_togdebug P((int)); #endif #ifdef ENCRYPTION extern int EncryptAutoEnc P((int)); extern int EncryptAutoDec P((int)); extern int EncryptDebug P((int)); extern int EncryptVerbose P((int)); #endif /* ENCRYPTION */ struct togglelist { char *name; /* name of toggle */ char *help; /* help message */ int (*handler)(); /* routine to do actual setting */ int *variable; char *actionexplanation; }; static struct togglelist Togglelist[] = { { "autoflush", "flushing of output when sending interrupt characters", 0, &autoflush, "flush output when sending interrupt characters" }, { "autosynch", "automatic sending of interrupt characters in urgent mode", 0, &autosynch, "send interrupt characters in urgent mode" }, #if defined(AUTHENTICATION) { "autologin", "automatic sending of login and/or authentication info", 0, &autologin, "send login name and/or authentication information" }, { "authdebug", "Toggle authentication debugging", auth_togdebug, 0, "print authentication debugging information" }, #endif #ifdef ENCRYPTION { "autoencrypt", "automatic encryption of data stream", EncryptAutoEnc, 0, "automatically encrypt output" }, { "autodecrypt", "automatic decryption of data stream", EncryptAutoDec, 0, "automatically decrypt input" }, { "verbose_encrypt", "Toggle verbose encryption output", EncryptVerbose, 0, "print verbose encryption output" }, { "encdebug", "Toggle encryption debugging", EncryptDebug, 0, "print encryption debugging information" }, #endif /* ENCRYPTION */ { "skiprc", "don't read ~/.telnetrc file", 0, &skiprc, "skip reading of ~/.telnetrc file" }, { "binary", "sending and receiving of binary data", togbinary, 0, 0 }, { "inbinary", "receiving of binary data", togrbinary, 0, 0 }, { "outbinary", "sending of binary data", togxbinary, 0, 0 }, { "crlf", "sending carriage returns as telnet ", togcrlf, &crlf, 0 }, { "crmod", "mapping of received carriage returns", 0, &crmod, "map carriage return on output" }, { "localchars", "local recognition of certain control characters", lclchars, &localchars, "recognize certain control characters" }, { " ", "", 0 }, /* empty line */ #if defined(unix) && defined(TN3270) { "apitrace", "(debugging) toggle tracing of API transactions", 0, &apitrace, "trace API transactions" }, { "cursesdata", "(debugging) toggle printing of hexadecimal curses data", 0, &cursesdata, "print hexadecimal representation of curses data" }, #endif /* defined(unix) && defined(TN3270) */ { "debug", "debugging", togdebug, &debug, "turn on socket level debugging" }, { "netdata", "printing of hexadecimal network data (debugging)", 0, &netdata, "print hexadecimal representation of network traffic" }, { "prettydump", "output of \"netdata\" to user readable format (debugging)", 0, &prettydump, "print user readable output for \"netdata\"" }, { "options", "viewing of options processing (debugging)", 0, &showoptions, "show option processing" }, #if defined(unix) { "termdata", "(debugging) toggle printing of hexadecimal terminal data", 0, &termdata, "print hexadecimal representation of terminal traffic" }, #endif /* defined(unix) */ { "?", 0, togglehelp }, { "help", 0, togglehelp }, { 0 } }; static int togglehelp() { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s toggle %s\n", c->name, c->help); else printf("\n"); } } printf("\n"); printf("%-15s %s\n", "?", "display help information"); return 0; } static void settogglehelp(set) int set; { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s %s\n", c->name, set ? "enable" : "disable", c->help); else printf("\n"); } } } #define GETTOGGLE(name) (struct togglelist *) \ genget(name, (char **) Togglelist, sizeof(struct togglelist)) static int toggle(argc, argv) int argc; char *argv[]; { int retval = 1; char *name; struct togglelist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); return 0; } argc--; argv++; while (argc--) { name = *argv++; c = GETTOGGLE(name); if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", name); return 0; } else if (c == 0) { fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", name); return 0; } else { if (c->variable) { *c->variable = !*c->variable; /* invert it */ if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) { retval &= (*c->handler)(-1); } } } return retval; } /* * The following perform the "set" command. */ #ifdef USE_TERMIO struct termio new_tc = { 0 }; #endif struct setlist { char *name; /* name */ char *help; /* help information */ void (*handler)(); cc_t *charp; /* where it is located at */ }; static struct setlist Setlist[] = { #ifdef KLUDGELINEMODE { "echo", "character to toggle local echoing on/off", 0, &echoc }, #endif { "escape", "character to escape back to telnet command mode", 0, &escape }, { "rlogin", "rlogin escape character", 0, &rlogin }, { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, { " ", "" }, { " ", "The following need 'localchars' to be toggled true", 0, 0 }, { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp }, { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp }, { "quit", "character to cause an Abort process", 0, termQuitCharp }, { "eof", "character to cause an EOF ", 0, termEofCharp }, { " ", "" }, { " ", "The following are for local editing in linemode", 0, 0 }, { "erase", "character to use to erase a character", 0, termEraseCharp }, { "kill", "character to use to erase a line", 0, termKillCharp }, { "lnext", "character to use for literal next", 0, termLiteralNextCharp }, { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, { "reprint", "character to use for line reprint", 0, termRprntCharp }, { "worderase", "character to use to erase a word", 0, termWerasCharp }, { "start", "character to use for XON", 0, termStartCharp }, { "stop", "character to use for XOFF", 0, termStopCharp }, { "forw1", "alternate end of line character", 0, termForw1Charp }, { "forw2", "alternate end of line character", 0, termForw2Charp }, { "ayt", "alternate AYT character", 0, termAytCharp }, { 0 } }; #if defined(CRAY) && !defined(__STDC__) /* Work around compiler bug in pcc 4.1.5 */ void _setlist_init() { #ifndef KLUDGELINEMODE #define N 5 #else #define N 6 #endif Setlist[N+0].charp = &termFlushChar; Setlist[N+1].charp = &termIntChar; Setlist[N+2].charp = &termQuitChar; Setlist[N+3].charp = &termEofChar; Setlist[N+6].charp = &termEraseChar; Setlist[N+7].charp = &termKillChar; Setlist[N+8].charp = &termLiteralNextChar; Setlist[N+9].charp = &termSuspChar; Setlist[N+10].charp = &termRprntChar; Setlist[N+11].charp = &termWerasChar; Setlist[N+12].charp = &termStartChar; Setlist[N+13].charp = &termStopChar; Setlist[N+14].charp = &termForw1Char; Setlist[N+15].charp = &termForw2Char; Setlist[N+16].charp = &termAytChar; #undef N } #endif /* defined(CRAY) && !defined(__STDC__) */ static struct setlist * getset(name) char *name; { return (struct setlist *) genget(name, (char **) Setlist, sizeof(struct setlist)); } void set_escape_char(s) char *s; { if (rlogin != _POSIX_VDISABLE) { rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet rlogin escape character is '%s'.\n", control(rlogin)); } else { escape = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet escape character is '%s'.\n", control(escape)); } } static int setcmd(argc, argv) int argc; char *argv[]; { int value; struct setlist *ct; struct togglelist *c; if (argc < 2 || argc > 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(1); printf("%-15s %s\n", "?", "display help information"); return 0; } ct = getset(argv[1]); if (ct == 0) { c = GETTOGGLE(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", argv[1]); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } if (c->variable) { if ((argc == 2) || (strcmp("on", argv[2]) == 0)) *c->variable = 1; else if (strcmp("off", argv[2]) == 0) *c->variable = 0; else { printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n"); return 0; } if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(1); } else if (argc != 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } else if (ct->handler) { (*ct->handler)(argv[2]); printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp); } else { if (strcmp("off", argv[2])) { value = special(argv[2]); } else { value = _POSIX_VDISABLE; } *(ct->charp) = (cc_t)value; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } slc_check(); return 1; } static int unsetcmd(argc, argv) int argc; char *argv[]; { struct setlist *ct; struct togglelist *c; register char *name; if (argc < 2) { fprintf(stderr, "Need an argument to 'unset' command. 'unset ?' for help.\n"); return 0; } if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(0); printf("%-15s %s\n", "?", "display help information"); return 0; } argc--; argv++; while (argc--) { name = *argv++; ct = getset(name); if (ct == 0) { c = GETTOGGLE(name); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n", name); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } if (c->variable) { *c->variable = 0; if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(0); } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } else if (ct->handler) { (*ct->handler)(0); printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp); } else { *(ct->charp) = _POSIX_VDISABLE; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } } return 1; } /* * The following are the data structures and routines for the * 'mode' command. */ #ifdef KLUDGELINEMODE extern int kludgelinemode; static int dokludgemode() { kludgelinemode = 1; send_wont(TELOPT_LINEMODE, 1); send_dont(TELOPT_SGA, 1); send_dont(TELOPT_ECHO, 1); return 1; } #endif static int dolinemode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_dont(TELOPT_SGA, 1); #endif send_will(TELOPT_LINEMODE, 1); send_dont(TELOPT_ECHO, 1); return 1; } static int docharmode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_do(TELOPT_SGA, 1); else #endif send_wont(TELOPT_LINEMODE, 1); send_do(TELOPT_ECHO, 1); return 1; } static int dolmmode(bit, on) int bit, on; { unsigned char c; extern int linemode; if (my_want_state_is_wont(TELOPT_LINEMODE)) { printf("?Need to have LINEMODE option enabled first.\n"); printf("'mode ?' for help.\n"); return 0; } if (on) c = (linemode | bit); else c = (linemode & ~bit); lm_mode(&c, 1, 1); return 1; } int setmod(bit) { return dolmmode(bit, 1); } int clearmode(bit) { return dolmmode(bit, 0); } struct modelist { char *name; /* command name */ char *help; /* help string */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ int arg1; }; extern int modehelp(); static struct modelist ModeList[] = { { "character", "Disable LINEMODE option", docharmode, 1 }, #ifdef KLUDGELINEMODE { "", "(or disable obsolete line-by-line mode)", 0 }, #endif { "line", "Enable LINEMODE option", dolinemode, 1 }, #ifdef KLUDGELINEMODE { "", "(or enable obsolete line-by-line mode)", 0 }, #endif { "", "", 0 }, { "", "These require the LINEMODE option to be enabled", 0 }, { "isig", "Enable signal trapping", setmod, 1, MODE_TRAPSIG }, { "+isig", 0, setmod, 1, MODE_TRAPSIG }, { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG }, { "edit", "Enable character editing", setmod, 1, MODE_EDIT }, { "+edit", 0, setmod, 1, MODE_EDIT }, { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT }, { "softtabs", "Enable tab expansion", setmod, 1, MODE_SOFT_TAB }, { "+softtabs", 0, setmod, 1, MODE_SOFT_TAB }, { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB }, { "litecho", "Enable literal character echo", setmod, 1, MODE_LIT_ECHO }, { "+litecho", 0, setmod, 1, MODE_LIT_ECHO }, { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO }, { "help", 0, modehelp, 0 }, #ifdef KLUDGELINEMODE { "kludgeline", 0, dokludgemode, 1 }, #endif { "", "", 0 }, { "?", "Print help information", modehelp, 0 }, { 0 }, }; int modehelp() { struct modelist *mt; printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); for (mt = ModeList; mt->name; mt++) { if (mt->help) { if (*mt->help) printf("%-15s %s\n", mt->name, mt->help); else printf("\n"); } } return 0; } #define GETMODECMD(name) (struct modelist *) \ genget(name, (char **) ModeList, sizeof(struct modelist)) static int modecmd(argc, argv) int argc; char *argv[]; { struct modelist *mt; if (argc != 2) { printf("'mode' command requires an argument\n"); printf("'mode ?' for help.\n"); } else if ((mt = GETMODECMD(argv[1])) == 0) { fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); } else if (Ambiguous(mt)) { fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); } else if (mt->needconnect && !connected) { printf("?Need to be connected first.\n"); printf("'mode ?' for help.\n"); } else if (mt->handler) { return (*mt->handler)(mt->arg1); } return 0; } /* * The following data structures and routines implement the * "display" command. */ static int display(argc, argv) int argc; char *argv[]; { struct togglelist *tl; struct setlist *sl; #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ if (*tl->variable) { \ printf("will"); \ } else { \ printf("won't"); \ } \ printf(" %s.\n", tl->actionexplanation); \ } #define doset(sl) if (sl->name && *sl->name != ' ') { \ if (sl->handler == 0) \ printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \ else \ printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \ } if (argc == 1) { for (tl = Togglelist; tl->name; tl++) { dotog(tl); } printf("\n"); for (sl = Setlist; sl->name; sl++) { doset(sl); } } else { int i; for (i = 1; i < argc; i++) { sl = getset(argv[i]); tl = GETTOGGLE(argv[i]); if (Ambiguous(sl) || Ambiguous(tl)) { printf("?Ambiguous argument '%s'.\n", argv[i]); return 0; } else if (!sl && !tl) { printf("?Unknown argument '%s'.\n", argv[i]); return 0; } else { if (tl) { dotog(tl); } if (sl) { doset(sl); } } } } /*@*/optionstatus(); #ifdef ENCRYPTION EncryptStatus(); #endif /* ENCRYPTION */ return 1; #undef doset #undef dotog } /* * The following are the data structures, and many of the routines, * relating to command processing. */ /* * Set the escape character. */ static int setescape(argc, argv) int argc; char *argv[]; { register char *arg; char buf[50]; printf( "Deprecated usage - please use 'set escape%s%s' in the future.\n", (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); if (argc > 2) arg = argv[1]; else { printf("new escape character: "); (void) fgets(buf, sizeof(buf), stdin); arg = buf; } if (arg[0] != '\0') escape = arg[0]; if (!In3270) { printf("Escape character is '%s'.\n", control(escape)); } (void) fflush(stdout); return 1; } /*VARARGS*/ static int togcrmod() { crmod = !crmod; printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); (void) fflush(stdout); return 1; } /*VARARGS*/ int suspend() { #ifdef SIGTSTP setcommandmode(); { long oldrows, oldcols, newrows, newcols, err; err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; (void) kill(0, SIGTSTP); /* * If we didn't get the window size before the SUSPEND, but we * can get them now (?), then send the NAWS to make sure that * we are set up for the right window size. */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } } /* reget parameters in case they were changed */ TerminalSaveState(); setconnmode(0); #else printf("Suspend is not supported. Try the '!' command instead\n"); #endif return 1; } #if !defined(TN3270) /*ARGSUSED*/ int shell(argc, argv) int argc; char *argv[]; { long oldrows, oldcols, newrows, newcols, err; setcommandmode(); err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; switch(vfork()) { case -1: perror("Fork failed\n"); break; case 0: { /* * Fire up the shell in the child. */ register char *shellp, *shellname; extern char *strrchr(); shellp = getenv("SHELL"); if (shellp == NULL) shellp = "/bin/sh"; if ((shellname = strrchr(shellp, '/')) == 0) shellname = shellp; else shellname++; if (argc > 1) execl(shellp, shellname, "-c", &saveline[1], 0); else execl(shellp, shellname, 0); perror("Execl"); _exit(1); } default: (void)wait((int *)0); /* Wait for the shell to complete */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } break; } return 1; } #else /* !defined(TN3270) */ extern int shell(); #endif /* !defined(TN3270) */ /*VARARGS*/ static int bye(argc, argv) int argc; /* Number of arguments */ char *argv[]; /* arguments */ { extern int resettermname; if (connected) { (void) shutdown(net, 2); printf("Connection closed.\n"); (void) NetClose(net); connected = 0; resettermname = 1; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ /* reset options */ tninit(); #if defined(TN3270) SetIn3270(); /* Get out of 3270 mode */ #endif /* defined(TN3270) */ } if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { longjmp(toplevel, 1); /* NOTREACHED */ } return 1; /* Keep lint, etc., happy */ } /*VARARGS*/ int quit() { (void) call(bye, "bye", "fromquit", 0); Exit(0); /*NOTREACHED*/ } /*VARARGS*/ int logout() { send_do(TELOPT_LOGOUT, 1); (void) netflush(); return 1; } /* * The SLC command. */ struct slclist { char *name; char *help; void (*handler)(); int arg; }; static void slc_help(); struct slclist SlcList[] = { { "export", "Use local special character definitions", slc_mode_export, 0 }, { "import", "Use remote special character definitions", slc_mode_import, 1 }, { "check", "Verify remote special character definitions", slc_mode_import, 0 }, { "help", 0, slc_help, 0 }, { "?", "Print help information", slc_help, 0 }, { 0 }, }; static void slc_help() { struct slclist *c; for (c = SlcList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct slclist * getslc(name) char *name; { return (struct slclist *) genget(name, (char **) SlcList, sizeof(struct slclist)); } static int slccmd(argc, argv) int argc; char *argv[]; { struct slclist *c; if (argc != 2) { fprintf(stderr, "Need an argument to 'slc' command. 'slc ?' for help.\n"); return 0; } c = getslc(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]); return 0; } (*c->handler)(c->arg); slcstate(); return 1; } /* * The ENVIRON command. */ struct envlist { char *name; char *help; void (*handler)(); int narg; }; extern struct env_lst * env_define P((unsigned char *, unsigned char *)); extern void env_undefine P((unsigned char *)), env_export P((unsigned char *)), env_unexport P((unsigned char *)), env_send P((unsigned char *)), #if defined(OLD_ENVIRON) && defined(ENV_HACK) env_varval P((unsigned char *)), #endif env_list P((void)); static void env_help P((void)); struct envlist EnvList[] = { { "define", "Define an environment variable", (void (*)())env_define, 2 }, { "undefine", "Undefine an environment variable", env_undefine, 1 }, { "export", "Mark an environment variable for automatic export", env_export, 1 }, { "unexport", "Don't mark an environment variable for automatic export", env_unexport, 1 }, { "send", "Send an environment variable", env_send, 1 }, { "list", "List the current environment variables", env_list, 0 }, #if defined(OLD_ENVIRON) && defined(ENV_HACK) { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", env_varval, 1 }, #endif { "help", 0, env_help, 0 }, { "?", "Print help information", env_help, 0 }, { 0 }, }; static void env_help() { struct envlist *c; for (c = EnvList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct envlist * getenvcmd(name) char *name; { return (struct envlist *) genget(name, (char **) EnvList, sizeof(struct envlist)); } int env_cmd(argc, argv) int argc; char *argv[]; { struct envlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'environ' command. 'environ ?' for help.\n"); return 0; } c = getenvcmd(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } (*c->handler)(argv[2], argv[3]); return 1; } struct env_lst { struct env_lst *next; /* pointer to next structure */ struct env_lst *prev; /* pointer to previous structure */ unsigned char *var; /* pointer to variable name */ unsigned char *value; /* pointer to variable value */ int export; /* 1 -> export with default list of variables */ int welldefined; /* A well defined variable */ }; struct env_lst envlisthead; struct env_lst * env_find(var) unsigned char *var; { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { if (strcmp((char *)ep->var, (char *)var) == 0) return(ep); } return(NULL); } void env_init() { extern char **environ; register char **epp, *cp; register struct env_lst *ep; extern char *strchr(); for (epp = environ; *epp; epp++) { if ((cp = strchr(*epp, '='))) { *cp = '\0'; ep = env_define((unsigned char *)*epp, (unsigned char *)cp+1); ep->export = 0; *cp = '='; } } /* * Special case for DISPLAY variable. If it is ":0.0" or * "unix:0.0", we have to get rid of "unix" and insert our * hostname. */ if ((ep = env_find("DISPLAY")) && ((*ep->value == ':') || (strncmp((char *)ep->value, "unix:", 5) == 0))) { char hbuf[256+1]; char *cp2 = strchr((char *)ep->value, ':'); gethostname(hbuf, 256); hbuf[256] = '\0'; cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1); sprintf((char *)cp, "%s%s", hbuf, cp2); free(ep->value); ep->value = (unsigned char *)cp; } /* * If USER is not defined, but LOGNAME is, then add * USER with the value from LOGNAME. By default, we * don't export the USER variable. */ if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) { env_define((unsigned char *)"USER", ep->value); env_unexport((unsigned char *)"USER"); } env_export((unsigned char *)"DISPLAY"); env_export((unsigned char *)"PRINTER"); } struct env_lst * env_define(var, value) unsigned char *var, *value; { register struct env_lst *ep; if ((ep = env_find(var))) { if (ep->var) free(ep->var); if (ep->value) free(ep->value); } else { ep = (struct env_lst *)malloc(sizeof(struct env_lst)); ep->next = envlisthead.next; envlisthead.next = ep; ep->prev = &envlisthead; if (ep->next) ep->next->prev = ep; } ep->welldefined = opt_welldefined(var); ep->export = 1; ep->var = (unsigned char *)strdup((char *)var); ep->value = (unsigned char *)strdup((char *)value); return(ep); } void env_undefine(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) { ep->prev->next = ep->next; if (ep->next) ep->next->prev = ep->prev; if (ep->var) free(ep->var); if (ep->value) free(ep->value); free(ep); } } void env_export(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) ep->export = 1; } void env_unexport(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) ep->export = 0; } void env_send(var) unsigned char *var; { register struct env_lst *ep; if (my_state_is_wont(TELOPT_NEW_ENVIRON) #ifdef OLD_ENVIRON && my_state_is_wont(TELOPT_OLD_ENVIRON) #endif ) { fprintf(stderr, "Cannot send '%s': Telnet ENVIRON option not enabled\n", var); return; } ep = env_find(var); if (ep == 0) { fprintf(stderr, "Cannot send '%s': variable not defined\n", var); return; } env_opt_start_info(); env_opt_add(ep->var); env_opt_end(0); } void env_list() { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { printf("%c %-20s %s\n", ep->export ? '*' : ' ', ep->var, ep->value); } } unsigned char * env_default(init, welldefined) int init; { static struct env_lst *nep = NULL; if (init) { nep = &envlisthead; return(NULL); } if (nep) { while ((nep = nep->next)) { if (nep->export && (nep->welldefined == welldefined)) return(nep->var); } } return(NULL); } unsigned char * env_getvalue(var) unsigned char *var; { register struct env_lst *ep; if ((ep = env_find(var))) return(ep->value); return(NULL); } #if defined(OLD_ENVIRON) && defined(ENV_HACK) void env_varval(what) unsigned char *what; { extern int old_env_var, old_env_value, env_auto; int len = strlen((char *)what); if (len == 0) goto unknown; if (strncasecmp((char *)what, "status", len) == 0) { if (env_auto) printf("%s%s", "VAR and VALUE are/will be ", "determined automatically\n"); if (old_env_var == OLD_ENV_VAR) printf("VAR and VALUE set to correct definitions\n"); else printf("VAR and VALUE definitions are reversed\n"); } else if (strncasecmp((char *)what, "auto", len) == 0) { env_auto = 1; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else if (strncasecmp((char *)what, "right", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VAR; old_env_value = OLD_ENV_VALUE; } else if (strncasecmp((char *)what, "wrong", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else { unknown: printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n"); } } #endif #if defined(AUTHENTICATION) /* * The AUTHENTICATE command. */ struct authlist { char *name; char *help; int (*handler)(); int narg; }; extern int auth_enable P((char *)), auth_disable P((char *)), auth_status P((void)); static int auth_help P((void)); struct authlist AuthList[] = { { "status", "Display current status of authentication information", auth_status, 0 }, { "disable", "Disable an authentication type ('auth disable ?' for more)", auth_disable, 1 }, { "enable", "Enable an authentication type ('auth enable ?' for more)", auth_enable, 1 }, { "help", 0, auth_help, 0 }, { "?", "Print help information", auth_help, 0 }, { 0 }, }; static int auth_help() { struct authlist *c; for (c = AuthList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } int auth_cmd(argc, argv) int argc; char *argv[]; { struct authlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'auth' command. 'auth ?' for help.\n"); return 0; } c = (struct authlist *) genget(argv[1], (char **) AuthList, sizeof(struct authlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } return((*c->handler)(argv[2], argv[3])); } #endif #ifdef ENCRYPTION /* * The ENCRYPT command. */ struct encryptlist { char *name; char *help; int (*handler)(); int needconnect; int minarg; int maxarg; }; extern int EncryptEnable P((char *, char *)), EncryptDisable P((char *, char *)), EncryptType P((char *, char *)), EncryptStart P((char *)), EncryptStartInput P((void)), EncryptStartOutput P((void)), EncryptStop P((char *)), EncryptStopInput P((void)), EncryptStopOutput P((void)), EncryptStatus P((void)); static int EncryptHelp P((void)); struct encryptlist EncryptList[] = { { "enable", "Enable encryption. ('encrypt enable ?' for more)", EncryptEnable, 1, 1, 2 }, { "disable", "Disable encryption. ('encrypt enable ?' for more)", EncryptDisable, 0, 1, 2 }, { "type", "Set encryption type. ('encrypt type ?' for more)", EncryptType, 0, 1, 1 }, { "start", "Start encryption. ('encrypt start ?' for more)", EncryptStart, 1, 0, 1 }, { "stop", "Stop encryption. ('encrypt stop ?' for more)", EncryptStop, 1, 0, 1 }, { "input", "Start encrypting the input stream", EncryptStartInput, 1, 0, 0 }, { "-input", "Stop encrypting the input stream", EncryptStopInput, 1, 0, 0 }, { "output", "Start encrypting the output stream", EncryptStartOutput, 1, 0, 0 }, { "-output", "Stop encrypting the output stream", EncryptStopOutput, 1, 0, 0 }, { "status", "Display current status of authentication information", EncryptStatus, 0, 0, 0 }, { "help", 0, EncryptHelp, 0, 0, 0 }, { "?", "Print help information", EncryptHelp, 0, 0, 0 }, { 0 }, }; static int EncryptHelp() { struct encryptlist *c; for (c = EncryptList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } int encrypt_cmd(argc, argv) int argc; char *argv[]; { struct encryptlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'encrypt' command. 'encrypt ?' for help.\n"); return 0; } c = (struct encryptlist *) genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('encrypt ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('encrypt ?' for help).\n", argv[1]); return 0; } argc -= 2; if (argc < c->minarg || argc > c->maxarg) { if (c->minarg == c->maxarg) { fprintf(stderr, "Need %s%d argument%s ", c->minarg < argc ? "only " : "", c->minarg, c->minarg == 1 ? "" : "s"); } else { fprintf(stderr, "Need %s%d-%d arguments ", c->maxarg < argc ? "only " : "", c->minarg, c->maxarg); } fprintf(stderr, "to 'encrypt %s' command. 'encrypt ?' for help.\n", c->name); return 0; } if (c->needconnect && !connected) { if (!(argc && (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) { printf("?Need to be connected first.\n"); return 0; } } return ((*c->handler)(argc > 0 ? argv[2] : 0, argc > 1 ? argv[3] : 0, argc > 2 ? argv[4] : 0)); } #endif /* ENCRYPTION */ #if defined(unix) && defined(TN3270) static void filestuff(fd) int fd; { int res; #ifdef F_GETOWN setconnmode(0); res = fcntl(fd, F_GETOWN, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } printf("\tOwner is %d.\n", res); #endif setconnmode(0); res = fcntl(fd, F_GETFL, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } #ifdef notdef printf("\tFlags are 0x%x: %s\n", res, decodeflags(res)); #endif } #endif /* defined(unix) && defined(TN3270) */ /* * Print status about the connection. */ /*ARGSUSED*/ static int status(argc, argv) int argc; char *argv[]; { if (connected) { printf("Connected to %s.\n", hostname); if ((argc < 2) || strcmp(argv[1], "notmuch")) { int mode = getconnmode(); if (my_want_state_is_will(TELOPT_LINEMODE)) { printf("Operating with LINEMODE option\n"); printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No"); printf("%s catching of signals\n", (mode&MODE_TRAPSIG) ? "Local" : "No"); slcstate(); #ifdef KLUDGELINEMODE } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) { printf("Operating in obsolete linemode\n"); #endif } else { printf("Operating in single character mode\n"); if (localchars) printf("Catching signals locally\n"); } printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote"); if (my_want_state_is_will(TELOPT_LFLOW)) printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No"); #ifdef ENCRYPTION encrypt_display(); #endif /* ENCRYPTION */ } } else { printf("No connection.\n"); } # if !defined(TN3270) printf("Escape character is '%s'.\n", control(escape)); (void) fflush(stdout); # else /* !defined(TN3270) */ if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { printf("Escape character is '%s'.\n", control(escape)); } # if defined(unix) if ((argc >= 2) && !strcmp(argv[1], "everything")) { printf("SIGIO received %d time%s.\n", sigiocount, (sigiocount == 1)? "":"s"); if (In3270) { printf("Process ID %d, process group %d.\n", getpid(), getpgrp(getpid())); printf("Terminal input:\n"); filestuff(tin); printf("Terminal output:\n"); filestuff(tout); printf("Network socket:\n"); filestuff(net); } } if (In3270 && transcom) { printf("Transparent mode command is '%s'.\n", transcom); } # endif /* defined(unix) */ (void) fflush(stdout); if (In3270) { return 0; } # endif /* defined(TN3270) */ return 1; } #ifdef SIGINFO /* * Function that gets called when SIGINFO is received. */ void ayt_status() { (void) call(status, "status", "notmuch", 0); } #endif -unsigned long inet_addr(); +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; - extern char *inet_ntoa(); -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - memset((char *)&sin, 0, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); setuid(getuid()); return 0; } if (argc < 2) { (void) strcpy(line, "open "); printf("(to) "); (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin); makeargv(); argc = margc; argv = margv; } cmd = *argv; --argc; ++argv; while (argc) { if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?")) goto usage; if (strcmp(*argv, "-l") == 0) { --argc; ++argv; if (argc == 0) goto usage; user = *argv++; --argc; continue; } if (strcmp(*argv, "-a") == 0) { --argc; ++argv; autologin = 1; continue; } if (strcmp(*argv, "-s") == 0) { --argc; ++argv; if (argc == 0) goto usage; src_addr = *argv++; --argc; continue; } if (hostp == 0) { hostp = *argv++; --argc; continue; } if (portp == 0) { portp = *argv++; --argc; continue; } usage: printf("usage: %s [-l user] [-a] [-s src_addr] host-name [port]\n", cmd); setuid(getuid()); return 0; } if (hostp == 0) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); - return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); + setuid(getuid()); + return 0; + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { setuid(getuid()); + freeaddrinfo(res); return 0; - } else if (temp == -1) { + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; } - telnetport = 1; } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(net, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) && (errno != ENOPROTOOPT)) perror("telnet: setsockopt (IP_TOS) (ignored)"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { perror("setsockopt (SO_DEBUG)"); } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } connected++; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; user = getenv("USER"); if (user == NULL || ((pw = getpwnam(user)) && pw->pw_uid != getuid())) { if ((pw = getpwuid(getuid()))) user = pw->pw_name; else user = NULL; } } if (user) { env_define((unsigned char *)"USER", (unsigned char *)user); env_export((unsigned char *)"USER"); } (void) call(status, "status", "notmuch", 0); if (setjmp(peerdied) == 0) telnet(user); (void) NetClose(net); ExitString("Connection closed by foreign host.\n",1); /*NOTREACHED*/ } #define HELPINDENT (sizeof ("connect")) static char openhelp[] = "connect to a site", closehelp[] = "close current connection", logouthelp[] = "forcibly logout remote user and close the connection", quithelp[] = "exit telnet", statushelp[] = "print status information", helphelp[] = "print help information", sendhelp[] = "transmit special characters ('send ?' for more)", sethelp[] = "set operating parameters ('set ?' for more)", unsethelp[] = "unset operating parameters ('unset ?' for more)", togglestring[] ="toggle operating parameters ('toggle ?' for more)", slchelp[] = "change state of special charaters ('slc ?' for more)", displayhelp[] = "display operating parameters", #if defined(TN3270) && defined(unix) transcomhelp[] = "specify Unix command for transparent mode pipe", #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) authhelp[] = "turn on (off) authentication ('auth ?' for more)", #endif #ifdef ENCRYPTION encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)", #endif /* ENCRYPTION */ #if defined(unix) zhelp[] = "suspend telnet", #endif /* defined(unix) */ #if defined(SKEY) skeyhelp[] = "compute response to s/key challenge", #endif shellhelp[] = "invoke a subshell", envhelp[] = "change environment variables ('environ ?' for more)", modestring[] = "try to enter line or character mode ('mode ?' for more)"; static Command cmdtab[] = { { "close", closehelp, bye, 1 }, { "logout", logouthelp, logout, 1 }, { "display", displayhelp, display, 0 }, { "mode", modestring, modecmd, 0 }, { "open", openhelp, tn, 0 }, { "quit", quithelp, quit, 0 }, { "send", sendhelp, sendcmd, 0 }, { "set", sethelp, setcmd, 0 }, { "unset", unsethelp, unsetcmd, 0 }, { "status", statushelp, status, 0 }, { "toggle", togglestring, toggle, 0 }, { "slc", slchelp, slccmd, 0 }, #if defined(TN3270) && defined(unix) { "transcom", transcomhelp, settranscom, 0 }, #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) { "auth", authhelp, auth_cmd, 0 }, #endif #ifdef ENCRYPTION { "encrypt", encrypthelp, encrypt_cmd, 0 }, #endif /* ENCRYPTION */ #if defined(unix) { "z", zhelp, suspend, 0 }, #endif /* defined(unix) */ #if defined(TN3270) { "!", shellhelp, shell, 1 }, #else { "!", shellhelp, shell, 0 }, #endif { "environ", envhelp, env_cmd, 0 }, { "?", helphelp, help, 0 }, #if defined(SKEY) { "skey", skeyhelp, skey_calc, 0 }, #endif { 0, 0, 0, 0 } }; static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; static char escapehelp[] = "deprecated command -- use 'set escape' instead"; static Command cmdtab2[] = { { "help", 0, help, 0 }, { "escape", escapehelp, setescape, 0 }, { "crmod", crmodhelp, togcrmod, 0 }, { 0, 0, 0, 0 } }; /* * Call routine with argc, argv set from args (terminated by 0). */ /*VARARGS1*/ static int call(va_alist) va_dcl { va_list ap; typedef int (*intrtn_t)(); intrtn_t routine; char *args[100]; int argno = 0; va_start(ap); routine = (va_arg(ap, intrtn_t)); while ((args[argno++] = va_arg(ap, char *)) != 0) { ; } va_end(ap); return (*routine)(argno-1, args); } static Command * getcmd(name) char *name; { Command *cm; if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command)))) return cm; return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); } void command(top, tbuf, cnt) int top; char *tbuf; int cnt; { register Command *c; setcommandmode(); if (!top) { putchar('\n'); #if defined(unix) } else { (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); #endif /* defined(unix) */ } for (;;) { if (rlogin == _POSIX_VDISABLE) printf("%s> ", prompt); if (tbuf) { register char *cp; cp = line; while (cnt > 0 && (*cp++ = *tbuf++) != '\n') cnt--; tbuf = 0; if (cp == line || *--cp != '\n' || cp == line) goto getline; *cp = '\0'; if (rlogin == _POSIX_VDISABLE) printf("%s\n", line); } else { getline: if (rlogin != _POSIX_VDISABLE) printf("%s> ", prompt); if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin) || ferror(stdin)) { (void) quit(); /*NOTREACHED*/ } break; } } if (line[0] == 0) break; makeargv(); if (margv[0] == 0) { break; } c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command\n"); continue; } if (c == 0) { printf("?Invalid command\n"); continue; } if (c->needconnect && !connected) { printf("?Need to be connected first.\n"); continue; } if ((*c->handler)(margc, margv)) { break; } } if (!top) { if (!connected) { longjmp(toplevel, 1); /*NOTREACHED*/ } #if defined(TN3270) if (shell_active == 0) { setconnmode(0); } #else /* defined(TN3270) */ setconnmode(0); #endif /* defined(TN3270) */ } } /* * Help command. */ static int help(argc, argv) int argc; char *argv[]; { register Command *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) if (c->help) { printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); } } else while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (Ambiguous(c)) printf("?Ambiguous help command %s\n", arg); else if (c == (Command *)0) printf("?Invalid help command %s\n", arg); else printf("%s\n", c->help); } return(0); } static char *rcname = 0; static char rcbuf[128]; void cmdrc(m1, m2) char *m1, *m2; { register Command *c; FILE *rcfile; int gotmachine = 0; int l1 = strlen(m1); int l2 = strlen(m2); char m1save[64]; if (skiprc) return; strcpy(m1save, m1); m1 = m1save; if (rcname == 0) { rcname = getenv("HOME"); if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf)) strcpy(rcbuf, rcname); else rcbuf[0] = '\0'; strcat(rcbuf, "/.telnetrc"); rcname = rcbuf; } if ((rcfile = fopen(rcname, "r")) == 0) { return; } for (;;) { if (fgets(line, sizeof(line), rcfile) == NULL) break; if (line[0] == 0) break; if (line[0] == '#') continue; if (gotmachine) { if (!isspace(line[0])) gotmachine = 0; } if (gotmachine == 0) { if (isspace(line[0])) continue; if (strncasecmp(line, m1, l1) == 0) strncpy(line, &line[l1], sizeof(line) - l1); else if (strncasecmp(line, m2, l2) == 0) strncpy(line, &line[l2], sizeof(line) - l2); else if (strncasecmp(line, "DEFAULT", 7) == 0) strncpy(line, &line[7], sizeof(line) - 7); else continue; if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n') continue; gotmachine = 1; } makeargv(); if (margv[0] == 0) continue; c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command: %s\n", margv[0]); continue; } if (c == 0) { printf("?Invalid command: %s\n", margv[0]); continue; } /* * This should never happen... */ if (c->needconnect && !connected) { printf("?Need to be connected first for %s.\n", margv[0]); continue; } (*c->handler)(margc, margv); } fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst * If the leading ! is present, it is a * strict source route, otherwise it is * assmed to be a loose source route. * * We fill in the source route option as * hop1,hop2,hop3...dest * and return a pointer to hop1, which will * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a * pointer to a pointer to a character array * that should be filled in with the option. * * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the * path is unknown, and *cpp is set to point to the bad * hostname. * * *cpp: If *cpp was equal to NULL, it will be filled * in with a pointer to our static area that has * the option filled in. This will be 32bit aligned. * * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* * Verify the arguments, and make sure we have * at least 7 bytes for the option. */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in * the begining of the option. */ #ifndef sysV88 if (*cp == '!') { cp++; *lsrp++ = IPOPT_SSRR; } else *lsrp++ = IPOPT_LSRR; #else if (*cp == '!') { cp++; ipopt.io_type = IPOPT_SSRR; } else ipopt.io_type = IPOPT_LSRR; #endif if (*cp != '@') return((unsigned long)-1); #ifndef sysV88 lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; (c = *cp2); cp2++) { if (c == ',') { *cp2++ = '\0'; if (*cp2 == '@') cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; break; } if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if ((host = gethostbyname(cp))) { -#if defined(h_addr) - memmove((caddr_t)&sin_addr, - host->h_addr_list[0], host->h_length); -#else - memmove((caddr_t)&sin_addr, host->h_addr, host->h_length); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memmove(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else break; /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) + return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ *lenp = lsrp - *cpp; #else ipopt.io_len = lsrp - *cpp; if (ipopt.io_len <= 5) { /* Is 3 better ? */ *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif + + + diff --git a/crypto/telnet/telnet/externs.h b/crypto/telnet/telnet/externs.h index 0c6894f53d4e..46253d9a6048 100644 --- a/crypto/telnet/telnet/externs.h +++ b/crypto/telnet/telnet/externs.h @@ -1,492 +1,502 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)externs.h 8.3 (Berkeley) 5/30/95 + * $FreeBSD$ */ #ifndef BSD # define BSD 43 #endif /* * ucb stdio.h defines BSD as something wierd */ #if defined(sun) && defined(__svr4__) #define BSD 43 #endif #ifndef USE_TERMIO # if BSD > 43 || defined(SYSV_TERMIO) # define USE_TERMIO # endif #endif #include #include #if defined(CRAY) && !defined(NO_BSD_SETJMP) #include #endif #ifndef FILIO_H #include #else #include #endif #ifdef CRAY # include #endif /* CRAY */ #ifdef USE_TERMIO # ifndef VINTR # ifdef SYSV_TERMIO # include # else # include # define termio termios # endif # endif #endif #if defined(NO_CC_T) || !defined(USE_TERMIO) # if !defined(USE_TERMIO) typedef char cc_t; # else typedef unsigned char cc_t; # endif #endif #ifndef NO_STRING_H #include #else #include #endif +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ # endif # ifdef VDISABLE # define _POSIX_VDISABLE VDISABLE # else # define _POSIX_VDISABLE ((cc_t)'\377') # endif #endif #define SUBBUFSIZE 256 #ifndef CRAY extern int errno; /* outside this world */ #endif /* !CRAY */ #if !defined(P) # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ In3270, /* Are we in 3270 mode? */ telnetport, /* Are we connected to the telnet port? */ localflow, /* Flow control handled locally */ restartany, /* If flow control, restart output on any character */ localchars, /* we recognize interrupt/quit */ donelclchars, /* the user has set "localchars" */ showoptions, net, /* Network file descriptor */ tin, /* Terminal input file descriptor */ tout, /* Terminal output file descriptor */ crlf, /* Should '\r' be mapped to (or )? */ autoflush, /* flush output when interrupting? */ autosynch, /* send interrupt characters with SYNCH? */ SYNCHing, /* Is the stream in telnet SYNCH mode? */ donebinarytoggle, /* the user has put us in binary */ dontlecho, /* do we suppress local echoing right now? */ crmod, netdata, /* Print out network data flow */ prettydump, /* Print "netdata" output in user readable format */ #if defined(unix) #if defined(TN3270) cursesdata, /* Print out curses data flow */ apitrace, /* Trace API transactions */ #endif /* defined(TN3270) */ termdata, /* Print out terminal data flow */ #endif /* defined(unix) */ debug, /* Debug level */ doaddrlookup, /* do a reverse lookup? */ clienteof; /* Client received EOF */ extern cc_t escape; /* Escape to command mode */ extern cc_t rlogin; /* Rlogin mode escape character */ #ifdef KLUDGELINEMODE extern cc_t echoc; /* Toggle local echoing */ #endif extern char *prompt; /* Prompt for command. */ extern char doopt[], dont[], will[], wont[], options[], /* All the little options */ *hostname; /* Who are we connected to? */ #ifdef ENCRYPTION extern void (*encrypt_output) P((unsigned char *, int)); extern int (*decrypt_input) P((int)); #endif /* ENCRYPTION */ /* * We keep track of each side of the option negotiation. */ #define MY_STATE_WILL 0x01 #define MY_WANT_STATE_WILL 0x02 #define MY_STATE_DO 0x04 #define MY_WANT_STATE_DO 0x08 /* * Macros to check the current state of things */ #define my_state_is_do(opt) (options[opt]&MY_STATE_DO) #define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) #define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) #define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) #define my_state_is_dont(opt) (!my_state_is_do(opt)) #define my_state_is_wont(opt) (!my_state_is_will(opt)) #define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) #define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) #define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;} #define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;} #define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;} #define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;} #define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;} #define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;} #define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;} #define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;} /* * Make everything symetrical */ #define HIS_STATE_WILL MY_STATE_DO #define HIS_WANT_STATE_WILL MY_WANT_STATE_DO #define HIS_STATE_DO MY_STATE_WILL #define HIS_WANT_STATE_DO MY_WANT_STATE_WILL #define his_state_is_do my_state_is_will #define his_state_is_will my_state_is_do #define his_want_state_is_do my_want_state_is_will #define his_want_state_is_will my_want_state_is_do #define his_state_is_dont my_state_is_wont #define his_state_is_wont my_state_is_dont #define his_want_state_is_dont my_want_state_is_wont #define his_want_state_is_wont my_want_state_is_dont #define set_his_state_do set_my_state_will #define set_his_state_will set_my_state_do #define set_his_want_state_do set_my_want_state_will #define set_his_want_state_will set_my_want_state_do #define set_his_state_dont set_my_state_wont #define set_his_state_wont set_my_state_dont #define set_his_want_state_dont set_my_want_state_wont #define set_his_want_state_wont set_my_want_state_dont extern FILE *NetTrace; /* Where debugging output goes */ extern unsigned char NetTraceFile[]; /* Name of file where debugging output goes */ extern void SetNetTrace P((char *)); /* Function to change where debugging goes */ extern jmp_buf peerdied, toplevel; /* For error conditions. */ extern void command P((int, char *, int)), Dump P((int, unsigned char *, int)), ExitString P((char *, int)), init_3270 P((void)), printoption P((char *, int, int)), printsub P((int, unsigned char *, int)), sendnaws P((void)), setconnmode P((int)), setcommandmode P((void)), setneturg P((void)), sys_telnet_init P((void)), telnet P((char *)), tel_enter_binary P((int)), TerminalFlushOutput P((void)), TerminalNewMode P((int)), TerminalRestoreState P((void)), TerminalSaveState P((void)), tninit P((void)), upcase P((char *)), willoption P((int)), wontoption P((int)); extern void send_do P((int, int)), send_dont P((int, int)), send_will P((int, int)), send_wont P((int, int)); extern void lm_will P((unsigned char *, int)), lm_wont P((unsigned char *, int)), lm_do P((unsigned char *, int)), lm_dont P((unsigned char *, int)), lm_mode P((unsigned char *, int, int)); extern void slc_init P((void)), slcstate P((void)), slc_mode_export P((void)), slc_mode_import P((int)), slc_import P((int)), slc_export P((void)), slc P((unsigned char *, int)), slc_check P((void)), slc_start_reply P((void)), slc_add_reply P((int, int, int)), slc_end_reply P((void)); extern int NetClose P((int)), netflush P((void)), SetSockOpt P((int, int, int, int)), slc_update P((void)), telrcv P((void)), TerminalWrite P((char *, int)), TerminalAutoFlush P((void)), ttyflush P((int)); extern void env_opt P((unsigned char *, int)), env_opt_start P((void)), env_opt_start_info P((void)), env_opt_add P((unsigned char *)), env_opt_end P((int)); extern unsigned char *env_default P((int, int)), *env_getvalue P((unsigned char *)); extern int get_status P((void)), dosynch P((void)); extern cc_t *tcval P((int)); #ifndef USE_TERMIO extern struct tchars ntc; extern struct ltchars nltc; extern struct sgttyb nttyb; # define termEofChar ntc.t_eofc # define termEraseChar nttyb.sg_erase # define termFlushChar nltc.t_flushc # define termIntChar ntc.t_intrc # define termKillChar nttyb.sg_kill # define termLiteralNextChar nltc.t_lnextc # define termQuitChar ntc.t_quitc # define termSuspChar nltc.t_suspc # define termRprntChar nltc.t_rprntc # define termWerasChar nltc.t_werasc # define termStartChar ntc.t_startc # define termStopChar ntc.t_stopc # define termForw1Char ntc.t_brkc extern cc_t termForw2Char; extern cc_t termAytChar; # define termEofCharp (cc_t *)&ntc.t_eofc # define termEraseCharp (cc_t *)&nttyb.sg_erase # define termFlushCharp (cc_t *)&nltc.t_flushc # define termIntCharp (cc_t *)&ntc.t_intrc # define termKillCharp (cc_t *)&nttyb.sg_kill # define termLiteralNextCharp (cc_t *)&nltc.t_lnextc # define termQuitCharp (cc_t *)&ntc.t_quitc # define termSuspCharp (cc_t *)&nltc.t_suspc # define termRprntCharp (cc_t *)&nltc.t_rprntc # define termWerasCharp (cc_t *)&nltc.t_werasc # define termStartCharp (cc_t *)&ntc.t_startc # define termStopCharp (cc_t *)&ntc.t_stopc # define termForw1Charp (cc_t *)&ntc.t_brkc # define termForw2Charp (cc_t *)&termForw2Char # define termAytCharp (cc_t *)&termAytChar # else extern struct termio new_tc; # define termEofChar new_tc.c_cc[VEOF] # define termEraseChar new_tc.c_cc[VERASE] # define termIntChar new_tc.c_cc[VINTR] # define termKillChar new_tc.c_cc[VKILL] # define termQuitChar new_tc.c_cc[VQUIT] # ifndef VSUSP extern cc_t termSuspChar; # else # define termSuspChar new_tc.c_cc[VSUSP] # endif # if defined(VFLUSHO) && !defined(VDISCARD) # define VDISCARD VFLUSHO # endif # ifndef VDISCARD extern cc_t termFlushChar; # else # define termFlushChar new_tc.c_cc[VDISCARD] # endif # ifndef VWERASE extern cc_t termWerasChar; # else # define termWerasChar new_tc.c_cc[VWERASE] # endif # ifndef VREPRINT extern cc_t termRprntChar; # else # define termRprntChar new_tc.c_cc[VREPRINT] # endif # ifndef VLNEXT extern cc_t termLiteralNextChar; # else # define termLiteralNextChar new_tc.c_cc[VLNEXT] # endif # ifndef VSTART extern cc_t termStartChar; # else # define termStartChar new_tc.c_cc[VSTART] # endif # ifndef VSTOP extern cc_t termStopChar; # else # define termStopChar new_tc.c_cc[VSTOP] # endif # ifndef VEOL extern cc_t termForw1Char; # else # define termForw1Char new_tc.c_cc[VEOL] # endif # ifndef VEOL2 extern cc_t termForw2Char; # else # define termForw2Char new_tc.c_cc[VEOL] # endif # ifndef VSTATUS extern cc_t termAytChar; #else # define termAytChar new_tc.c_cc[VSTATUS] #endif # if !defined(CRAY) || defined(__STDC__) # define termEofCharp &termEofChar # define termEraseCharp &termEraseChar # define termIntCharp &termIntChar # define termKillCharp &termKillChar # define termQuitCharp &termQuitChar # define termSuspCharp &termSuspChar # define termFlushCharp &termFlushChar # define termWerasCharp &termWerasChar # define termRprntCharp &termRprntChar # define termLiteralNextCharp &termLiteralNextChar # define termStartCharp &termStartChar # define termStopCharp &termStopChar # define termForw1Charp &termForw1Char # define termForw2Charp &termForw2Char # define termAytCharp &termAytChar # else /* Work around a compiler bug */ # define termEofCharp 0 # define termEraseCharp 0 # define termIntCharp 0 # define termKillCharp 0 # define termQuitCharp 0 # define termSuspCharp 0 # define termFlushCharp 0 # define termWerasCharp 0 # define termRprntCharp 0 # define termLiteralNextCharp 0 # define termStartCharp 0 # define termStopCharp 0 # define termForw1Charp 0 # define termForw2Charp 0 # define termAytCharp 0 # endif #endif /* Ring buffer structures which are shared */ extern Ring netoring, netiring, ttyoring, ttyiring; /* Tn3270 section */ #if defined(TN3270) extern int HaveInput, /* Whether an asynchronous I/O indication came in */ noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */ noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */ sigiocount, /* Count of SIGIO receptions */ shell_active; /* Subshell is active */ extern char *Ibackp, /* Oldest byte of 3270 data */ Ibuf[], /* 3270 buffer */ *Ifrontp, /* Where next 3270 byte goes */ tline[], *transcom; /* Transparent command */ extern int settranscom P((int, char**)); extern void inputAvailable P((int)); #endif /* defined(TN3270) */ diff --git a/crypto/telnet/telnet/main.c b/crypto/telnet/telnet/main.c index 2dc05fd73eb0..c3d0f5d74ba9 100644 --- a/crypto/telnet/telnet/main.c +++ b/crypto/telnet/telnet/main.c @@ -1,350 +1,389 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static const char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; #endif /* not lint */ #include +#include #include #include "ring.h" #include "externs.h" #include "defines.h" #if defined(AUTHENTICATION) #include #endif #if defined(ENCRYPTION) #include #endif /* These values need to be the same as defined in libtelnet/kerberos5.c */ /* Either define them in both places, or put in some common header file. */ #define OPTS_FORWARD_CREDS 0x00000002 #define OPTS_FORWARDABLE_CREDS 0x00000001 #if 0 #define FORWARD #endif void init_terminal(void); void init_network(void); void init_telnet(void); void init_sys(void); void init_3270(void); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ void tninit() { init_terminal(); init_network(); init_telnet(); init_sys(); #if defined(TN3270) init_3270(); #endif } void usage() { fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) # ifdef AUTHENTICATION "[-noasynch] [-noasynctty]\n\t" "[-noasyncnet] [-r] [-s src_addr] [-t transcom] ", # else "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t" "[-s src_addr] [-t transcom]", # endif #else "[-r] [-s src_addr] ", #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" +#endif #ifdef ENCRYPTION "[-x] [host-name [port]]" #else /* ENCRYPTION */ "[host-name [port]]" #endif /* ENCRYPTION */ ); exit(1); } /* * main. Parse arguments, invoke the protocol or command parser. */ int main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int ch; char *user, *strrchr(); char *src_addr = NULL; #ifdef FORWARD extern int forward_flags; #endif /* FORWARD */ tninit(); /* Clear out things */ #if defined(CRAY) && !defined(__STDC__) _setlist_init(); /* Work around compiler bug */ #endif TerminalSaveState(); if ((prompt = strrchr(argv[0], '/'))) ++prompt; else prompt = argv[0]; user = NULL; rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != EOF) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; case 'E': rlogin = escape = _POSIX_VDISABLE; break; case 'K': #ifdef AUTHENTICATION autologin = 0; #endif break; case 'L': eight |= 2; /* binary output only */ break; case 'N': doaddrlookup = 0; break; case 'S': { #ifdef HAS_GETTOS extern int tos; if ((tos = parsetos(optarg, "tcp")) < 0) fprintf(stderr, "%s%s%s%s\n", prompt, ": Bad TOS argument '", optarg, "; will try to use default TOS"); #else fprintf(stderr, "%s: Warning: -S ignored, no parsetos() support.\n", prompt); #endif } break; case 'X': #ifdef AUTHENTICATION auth_disable_name(optarg); #endif break; case 'a': autologin = 1; break; case 'c': skiprc = 1; break; case 'd': debug = 1; break; case 'e': set_escape_char(optarg); break; case 'f': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; #else fprintf(stderr, "%s: Warning: -f ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'F': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; forward_flags |= OPTS_FORWARDABLE_CREDS; #else fprintf(stderr, "%s: Warning: -F ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'k': #if defined(AUTHENTICATION) && defined(KRB4) { extern char *dest_realm, dst_realm_buf[], dst_realm_sz; dest_realm = dst_realm_buf; (void)strncpy(dest_realm, optarg, dst_realm_sz); } #else fprintf(stderr, "%s: Warning: -k ignored, no Kerberos V4 support.\n", prompt); #endif break; case 'l': autologin = 1; user = optarg; break; case 'n': #if defined(TN3270) && defined(unix) /* distinguish between "-n oasynch" and "-noasynch" */ if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == 'n' && argv[optind - 1][2] == 'o') { if (!strcmp(optarg, "oasynch")) { noasynchtty = 1; noasynchnet = 1; } else if (!strcmp(optarg, "oasynchtty")) noasynchtty = 1; else if (!strcmp(optarg, "oasynchnet")) noasynchnet = 1; } else #endif /* defined(TN3270) && defined(unix) */ SetNetTrace(optarg); break; case 'r': rlogin = '~'; break; case 's': src_addr = optarg; break; case 't': #if defined(TN3270) && defined(unix) transcom = tline; (void)strcpy(transcom, optarg); #else fprintf(stderr, "%s: Warning: -t ignored, no TN3270 support.\n", prompt); #endif break; case 'x': #ifdef ENCRYPTION encrypt_auto(1); decrypt_auto(1); #else /* ENCRYPTION */ fprintf(stderr, "%s: Warning: -x ignored, no ENCRYPT support.\n", prompt); #endif /* ENCRYPTION */ break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); /* NOTREACHED */ } } if (autologin == -1) autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1; argc -= optind; argv += optind; if (argc) { char *args[9], **argp = args; if (argc > 2) usage(); *argp++ = prompt; if (user) { *argp++ = "-l"; *argp++ = user; } if (src_addr) { *argp++ = "-s"; *argp++ = src_addr; } *argp++ = argv[0]; /* host */ if (argc > 1) *argp++ = argv[1]; /* port */ *argp = 0; if (setjmp(toplevel) != 0) Exit(0); if (tn(argp - args, args) == 1) return (0); else return (1); } (void)setjmp(toplevel); for (;;) { #ifdef TN3270 if (shell_active) shell_continue(); else #endif command(1, 0, 0); } return 0; } diff --git a/crypto/telnet/telnet/telnet.1 b/crypto/telnet/telnet/telnet.1 index 984146232215..3378d3a4ad92 100644 --- a/crypto/telnet/telnet/telnet.1 +++ b/crypto/telnet/telnet/telnet.1 @@ -1,1380 +1,1384 @@ .\" Copyright (c) 1983, 1990, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)telnet.1 8.6 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME .Nm telnet .Nd user interface to the .Tn TELNET protocol .Sh SYNOPSIS .Nm telnet .Op Fl 8EFKLNacdfrx .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl e Ar escapechar .Op Fl k Ar realm .Op Fl l Ar user .Op Fl n Ar tracefile .Op Fl s Ar src_addr .Oo .Ar host .Op port .Oc .Sh DESCRIPTION The .Nm telnet command is used to communicate with another host using the .Tn TELNET protocol. If .Nm telnet is invoked without the .Ar host argument, it enters command mode, indicated by its prompt .Pq Nm telnet\&> . In this mode, it accepts and executes the commands listed below. If it is invoked with arguments, it performs an .Ic open command with those arguments. .Pp Options: .Bl -tag -width indent .It Fl 8 Specifies an 8-bit data path. This causes an attempt to negotiate the .Dv TELNET BINARY option on both input and output. .It Fl E Stops any character from being recognized as an escape character. .It Fl F If Kerberos V5 authentication is being used, the .Fl F option allows the local credentials to be forwarded to the remote system, including any credentials that have already been forwarded into the local environment. .It Fl K Specifies no automatic login to the remote system. .It Fl L Specifies an 8-bit data path on output. This causes the BINARY option to be negotiated on output. .It Fl N Prevents IP address to name lookup when destination host is given as an IP address. .It Fl S Ar tos Sets the IP type-of-service (TOS) option for the telnet connection to the value .Ar tos, which can be a numeric TOS value or, on systems that support it, a symbolic TOS name found in the /etc/iptos file. .It Fl X Ar atype Disables the .Ar atype type of authentication. .It Fl a Attempt automatic login. Currently, this sends the user name via the .Ev USER variable of the .Ev ENVIRON option if supported by the remote system. The name used is that of the current user as returned by .Xr getlogin 2 if it agrees with the current user ID, otherwise it is the name associated with the user ID. .It Fl c Disables the reading of the user's .Pa \&.telnetrc file. (See the .Ic toggle skiprc command on this man page.) .It Fl d Sets the initial value of the .Ic debug toggle to .Dv TRUE .It Fl e Ar escape char Sets the initial .Nm .Nm telnet escape character to .Ar escape char. If .Ar escape char is omitted, then there will be no escape character. .It Fl f If Kerberos V5 authentication is being used, the .Fl f option allows the local credentials to be forwarded to the remote system. .ne 1i .It Fl k Ar realm If Kerberos authentication is being used, the .Fl k option requests that telnet obtain tickets for the remote host in realm realm instead of the remote host's realm, as determined by .Xr krb_realmofhost 3 . .It Fl l Ar user When connecting to the remote system, if the remote system understands the .Ev ENVIRON option, then .Ar user will be sent to the remote system as the value for the variable USER. This option implies the .Fl a option. This option may also be used with the .Ic open command. .It Fl n Ar tracefile Opens .Ar tracefile for recording trace information. See the .Ic set tracefile command below. .It Fl r Specifies a user interface similar to .Xr rlogin 1 . In this mode, the escape character is set to the tilde (~) character, unless modified by the -e option. .It Fl s Ar src_addr Set the source IP address for the .Nm connection to .Ar src_addr , which can be an IP address or a host name. .It Fl x Turns on encryption of the data stream if possible. This option is not available outside of the United States and Canada. .It Ar host Indicates the official name, an alias, or the Internet address of a remote host. .It Ar port Indicates a port number (address of an application). If a number is not specified, the default .Nm telnet port is used. .El .Pp When in rlogin mode, a line of the form ~. disconnects from the remote host; ~ is the telnet escape character. Similarly, the line ~^Z suspends the telnet session. The line ~^] escapes to the normal telnet escape prompt. .Pp Once a connection has been opened, .Nm telnet will attempt to enable the .Dv TELNET LINEMODE option. If this fails, then .Nm telnet will revert to one of two input modes: either \*(Lqcharacter at a time\*(Rq or \*(Lqold line by line\*(Rq depending on what the remote system supports. .Pp When .Dv LINEMODE is enabled, character processing is done on the local system, under the control of the remote system. When input editing or character echoing is to be disabled, the remote system will relay that information. The remote system will also relay changes to any special characters that happen on the remote system, so that they can take effect on the local system. .Pp In \*(Lqcharacter at a time\*(Rq mode, most text typed is immediately sent to the remote host for processing. .Pp In \*(Lqold line by line\*(Rq mode, all text is echoed locally, and (normally) only completed lines are sent to the remote host. The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used to turn off and on the local echo (this would mostly be used to enter passwords without the password being echoed). .Pp If the .Dv LINEMODE option is enabled, or if the .Ic localchars toggle is .Dv TRUE (the default for \*(Lqold line by line\*(Lq; see below), the user's .Ic quit , .Ic intr , and .Ic flush characters are trapped locally, and sent as .Tn TELNET protocol sequences to the remote side. If .Dv LINEMODE has ever been enabled, then the user's .Ic susp and .Ic eof are also sent as .Tn TELNET protocol sequences, and .Ic quit is sent as a .Dv TELNET ABORT instead of .Dv BREAK There are options (see .Ic toggle .Ic autoflush and .Ic toggle .Ic autosynch below) which cause this action to flush subsequent output to the terminal (until the remote host acknowledges the .Tn TELNET sequence) and flush previous terminal input (in the case of .Ic quit and .Ic intr ) . .Pp While connected to a remote host, .Nm telnet command mode may be entered by typing the .Nm telnet \*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq). When in command mode, the normal terminal editing conventions are available. .Pp The following .Nm telnet commands are available. Only enough of each command to uniquely identify it need be typed (this is also true for arguments to the .Ic mode , .Ic set , .Ic toggle , .Ic unset , .Ic slc , .Ic environ , and .Ic display commands). .Pp .Bl -tag -width "mode type" .It Ic auth Ar argument ... The auth command manipulates the information sent through the .Dv TELNET AUTHENTICATE option. Valid arguments for the auth command are as follows: .Bl -tag -width "disable type" .It Ic disable Ar type Disables the specified type of authentication. To obtain a list of available types, use the .Ic auth disable \&? command. .It Ic enable Ar type Enables the specified type of authentication. To obtain a list of available types, use the .Ic auth enable \&? command. .It Ic status Lists the current status of the various types of authentication. .El .It Ic close Close a .Tn TELNET session and return to command mode. .It Ic display Ar argument ... Displays all, or some, of the .Ic set and .Ic toggle values (see below). .It Ic encrypt Ar argument ... The encrypt command manipulates the information sent through the .Dv TELNET ENCRYPT option. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside of the United States and Canada. .Pp Valid arguments for the encrypt command are as follows: .Bl -tag -width Ar .It Ic disable Ar type Ic [input|output] Disables the specified type of encryption. If you omit the input and output, both input and output are disabled. To obtain a list of available types, use the .Ic encrypt disable \&? command. .It Ic enable Ar type Ic [input|output] Enables the specified type of encryption. If you omit input and output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic input This is the same as the .Ic encrypt start input command. .It Ic -input This is the same as the .Ic encrypt stop input command. .It Ic output This is the same as the .Ic encrypt start output command. .It Ic -output This is the same as the .Ic encrypt stop output command. .It Ic start Ic [input|output] Attempts to start encryption. If you omit .Ic input and .Ic output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic status Lists the current status of encryption. .It Ic stop Ic [input|output] Stops encryption. If you omit input and output, encryption is on both input and output. .It Ic type Ar type Sets the default type of encryption to be used with later .Ic encrypt start or .Ic encrypt stop commands. .El .It Ic environ Ar arguments... The .Ic environ command is used to manipulate the the variables that my be sent through the .Dv TELNET ENVIRON option. The initial set of variables is taken from the users environment, with only the .Ev DISPLAY and .Ev PRINTER variables being exported by default. The .Ev USER variable is also exported if the .Fl a or .Fl l options are used. .br Valid arguments for the .Ic environ command are: .Bl -tag -width Fl .It Ic define Ar variable value Define the variable .Ar variable to have a value of .Ar value. Any variables defined by this command are automatically exported. The .Ar value may be enclosed in single or double quotes so that tabs and spaces may be included. .It Ic undefine Ar variable Remove .Ar variable from the list of environment variables. .It Ic export Ar variable Mark the variable .Ar variable to be exported to the remote side. .It Ic unexport Ar variable Mark the variable .Ar variable to not be exported unless explicitly asked for by the remote side. .It Ic list List the current set of environment variables. Those marked with a .Cm * will be sent automatically, other variables will only be sent if explicitly requested. .It Ic \&? Prints out help information for the .Ic environ command. .El .It Ic logout Sends the .Dv TELNET LOGOUT option to the remote side. This command is similar to a .Ic close command; however, if the remote side does not support the .Dv LOGOUT option, nothing happens. If, however, the remote side does support the .Dv LOGOUT option, this command should cause the remote side to close the .Tn TELNET connection. If the remote side also supports the concept of suspending a user's session for later reattachment, the logout argument indicates that you should terminate the session immediately. .It Ic mode Ar type .Ar Type is one of several options, depending on the state of the .Tn TELNET session. The remote host is asked for permission to go into the requested mode. If the remote host is capable of entering that mode, the requested mode will be entered. .Bl -tag -width Ar .It Ic character Disable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then enter \*(Lqcharacter at a time\*(Lq mode. .It Ic line Enable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then attempt to enter \*(Lqold-line-by-line\*(Lq mode. .It Ic isig Pq Ic \-isig Attempt to enable (disable) the .Dv TRAPSIG mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic edit Pq Ic \-edit Attempt to enable (disable) the .Dv EDIT mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic softtabs Pq Ic \-softtabs Attempt to enable (disable) the .Dv SOFT_TAB mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .ne 1i .It Ic litecho Pq Ic \-litecho Attempt to enable (disable) the .Dv LIT_ECHO mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic \&? Prints out help information for the .Ic mode command. .El .It Xo .Ic open Ar host .Oo Op Fl l .Ar user .Oc Ns Oo Fl .Ar port Oc .Xc Open a connection to the named host. If no port number is specified, .Nm telnet will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Op Fl l option may be used to specify the user name to be passed to the remote system via the .Ev ENVIRON option. When connecting to a non-standard port, .Nm telnet omits any automatic initiation of .Tn TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done. After establishing a connection, the file .Pa \&.telnetrc in the users home directory is opened. Lines beginning with a # are comment lines. Blank lines are ignored. Lines that begin without white space are the start of a machine entry. The first thing on the line is the name of the machine that is being connected to. The rest of the line, and successive lines that begin with white space are assumed to be .Nm telnet commands and are processed as if they had been typed in manually to the .Nm telnet command prompt. .It Ic quit Close any open .Tn TELNET session and exit .Nm telnet . An end of file (in command mode) will also close a session and exit. .It Ic send Ar arguments Sends one or more special character sequences to the remote host. The following are the arguments which may be specified (more than one argument may be specified at a time): .Pp .Bl -tag -width escape .It Ic abort Sends the .Dv TELNET ABORT (Abort processes) sequence. .It Ic ao Sends the .Dv TELNET AO (Abort Output) sequence, which should cause the remote system to flush all output .Em from the remote system .Em to the user's terminal. .It Ic ayt Sends the .Dv TELNET AYT (Are You There) sequence, to which the remote system may or may not choose to respond. .It Ic brk Sends the .Dv TELNET BRK (Break) sequence, which may have significance to the remote system. .It Ic ec Sends the .Dv TELNET EC (Erase Character) sequence, which should cause the remote system to erase the last character entered. .It Ic el Sends the .Dv TELNET EL (Erase Line) sequence, which should cause the remote system to erase the line currently being entered. .It Ic eof Sends the .Dv TELNET EOF (End Of File) sequence. .It Ic eor Sends the .Dv TELNET EOR (End of Record) sequence. .It Ic escape Sends the current .Nm telnet escape character (initially \*(Lq^\*(Rq). .It Ic ga Sends the .Dv TELNET GA (Go Ahead) sequence, which likely has no significance to the remote system. .It Ic getstatus If the remote side supports the .Dv TELNET STATUS command, .Ic getstatus will send the subnegotiation to request that the server send its current option status. .ne 1i .It Ic ip Sends the .Dv TELNET IP (Interrupt Process) sequence, which should cause the remote system to abort the currently running process. .It Ic nop Sends the .Dv TELNET NOP (No OPeration) sequence. .It Ic susp Sends the .Dv TELNET SUSP (SUSPend process) sequence. .It Ic synch Sends the .Dv TELNET SYNCH sequence. This sequence causes the remote system to discard all previously typed (but not yet read) input. This sequence is sent as .Tn TCP urgent data (and may not work if the remote system is a .Bx 4.2 system -- if it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal). .It Ic do Ar cmd .It Ic dont Ar cmd .It Ic will Ar cmd .It Ic wont Ar cmd Sends the .Dv TELNET DO .Ar cmd sequence. .Ar Cmd can be either a decimal number between 0 and 255, or a symbolic name for a specific .Dv TELNET command. .Ar Cmd can also be either .Ic help or .Ic \&? to print out help information, including a list of known symbolic names. .It Ic \&? Prints out help information for the .Ic send command. .El .It Ic set Ar argument value .It Ic unset Ar argument value The .Ic set command will set any one of a number of .Nm telnet variables to a specific value or to .Dv TRUE . The special value .Ic off turns off the function associated with the variable, this is equivalent to using the .Ic unset command. The .Ic unset command will disable or set to .Dv FALSE any of the specified functions. The values of variables may be interrogated with the .Ic display command. The variables which may be set or unset, but not toggled, are listed here. In addition, any of the variables for the .Ic toggle command may be explicitly set or unset using the .Ic set and .Ic unset commands. .Bl -tag -width escape .It Ic ayt If .Tn TELNET is in localchars mode, or .Dv LINEMODE is enabled, and the status character is typed, a .Dv TELNET AYT sequence (see .Ic send ayt preceding) is sent to the remote host. The initial value for the "Are You There" character is the terminal's status character. .It Ic echo This is the value (initially \*(Lq^E\*(Rq) which, when in \*(Lqline by line\*(Rq mode, toggles between doing local echoing of entered characters (for normal processing), and suppressing echoing of entered characters (for entering, say, a password). .It Ic eof If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, entering this character as the first character on a line will cause this character to be sent to the remote system. The initial value of the eof character is taken to be the terminal's .Ic eof character. .It Ic erase If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Sy and if .Nm telnet is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EC sequence (see .Ic send .Ic ec above) is sent to the remote system. The initial value for the erase character is taken to be the terminal's .Ic erase character. .It Ic escape This is the .Nm telnet escape character (initially \*(Lq^[\*(Rq) which causes entry into .Nm telnet command mode (when connected to a remote system). .It Ic flushoutput If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic flushoutput character is typed, a .Dv TELNET AO sequence (see .Ic send .Ic ao above) is sent to the remote host. The initial value for the flush character is taken to be the terminal's .Ic flush character. .It Ic forw1 .It Ic forw2 If .Tn TELNET is operating in .Dv LINEMODE , these are the characters that, when typed, cause partial lines to be forwarded to the remote system. The initial value for the forwarding characters are taken from the terminal's eol and eol2 characters. .It Ic interrupt If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic interrupt character is typed, a .Dv TELNET IP sequence (see .Ic send .Ic ip above) is sent to the remote host. The initial value for the interrupt character is taken to be the terminal's .Ic intr character. .It Ic kill If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Ic and if .Nm telnet is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EL sequence (see .Ic send .Ic el above) is sent to the remote system. The initial value for the kill character is taken to be the terminal's .Ic kill character. .It Ic lnext If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic lnext character. The initial value for the lnext character is taken to be the terminal's .Ic lnext character. .It Ic quit If .Nm telnet is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic quit character is typed, a .Dv TELNET BRK sequence (see .Ic send .Ic brk above) is sent to the remote host. The initial value for the quit character is taken to be the terminal's .Ic quit character. .It Ic reprint If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic reprint character. The initial value for the reprint character is taken to be the terminal's .Ic reprint character. .It Ic rlogin This is the rlogin escape character. If set, the normal .Tn TELNET escape character is ignored unless it is preceded by this character at the beginning of a line. This character, at the beginning of a line followed by a "." closes the connection; when followed by a ^Z it suspends the telnet command. The initial state is to disable the rlogin escape character. .It Ic start If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic start character. The initial value for the kill character is taken to be the terminal's .Ic start character. .It Ic stop If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic stop character. The initial value for the kill character is taken to be the terminal's .Ic stop character. .It Ic susp If .Nm telnet is in .Ic localchars mode, or .Dv LINEMODE is enabled, and the .Ic suspend character is typed, a .Dv TELNET SUSP sequence (see .Ic send .Ic susp above) is sent to the remote host. The initial value for the suspend character is taken to be the terminal's .Ic suspend character. .ne 1i .It Ic tracefile This is the file to which the output, caused by .Ic netdata or .Ic option tracing being .Dv TRUE , will be written. If it is set to .Dq Fl , then tracing information will be written to standard output (the default). .It Ic worderase If .Nm telnet is operating in .Dv LINEMODE or \*(Lqold line by line\*(Lq mode, then this character is taken to be the terminal's .Ic worderase character. The initial value for the worderase character is taken to be the terminal's .Ic worderase character. .It Ic \&? Displays the legal .Ic set .Pq Ic unset commands. .El .It Ic skey Ar sequence challenge The .Ic skey command computes a response to the S/Key challenge. .It Ic slc Ar state The .Ic slc command (Set Local Characters) is used to set or change the state of the the special characters when the .Dv TELNET LINEMODE option has been enabled. Special characters are characters that get mapped to .Tn TELNET commands sequences (like .Ic ip or .Ic quit ) or line editing characters (like .Ic erase and .Ic kill ) . By default, the local special characters are exported. .Bl -tag -width Fl .It Ic check Verify the current settings for the current special characters. The remote side is requested to send all the current special character settings, and if there are any discrepancies with the local side, the local side will switch to the remote value. .It Ic export Switch to the local defaults for the special characters. The local default characters are those of the local terminal at the time when .Nm telnet was started. .It Ic import Switch to the remote defaults for the special characters. The remote default characters are those of the remote system at the time when the .Tn TELNET connection was established. .It Ic \&? Prints out help information for the .Ic slc command. .El .It Ic status Show the current status of .Nm telnet . This includes the peer one is connected to, as well as the current mode. .It Ic toggle Ar arguments ... Toggle (between .Dv TRUE and .Dv FALSE ) various flags that control how .Nm telnet responds to events. These flags may be set explicitly to .Dv TRUE or .Dv FALSE using the .Ic set and .Ic unset commands listed above. More than one argument may be specified. The state of these flags may be interrogated with the .Ic display command. Valid arguments are: .Bl -tag -width Ar .It Ic authdebug Turns on debugging information for the authentication code. .It Ic autoflush If .Ic autoflush and .Ic localchars are both .Dv TRUE , then when the .Ic ao , or .Ic quit characters are recognized (and transformed into .Tn TELNET sequences; see .Ic set above for details), .Nm telnet refuses to display any data on the user's terminal until the remote system acknowledges (via a .Dv TELNET TIMING MARK option) that it has processed those .Tn TELNET sequences. The initial value for this toggle is .Dv TRUE if the terminal user had not done an "stty noflsh", otherwise .Dv FALSE (see .Xr stty 1 ) . .It Ic autodecrypt When the .Dv TELNET ENCRYPT option is negotiated, by default the actual encryption (decryption) of the data stream does not start automatically. The autoencrypt (autodecrypt) command states that encryption of the output (input) stream should be enabled as soon as possible. .sp .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside the United States and Canada. .It Ic autologin If the remote side supports the .Dv TELNET AUTHENTICATION option .Tn TELNET attempts to use it to perform automatic authentication. If the .Dv AUTHENTICATION option is not supported, the user's login name are propagated through the .Dv TELNET ENVIRON option. This command is the same as specifying .Ar a option on the .Ic open command. .It Ic autosynch If .Ic autosynch and .Ic localchars are both .Dv TRUE , then when either the .Ic intr or .Ic quit characters is typed (see .Ic set above for descriptions of the .Ic intr and .Ic quit characters), the resulting .Tn TELNET sequence sent is followed by the .Dv TELNET SYNCH sequence. This procedure .Ic should cause the remote system to begin throwing away all previously typed input until both of the .Tn TELNET sequences have been read and acted upon. The initial value of this toggle is .Dv FALSE . .It Ic binary Enable or disable the .Dv TELNET BINARY option on both input and output. .It Ic inbinary Enable or disable the .Dv TELNET BINARY option on input. .It Ic outbinary Enable or disable the .Dv TELNET BINARY option on output. .It Ic crlf If this is .Dv TRUE , then carriage returns will be sent as .Li . If this is .Dv FALSE , then carriage returns will be send as .Li . The initial value for this toggle is .Dv FALSE . .It Ic crmod Toggle carriage return mode. When this mode is enabled, most carriage return characters received from the remote host will be mapped into a carriage return followed by a line feed. This mode does not affect those characters typed by the user, only those received from the remote host. This mode is not very useful unless the remote host only sends carriage return, but never line feed. The initial value for this toggle is .Dv FALSE . .It Ic debug Toggles socket level debugging (useful only to the .Ic super user ) . The initial value for this toggle is .Dv FALSE . .It Ic encdebug Turns on debugging information for the encryption code. .It Ic localchars If this is .Dv TRUE , then the .Ic flush , .Ic interrupt , .Ic quit , .Ic erase , and .Ic kill characters (see .Ic set above) are recognized locally, and transformed into (hopefully) appropriate .Tn TELNET control sequences (respectively .Ic ao , .Ic ip , .Ic brk , .Ic ec , and .Ic el ; see .Ic send above). The initial value for this toggle is .Dv TRUE in \*(Lqold line by line\*(Rq mode, and .Dv FALSE in \*(Lqcharacter at a time\*(Rq mode. When the .Dv LINEMODE option is enabled, the value of .Ic localchars is ignored, and assumed to always be .Dv TRUE . If .Dv LINEMODE has ever been enabled, then .Ic quit is sent as .Ic abort , and .Ic eof and .B suspend are sent as .Ic eof and .Ic susp , see .Ic send above). .It Ic netdata Toggles the display of all network data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic options Toggles the display of some internal .Nm telnet protocol processing (having to do with .Tn TELNET options). The initial value for this toggle is .Dv FALSE . .ne 1i .It Ic prettydump When the .Ic netdata toggle is enabled, if .Ic prettydump is enabled the output from the .Ic netdata command will be formatted in a more user readable format. Spaces are put between each character in the output, and the beginning of any .Tn TELNET escape sequence is preceded by a '*' to aid in locating them. .It Ic skiprc When the skiprc toggle is .Dv TRUE , .Tn TELNET skips the reading of the .Pa \&.telnetrc file in the users home directory when connections are opened. The initial value for this toggle is .Dv FALSE. .It Ic termdata Toggles the display of all terminal data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic verbose_encrypt When the .Ic verbose_encrypt toggle is .Dv TRUE , .Tn TELNET prints out a message each time encryption is enabled or disabled. The initial value for this toggle is .Dv FALSE. Note: Because of export controls, data encryption is not supported outside of the United States and Canada. .It Ic \&? Displays the legal .Ic toggle commands. .El .It Ic z Suspend .Nm telnet . This command only works when the user is using the .Xr csh 1 . .It Ic \&! Op Ar command Execute a single command in a subshell on the local system. If .Ic command is omitted, then an interactive subshell is invoked. .It Ic \&? Op Ar command Get help. With no arguments, .Nm telnet prints a help summary. If a command is specified, .Nm telnet will print the help information for just that command. .El .Sh ENVIRONMENT .Nm Telnet uses at least the .Ev HOME , .Ev SHELL , .Ev DISPLAY , and .Ev TERM environment variables. Other environment variables may be propagated to the other side via the .Dv TELNET ENVIRON option. .Sh FILES .Bl -tag -width ~/.telnetrc -compact .It Pa ~/.telnetrc user customized telnet startup values .El .Sh HISTORY The .Nm Telnet command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in \*(Lqold line by line\*(Rq mode. .Pp In \*(Lqold line by line\*(Rq mode or .Dv LINEMODE the terminal's .Ic eof character is only recognized (and sent to the remote system) when it is the first character on a line. diff --git a/crypto/telnet/telnetd/telnetd.8 b/crypto/telnet/telnetd/telnetd.8 index e03f290daaa9..4e004a4edede 100644 --- a/crypto/telnet/telnetd/telnetd.8 +++ b/crypto/telnet/telnetd/telnetd.8 @@ -1,612 +1,615 @@ .\" Copyright (c) 1983, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)telnetd.8 8.4 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNETD 8 .Os BSD 4.2 .Sh NAME .Nm telnetd .Nd DARPA .Tn TELNET protocol server .Sh SYNOPSIS .Nm /usr/libexec/telnetd .Op Fl BUhlkns .Op Fl D Ar debugmode .Op Fl I Ns Ar initid .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl a Ar authmode .Op Fl edebug .Op Fl p Ar loginprog .Op Fl r Ns Ar lowpty-highpty .Op Fl u Ar len .Op Fl debug Op Ar port .Sh DESCRIPTION The .Nm command is a server which supports the .Tn DARPA standard .Tn TELNET virtual terminal protocol. .Nm Telnetd is normally invoked by the internet server (see .Xr inetd 8 ) for requests to connect to the .Tn TELNET port as indicated by the .Pa /etc/services file (see .Xr services 5 ). The .Fl debug option may be used to start up .Nm manually, instead of through .Xr inetd 8 . If started up this way, .Ar port may be specified to run .Nm on an alternate .Tn TCP port number. .Pp The .Nm command accepts the following options: .Bl -tag -width indent .It Fl a Ar authmode This option may be used for specifying what mode should be used for authentication. Note that this option is only useful if .Nm has been compiled with support for the .Dv AUTHENTICATION option. There are several valid values for .Ar authmode : .Bl -tag -width debug .It Cm debug Turn on authentication debugging code. .It Cm user Only allow connections when the remote user can provide valid authentication information to identify the remote user, and is allowed access to the specified account without providing a password. .It Cm valid Only allow connections when the remote user can provide valid authentication information to identify the remote user. The .Xr login 1 command will provide any additional user verification needed if the remote user is not allowed automatic access to the specified account. .It Cm other Only allow connections that supply some authentication information. This option is currently not supported by any of the existing authentication mechanisms, and is thus the same as specifying .Fl a .Cm valid . .It Cm none This is the default state. Authentication information is not required. If no or insufficient authentication information is provided, then the .Xr login 1 program will provide the necessary user verification. .It Cm off Disable the authentication code. All user verification will happen through the .Xr login 1 program. .El .It Fl B Specify bftp server mode. In this mode, .Nm causes login to start a .Xr bftp 1 session rather than the user's normal shell. In bftp daemon mode normal logins are not supported, and it must be used on a port other than the normal .Tn TELNET port. .It Fl D Ar debugmode This option may be used for debugging purposes. This allows .Nm to print out debugging information to the connection, allowing the user to see what .Nm is doing. There are several possible values for .Ar debugmode : .Bl -tag -width exercise .It Cm options Print information about the negotiation of .Tn TELNET options. .It Cm report Print the .Cm options information, plus some additional information about what processing is going on. .It Cm netdata Display the data stream received by .Nm Ns . .It Cm ptydata Display data written to the pty. .It Cm exercise Has not been implemented yet. .El .It Fl debug Enable debugging on each socket created by .Nm (see .Dv SO_DEBUG in .Xr socket 2 ) . .It Fl edebug If .Nm has been compiled with support for data encryption, then the .Fl edebug option may be used to enable encryption debugging code. .It Fl p Ar loginprog Specify an alternate .Xr login 1 command to run to complete the login. The alternate command must understand the same command arguments as the standard login. .It Fl h Disable the printing of host-specific information before login has been completed. .It Fl I Ar initid This option is only applicable to .Tn UNICOS systems prior to 7.0. It specifies the .Dv ID from .Pa /etc/inittab to use when init starts login sessions. The default .Dv ID is .Dv fe. .It Fl k This option is only useful if .Nm has been compiled with both linemode and kludge linemode support. If the .Fl k option is specified, then if the remote client does not support the .Dv LINEMODE option, then .Nm will operate in character at a time mode. It will still support kludge linemode, but will only go into kludge linemode if the remote client requests it. (This is done by the client sending .Dv DONT SUPPRESS-GO-AHEAD and .Dv DONT ECHO . ) The .Fl k option is most useful when there are remote clients that do not support kludge linemode, but pass the heuristic (if they respond with .Dv WILL TIMING-MARK in response to a .Dv DO TIMING-MARK) for kludge linemode support. .It Fl l Specify line mode. Try to force clients to use line- at-a-time mode. If the .Dv LINEMODE option is not supported, it will go into kludge linemode. .It Fl n Disable .Dv TCP keep-alives. Normally .Nm enables the .Tn TCP keep-alive mechanism to probe connections that have been idle for some period of time to determine if the client is still there, so that idle connections from machines that have crashed or can no longer be reached may be cleaned up. .It Fl r Ar lowpty-highpty This option is only enabled when .Nm is compiled for .Dv UNICOS. It specifies an inclusive range of pseudo-terminal devices to use. If the system has sysconf variable .Dv _SC_CRAY_NPTY configured, the default pty search range is 0 to .Dv _SC_CRAY_NPTY; otherwise, the default range is 0 to 128. Either .Ar lowpty or .Ar highpty may be omitted to allow changing either end of the search range. If .Ar lowpty is omitted, the - character is still required so that .Nm can differentiate .Ar highpty from .Ar lowpty . .It Fl s This option is only enabled if .Nm is compiled with support for .Tn SecurID cards. It causes the .Fl s option to be passed on to .Xr login 1 , and thus is only useful if .Xr login 1 supports the .Fl s flag to indicate that only .Tn SecurID validated logins are allowed, and is usually useful for controlling remote logins from outside of a firewall. .It Fl S Ar tos .It Fl u Ar len This option is used to specify the size of the field in the .Dv utmp structure that holds the remote host name. If the resolved host name is longer than .Ar len , the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying .Fl u0 indicates that only dotted decimal addresses should be put into the .Pa utmp file. .ne 1i .It Fl U This option causes .Nm to refuse connections from addresses that cannot be mapped back into a symbolic name via the .Xr gethostbyaddr 3 routine. .It Fl X Ar authtype This option is only valid if .Nm has been built with support for the authentication option. It disables the use of .Ar authtype authentication, and can be used to temporarily disable a specific authentication type without having to recompile .Nm Ns . .El .Pp .Nm Telnetd operates by allocating a pseudo-terminal device (see .Xr pty 4 ) for a client, then creating a login process which has the slave side of the pseudo-terminal as .Dv stdin , .Dv stdout and .Dv stderr . .Nm Telnetd manipulates the master side of the pseudo-terminal, implementing the .Tn TELNET protocol and passing characters between the remote client and the login process. .Pp When a .Tn TELNET session is started up, .Nm sends .Tn TELNET options to the client side indicating a willingness to do the following .Tn TELNET options, which are described in more detail below: .Bd -literal -offset indent DO AUTHENTICATION WILL ENCRYPT DO TERMINAL TYPE DO TSPEED DO XDISPLOC DO NEW-ENVIRON DO ENVIRON WILL SUPPRESS GO AHEAD DO ECHO DO LINEMODE DO NAWS WILL STATUS DO LFLOW DO TIMING-MARK .Ed .Pp The pseudo-terminal allocated to the client is configured to operate in \*(lqcooked\*(rq mode, and with .Dv XTABS and .Dv CRMOD enabled (see .Xr tty 4 ) . .Pp .Nm Telnetd has support for enabling locally the following .Tn TELNET options: .Bl -tag -width "DO AUTHENTICATION" .It "WILL ECHO" When the .Dv LINEMODE option is enabled, a .Dv WILL ECHO or .Dv WONT ECHO will be sent to the client to indicate the current state of terminal echoing. When terminal echo is not desired, a .Dv WILL ECHO is sent to indicate that .Nm will take care of echoing any data that needs to be echoed to the terminal, and then nothing is echoed. When terminal echo is desired, a .Dv WONT ECHO is sent to indicate that .Nm will not be doing any terminal echoing, so the client should do any terminal echoing that is needed. .It "WILL BINARY" Indicate that the client is willing to send a 8 bits of data, rather than the normal 7 bits of the Network Virtual Terminal. .It "WILL SGA" Indicate that it will not be sending .Dv IAC GA, go ahead, commands. .It "WILL STATUS" Indicate a willingness to send the client, upon request, of the current status of all .Tn TELNET options. .It "WILL TIMING-MARK" Whenever a .Dv DO TIMING-MARK command is received, it is always responded to with a .Dv WILL TIMING-MARK .ne 1i .It "WILL LOGOUT" When a .Dv DO LOGOUT is received, a .Dv WILL LOGOUT is sent in response, and the .Tn TELNET session is shut down. .It "WILL ENCRYPT" Only sent if .Nm is compiled with support for data encryption, and indicates a willingness to decrypt the data stream. .El .Pp .Nm Telnetd has support for enabling remotely the following .Tn TELNET options: .Bl -tag -width "DO AUTHENTICATION" .It "DO BINARY" Sent to indicate that .Nm is willing to receive an 8 bit data stream. .It "DO LFLOW" Requests that the client handle flow control characters remotely. .It "DO ECHO" This is not really supported, but is sent to identify a 4.2BSD .Xr telnet 1 client, which will improperly respond with .Dv WILL ECHO. If a .Dv WILL ECHO is received, a .Dv DONT ECHO will be sent in response. .It "DO TERMINAL-TYPE" Indicate a desire to be able to request the name of the type of terminal that is attached to the client side of the connection. .It "DO SGA" Indicate that it does not need to receive .Dv IAC GA, the go ahead command. .It "DO NAWS" Requests that the client inform the server when the window (display) size changes. .It "DO TERMINAL-SPEED" Indicate a desire to be able to request information about the speed of the serial line to which the client is attached. .It "DO XDISPLOC" Indicate a desire to be able to request the name of the X Window System display that is associated with the telnet client. .It "DO NEW-ENVIRON" Indicate a desire to be able to request environment variable information, as described in RFC 1572. .It "DO ENVIRON" Indicate a desire to be able to request environment variable information, as described in RFC 1408. .It "DO LINEMODE" Only sent if .Nm is compiled with support for linemode, and requests that the client do line by line processing. .It "DO TIMING-MARK" Only sent if .Nm is compiled with support for both linemode and kludge linemode, and the client responded with .Dv WONT LINEMODE. If the client responds with .Dv WILL TM, the it is assumed that the client supports kludge linemode. Note that the .Op Fl k option can be used to disable this. .It "DO AUTHENTICATION" Only sent if .Nm is compiled with support for authentication, and indicates a willingness to receive authentication information for automatic login. .It "DO ENCRYPT" Only sent if .Nm is compiled with support for data encryption, and indicates a willingness to decrypt the data stream. .Sh ENVIRONMENT .Sh FILES .Bl -tag -width /usr/ucb/bftp -compact .It Pa /etc/services .It Pa /etc/inittab (UNICOS systems only) .It Pa /etc/iptos (if supported) .It Pa /usr/ucb/bftp (if supported) .El .Sh "SEE ALSO" .Xr bftp 1 , .Xr login 1 , .Xr telnet 1 (if supported) .Sh STANDARDS .Bl -tag -compact -width RFC-1572 .It Cm RFC-854 .Tn TELNET PROTOCOL SPECIFICATION .It Cm RFC-855 TELNET OPTION SPECIFICATIONS .It Cm RFC-856 TELNET BINARY TRANSMISSION .It Cm RFC-857 TELNET ECHO OPTION .It Cm RFC-858 TELNET SUPPRESS GO AHEAD OPTION .It Cm RFC-859 TELNET STATUS OPTION .It Cm RFC-860 TELNET TIMING MARK OPTION .It Cm RFC-861 TELNET EXTENDED OPTIONS - LIST OPTION .It Cm RFC-885 TELNET END OF RECORD OPTION .It Cm RFC-1073 Telnet Window Size Option .It Cm RFC-1079 Telnet Terminal Speed Option .It Cm RFC-1091 Telnet Terminal-Type Option .It Cm RFC-1096 Telnet X Display Location Option .It Cm RFC-1123 Requirements for Internet Hosts -- Application and Support .It Cm RFC-1184 Telnet Linemode Option .It Cm RFC-1372 Telnet Remote Flow Control Option .It Cm RFC-1416 Telnet Authentication Option .It Cm RFC-1411 Telnet Authentication: Kerberos Version 4 .It Cm RFC-1412 Telnet Authentication: SPX .It Cm RFC-1571 Telnet Environment Option Interoperability Issues .It Cm RFC-1572 Telnet Environment Option .Sh BUGS Some .Tn TELNET commands are only partially implemented. .Pp Because of bugs in the original 4.2 BSD .Xr telnet 1 , .Nm performs some dubious protocol exchanges to try to discover if the remote client is, in fact, a 4.2 BSD .Xr telnet 1 . .Pp Binary mode has no common interpretation except between similar operating systems (Unix in this case). .Pp The terminal type name received from the remote client is converted to lower case. .Pp .Nm Telnetd never sends .Tn TELNET .Dv IAC GA (go ahead) commands. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. diff --git a/crypto/telnet/telnetd/telnetd.c b/crypto/telnet/telnetd/telnetd.c index 57e6ed2a7a79..6d7bcde5fc4f 100644 --- a/crypto/telnet/telnetd/telnetd.c +++ b/crypto/telnet/telnetd/telnetd.c @@ -1,1614 +1,1634 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include "telnetd.h" #include "pathnames.h" #if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) /* * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can * use it to tell us to turn off all the socket security code, * since that is only used in UNICOS 7.0 and later. */ # undef _SC_CRAY_SECURE_SYS #endif #include #include #include #include #if defined(_SC_CRAY_SECURE_SYS) #include #include # ifdef SO_SEC_MULTI /* 8.0 code */ #include #include # endif /* SO_SEC_MULTI */ + +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int secflag; char tty_dev[16]; struct secdev dv; struct sysv sysv; # ifdef SO_SEC_MULTI /* 8.0 code */ struct socksec ss; # else /* SO_SEC_MULTI */ /* 7.0 code */ struct socket_security ss; # endif /* SO_SEC_MULTI */ #endif /* _SC_CRAY_SECURE_SYS */ #if defined(AUTHENTICATION) #include int auth_level = 0; #endif #if defined(ENCRYPTION) #include #endif #include #if defined(SecurID) int require_SecurID = 0; #endif char remote_hostname[MAXHOSTNAMELEN]; int utmp_len = sizeof(remote_hostname) - 1; int registerd_host_only = 0; #ifdef STREAMSPTY # include # include /* make sure we don't get the bsd version */ # include "/usr/include/sys/tty.h" # include /* * Because of the way ptyibuf is used with streams messages, we need * ptyibuf+1 to be on a full-word boundary. The following wierdness * is simply to make that happen. */ long ptyibufbuf[BUFSIZ/sizeof(long)+1]; char *ptyibuf = ((char *)&ptyibufbuf[1])-1; char *ptyip = ((char *)&ptyibufbuf[1])-1; char ptyibuf2[BUFSIZ]; unsigned char ctlbuf[BUFSIZ]; struct strbuf strbufc, strbufd; #else /* ! STREAMPTY */ /* * I/O data buffers, * pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; char ptyibuf2[BUFSIZ]; # include int readstream(int p, char *ibuf, int bufsize); -void doit(struct sockaddr_in *who); +void doit(struct sockaddr *who); int terminaltypeok(char *s); void startslave(char *host, int autologin, char *autoname); #endif /* ! STREAMPTY */ int hostinfo = 1; /* do we print login banner? */ #ifdef CRAY extern int newmap; /* nonzero if \n maps to ^M^J */ int lowpty = 0, highpty; /* low, high pty numbers */ #endif /* CRAY */ int debug = 0; int keepalive = 1; char *altlogin; -void doit __P((struct sockaddr_in *)); +void doit __P((struct sockaddr *)); int terminaltypeok __P((char *)); void startslave __P((char *, int, char *)); extern void usage P((void)); /* * The string to pass to getopt(). We do it this way so * that only the actual options that we support will be * passed off to getopt(). */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', + '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif #ifdef BFTPDAEMON 'B', #endif #ifdef DIAGNOSTICS 'D', ':', #endif #ifdef ENCRYPTION 'e', ':', #endif #if defined(CRAY) && defined(NEWINIT) 'I', ':', #endif #ifdef LINEMODE 'l', #endif #ifdef CRAY 'r', ':', #endif #ifdef SecurID 's', #endif '\0' }; - int +int family = AF_INET; + +int main(argc, argv) char *argv[]; { - struct sockaddr_in from; + struct sockaddr_storage from; int on = 1, fromlen; register int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif pfrontp = pbackp = ptyobuf; netip = netibuf; nfrontp = nbackp = netobuf; #ifdef ENCRYPTION nclearto = 0; #endif /* ENCRYPTION */ /* * This initialization causes linemode to default to a configuration * that works on all telnet clients, including the FreeBSD client. * This is not quite the same as the telnet client issuing a "mode * character" command, but has most of the same benefits, and is * preferable since some clients (like usofts) don't have the * mode character command anyway and linemode breaks things. * The most notable symptom of fix is that csh "set filec" operations * like (filename completion) and ^D (choices) keys now work * in telnet sessions and can be used more than once on the same line. * CR/LF handling is also corrected in some termio modes. This * change resolves problem reports bin/771 and bin/1037. */ linemode=1; /*Default to mode that works on bulk of clients*/ #ifdef CRAY /* * Get number of pty's before trying to process options, * which may include changing pty range. */ highpty = getnpty(); #endif /* CRAY */ while ((ch = getopt(argc, argv, valid_opts)) != -1) { switch(ch) { #ifdef AUTHENTICATION case 'a': /* * Check for required authentication level */ if (strcmp(optarg, "debug") == 0) { extern int auth_debug_mode; auth_debug_mode = 1; } else if (strcasecmp(optarg, "none") == 0) { auth_level = 0; } else if (strcasecmp(optarg, "other") == 0) { auth_level = AUTH_OTHER; } else if (strcasecmp(optarg, "user") == 0) { auth_level = AUTH_USER; } else if (strcasecmp(optarg, "valid") == 0) { auth_level = AUTH_VALID; } else if (strcasecmp(optarg, "off") == 0) { /* * This hack turns off authentication */ auth_level = -1; } else { warnx("unknown authorization level for -a"); } break; #endif /* AUTHENTICATION */ #ifdef BFTPDAEMON case 'B': bftpd++; break; #endif /* BFTPDAEMON */ case 'd': if (strcmp(optarg, "ebug") == 0) { debug++; break; } usage(); /* NOTREACHED */ break; #ifdef DIAGNOSTICS case 'D': /* * Check for desired diagnostics capabilities. */ if (!strcmp(optarg, "report")) { diagnostic |= TD_REPORT|TD_OPTIONS; } else if (!strcmp(optarg, "exercise")) { diagnostic |= TD_EXERCISE; } else if (!strcmp(optarg, "netdata")) { diagnostic |= TD_NETDATA; } else if (!strcmp(optarg, "ptydata")) { diagnostic |= TD_PTYDATA; } else if (!strcmp(optarg, "options")) { diagnostic |= TD_OPTIONS; } else { usage(); /* NOT REACHED */ } break; #endif /* DIAGNOSTICS */ #ifdef ENCRYPTION case 'e': if (strcmp(optarg, "debug") == 0) { extern int encrypt_debug_mode; encrypt_debug_mode = 1; break; } usage(); /* NOTREACHED */ break; #endif /* ENCRYPTION */ case 'h': hostinfo = 0; break; #if defined(CRAY) && defined(NEWINIT) case 'I': { extern char *gen_id; gen_id = optarg; break; } #endif /* defined(CRAY) && defined(NEWINIT) */ #ifdef LINEMODE case 'l': alwayslinemode = 1; break; #endif /* LINEMODE */ case 'k': #if defined(LINEMODE) && defined(KLUDGELINEMODE) lmodetype = NO_AUTOKLUDGE; #else /* ignore -k option if built without kludge linemode */ #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ break; case 'n': keepalive = 0; break; case 'p': altlogin = optarg; break; #ifdef CRAY case 'r': { char *strchr(); char *c; /* * Allow the specification of alterations * to the pty search range. It is legal to * specify only one, and not change the * other from its default. */ c = strchr(optarg, '-'); if (c) { *c++ = '\0'; highpty = atoi(c); } if (*optarg != '\0') lowpty = atoi(optarg); if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { usage(); /* NOT REACHED */ } break; } #endif /* CRAY */ #ifdef SecurID case 's': /* SecurID required */ require_SecurID = 1; break; #endif /* SecurID */ case 'S': #ifdef HAS_GETTOS if ((tos = parsetos(optarg, "tcp")) < 0) warnx("%s%s%s", "bad TOS argument '", optarg, "'; will try to use default TOS"); #else warnx("TOS option unavailable; -S flag not supported"); #endif break; case 'u': utmp_len = atoi(optarg); if (utmp_len < 0) utmp_len = -utmp_len; if (utmp_len >= sizeof(remote_hostname)) utmp_len = sizeof(remote_hostname) - 1; break; case 'U': registerd_host_only = 1; break; #ifdef AUTHENTICATION case 'X': /* * Check for invalid authentication types */ auth_disable_name(optarg); break; #endif /* AUTHENTICATION */ + case '4': + family = AF_INET; + break; + +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif + default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ case '?': usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (debug) { - int s, ns, foo; - struct servent *sp; - static struct sockaddr_in sin = { AF_INET }; + int s, ns, foo, error; + char *service = "telnet"; + struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ - } else if (argc == 1) { - if ((sp = getservbyname(*argv, "tcp"))) { - sin.sin_port = sp->s_port; - } else { - sin.sin_port = atoi(*argv); - if ((int)sin.sin_port <= 0) { - warnx("%s: bad port #", *argv); - usage(); - /* NOT REACHED */ - } - sin.sin_port = htons((u_short)sin.sin_port); - } - } else { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) - errx(1, "tcp/telnet: unknown service"); - sin.sin_port = sp->s_port; + } else if (argc == 1) + service = *argv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + + if (error) { + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "tcp/%s: %s\n", service, strerror(errno)); + usage(); } - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); - foo = sizeof sin; - ns = accept(s, (struct sockaddr *)&sin, &foo); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) dup2(ns, 0); (void) close(ns); (void) close(s); #ifdef convex } else if (argc == 1) { ; /* VOID*/ /* Just ignore the host/port name */ #endif } else if (argc > 0) { usage(); /* NOT REACHED */ } #if defined(_SC_CRAY_SECURE_SYS) secflag = sysconf(_SC_CRAY_SECURE_SYS); /* * Get socket's security label */ if (secflag) { int szss = sizeof(ss); #ifdef SO_SEC_MULTI /* 8.0 code */ int sock_multi; int szi = sizeof(int); #endif /* SO_SEC_MULTI */ memset((char *)&dv, 0, sizeof(dv)); if (getsysv(&sysv, sizeof(struct sysv)) != 0) err(1, "getsysv"); /* * Get socket security label and set device values * {security label to be set on ttyp device} */ #ifdef SO_SEC_MULTI /* 8.0 code */ if ((getsockopt(0, SOL_SOCKET, SO_SECURITY, (char *)&ss, &szss) < 0) || (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI, (char *)&sock_multi, &szi) < 0)) { err(1, "getsockopt"); } else { dv.dv_actlvl = ss.ss_actlabel.lt_level; dv.dv_actcmp = ss.ss_actlabel.lt_compart; if (!sock_multi) { dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl; dv.dv_valcmp = dv.dv_actcmp; } else { dv.dv_minlvl = ss.ss_minlabel.lt_level; dv.dv_maxlvl = ss.ss_maxlabel.lt_level; dv.dv_valcmp = ss.ss_maxlabel.lt_compart; } dv.dv_devflg = 0; } #else /* SO_SEC_MULTI */ /* 7.0 code */ if (getsockopt(0, SOL_SOCKET, SO_SECURITY, (char *)&ss, &szss) >= 0) { dv.dv_actlvl = ss.ss_slevel; dv.dv_actcmp = ss.ss_compart; dv.dv_minlvl = ss.ss_minlvl; dv.dv_maxlvl = ss.ss_maxlvl; dv.dv_valcmp = ss.ss_maxcmp; } #endif /* SO_SEC_MULTI */ } #endif /* _SC_CRAY_SECURE_SYS */ openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { warn("getpeername"); _exit(1); } if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) < 0) && (errno != ENOPROTOOPT) ) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; - doit(&from); + doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ void usage() { fprintf(stderr, "usage: telnetd"); #ifdef AUTHENTICATION fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t"); #endif #ifdef BFTPDAEMON fprintf(stderr, " [-B]"); #endif fprintf(stderr, " [-debug]"); #ifdef DIAGNOSTICS fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-edebug]"); #endif fprintf(stderr, " [-h]"); #if defined(CRAY) && defined(NEWINIT) fprintf(stderr, " [-Iinitid]"); #endif #if defined(LINEMODE) && defined(KLUDGELINEMODE) fprintf(stderr, " [-k]"); #endif #ifdef LINEMODE fprintf(stderr, " [-l]"); #endif fprintf(stderr, " [-n]"); #ifdef CRAY fprintf(stderr, " [-r[lowpty]-[highpty]]"); #endif fprintf(stderr, "\n\t"); #ifdef SecurID fprintf(stderr, " [-s]"); #endif #ifdef HAS_GETTOS fprintf(stderr, " [-S tos]"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-X auth-type]"); #endif fprintf(stderr, " [-u utmp_hostname_length] [-U]"); fprintf(stderr, " [port]\n"); exit(1); } /* * getterminaltype * * Ask the other end to send along its terminal type and speed. * Output is the variable terminaltype filled in. */ static unsigned char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; int getterminaltype(name) char *name; { int retval = -1; void _gettermname(); settimer(baseline); #if defined(AUTHENTICATION) /* * Handle the Authentication option before we do anything else. */ send_do(TELOPT_AUTHENTICATION, 1); while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) ttloop(); if (his_state_is_will(TELOPT_AUTHENTICATION)) { retval = auth_wait(name); } #endif #ifdef ENCRYPTION send_will(TELOPT_ENCRYPT, 1); #endif /* ENCRYPTION */ send_do(TELOPT_TTYPE, 1); send_do(TELOPT_TSPEED, 1); send_do(TELOPT_XDISPLOC, 1); send_do(TELOPT_NEW_ENVIRON, 1); send_do(TELOPT_OLD_ENVIRON, 1); while ( #ifdef ENCRYPTION his_do_dont_is_changing(TELOPT_ENCRYPT) || #endif /* ENCRYPTION */ his_will_wont_is_changing(TELOPT_TTYPE) || his_will_wont_is_changing(TELOPT_TSPEED) || his_will_wont_is_changing(TELOPT_XDISPLOC) || his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { ttloop(); } #ifdef ENCRYPTION /* * Wait for the negotiation of what type of encryption we can * send with. If autoencrypt is not set, this will just return. */ if (his_state_is_will(TELOPT_ENCRYPT)) { encrypt_wait(); } #endif /* ENCRYPTION */ if (his_state_is_will(TELOPT_TSPEED)) { static unsigned char sb[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_XDISPLOC)) { static unsigned char sb[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; memmove(nfrontp, sb, sizeof sb); nfrontp += sizeof sb; DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_TTYPE)) { memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); } if (his_state_is_will(TELOPT_TSPEED)) { while (sequenceIs(tspeedsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_XDISPLOC)) { while (sequenceIs(xdisplocsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { while (sequenceIs(environsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_OLD_ENVIRON)) { while (sequenceIs(oenvironsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_TTYPE)) { char first[256], last[256]; while (sequenceIs(ttypesubopt, baseline)) ttloop(); /* * If the other side has already disabled the option, then * we have to just go with what we (might) have already gotten. */ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { (void) strncpy(first, terminaltype, sizeof(first)-1); first[sizeof(first)-1] = '\0'; for(;;) { /* * Save the unknown name, and request the next name. */ (void) strncpy(last, terminaltype, sizeof(last)-1); last[sizeof(last)-1] = '\0'; _gettermname(); if (terminaltypeok(terminaltype)) break; if ((strncmp(last, terminaltype, sizeof(last)) == 0) || his_state_is_wont(TELOPT_TTYPE)) { /* * We've hit the end. If this is the same as * the first name, just go with it. */ if (strncmp(first, terminaltype, sizeof(first)) == 0) break; /* * Get the terminal name one more time, so that * RFC1091 compliant telnets will cycle back to * the start of the list. */ _gettermname(); if (strncmp(first, terminaltype, sizeof(first)) != 0) { (void) strncpy(terminaltype, first, sizeof(terminaltype)-1); terminaltype[sizeof(terminaltype)-1] = '\0'; } break; } } } } return(retval); } /* end of getterminaltype */ void _gettermname() { /* * If the client turned off the option, * we can't send another request, so we * just return. */ if (his_state_is_wont(TELOPT_TTYPE)) return; settimer(baseline); memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); while (sequenceIs(ttypesubopt, baseline)) ttloop(); } int terminaltypeok(s) char *s; { char buf[1024]; if (terminaltype == NULL) return(1); /* * tgetent() will return 1 if the type is known, and * 0 if it is not known. If it returns -1, it couldn't * open the database. But if we can't open the database, * it won't help to say we failed, because we won't be * able to verify anything else. So, we treat -1 like 1. */ if (tgetent(buf, s) == 0) return(0); return(1); } #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ char *hostname; char host_name[MAXHOSTNAMELEN]; extern void telnet P((int, int, char *)); int level; char user_name[256]; /* * Get a pty, scan input lines. */ void doit(who) - struct sockaddr_in *who; + struct sockaddr *who; { + int err; int ptynum; /* * Find an available pty to use. */ #ifndef convex pty = getpty(&ptynum); if (pty < 0) fatal(net, "All network ports in use"); #else for (;;) { char *lp; extern char *line, *getpty(); if ((lp = getpty()) == NULL) fatal(net, "Out of ptys"); if ((pty = open(lp, 2)) >= 0) { strcpy(line,lp); line[5] = 't'; break; } } #endif #if defined(_SC_CRAY_SECURE_SYS) /* * set ttyp line security label */ if (secflag) { char slave_dev[16]; sprintf(tty_dev, "/dev/pty/%03d", ptynum); if (setdevs(tty_dev, &dv) < 0) fatal(net, "cannot set pty security"); sprintf(slave_dev, "/dev/ttyp%03d", ptynum); if (setdevs(slave_dev, &dv) < 0) fatal(net, "cannot set tty security"); } #endif /* _SC_CRAY_SECURE_SYS */ /* get name of connected client */ - if (realhostname(remote_hostname, sizeof(remote_hostname) - 1, - &who->sin_addr) == HOSTNAME_INVALIDADDR && registerd_host_only) + if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, + who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; trimdomain(remote_hostname, UT_HOSTSIZE); if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) - strncpy(remote_hostname, inet_ntoa(who->sin_addr), - sizeof(remote_hostname) - 1); + err = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; hostname = host_name; #if defined(AUTHENTICATION) || defined(ENCRYPTION) auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); #endif init_env(); /* * get terminal type. */ *user_name = 0; level = getterminaltype(user_name); setenv("TERM", terminaltype ? terminaltype : "network", 1); #if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (setulvl(dv.dv_actlvl) < 0) fatal(net,"cannot setulvl()"); if (setucmp(dv.dv_actcmp) < 0) fatal(net, "cannot setucmp()"); } #endif /* _SC_CRAY_SECURE_SYS */ telnet(net, pty, remote_hostname); /* begin server process */ /*NOTREACHED*/ } /* end of doit */ #if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) int Xterm_output(ibufp, obuf, icountp, ocount) char **ibufp, *obuf; int *icountp, ocount; { int ret; ret = term_output(*ibufp, obuf, *icountp, ocount); *ibufp += *icountp; *icountp = 0; return(ret); } #define term_output Xterm_output #endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */ /* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */ void telnet(f, p, host) int f, p; char *host; { int on = 1; #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char defstrs[TABBUFSIZ]; #undef TABBUFSIZ char *HE; char *HN; char *IM; void netflush(); int nfd; /* * Initialize the slc mapping table. */ get_slc_defaults(); /* * Do some tests where it is desireable to wait for a response. * Rather than doing them slowly, one at a time, do them all * at once. */ if (my_state_is_wont(TELOPT_SGA)) send_will(TELOPT_SGA, 1); /* * Is the client side a 4.2 (NOT 4.3) system? We need to know this * because 4.2 clients are unable to deal with TCP urgent data. * * To find out, we send out a "DO ECHO". If the remote system * answers "WILL ECHO" it is probably a 4.2 client, and we note * that fact ("WILL ECHO" ==> that the client will echo what * WE, the server, sends it; it does NOT mean that the client will * echo the terminal input). */ send_do(TELOPT_ECHO, 1); #ifdef LINEMODE if (his_state_is_wont(TELOPT_LINEMODE)) { /* Query the peer for linemode support by trying to negotiate * the linemode option. */ linemode = 0; editmode = 0; send_do(TELOPT_LINEMODE, 1); /* send do linemode */ } #endif /* LINEMODE */ /* * Send along a couple of other options that we wish to negotiate. */ send_do(TELOPT_NAWS, 1); send_will(TELOPT_STATUS, 1); flowmode = 1; /* default flow control state */ restartany = -1; /* uninitialized... */ send_do(TELOPT_LFLOW, 1); /* * Spin, waiting for a response from the DO ECHO. However, * some REALLY DUMB telnets out there might not respond * to the DO ECHO. So, we spin looking for NAWS, (most dumb * telnets so far seem to respond with WONT for a DO that * they don't understand...) because by the time we get the * response, it will already have processed the DO ECHO. * Kludge upon kludge. */ while (his_will_wont_is_changing(TELOPT_NAWS)) ttloop(); /* * But... * The client might have sent a WILL NAWS as part of its * startup code; if so, we'll be here before we get the * response to the DO ECHO. We'll make the assumption * that any implementation that understands about NAWS * is a modern enough implementation that it will respond * to our DO ECHO request; hence we'll do another spin * waiting for the ECHO option to settle down, which is * what we wanted to do in the first place... */ if (his_want_state_is_will(TELOPT_ECHO) && his_state_is_will(TELOPT_NAWS)) { while (his_will_wont_is_changing(TELOPT_ECHO)) ttloop(); } /* * On the off chance that the telnet client is broken and does not * respond to the DO ECHO we sent, (after all, we did send the * DO NAWS negotiation after the DO ECHO, and we won't get here * until a response to the DO NAWS comes back) simulate the * receipt of a will echo. This will also send a WONT ECHO * to the client, since we assume that the client failed to * respond because it believes that it is already in DO ECHO * mode, which we do not want. */ if (his_want_state_is_will(TELOPT_ECHO)) { DIAG(TD_OPTIONS, {sprintf(nfrontp, "td: simulating recv\r\n"); nfrontp += strlen(nfrontp);}); willoption(TELOPT_ECHO); } /* * Finally, to clean things up, we turn on our echo. This * will break stupid 4.2 telnets out of local terminal echo. */ if (my_state_is_wont(TELOPT_ECHO)) send_will(TELOPT_ECHO, 1); #ifndef STREAMSPTY /* * Turn on packet mode */ (void) ioctl(p, TIOCPKT, (char *)&on); #endif #if defined(LINEMODE) && defined(KLUDGELINEMODE) /* * Continuing line mode support. If client does not support * real linemode, attempt to negotiate kludge linemode by sending * the do timing mark sequence. */ if (lmodetype < REAL_LINEMODE) send_do(TELOPT_TM, 1); #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ /* * Call telrcv() once to pick up anything received during * terminal type negotiation, 4.2/4.3 determination, and * linemode negotiation. */ telrcv(); (void) ioctl(f, FIONBIO, (char *)&on); (void) ioctl(p, FIONBIO, (char *)&on); #if defined(CRAY2) && defined(UNICOS5) init_termdriver(f, p, interrupt, sendbrk); #endif #if defined(SO_OOBINLINE) (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof on); #endif /* defined(SO_OOBINLINE) */ #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); #endif #ifdef SIGTTOU /* * Ignoring SIGTTOU keeps the kernel from blocking us * in ttioct() in /sys/tty.c. */ (void) signal(SIGTTOU, SIG_IGN); #endif (void) signal(SIGCHLD, cleanup); #if defined(CRAY2) && defined(UNICOS5) /* * Cray-2 will send a signal when pty modes are changed by slave * side. Set up signal handler now. */ if ((int)signal(SIGUSR1, termstat) < 0) warn("signal"); else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) warn("ioctl:TCSIGME"); /* * Make processing loop check terminal characteristics early on. */ termstat(); #endif #ifdef TIOCNOTTY { register int t; t = open(_PATH_TTY, O_RDWR); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } } #endif #if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY) (void) setsid(); ioctl(p, TIOCSCTTY, 0); #endif /* * Show banner that getty never gave. * * We put the banner in the pty input buffer. This way, it * gets carriage return null processing, etc., just like all * other pty --> client data. */ #if !defined(CRAY) || !defined(NEWINIT) if (getenv("USER")) hostinfo = 0; #endif if (getent(defent, "default") == 1) { char *Getstr(); char *cp=defstrs; HE = Getstr("he", &cp); HN = Getstr("hn", &cp); IM = Getstr("im", &cp); if (HN && *HN) (void) strcpy(host_name, HN); if (IM == 0) IM = ""; } else { IM = DEFAULT_IM; HE = 0; } edithost(HE, host_name); if (hostinfo && *IM) putf(IM, ptyibuf2); if (pcc) (void) strncat(ptyibuf2, ptyip, pcc+1); ptyip = ptyibuf2; pcc = strlen(ptyip); #ifdef LINEMODE /* * Last check to make sure all our states are correct. */ init_termbuf(); localstat(); #endif /* LINEMODE */ DIAG(TD_REPORT, {sprintf(nfrontp, "td: Entering processing loop\r\n"); nfrontp += strlen(nfrontp);}); /* * Startup the login process on the slave side of the terminal * now. We delay this until here to insure option negotiation * is complete. */ startslave(host, level, user_name); nfd = ((f > p) ? f : p) + 1; for (;;) { fd_set ibits, obits, xbits; register int c; if (ncc < 0 && pcc < 0) break; #if defined(CRAY2) && defined(UNICOS5) if (needtermstat) _termstat(); #endif /* defined(CRAY2) && defined(UNICOS5) */ FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); /* * Never look for input if there's still * stuff in the corresponding output buffer */ if (nfrontp - nbackp || pcc > 0) { FD_SET(f, &obits); } else { FD_SET(p, &ibits); } if (pfrontp - pbackp || ncc > 0) { FD_SET(p, &obits); } else { FD_SET(f, &ibits); } if (!SYNCHing) { FD_SET(f, &xbits); } if ((c = select(nfd, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep(5); continue; } /* * Any urgent data? */ if (FD_ISSET(net, &xbits)) { SYNCHing = 1; } /* * Something to read from the network... */ if (FD_ISSET(net, &ibits)) { #if !defined(SO_OOBINLINE) /* * In 4.2 (and 4.3 beta) systems, the * OOB indication and data handling in the kernel * is such that if two separate TCP Urgent requests * come in, one byte of TCP data will be overlaid. * This is fatal for Telnet, but we try to live * with it. * * In addition, in 4.2 (and...), a special protocol * is needed to pick up the TCP Urgent data in * the correct sequence. * * What we do is: if we think we are in urgent * mode, we look to see if we are "at the mark". * If we are, we do an OOB receive. If we run * this twice, we will do the OOB receive twice, * but the second will fail, since the second * time we were "at the mark", but there wasn't * any data there (the kernel doesn't reset * "at the mark" until we do a normal read). * Once we've read the OOB data, we go ahead * and do normal reads. * * There is also another problem, which is that * since the OOB byte we read doesn't put us * out of OOB state, and since that byte is most * likely the TELNET DM (data mark), we would * stay in the TELNET SYNCH (SYNCHing) state. * So, clocks to the rescue. If we've "just" * received a DM, then we test for the * presence of OOB data when the receive OOB * fails (and AFTER we did the normal mode read * to clear "at the mark"). */ if (SYNCHing) { int atmark; (void) ioctl(net, SIOCATMARK, (char *)&atmark); if (atmark) { ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { ncc = read(net, netibuf, sizeof (netibuf)); if (sequenceIs(didnetreceive, gotDM)) { SYNCHing = stilloob(net); } } } else { ncc = read(net, netibuf, sizeof (netibuf)); } } else { ncc = read(net, netibuf, sizeof (netibuf)); } settimer(didnetreceive); #else /* !defined(SO_OOBINLINE)) */ ncc = read(net, netibuf, sizeof (netibuf)); #endif /* !defined(SO_OOBINLINE)) */ if (ncc < 0 && errno == EWOULDBLOCK) ncc = 0; else { if (ncc <= 0) { break; } netip = netibuf; } DIAG((TD_REPORT | TD_NETDATA), {sprintf(nfrontp, "td: netread %d chars\r\n", ncc); nfrontp += strlen(nfrontp);}); DIAG(TD_NETDATA, printdata("nd", netip, ncc)); } /* * Something to read from the pty... */ if (FD_ISSET(p, &ibits)) { #ifndef STREAMSPTY pcc = read(p, ptyibuf, BUFSIZ); #else pcc = readstream(p, ptyibuf, BUFSIZ); #endif /* * On some systems, if we try to read something * off the master side before the slave side is * opened, we get EIO. */ if (pcc < 0 && (errno == EWOULDBLOCK || #ifdef EAGAIN errno == EAGAIN || #endif errno == EIO)) { pcc = 0; } else { if (pcc <= 0) break; #if !defined(CRAY2) || !defined(UNICOS5) #ifdef LINEMODE /* * If ioctl from pty, pass it through net */ if (ptyibuf[0] & TIOCPKT_IOCTL) { copy_termbuf(ptyibuf+1, pcc-1); localstat(); pcc = 1; } #endif /* LINEMODE */ if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { netclear(); /* clear buffer back */ #ifndef NO_URGENT /* * There are client telnets on some * operating systems get screwed up * royally if we send them urgent * mode data. */ *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp-1; /* off by one XXX */ DIAG(TD_OPTIONS, printoption("td: send IAC", DM)); #endif } if (his_state_is_will(TELOPT_LFLOW) && (ptyibuf[0] & (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { int newflow = ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; if (newflow != flowmode) { flowmode = newflow; (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB, TELOPT_LFLOW, flowmode ? LFLOW_ON : LFLOW_OFF, IAC, SE); nfrontp += 6; DIAG(TD_OPTIONS, printsub('>', (unsigned char *)nfrontp-4, 4);); } } pcc--; ptyip = ptyibuf+1; #else /* defined(CRAY2) && defined(UNICOS5) */ if (!uselinemode) { unpcc = pcc; unptyip = ptyibuf; pcc = term_output(&unptyip, ptyibuf2, &unpcc, BUFSIZ); ptyip = ptyibuf2; } else ptyip = ptyibuf; #endif /* defined(CRAY2) && defined(UNICOS5) */ } } while (pcc > 0) { if ((&netobuf[BUFSIZ] - nfrontp) < 2) break; c = *ptyip++ & 0377, pcc--; if (c == IAC) *nfrontp++ = c; #if defined(CRAY2) && defined(UNICOS5) else if (c == '\n' && my_state_is_wont(TELOPT_BINARY) && newmap) *nfrontp++ = '\r'; #endif /* defined(CRAY2) && defined(UNICOS5) */ *nfrontp++ = c; if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { *nfrontp++ = *ptyip++ & 0377; pcc--; } else *nfrontp++ = '\0'; } } #if defined(CRAY2) && defined(UNICOS5) /* * If chars were left over from the terminal driver, * note their existence. */ if (!uselinemode && unpcc) { pcc = unpcc; unpcc = 0; ptyip = unptyip; } #endif /* defined(CRAY2) && defined(UNICOS5) */ if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) netflush(); if (ncc > 0) telrcv(); if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) ptyflush(); } cleanup(0); } /* end of telnet */ #ifndef TCSIG # ifdef TIOCSIG # define TCSIG TIOCSIG # endif #endif #ifdef STREAMSPTY int flowison = -1; /* current state of flow: -1 is unknown */ int readstream(p, ibuf, bufsize) int p; char *ibuf; int bufsize; { int flags = 0; int ret = 0; struct termios *tsp; struct termio *tp; struct iocblk *ip; char vstop, vstart; int ixon; int newflow; strbufc.maxlen = BUFSIZ; strbufc.buf = (char *)ctlbuf; strbufd.maxlen = bufsize-1; strbufd.len = 0; strbufd.buf = ibuf+1; ibuf[0] = 0; ret = getmsg(p, &strbufc, &strbufd, &flags); if (ret < 0) /* error of some sort -- probably EAGAIN */ return(-1); if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) { /* data message */ if (strbufd.len > 0) { /* real data */ return(strbufd.len + 1); /* count header char */ } else { /* nothing there */ errno = EAGAIN; return(-1); } } /* * It's a control message. Return 1, to look at the flag we set */ switch (ctlbuf[0]) { case M_FLUSH: if (ibuf[1] & FLUSHW) ibuf[0] = TIOCPKT_FLUSHWRITE; return(1); case M_IOCTL: ip = (struct iocblk *) (ibuf+1); switch (ip->ioc_cmd) { case TCSETS: case TCSETSW: case TCSETSF: tsp = (struct termios *) (ibuf+1 + sizeof(struct iocblk)); vstop = tsp->c_cc[VSTOP]; vstart = tsp->c_cc[VSTART]; ixon = tsp->c_iflag & IXON; break; case TCSETA: case TCSETAW: case TCSETAF: tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk)); vstop = tp->c_cc[VSTOP]; vstart = tp->c_cc[VSTART]; ixon = tp->c_iflag & IXON; break; default: errno = EAGAIN; return(-1); } newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0; if (newflow != flowison) { /* it's a change */ flowison = newflow; ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP; return(1); } } /* nothing worth doing anything about */ errno = EAGAIN; return(-1); } #endif /* STREAMSPTY */ /* * Send interrupt to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write intr char. */ void interrupt() { ptyflush(); /* half-hearted */ #if defined(STREAMSPTY) && defined(TIOCSIGNAL) /* Streams PTY style ioctl to post a signal */ { int sig = SIGINT; (void) ioctl(pty, TIOCSIGNAL, &sig); (void) ioctl(pty, I_FLUSH, FLUSHR); } #else #ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGINT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_IP].sptr ? (unsigned char)*slctab[SLC_IP].sptr : '\177'; #endif /* TCSIG */ #endif } /* * Send quit to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write quit char. */ void sendbrk() { ptyflush(); /* half-hearted */ #ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGQUIT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_ABORT].sptr ? (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; #endif /* TCSIG */ } void sendsusp() { #ifdef SIGTSTP ptyflush(); /* half-hearted */ # ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGTSTP); # else /* TCSIG */ *pfrontp++ = slctab[SLC_SUSP].sptr ? (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; # endif /* TCSIG */ #endif /* SIGTSTP */ } /* * When we get an AYT, if ^T is enabled, use that. Otherwise, * just send back "[Yes]". */ void recv_ayt() { #if defined(SIGINFO) && defined(TCSIG) if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { (void) ioctl(pty, TCSIG, (char *)SIGINFO); return; } #endif (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); nfrontp += 9; } void doeof() { init_termbuf(); #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) if (!tty_isediting()) { extern char oldeofc; *pfrontp++ = oldeofc; return; } #endif *pfrontp++ = slctab[SLC_EOF].sptr ? (unsigned char)*slctab[SLC_EOF].sptr : '\004'; } diff --git a/kerberos5/libexec/telnetd/Makefile b/kerberos5/libexec/telnetd/Makefile index 4928c8de6bc5..221bdb48d945 100644 --- a/kerberos5/libexec/telnetd/Makefile +++ b/kerberos5/libexec/telnetd/Makefile @@ -1,23 +1,23 @@ # $FreeBSD$ # Do not define -DKLUDGELINEMODE, as it does not interact well with many # telnet implementations. PROG= telnetd MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c DPADD= ${LIBUTIL} ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} \ ${LIBCRYPT} ${LIBCOM_ERR} LDADD= -lutil -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp .include .PATH: ${TELNETDIR}/telnetd diff --git a/kerberos5/usr.bin/telnet/Makefile b/kerberos5/usr.bin/telnet/Makefile index eb41f26a3c34..fb5219bfb9b1 100644 --- a/kerberos5/usr.bin/telnet/Makefile +++ b/kerberos5/usr.bin/telnet/Makefile @@ -1,19 +1,19 @@ # $FreeBSD$ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -DKRB4 \ - -I${TELNETDIR} + -I${TELNETDIR} -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} ${LIBCRYPT} \ - ${LIBCOM_ERR} + ${LIBCOM_ERR} ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ - -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp + -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp -lipsec .include .PATH: ${TELNETDIR}/telnet diff --git a/kerberosIV/libexec/telnetd/Makefile b/kerberosIV/libexec/telnetd/Makefile index 4928c8de6bc5..221bdb48d945 100644 --- a/kerberosIV/libexec/telnetd/Makefile +++ b/kerberosIV/libexec/telnetd/Makefile @@ -1,23 +1,23 @@ # $FreeBSD$ # Do not define -DKLUDGELINEMODE, as it does not interact well with many # telnet implementations. PROG= telnetd MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c DPADD= ${LIBUTIL} ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} \ ${LIBCRYPT} ${LIBCOM_ERR} LDADD= -lutil -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp .include .PATH: ${TELNETDIR}/telnetd diff --git a/kerberosIV/usr.bin/telnet/Makefile b/kerberosIV/usr.bin/telnet/Makefile index eb41f26a3c34..fb5219bfb9b1 100644 --- a/kerberosIV/usr.bin/telnet/Makefile +++ b/kerberosIV/usr.bin/telnet/Makefile @@ -1,19 +1,19 @@ # $FreeBSD$ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -DKRB4 \ - -I${TELNETDIR} + -I${TELNETDIR} -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} ${LIBCRYPT} \ - ${LIBCOM_ERR} + ${LIBCOM_ERR} ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ - -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp + -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp -lipsec .include .PATH: ${TELNETDIR}/telnet diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index c23e85b6078a..4ddfb53a3a42 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -1,27 +1,28 @@ # @(#)Makefile 8.2 (Berkeley) 4/4/94 # $FreeBSD$ PROG= ftpd MAN8= ftpd.8 SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c skey-stuff.c CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall \ -I${.CURDIR}/../../contrib-crypto/telnet +CFLAGS+=-DINET6 -g YFLAGS= LDADD= -lskey -lmd -lcrypt -lutil DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL} LSDIR= ../../bin/ls .PATH: ${.CURDIR}/${LSDIR} SRCS+= ls.c cmp.c print.c util.c CFLAGS+=-DINTERNAL_LS -Dmain=ls_main -I${.CURDIR}/${LSDIR} .if defined(NOPAM) CFLAGS+=-DNOPAM .else DPADD+= ${LIBPAM} LDADD+= ${MINUSLPAM} .endif .include diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index 9888cb6946b6..684703eee2f8 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -1,73 +1,89 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)extern.h 8.2 (Berkeley) 4/4/94 * $FreeBSD$ */ void blkfree __P((char **)); char **copyblk __P((char **)); void cwd __P((char *)); void delete __P((char *)); void dologout __P((int)); void fatal __P((char *)); void ftpd_logwtmp __P((char *, char *, char *)); int ftpd_pclose __P((FILE *)); FILE *ftpd_popen __P((char *, char *)); char *getline __P((char *, int, FILE *)); void lreply __P((int, const char *, ...)); void makedir __P((char *)); void nack __P((char *)); void pass __P((char *)); void passive __P((void)); +void long_passive __P((char *, int)); void perror_reply __P((int, char *)); void pwd __P((void)); void removedir __P((char *)); void renamecmd __P((char *, char *)); char *renamefrom __P((char *)); void reply __P((int, const char *, ...)); void retrieve __P((char *, char *)); void send_file_list __P((char *)); #ifdef OLD_SETPROCTITLE void setproctitle __P((const char *, ...)); #endif void statcmd __P((void)); void statfilecmd __P((char *)); void store __P((char *, char *, int)); void upper __P((char *)); void user __P((char *)); void yyerror __P((char *)); int yyparse __P((void)); #if defined(SKEY) && defined(_PWD_H_) /* XXX evil */ char *skey_challenge __P((char *, struct passwd *, int)); #endif int ls_main __P((int, char **)); + +struct sockaddr_in; +struct sockaddr_in6; +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index e085e29f57d6..bb5bff97ff86 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -1,1291 +1,1609 @@ /* * Copyright (c) 1985, 1988, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 */ /* * Grammar for FTP commands. * See RFC 959. */ %{ #ifndef lint #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include "extern.h" -extern struct sockaddr_in data_dest, his_addr; +extern union sockunion data_dest, his_addr; extern int logged_in; extern struct passwd *pw; extern int guest; extern int paranoid; extern int logging; extern int type; extern int form; extern int debug; extern int timeout; extern int maxtimeout; extern int pdata; extern char *hostname; extern char remotehost[]; extern char proctitle[]; extern int usedefault; extern int transflag; extern char tmpline[]; off_t restart_point; static int cmd_type; static int cmd_form; static int cmd_bytesz; char cbuf[512]; char *fromname; +extern int epsvall; + %} %union { int i; char *s; } %token A B C E F I L N P R S T + ALL SP CRLF COMMA USER PASS ACCT REIN QUIT PORT PASV TYPE STRU MODE RETR STOR APPE MLFL MAIL MSND MSOM MSAM MRSQ MRCP ALLO REST RNFR RNTO ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM + LPRT LPSV EPRT EPSV UMASK IDLE CHMOD LEXERR %token STRING %token NUMBER %type check_login octal_number byte_size %type struct_code mode_code type_code form_code -%type pathstring pathname password username +%type pathstring pathname password username ext_arg +%type ALL %start cmd_list %% cmd_list : /* empty */ | cmd_list cmd { fromname = (char *) 0; restart_point = (off_t) 0; } | cmd_list rcmd ; cmd : USER SP username CRLF { user($3); free($3); } | PASS SP password CRLF { pass($3); free($3); } | PORT check_login SP host_port CRLF { - if ($2) { - if (paranoid && - ((ntohs(data_dest.sin_port) < - IPPORT_RESERVED) || - memcmp(&data_dest.sin_addr, - &his_addr.sin_addr, - sizeof(data_dest.sin_addr)))) { - usedefault = 1; + if (epsvall) { + reply(501, "no PORT allowed after EPSV ALL"); + goto port_done; + } + if (!$2) + goto port_done; + if (port_check("PORT") == 1) + goto port_done; +#ifdef INET6 + if ((his_addr.su_family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { + /* shoud never happen */ + usedefault = 1; + reply(500, "Invalid address rejected."); + goto port_done; + } + port_check_v6("pcmd"); +#endif + port_done: + } + | LPRT check_login SP host_long_port CRLF + { + if (epsvall) { + reply(501, "no LPRT allowed after EPSV ALL"); + goto lprt_done; + } + if (!$2) + goto lprt_done; + if (port_check("LPRT") == 1) + goto lprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto lprt_done; + } + if (port_check_v6("LPRT") == 1) + goto lprt_done; +#endif + lprt_done: + } + | EPRT check_login SP STRING CRLF + { + char delim; + char *tmp = NULL; + char *p, *q; + char *result[3]; + struct addrinfo hints; + struct addrinfo *res; + int i; + + if (epsvall) { + reply(501, "no EPRT allowed after EPSV ALL"); + goto eprt_done; + } + if (!$2) + goto eprt_done; + + memset(&data_dest, 0, sizeof(data_dest)); + tmp = strdup($4); + if (debug) + syslog(LOG_DEBUG, "%s", tmp); + if (!tmp) { + fatal("not enough core"); + /*NOTREACHED*/ + } + p = tmp; + delim = p[0]; + p++; + memset(result, 0, sizeof(result)); + for (i = 0; i < 3; i++) { + q = strchr(p, delim); + if (!q || *q != delim) { + parsefail: reply(500, - "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "PORT command successful."); + "Invalid argument, rejected."); + if (tmp) + free(tmp); + usedefault = 1; + goto eprt_done; } + *q++ = '\0'; + result[i] = p; + if (debug) + syslog(LOG_DEBUG, "%d: %s", i, p); + p = q; + } + + /* some more sanity check */ + p = result[0]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; } + p = result[2]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; + } + + /* grab address */ + memset(&hints, 0, sizeof(hints)); + if (atoi(result[0]) == 1) + hints.ai_family = PF_INET; +#ifdef INET6 + else if (atoi(result[0]) == 2) + hints.ai_family = PF_INET6; +#endif + else + hints.ai_family = PF_UNSPEC; /*XXX*/ + hints.ai_socktype = SOCK_STREAM; + i = getaddrinfo(result[1], result[2], &hints, &res); + if (i) + goto parsefail; + memcpy(&data_dest, res->ai_addr, res->ai_addrlen); +#ifdef INET6 + if (his_addr.su_family == AF_INET6 + && data_dest.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } +#endif + free(tmp); + tmp = NULL; + + if (port_check("EPRT") == 1) + goto eprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto eprt_done; + } + if (port_check_v6("EPRT") == 1) + goto eprt_done; +#endif + eprt_done:; } | PASV check_login CRLF { - if ($2) + if (epsvall) + reply(501, "no PASV allowed after EPSV ALL"); + else if ($2) passive(); } + | LPSV check_login CRLF + { + if (epsvall) + reply(501, "no LPSV allowed after EPSV ALL"); + else if ($2) + long_passive("LPSV", PF_UNSPEC); + } + | EPSV check_login SP NUMBER CRLF + { + if ($2) { + int pf; + switch ($4) { + case 1: + pf = PF_INET; + break; +#ifdef INET6 + case 2: + pf = PF_INET6; + break; +#endif + default: + pf = -1; /*junk value*/ + break; + } + long_passive("EPSV", pf); + } + } + | EPSV check_login SP ALL CRLF + { + if ($2) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } + } + | EPSV check_login CRLF + { + if ($2) + long_passive("EPSV", PF_UNSPEC); + } | TYPE SP type_code CRLF { switch (cmd_type) { case TYPE_A: if (cmd_form == FORM_N) { reply(200, "Type set to A."); type = cmd_type; form = cmd_form; } else reply(504, "Form must be N."); break; case TYPE_E: reply(504, "Type E not implemented."); break; case TYPE_I: reply(200, "Type set to I."); type = cmd_type; break; case TYPE_L: #if NBBY == 8 if (cmd_bytesz == 8) { reply(200, "Type set to L (byte size 8)."); type = cmd_type; } else reply(504, "Byte size must be 8."); #else /* NBBY == 8 */ UNIMPLEMENTED for NBBY != 8 #endif /* NBBY == 8 */ } } | STRU SP struct_code CRLF { switch ($3) { case STRU_F: reply(200, "STRU F ok."); break; default: reply(504, "Unimplemented STRU type."); } } | MODE SP mode_code CRLF { switch ($3) { case MODE_S: reply(200, "MODE S ok."); break; default: reply(502, "Unimplemented MODE type."); } } | ALLO SP NUMBER CRLF { reply(202, "ALLO command ignored."); } | ALLO SP NUMBER SP R SP NUMBER CRLF { reply(202, "ALLO command ignored."); } | RETR check_login SP pathname CRLF { if ($2 && $4 != NULL) retrieve((char *) 0, $4); if ($4 != NULL) free($4); } | STOR check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 0); if ($4 != NULL) free($4); } | APPE check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "a", 0); if ($4 != NULL) free($4); } | NLST check_login CRLF { if ($2) send_file_list("."); } | NLST check_login SP STRING CRLF { if ($2 && $4 != NULL) send_file_list($4); if ($4 != NULL) free($4); } | LIST check_login CRLF { if ($2) retrieve("/bin/ls -lgA", ""); } | LIST check_login SP pathname CRLF { if ($2 && $4 != NULL) retrieve("/bin/ls -lgA %s", $4); if ($4 != NULL) free($4); } | STAT check_login SP pathname CRLF { if ($2 && $4 != NULL) statfilecmd($4); if ($4 != NULL) free($4); } | STAT CRLF { statcmd(); } | DELE check_login SP pathname CRLF { if ($2 && $4 != NULL) delete($4); if ($4 != NULL) free($4); } | RNTO check_login SP pathname CRLF { if ($2) { if (fromname) { renamecmd(fromname, $4); free(fromname); fromname = (char *) 0; } else { reply(503, "Bad sequence of commands."); } } free($4); } | ABOR CRLF { reply(225, "ABOR command successful."); } | CWD check_login CRLF { if ($2) cwd(pw->pw_dir); } | CWD check_login SP pathname CRLF { if ($2 && $4 != NULL) cwd($4); if ($4 != NULL) free($4); } | HELP CRLF { help(cmdtab, (char *) 0); } | HELP SP STRING CRLF { char *cp = $3; if (strncasecmp(cp, "SITE", 4) == 0) { cp = $3 + 4; if (*cp == ' ') cp++; if (*cp) help(sitetab, cp); else help(sitetab, (char *) 0); } else help(cmdtab, $3); } | NOOP CRLF { reply(200, "NOOP command successful."); } | MKD check_login SP pathname CRLF { if ($2 && $4 != NULL) makedir($4); if ($4 != NULL) free($4); } | RMD check_login SP pathname CRLF { if ($2 && $4 != NULL) removedir($4); if ($4 != NULL) free($4); } | PWD check_login CRLF { if ($2) pwd(); } | CDUP check_login CRLF { if ($2) cwd(".."); } | SITE SP HELP CRLF { help(sitetab, (char *) 0); } | SITE SP HELP SP STRING CRLF { help(sitetab, $5); } | SITE SP UMASK check_login CRLF { int oldmask; if ($4) { oldmask = umask(0); (void) umask(oldmask); reply(200, "Current UMASK is %03o", oldmask); } } | SITE SP UMASK check_login SP octal_number CRLF { int oldmask; if ($4) { if (($6 == -1) || ($6 > 0777)) { reply(501, "Bad UMASK value"); } else { oldmask = umask($6); reply(200, "UMASK set to %03o (was %03o)", $6, oldmask); } } } | SITE SP CHMOD check_login SP octal_number SP pathname CRLF { if ($4 && ($8 != NULL)) { if ($6 > 0777) reply(501, "CHMOD: Mode value must be between 0 and 0777"); else if (chmod($8, $6) < 0) perror_reply(550, $8); else reply(200, "CHMOD command successful."); } if ($8 != NULL) free($8); } | SITE SP IDLE CRLF { reply(200, "Current IDLE time limit is %d seconds; max %d", timeout, maxtimeout); } | SITE SP IDLE SP NUMBER CRLF { if ($5 < 30 || $5 > maxtimeout) { reply(501, "Maximum IDLE time must be between 30 and %d seconds", maxtimeout); } else { timeout = $5; (void) alarm((unsigned) timeout); reply(200, "Maximum IDLE time set to %d seconds", timeout); } } | STOU check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 1); if ($4 != NULL) free($4); } | SYST CRLF { #ifdef unix #ifdef BSD reply(215, "UNIX Type: L%d Version: BSD-%d", NBBY, BSD); #else /* BSD */ reply(215, "UNIX Type: L%d", NBBY); #endif /* BSD */ #else /* unix */ reply(215, "UNKNOWN Type: L%d", NBBY); #endif /* unix */ } /* * SIZE is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return size of file in a format suitable for * using with RESTART (we just count bytes). */ | SIZE check_login SP pathname CRLF { if ($2 && $4 != NULL) sizecmd($4); if ($4 != NULL) free($4); } /* * MDTM is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return modification time of file as an ISO 3307 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx * where xxx is the fractional second (of any precision, * not necessarily 3 digits) */ | MDTM check_login SP pathname CRLF { if ($2 && $4 != NULL) { struct stat stbuf; if (stat($4, &stbuf) < 0) reply(550, "%s: %s", $4, strerror(errno)); else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", $4); } else { struct tm *t; t = gmtime(&stbuf.st_mtime); reply(213, "%04d%02d%02d%02d%02d%02d", 1900 + t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); } } if ($4 != NULL) free($4); } | QUIT CRLF { reply(221, "Goodbye."); dologout(0); } | error CRLF { yyerrok; } ; rcmd : RNFR check_login SP pathname CRLF { char *renamefrom(); restart_point = (off_t) 0; if ($2 && $4) { fromname = renamefrom($4); if (fromname == (char *) 0 && $4) { free($4); } } } | REST SP byte_size CRLF { fromname = (char *) 0; restart_point = $3; /* XXX $3 is only "int" */ reply(350, "Restarting at %qd. %s", restart_point, "Send STORE or RETRIEVE to initiate transfer."); } ; username : STRING ; password : /* empty */ { $$ = (char *)calloc(1, sizeof(char)); } | STRING ; byte_size : NUMBER ; host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; - data_dest.sin_len = sizeof(struct sockaddr_in); - data_dest.sin_family = AF_INET; - p = (char *)&data_dest.sin_port; + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_sin.sin_port; p[0] = $9; p[1] = $11; - a = (char *)&data_dest.sin_addr; + a = (char *)&data_dest.su_sin.sin_addr; a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; } ; +host_long_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in6); + data_dest.su_family = AF_INET6; + p = (char *)&data_dest.su_port; + p[0] = $39; p[1] = $41; + a = (char *)&data_dest.su_sin6.sin6_addr; + a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; + a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; + a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; + if (his_addr.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } + if ($1 != 6 || $3 != 16 || $37 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = $15; p[1] = $17; + a = (char *)&data_dest.su_sin.sin_addr; + a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + if ($1 != 4 || $3 != 4 || $13 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + ; + form_code : N { $$ = FORM_N; } | T { $$ = FORM_T; } | C { $$ = FORM_C; } ; type_code : A { cmd_type = TYPE_A; cmd_form = FORM_N; } | A SP form_code { cmd_type = TYPE_A; cmd_form = $3; } | E { cmd_type = TYPE_E; cmd_form = FORM_N; } | E SP form_code { cmd_type = TYPE_E; cmd_form = $3; } | I { cmd_type = TYPE_I; } | L { cmd_type = TYPE_L; cmd_bytesz = NBBY; } | L SP byte_size { cmd_type = TYPE_L; cmd_bytesz = $3; } /* this is for a bug in the BBN ftp */ | L byte_size { cmd_type = TYPE_L; cmd_bytesz = $2; } ; struct_code : F { $$ = STRU_F; } | R { $$ = STRU_R; } | P { $$ = STRU_P; } ; mode_code : S { $$ = MODE_S; } | B { $$ = MODE_B; } | C { $$ = MODE_C; } ; pathname : pathstring { /* * Problem: this production is used for all pathname * processing, but only gives a 550 error reply. * This is a valid reply in some cases but not in others. */ if (logged_in && $1 && *$1 == '~') { glob_t gl; int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; memset(&gl, 0, sizeof(gl)); if (glob($1, flags, NULL, &gl) || gl.gl_pathc == 0) { reply(550, "not found"); $$ = NULL; } else { $$ = strdup(gl.gl_pathv[0]); } globfree(&gl); free($1); } else $$ = $1; } ; pathstring : STRING ; octal_number : NUMBER { int ret, dec, multby, digit; /* * Convert a number that was read as decimal number * to what it would be if it had been read as octal. */ dec = $1; multby = 1; ret = 0; while (dec) { digit = dec%10; if (digit > 7) { ret = -1; break; } ret += digit * multby; multby *= 8; dec /= 10; } $$ = ret; } ; check_login : /* empty */ { if (logged_in) $$ = 1; else { reply(530, "Please login with USER and PASS."); $$ = 0; } } ; %% extern jmp_buf errcatch; #define CMD 0 /* beginning of command */ #define ARGS 1 /* expect miscellaneous arguments */ #define STR1 2 /* expect SP followed by STRING */ #define STR2 3 /* expect STRING */ #define OSTR 4 /* optional SP then STRING */ #define ZSTR1 5 /* SP then optional STRING */ #define ZSTR2 6 /* optional STRING after SP */ #define SITECMD 7 /* SITE command */ #define NSTR 8 /* Number followed by a string */ struct tab { char *name; short token; short state; short implemented; /* 1 if command is implemented */ char *help; }; struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, " password" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4" }, + { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2..." }, + { "EPRT", EPRT, STR1, 1, " |af|addr|port|" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, + { "EPSV", EPSV, ARGS, 1, "[ af|ALL]" }, { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, { "RETR", RETR, STR1, 1, " file-name" }, { "STOR", STOR, STR1, 1, " file-name" }, { "APPE", APPE, STR1, 1, " file-name" }, { "MLFL", MLFL, OSTR, 0, "(mail file)" }, { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, { "REST", REST, ARGS, 1, " offset (restart command)" }, { "RNFR", RNFR, STR1, 1, " file-name" }, { "RNTO", RNTO, STR1, 1, " file-name" }, { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, { "DELE", DELE, STR1, 1, " file-name" }, { "CWD", CWD, OSTR, 1, "[ directory-name ]" }, { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, { "LIST", LIST, OSTR, 1, "[ path-name ]" }, { "NLST", NLST, OSTR, 1, "[ path-name ]" }, { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, { "STAT", STAT, OSTR, 1, "[ path-name ]" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { "NOOP", NOOP, ARGS, 1, "" }, { "MKD", MKD, STR1, 1, " path-name" }, { "XMKD", MKD, STR1, 1, " path-name" }, { "RMD", RMD, STR1, 1, " path-name" }, { "XRMD", RMD, STR1, 1, " path-name" }, { "PWD", PWD, ARGS, 1, "(return current directory)" }, { "XPWD", PWD, ARGS, 1, "(return current directory)" }, { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "STOU", STOU, STR1, 1, " file-name" }, { "SIZE", SIZE, OSTR, 1, " path-name" }, { "MDTM", MDTM, OSTR, 1, " path-name" }, { NULL, 0, 0, 0, 0 } }; struct tab sitetab[] = { { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { NULL, 0, 0, 0, 0 } }; static char *copy __P((char *)); static void help __P((struct tab *, char *)); static struct tab * lookup __P((struct tab *, char *)); +static int port_check __P((const char *)); +static int port_check_v6 __P((const char *)); static void sizecmd __P((char *)); static void toolong __P((int)); +static void v4map_data_dest __P((void)); static int yylex __P((void)); static struct tab * lookup(p, cmd) struct tab *p; char *cmd; { for (; p->name != NULL; p++) if (strcmp(cmd, p->name) == 0) return (p); return (0); } #include /* * getline - a hacked up version of fgets to ignore TELNET escape codes. */ char * getline(s, n, iop) char *s; int n; FILE *iop; { int c; register char *cs; cs = s; /* tmpline may contain saved command from urgent mode interruption */ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { *cs++ = tmpline[c]; if (tmpline[c] == '\n') { *cs++ = '\0'; if (debug) syslog(LOG_DEBUG, "command: %s", s); tmpline[0] = '\0'; return(s); } if (c == 0) tmpline[0] = '\0'; } while ((c = getc(iop)) != EOF) { c &= 0377; if (c == IAC) { if ((c = getc(iop)) != EOF) { c &= 0377; switch (c) { case WILL: case WONT: c = getc(iop); printf("%c%c%c", IAC, DONT, 0377&c); (void) fflush(stdout); continue; case DO: case DONT: c = getc(iop); printf("%c%c%c", IAC, WONT, 0377&c); (void) fflush(stdout); continue; case IAC: break; default: continue; /* ignore command */ } } } *cs++ = c; if (--n <= 0 || c == '\n') break; } if (c == EOF && cs == s) return (NULL); *cs++ = '\0'; if (debug) { if (!guest && strncasecmp("pass ", s, 5) == 0) { /* Don't syslog passwords */ syslog(LOG_DEBUG, "command: %.5s ???", s); } else { register char *cp; register int len; /* Don't syslog trailing CR-LF */ len = strlen(s); cp = s + len - 1; while (cp >= s && (*cp == '\n' || *cp == '\r')) { --cp; --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); } } return (s); } static void toolong(signo) int signo; { reply(421, "Timeout (%d seconds): closing control connection.", timeout); if (logging) syslog(LOG_INFO, "User %s timed out after %d seconds", (pw ? pw -> pw_name : "unknown"), timeout); dologout(1); } static int yylex() { static int cpos, state; char *cp, *cp2; struct tab *p; int n; char c; for (;;) { switch (state) { case CMD: (void) signal(SIGALRM, toolong); (void) alarm((unsigned) timeout); if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } (void) alarm(0); #ifdef SETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) setproctitle("%s: %s", proctitle, cbuf); #endif /* SETPROCTITLE */ if ((cp = strchr(cbuf, '\r'))) { *cp++ = '\n'; *cp = '\0'; } if ((cp = strpbrk(cbuf, " \n"))) cpos = cp - cbuf; if (cpos == 0) cpos = 4; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cbuf); p = lookup(cmdtab, cbuf); cbuf[cpos] = c; if (p != 0) { if (p->implemented == 0) { nack(p->name); longjmp(errcatch,0); /* NOTREACHED */ } state = p->state; yylval.s = p->name; return (p->token); } break; case SITECMD: if (cbuf[cpos] == ' ') { cpos++; return (SP); } cp = &cbuf[cpos]; if ((cp2 = strpbrk(cp, " \n"))) cpos = cp2 - cbuf; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cp); p = lookup(sitetab, cp); cbuf[cpos] = c; if (guest == 0 && p != 0) { if (p->implemented == 0) { state = CMD; nack(p->name); longjmp(errcatch,0); /* NOTREACHED */ } state = p->state; yylval.s = p->name; return (p->token); } state = CMD; break; case OSTR: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR1: case ZSTR1: dostr1: if (cbuf[cpos] == ' ') { cpos++; state = state == OSTR ? STR2 : state+1; return (SP); } break; case ZSTR2: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR2: cp = &cbuf[cpos]; n = strlen(cp); cpos += n - 1; /* * Make sure the string is nonempty and \n terminated. */ if (n > 1 && cbuf[cpos] == '\n') { cbuf[cpos] = '\0'; yylval.s = copy(cp); cbuf[cpos] = '\n'; state = ARGS; return (STRING); } break; case NSTR: if (cbuf[cpos] == ' ') { cpos++; return (SP); } if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.i = atoi(cp); cbuf[cpos] = c; state = STR1; return (NUMBER); } state = STR1; goto dostr1; case ARGS: if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.i = atoi(cp); cbuf[cpos] = c; return (NUMBER); } + if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 + && !isalnum(cbuf[cpos + 3])) { + cpos += 3; + return ALL; + } switch (cbuf[cpos++]) { case '\n': state = CMD; return (CRLF); case ' ': return (SP); case ',': return (COMMA); case 'A': case 'a': return (A); case 'B': case 'b': return (B); case 'C': case 'c': return (C); case 'E': case 'e': return (E); case 'F': case 'f': return (F); case 'I': case 'i': return (I); case 'L': case 'l': return (L); case 'N': case 'n': return (N); case 'P': case 'p': return (P); case 'R': case 'r': return (R); case 'S': case 's': return (S); case 'T': case 't': return (T); } break; default: fatal("Unknown state in scanner."); } yyerror((char *) 0); state = CMD; longjmp(errcatch,0); } } void upper(s) char *s; { while (*s != '\0') { if (islower(*s)) *s = toupper(*s); s++; } } static char * copy(s) char *s; { char *p; p = malloc((unsigned) strlen(s) + 1); if (p == NULL) fatal("Ran out of memory."); (void) strcpy(p, s); return (p); } static void help(ctab, s) struct tab *ctab; char *s; { struct tab *c; int width, NCMDS; char *type; if (ctab == sitetab) type = "SITE "; else type = ""; width = 0, NCMDS = 0; for (c = ctab; c->name != NULL; c++) { int len = strlen(c->name); if (len > width) width = len; NCMDS++; } width = (width + 8) &~ 7; if (s == 0) { int i, j, w; int columns, lines; lreply(214, "The following %scommands are recognized %s.", type, "(* =>'s unimplemented)"); columns = 76 / width; if (columns == 0) columns = 1; lines = (NCMDS + columns - 1) / columns; for (i = 0; i < lines; i++) { printf(" "); for (j = 0; j < columns; j++) { c = ctab + j * lines + i; printf("%s%c", c->name, c->implemented ? ' ' : '*'); if (c + lines >= &ctab[NCMDS]) break; w = strlen(c->name) + 1; while (w < width) { putchar(' '); w++; } } printf("\r\n"); } (void) fflush(stdout); reply(214, "Direct comments to ftp-bugs@%s.", hostname); return; } upper(s); c = lookup(ctab, s); if (c == (struct tab *)0) { reply(502, "Unknown command %s.", s); return; } if (c->implemented) reply(214, "Syntax: %s%s %s", type, c->name, c->help); else reply(214, "%s%-*s\t%s; unimplemented.", type, width, c->name, c->help); } static void sizecmd(filename) char *filename; { switch (type) { case TYPE_L: case TYPE_I: { struct stat stbuf; if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) reply(550, "%s: not a plain file.", filename); else reply(213, "%qu", stbuf.st_size); break; } case TYPE_A: { FILE *fin; int c; off_t count; struct stat stbuf; fin = fopen(filename, "r"); if (fin == NULL) { perror_reply(550, filename); return; } if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", filename); (void) fclose(fin); return; } count = 0; while((c=getc(fin)) != EOF) { if (c == '\n') /* will get expanded to \r\n */ count++; count++; } (void) fclose(fin); reply(213, "%qd", count); break; } default: reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } } + +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET) { + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + memcmp(&data_dest.su_sin.sin_addr, + &his_addr.su_sin.sin_addr, + sizeof(data_dest.su_sin.sin_addr)))) { + usedefault = 1; + reply(500, "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +#ifdef INET6 +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check_v6(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) + /* Convert data_dest into v4 mapped sockaddr.*/ + v4map_data_dest(); + if (data_dest.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + memcmp(&data_dest.su_sin6.sin6_addr, + &his_addr.su_sin6.sin6_addr, + sizeof(data_dest.su_sin6.sin6_addr)))) { + usedefault = 1; + reply(500, "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +static void +v4map_data_dest() +{ + struct in_addr savedaddr; + int savedport; + + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return; + } + + savedaddr = data_dest.su_sin.sin_addr; + savedport = data_dest.su_port; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); + data_dest.su_sin6.sin6_family = AF_INET6; + data_dest.su_sin6.sin6_port = savedport; + memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); + memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], + (caddr_t)&savedaddr, sizeof(savedaddr)); +} +#endif diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 8f2cca788ad5..fb10aa64de42 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -1,455 +1,476 @@ .\" Copyright (c) 1985, 1988, 1991, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd April 19, 1994 +.Dd January 27, 2000 .Dt FTPD 8 .Os BSD 4.2 .Sh NAME .Nm ftpd .Nd Internet File Transfer Protocol server .Sh SYNOPSIS .Nm ftpd +.Op Fl 4 +.Op Fl 6 .Op Fl d .Op Fl l Op Fl l .Op Fl A .Op Fl D .Op Fl R .Op Fl S .Op Fl U .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Op Fl a Ar address .Op Fl p Ar file .Sh DESCRIPTION .Nm Ftpd is the Internet File Transfer Protocol server process. The server uses the .Tn TCP protocol and listens at the port specified in the .Dq ftp service specification; see .Xr services 5 . .Pp Available options: .Bl -tag -width indent .It Fl d Debugging information is written to the syslog using LOG_FTP. .It Fl l Each successful and failed .Xr ftp 1 session is logged using syslog with a facility of LOG_FTP. If this option is specified twice, the retrieve (get), store (put), append, delete, make directory, remove directory and rename operations and their filename arguments are also logged. Note: LOG_FTP messages are not displayed by .Xr syslogd 8 by default, and may have to be enabled in .Xr syslogd 8 Ns 's configuration file. .It Fl D With this option set, .Nm will detach and become a daemon, accepting connections on the FTP port and forking children processes to handle them. This is lower overhead than starting .Nm from .Xr inetd 8 and is thus useful on busy servers to reduce load. .It Fl R With this option set, .Nm will revert to historical behavior with regard to security checks on user operations and restrictions on PORT requests. Currently, .Nm will only honor PORT commands directed to unprivileged ports on the remote user's host (which violates the FTP protocol specification but closes some security holes). .It Fl S With this option set, .Nm logs all anonymous transfers to the file .Pa /var/log/ftpd when this file exists. .It Fl U In previous versions of .Nm Ns , when a passive mode client requested a data connection to the server, the server would use data ports in the range 1024..4999. Now, by default, the server will use data ports in the range 49152..65535. Specifying this option will revert to the old behavior. .It Fl T A client may also request a different timeout period; the maximum period allowed may be set to .Ar timeout seconds with the .Fl T option. The default limit is 2 hours. .It Fl t The inactivity timeout period is set to .Ar timeout seconds (the default is 15 minutes). .It Fl a When .Fl D is specified, accept connections only on the specified .Ar address . .It Fl p When .Fl D is specified, write the daemon's process ID to .Ar file . +.It Fl 6 +When +.Fl D +is specified, accept connections via AF_INET6 socket. +.It Fl 4 +When +.Fl D +is specified, accept IPv4 connections. +When +.Fl 6 +is also specified, accept IPv4 connection via AF_INET6 socket. +When +.Fl 6 +is not specified, accept IPv4 connection via AF_INET socket. .It Fl A Allow only anonymous ftp access. .El .Pp The file .Pa /var/run/nologin can be used to disable ftp access. If the file exists, .Nm displays it and exits. If the file .Pa /etc/ftpwelcome exists, .Nm prints it before issuing the .Dq ready message. If the file .Pa /etc/ftpmotd exists, .Nm prints it after a successful login. Note the motd file used is the one relative to the login environment. This means the one in .Pa ~ftp/etc in the anonymous user's case. .Pp The ftp server currently supports the following ftp requests. The case of the requests is ignored. .Bl -column "Request" -offset indent .It Sy Request Ta Sy "Description" .It ABOR Ta "abort previous command" .It ACCT Ta "specify account (ignored)" .It ALLO Ta "allocate storage (vacuously)" .It APPE Ta "append to a file" .It CDUP Ta "change to parent of current working directory" .It CWD Ta "change working directory" .It DELE Ta "delete a file" .It HELP Ta "give help information" .It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA" .It MKD Ta "make a directory" .It MDTM Ta "show last modification time of file" .It MODE Ta "specify data transfer" Em mode .It NLST Ta "give name list of files in directory" .It NOOP Ta "do nothing" .It PASS Ta "specify password" .It PASV Ta "prepare for server-to-server transfer" .It PORT Ta "specify data connection port" .It PWD Ta "print the current working directory" .It QUIT Ta "terminate session" .It REST Ta "restart incomplete transfer" .It RETR Ta "retrieve a file" .It RMD Ta "remove a directory" .It RNFR Ta "specify rename-from file name" .It RNTO Ta "specify rename-to file name" .It SITE Ta "non-standard commands (see next section)" .It SIZE Ta "return size of file" .It STAT Ta "return status of server" .It STOR Ta "store a file" .It STOU Ta "store a file with a unique name" .It STRU Ta "specify data transfer" Em structure .It SYST Ta "show operating system type of server system" .It TYPE Ta "specify data transfer" Em type .It USER Ta "specify user name" .It XCUP Ta "change to parent of current working directory (deprecated)" .It XCWD Ta "change working directory (deprecated)" .It XMKD Ta "make a directory (deprecated)" .It XPWD Ta "print the current working directory (deprecated)" .It XRMD Ta "remove a directory (deprecated)" +.It LPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It LPRT Ta "specify data connection port, multiprotocol" +.It EPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It EPRT Ta "specify data connection port, multiprotocol" .El .Pp The following non-standard or .Tn UNIX specific commands are supported by the SITE request. .Pp .Bl -column Request -offset indent .It Sy Request Ta Sy Description .It UMASK Ta change umask, e.g. ``SITE UMASK 002'' .It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' .It CHMOD Ta "change mode of a file, e.g. ``SITE CHMOD 755 filename''" .It HELP Ta give help information .El .Pp The remaining ftp requests specified in Internet RFC 959 are recognized, but not implemented. MDTM and SIZE are not specified in RFC 959, but will appear in the next updated FTP RFC. .Pp The ftp server will abort an active file transfer only when the ABOR command is preceded by a Telnet "Interrupt Process" (IP) signal and a Telnet "Synch" signal in the command Telnet stream, as described in Internet RFC 959. If a STAT command is received during a data transfer, preceded by a Telnet IP and Synch, transfer status will be returned. .Pp .Nm Ftpd interprets file names according to the .Dq globbing conventions used by .Xr csh 1 . This allows users to utilize the metacharacters .Dq Li \&*?[]{}~ . .Pp .Nm Ftpd authenticates users according to five rules. .Pp .Bl -enum -offset indent .It The login name must be in the password data base and not have a null password. In this case a password must be provided by the client before any file operations may be performed. If the user has an S/Key key, the response from a successful USER command will include an S/Key challenge. The client may choose to respond with a PASS command giving either a standard password or an S/Key one-time password. The server will automatically determine which type of password it has been given and attempt to authenticate accordingly. See .Xr key 1 for more information on S/Key authentication. S/Key is a Trademark of Bellcore. .It The login name must not appear in the file .Pa /etc/ftpusers . .It The login name must not be a member of a group specified in the file .Pa /etc/ftpusers . Entries in this file interpreted as group names are prefixed by an "at" .Ql \&@ sign. .It The user must have a standard shell returned by .Xr getusershell 3 . .It If the user name appears in the file .Pa /etc/ftpchroot , or the user is a member of a group with a group entry in this file, i.e. one prefixed with .Ql \&@ , the session's root will be changed to the user's login directory by .Xr chroot 2 as for an .Dq anonymous or .Dq ftp account (see next item). This facility may also be triggered by enabling the boolean "ftp-chroot" capability in .Xr login.conf 5 . However, the user must still supply a password. This feature is intended as a compromise between a fully anonymous account and a fully privileged account. The account should also be set up as for an anonymous account. .It If the user name is .Dq anonymous or .Dq ftp , an anonymous ftp account must be present in the password file (user .Dq ftp ) . In this case the user is allowed to log in by specifying any password (by convention an email address for the user should be used as the password). When the .Fl S option is set, all transfers are logged as well. .El .Pp In the last case, .Nm takes special measures to restrict the client's access privileges. The server performs a .Xr chroot 2 to the home directory of the .Dq ftp user. In order that system security is not breached, it is recommended that the .Dq ftp subtree be constructed with care, following these rules: .Bl -tag -width "~ftp/pub" -offset indent .It Pa ~ftp Make the home directory owned by .Dq root and unwritable by anyone. .It Pa ~ftp/bin Make this directory owned by .Dq root and unwritable by anyone (mode 555). The program .Xr ls 1 must be present to support the list command. This program should be mode 111. .It Pa ~ftp/etc Make this directory owned by .Dq root and unwritable by anyone (mode 555). The files pwd.db (see .Xr passwd 5 ) and .Xr group 5 must be present for the .Xr ls command to be able to produce owner names rather than numbers. The password field in .Xr passwd is not used, and should not contain real passwords. The file .Pa ftpmotd , if present, will be printed after a successful login. These files should be mode 444. .It Pa ~ftp/pub Make this directory mode 777 and owned by .Dq ftp . Guests can then place files which are to be accessible via the anonymous account in this directory. .El .Pp If the system has multiple IP addresses, .Nm supports the idea of virtual hosts, which provides the ability to define multiple anonymous ftp areas, each one allocated to a different internet address. The file .Pa /etc/ftphosts contains information pertaining to each of the virtual hosts. Each host is defined on its own line which contains a number of fields separated by whitespace: .Bl -tag -offset indent -width hostname .It hostname Contains the hostname or IP address of the virtual host. .It user Contains a user record in the system password file. As with normal anonymous ftp, this user's access uid, gid and group memberships determine file access to the anonymous ftp area. The anonymous ftp area (to which any user is chrooted on login) is determined by the home directory defined for the account. User id and group for any ftp account may be the same as for the standard ftp user. .It statfile File to which all file transfers are logged, which defaults to .Pa /var/log/ftpd . .It welcome This file is the welcome message displayed before the server ready prompt. It defaults to .Pa /etc/ftpwelcome . .It motd This file is displayed after the user logs in. It defaults to .Pa /etc/ftpmotd . .El .Pp Lines beginning with a '#' are ignored and can be used to include comments. .Pp Defining a virtual host for the primary IP address or hostname changes the default for ftp logins to that address. The 'user', 'statfile', 'welcome' and 'motd' fields may be left blank, or a single hypen '-' used to indicate that the default value is to be used. .Pp As with any anonymous login configuration, due care must be given to setup and maintenance to guard against security related problems. .Pp .Nm has internal support for handling remote requests to list files, and will not execute .Pa /bin/ls in either a chrooted or non-chrooted environment. The .Pa ~/bin/ls executable need not be placed into the chrooted tree, nor need the .Pa ~/bin directory exist. .Sh FILES .Bl -tag -width /etc/ftpwelcome -compact .It Pa /etc/ftpusers List of unwelcome/restricted users. .It Pa /etc/ftpchroot List of normal users who should be chroot'd. .It Pa /etc/ftphosts Virtual hosting configuration file. .It Pa /etc/ftpwelcome Welcome notice. .It Pa /etc/ftpmotd Welcome notice after login. .It Pa /var/run/nologin Displayed and access refused. .It Pa /var/log/ftpd Log file for anonymous transfers. .El .Sh SEE ALSO .Xr ftp 1 , .Xr key 1 , .Xr getusershell 3 , .Xr login.conf 5 , .Xr inetd 8 , .Xr syslogd 8 .Sh BUGS The server must run as the super-user to create sockets with privileged port numbers. It maintains an effective user id of the logged in user, reverting to the super-user only when binding addresses to sockets. The possible security holes have been extensively scrutinized, but are possibly incomplete. .Sh HISTORY The .Nm command appeared in .Bx 4.2 . +IPv6 support was added in WIDE Hydrangea IPv6 stack kit. diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 0f9d20c6b5aa..cd72ed277756 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -1,2417 +1,2744 @@ /* * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #if 0 #ifndef lint static char copyright[] = "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #endif #ifndef lint #if 0 static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * FTP server. */ #include #include #include #include #include #include #include #include #include #include #define FTP_NAMES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LOGIN_CAP #include #endif #ifdef SKEY #include #endif #if !defined(NOPAM) #include #endif #include "pathnames.h" #include "extern.h" #if __STDC__ #include #else #include #endif static char version[] = "Version 6.00LS"; #undef main +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + extern off_t restart_point; extern char cbuf[]; -struct sockaddr_in server_addr; -struct sockaddr_in ctrl_addr; -struct sockaddr_in data_source; -struct sockaddr_in data_dest; -struct sockaddr_in his_addr; -struct sockaddr_in pasv_addr; +union sockunion server_addr; +union sockunion ctrl_addr; +union sockunion data_source; +union sockunion data_dest; +union sockunion his_addr; +union sockunion pasv_addr; int daemon_mode; int data; jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; int debug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ int logging; int restricted_data_ports = 1; int paranoid = 1; /* be extra careful about security */ int anon_only = 0; /* Only anonymous ftp allowed */ int guest; int dochroot; int stats; int statfd = -1; int type; int form; int stru; /* avoid C keyword */ int mode; int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ sig_atomic_t transflag; off_t file_size; off_t byte_count; #if !defined(CMASK) || CMASK == 0 #undef CMASK #define CMASK 027 #endif int defumask = CMASK; /* default umask value */ char tmpline[7]; char *hostname; #ifdef VIRTUAL_HOSTING char *ftpuser; +int epsvall = 0; + static struct ftphost { struct ftphost *next; - struct in_addr hostaddr; + union sockunion hostaddr; char *hostname; char *anonuser; char *statfile; char *welcome; char *loginmsg; } *thishost, *firsthost; #endif char remotehost[MAXHOSTNAMELEN]; char *ident = NULL; static char ttyline[20]; char *tty = ttyline; /* for klogin */ #if !defined(NOPAM) static int auth_pam __P((struct passwd**, const char*)); #endif -struct in_addr bind_address; char *pid_file = NULL; /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */ #define SWAITMAX 90 /* wait at most 90 seconds */ #define SWAITINT 5 /* interval between retries */ int swaitmax = SWAITMAX; int swaitint = SWAITINT; #ifdef SETPROCTITLE #ifdef OLD_SETPROCTITLE char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ #endif /* OLD_SETPROCTITLE */ char proctitle[LINE_MAX]; /* initial part of title */ #endif /* SETPROCTITLE */ #ifdef SKEY int pwok = 0; -char addr_string[20]; /* XXX */ +char addr_string[INET6_ADDRSTRLEN]; /* XXX */ #endif #define LOGCMD(cmd, file) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); #define LOGCMD2(cmd, file1, file2) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ *(file1) == '/' ? "" : curdir(), file1, \ *(file2) == '/' ? "" : curdir(), file2); #define LOGBYTES(cmd, file, cnt) \ if (logging > 1) { \ if (cnt == (off_t)-1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); \ else \ syslog(LOG_INFO, "%s %s%s = %qd bytes", \ cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ } #ifdef VIRTUAL_HOSTING static void inithosts __P((void)); -static void selecthost __P((struct in_addr *)); +static void selecthost __P((union sockunion *)); #endif static void ack __P((char *)); static void myoob __P((int)); static int checkuser __P((char *, char *, int)); static FILE *dataconn __P((char *, off_t, char *)); -static void dolog __P((struct sockaddr_in *)); +static void dolog __P((struct sockaddr *)); static char *curdir __P((void)); static void end_login __P((void)); static FILE *getdatasock __P((char *)); static char *gunique __P((char *)); static void lostconn __P((int)); static int receive_data __P((FILE *, FILE *)); static void send_data __P((FILE *, FILE *, off_t, off_t, int)); static struct passwd * sgetpwnam __P((char *)); static char *sgetsave __P((char *)); static void reapchild __P((int)); static void logxfer __P((char *, long, long)); static char * curdir() { static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ if (getcwd(path, sizeof(path)-2) == NULL) return (""); if (path[1] != '\0') /* special case for root dir. */ strcat(path, "/"); /* For guest account, skip / since it's chrooted */ return (guest ? path+1 : path); } int main(argc, argv, envp) int argc; char *argv[]; char **envp; { int addrlen, ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; + int error; + char *bindname = NULL; + int family = AF_UNSPEC; + int enable_v4 = 0; tzset(); /* in case no timezone database in ~ftp */ #ifdef OLD_SETPROCTITLE /* * Save start and extent of argv for setproctitle. */ Argv = argv; while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]); #endif /* OLD_SETPROCTITLE */ - bind_address.s_addr = htonl(INADDR_ANY); - while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { + while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) { switch (ch) { case 'D': daemon_mode++; break; case 'd': debug++; break; case 'l': logging++; /* > 1 == extra logging */ break; case 'R': paranoid = 0; break; case 'S': stats++; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'U': restricted_data_ports = 0; break; case 'a': - if (!inet_aton(optarg, &bind_address)) - errx(1, "invalid address for -a"); + bindname = optarg; break; case 'p': pid_file = optarg; break; case 'u': { long val = 0; val = strtol(optarg, &optarg, 8); if (*optarg != '\0' || val < 0) warnx("bad value for -u"); else defumask = val; break; } case 'A': anon_only = 1; break; case 'v': debug = 1; break; + case '4': + enable_v4 = 1; + if (family == AF_UNSPEC) + family = AF_INET; + break; + + case '6': + family = AF_INET6; + break; + default: warnx("unknown flag -%c ignored", optopt); break; } } #ifdef VIRTUAL_HOSTING inithosts(); #endif (void) freopen(_PATH_DEVNULL, "w", stderr); /* * LOG_NDELAY sets up the logging connection immediately, * necessary for anonymous ftp's that chroot and can't do it later. */ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); if (daemon_mode) { int ctl_sock, fd; - struct servent *sv; + struct addrinfo hints, *res; /* * Detach from parent. */ if (daemon(1, 1) < 0) { syslog(LOG_ERR, "failed to become a daemon"); exit(1); } (void) signal(SIGCHLD, reapchild); - /* - * Get port number for ftp/tcp. - */ - sv = getservbyname("ftp", "tcp"); - if (sv == NULL) { - syslog(LOG_ERR, "getservbyname for ftp failed"); + /* init bind_sa */ + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = family == AF_UNSPEC ? AF_INET : family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(bindname, "ftp", &hints, &res); + if (error) { + if (family == AF_UNSPEC) { + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(bindname, "ftp", &hints, + &res); + } + if (error == 0 && res->ai_addr != NULL) + family = res->ai_addr->sa_family; + } + if (error) { + syslog(LOG_ERR, gai_strerror(error)); + if (error == EAI_SYSTEM) + syslog(LOG_ERR, strerror(errno)); + exit(1); + } + if (res->ai_addr == NULL) { + syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); exit(1); } /* * Open a socket, bind it to the FTP port, and start * listening. */ - ctl_sock = socket(AF_INET, SOCK_STREAM, 0); + ctl_sock = socket(family, SOCK_STREAM, 0); if (ctl_sock < 0) { syslog(LOG_ERR, "control socket: %m"); exit(1); } if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "control setsockopt: %m");; - server_addr.sin_family = AF_INET; - server_addr.sin_addr = bind_address; - server_addr.sin_port = sv->s_port; - if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { + syslog(LOG_ERR, "control setsockopt: %m"); +#ifdef IPV6_BINDV6ONLY + if (family == AF_INET6 && enable_v4 == 0) { + if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, + (char *)&on, sizeof (on)) < 0) + syslog(LOG_ERR, + "control setsockopt(IPV6_BINDV6ONLY): %m"); + } +#endif /* IPV6_BINDV6ONLY */ + memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len); + if (bind(ctl_sock, (struct sockaddr *)&server_addr, + server_addr.su_len) < 0) { syslog(LOG_ERR, "control bind: %m"); exit(1); } if (listen(ctl_sock, 32) < 0) { syslog(LOG_ERR, "control listen: %m"); exit(1); } /* * Atomically write process ID */ if (pid_file) { int fd; char buf[20]; fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC | O_NONBLOCK | O_EXLOCK, 0644); if (fd < 0) { if (errno == EAGAIN) errx(1, "%s: file locked", pid_file); else err(1, "%s", pid_file); } snprintf(buf, sizeof(buf), "%lu\n", (unsigned long) getpid()); if (write(fd, buf, strlen(buf)) < 0) err(1, "%s: write", pid_file); /* Leave the pid file open and locked */ } /* * Loop forever accepting connection requests and forking off * children to handle them. */ while (1) { - addrlen = sizeof(his_addr); + addrlen = server_addr.su_len; fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); if (fork() == 0) { /* child */ (void) dup2(fd, 0); (void) dup2(fd, 1); close(ctl_sock); break; } close(fd); } } else { addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } } (void) signal(SIGCHLD, SIG_IGN); (void) signal(SIGPIPE, lostconn); if (signal(SIGURG, myoob) == SIG_ERR) syslog(LOG_ERR, "signal: %m"); #ifdef SKEY - strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string)); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + addr_string, sizeof(addr_string) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); #endif addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } #ifdef VIRTUAL_HOSTING /* select our identity from virtual host table */ - selecthost(&ctrl_addr.sin_addr); + selecthost(&ctrl_addr); #endif #ifdef IP_TOS + if (ctrl_addr.su_family == AF_INET) + { tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif /* * Disable Nagle on the control channel so that we don't have to wait * for peer's ACK before issuing our next reply. */ if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m"); - data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt: %m"); #endif #ifdef F_SETOWN if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif - dolog(&his_addr); + dolog((struct sockaddr *)&his_addr); /* * Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; /* If logins are disabled, print out the message. */ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(530, "%s", line); } (void) fflush(stdout); (void) fclose(fd); reply(530, "System not available."); exit(0); } #ifdef VIRTUAL_HOSTING if ((fd = fopen(thishost->welcome, "r")) != NULL) { #else if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { #endif while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(220, "%s", line); } (void) fflush(stdout); (void) fclose(fd); /* reply(220,) must follow */ } #ifndef VIRTUAL_HOSTING if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) fatal("Ran out of memory."); (void) gethostname(hostname, MAXHOSTNAMELEN - 1); hostname[MAXHOSTNAMELEN - 1] = '\0'; #endif reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */ } static void lostconn(signo) int signo; { if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(1); } #ifdef VIRTUAL_HOSTING /* * read in virtual host tables (if they exist) */ static void inithosts() { FILE *fp; char *cp; - struct hostent *hp; struct ftphost *hrp, *lhrp; char line[1024]; + struct addrinfo hints, *res, *ai; /* * Fill in the default host information */ if (gethostname(line, sizeof(line)) < 0) line[0] = '\0'; if ((hrp = malloc(sizeof(struct ftphost))) == NULL || (hrp->hostname = strdup(line)) == NULL) fatal("Ran out of memory."); - memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); - if ((hp = gethostbyname(hrp->hostname)) != NULL) - (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + memset(&hrp->hostaddr, 0, sizeof(hrp->hostaddr)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + getaddrinfo(hrp->hostname, NULL, &hints, &res); + if (res) + memcpy(&hrp->hostaddr, res->ai_addr, res->ai_addrlen); hrp->statfile = _PATH_FTPDSTATFILE; hrp->welcome = _PATH_FTPWELCOME; hrp->loginmsg = _PATH_FTPLOGINMESG; hrp->anonuser = "ftp"; hrp->next = NULL; thishost = firsthost = lhrp = hrp; + freeaddrinfo(res); if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { + int addrsize, error; + void *addr; + struct hostent *hp; + while (fgets(line, sizeof(line), fp) != NULL) { - int i; + int i, hp_error; if ((cp = strchr(line, '\n')) == NULL) { /* ignore long lines */ while (fgets(line, sizeof(line), fp) != NULL && strchr(line, '\n') == NULL) ; continue; } *cp = '\0'; cp = strtok(line, " \t"); /* skip comments and empty lines */ if (cp == NULL || line[0] == '#') continue; - /* first, try a standard gethostbyname() */ - if ((hp = gethostbyname(cp)) == NULL) + + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error != NULL) continue; + for (ai = res; ai != NULL && ai->ai_addr != NULL; + ai = ai->ai_next) + { + for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { if (memcmp(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)) == 0) + ai->ai_addr, + ai->ai_addr->sa_len) == 0) break; } if (hrp == NULL) { if ((hrp = malloc(sizeof(struct ftphost))) == NULL) continue; /* defaults */ hrp->statfile = _PATH_FTPDSTATFILE; hrp->welcome = _PATH_FTPWELCOME; hrp->loginmsg = _PATH_FTPLOGINMESG; hrp->anonuser = "ftp"; hrp->next = NULL; lhrp->next = hrp; lhrp = hrp; } (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + ai->ai_addr, + ai->ai_addr->sa_len); /* * determine hostname to use. - * force defined name if it is a valid alias + * force defined name if there is a valid alias * otherwise fallback to primary hostname */ - if ((hp = gethostbyaddr((char*)&hrp->hostaddr, - sizeof(hrp->hostaddr), - AF_INET)) != NULL) { + /* XXX: getaddrinfo() can't do alias check */ + switch(hrp->hostaddr.su_family) { + case AF_INET: + addr = &((struct sockaddr_in *)&hrp->hostaddr)->sin_addr; + addrsize = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addr = &((struct sockaddr_in6 *)&hrp->hostaddr)->sin6_addr; + addrsize = sizeof(struct sockaddr_in6); + break; + default: + /* should not reach here */ + free(hrp); + continue; + /* NOTREACHED */ + } + if ((hp = getipnodebyaddr((char*)addr, addrsize, + hrp->hostaddr.su_family, + &hp_error)) != NULL) { if (strcmp(cp, hp->h_name) != 0) { if (hp->h_aliases == NULL) cp = hp->h_name; else { i = 0; while (hp->h_aliases[i] && strcmp(cp, hp->h_aliases[i]) != 0) ++i; if (hp->h_aliases[i] == NULL) cp = hp->h_name; } } } hrp->hostname = strdup(cp); + freehostent(hp); /* ok, now we now peel off the rest */ i = 0; while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { if (*cp != '-' && (cp = strdup(cp)) != NULL) { switch (i) { case 0: /* anon user permissions */ hrp->anonuser = cp; break; case 1: /* statistics file */ hrp->statfile = cp; break; case 2: /* welcome message */ hrp->welcome = cp; break; case 3: /* login message */ hrp->loginmsg = cp; break; } } ++i; } + /* XXX: re-initialization for getaddrinfo() loop */ + cp = strtok(line, " \t"); + } } (void) fclose(fp); } } static void -selecthost(a) - struct in_addr *a; +selecthost(su) + union sockunion *su; { struct ftphost *hrp; + u_int16_t port; +#ifdef INET6 + struct in6_addr *mapped_in6 = NULL; +#endif + +#ifdef INET6 + /* + * XXX IPv4 mapped IPv6 addr consideraton, + * specified in rfc2373. + */ + if (su->su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) + mapped_in6 = &su->su_sin6.sin6_addr; +#endif hrp = thishost = firsthost; /* default */ + port = su->su_port; + su->su_port = 0; while (hrp != NULL) { - if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + if (memcmp(su, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + thishost = hrp; + break; + } +#ifdef INET6 + /* XXX IPv4 mapped IPv6 addr consideraton */ + if (hrp->hostaddr.su_family == AF_INET && mapped_in6 != NULL && + (memcmp(&mapped_in6->s6_addr[12], + &hrp->hostaddr.su_sin.sin_addr, + sizeof(struct in_addr)) == 0)) { thishost = hrp; break; } +#endif hrp = hrp->next; } + su->su_port = port; /* setup static variables as appropriate */ hostname = thishost->hostname; ftpuser = thishost->anonuser; } #endif /* * Helper function for sgetpwnam(). */ static char * sgetsave(s) char *s; { char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new); } /* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */ static struct passwd * sgetpwnam(name) char *name; { static struct passwd save; struct passwd *p; if ((p = getpwnam(name)) == NULL) return (p); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save); } static int login_attempts; /* number of failed login attempts */ static int askpasswd; /* had user command, ask for passwd */ static char curname[10]; /* current USER name */ /* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */ void user(name) char *name; { char *cp, *shell; if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } else if (dochroot) { reply(530, "Can't change user from chroot user."); return; } end_login(); } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if (checkuser(_PATH_FTPUSERS, "ftp", 0) || checkuser(_PATH_FTPUSERS, "anonymous", 0)) reply(530, "User %s access denied.", name); #ifdef VIRTUAL_HOSTING else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { #else else if ((pw = sgetpwnam("ftp")) != NULL) { #endif guest = 1; askpasswd = 1; reply(331, "Guest login ok, send your email address as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) syslog(LOG_NOTICE, "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } if (anon_only != 0) { reply(530, "Sorry, only anonymous ftp allowed."); return; } if ((pw = sgetpwnam(name))) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = (struct passwd *) NULL; return; } } if (logging) strncpy(curname, name, sizeof(curname)-1); #ifdef SKEY pwok = skeyaccess(name, NULL, remotehost, addr_string); reply(331, "%s", skey_challenge(name, pw, pwok)); #else reply(331, "Password required for %s.", name); #endif askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts); } /* * Check if a user is in the file "fname" */ static int checkuser(fname, name, pwset) char *fname; char *name; int pwset; { FILE *fd; int found = 0; char *p, line[BUFSIZ]; if ((fd = fopen(fname, "r")) != NULL) { while (!found && fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; /* * if first chr is '@', check group membership */ if (line[0] == '@') { int i = 0; struct group *grp; if ((grp = getgrnam(line+1)) == NULL) continue; /* * Check user's default group */ if (pwset && grp->gr_gid == pw->pw_gid) found = 1; /* * Check supplementary groups */ while (!found && grp->gr_mem[i]) found = strcmp(name, grp->gr_mem[i++]) == 0; } /* * Otherwise, just check for username match */ else found = strcmp(line, name) == 0; } (void) fclose(fd); } return (found); } /* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */ static void end_login() { (void) seteuid((uid_t)0); if (logged_in) ftpd_logwtmp(ttyline, "", ""); pw = NULL; #ifdef LOGIN_CAP setusercontext(NULL, getpwuid(0), (uid_t)0, LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); #endif logged_in = 0; guest = 0; dochroot = 0; } #if !defined(NOPAM) /* * the following code is stolen from imap-uw PAM authentication module and * login.c */ #define COPY_STRING(s) (s ? strdup(s) : NULL) struct cred_t { const char *uname; /* user name */ const char *pass; /* password */ }; typedef struct cred_t cred_t; static int auth_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata) { int i; cred_t *cred = (cred_t *) appdata; struct pam_response *reply = malloc(sizeof(struct pam_response) * num_msg); for (i = 0; i < num_msg; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* assume want user name */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING(cred->uname); /* PAM frees resp. */ break; case PAM_PROMPT_ECHO_OFF: /* assume want password */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING(cred->pass); /* PAM frees resp. */ break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: /* unknown message style */ free(reply); return PAM_CONV_ERR; } } *resp = reply; return PAM_SUCCESS; } /* * Attempt to authenticate the user using PAM. Returns 0 if the user is * authenticated, or 1 if not authenticated. If some sort of PAM system * error occurs (e.g., the "/etc/pam.conf" file is missing) then this * function returns -1. This can be used as an indication that we should * fall back to a different authentication mechanism. */ static int auth_pam(struct passwd **ppw, const char *pass) { pam_handle_t *pamh = NULL; const char *tmpl_user; const void *item; int rval; int e; cred_t auth_cred = { (*ppw)->pw_name, pass }; struct pam_conv conv = { &auth_conv, &auth_cred }; e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); if (e != PAM_SUCCESS) { syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); return -1; } e = pam_authenticate(pamh, 0); switch (e) { case PAM_SUCCESS: /* * With PAM we support the concept of a "template" * user. The user enters a login name which is * authenticated by PAM, usually via a remote service * such as RADIUS or TACACS+. If authentication * succeeds, a different but related "template" name * is used for setting the credentials, shell, and * home directory. The name the user enters need only * exist on the remote authentication server, but the * template name must be present in the local password * database. * * This is supported by two various mechanisms in the * individual modules. However, from the application's * point of view, the template user is always passed * back as a changed value of the PAM_USER item. */ if ((e = pam_get_item(pamh, PAM_USER, &item)) == PAM_SUCCESS) { tmpl_user = (const char *) item; if (strcmp((*ppw)->pw_name, tmpl_user) != 0) *ppw = getpwnam(tmpl_user); } else syslog(LOG_ERR, "Couldn't get PAM_USER: %s", pam_strerror(pamh, e)); rval = 0; break; case PAM_AUTH_ERR: case PAM_USER_UNKNOWN: case PAM_MAXTRIES: rval = 1; break; default: syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); rval = -1; break; } if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); rval = -1; } return rval; } #endif /* !defined(NOPAM) */ void pass(passwd) char *passwd; { int rval; FILE *fd; #ifdef LOGIN_CAP login_cap_t *lc = NULL; #endif if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) { rval = 1; /* failure below */ goto skip; } #if !defined(NOPAM) rval = auth_pam(&pw, passwd); if (rval >= 0) goto skip; #endif #ifdef SKEY rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok), pw->pw_passwd); pwok = 0; #else rval = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); #endif /* The strcmp does not catch null passwords! */ if (*pw->pw_passwd == '\0' || (pw->pw_expire && time(NULL) >= pw->pw_expire)) rval = 1; /* failure */ skip: /* * If rval == 1, the user failed the authentication check * above. If rval == 0, either PAM or local authentication * succeeded. */ if (rval) { reply(530, "Login incorrect."); if (logging) syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", remotehost, curname); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); exit(0); } return; } } login_attempts = 0; /* this time successful */ if (setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; } /* May be overridden by login.conf */ (void) umask(defumask); #ifdef LOGIN_CAP if ((lc = login_getpwclass(pw)) != NULL) { char remote_ip[MAXHOSTNAMELEN]; - strncpy(remote_ip, inet_ntoa(his_addr.sin_addr), - sizeof(remote_ip) - 1); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + remote_ip, sizeof(remote_ip) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, remotehost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, "FTP LOGIN FAILED (HOST) as %s: permission denied.", pw->pw_name); reply(530, "Permission denied.\n"); pw = NULL; return; } if (!auth_timeok(lc, time(NULL))) { reply(530, "Login not available right now.\n"); pw = NULL; return; } } setusercontext(lc, pw, (uid_t)0, LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| LOGIN_SETRESOURCES|LOGIN_SETUMASK); #else setlogin(pw->pw_name); (void) initgroups(pw->pw_name, pw->pw_gid); #endif /* open wtmp before chroot */ ftpd_logwtmp(ttyline, pw->pw_name, remotehost); logged_in = 1; if (guest && stats && statfd < 0) #ifdef VIRTUAL_HOSTING if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) #else if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) #endif stats = 0; dochroot = #ifdef LOGIN_CAP /* Allow login.conf configuration as well */ login_getcapbool(lc, "ftp-chroot", 0) || #endif checkuser(_PATH_FTPCHROOT, pw->pw_name, 1); if (guest) { /* * We MUST do a chdir() after the chroot. Otherwise * the old current directory will be accessible as "." * outside the new root! */ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { reply(550, "Can't set guest privileges."); goto bad; } } else if (dochroot) { if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { reply(550, "Can't change root."); goto bad; } } else if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; } else lreply(230, "No directory! Logging in with home=/"); } if (seteuid((uid_t)pw->pw_uid) < 0) { reply(550, "Can't set uid."); goto bad; } /* * Display a login message, if it exists. * N.B. reply(230,) must follow the message. */ #ifdef VIRTUAL_HOSTING if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { #else if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { #endif char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(230, "%s", line); } (void) fflush(stdout); (void) fclose(fd); } if (guest) { if (ident != NULL) free(ident); ident = strdup(passwd); if (ident == NULL) fatal("Ran out of memory."); reply(230, "Guest login ok, access restrictions apply."); #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING if (thishost != firsthost) snprintf(proctitle, sizeof(proctitle), "%s: anonymous(%s)/%.*s", remotehost, hostname, (int)(sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/")), passwd); else #endif snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%.*s", remotehost, (int)(sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/")), passwd); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", remotehost, passwd); } else { if (dochroot) reply(230, "User %s logged in, access restrictions apply.", pw->pw_name); else reply(230, "User %s logged in.", pw->pw_name); #ifdef SETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", remotehost, pw->pw_name); } #ifdef LOGIN_CAP login_close(lc); #endif return; bad: /* Forget all about it... */ #ifdef LOGIN_CAP login_close(lc); #endif end_login(); } void retrieve(cmd, name) char *cmd, *name; { FILE *fin, *dout; struct stat st; int (*closefunc) __P((FILE *)); time_t start; if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; (void) snprintf(line, sizeof(line), cmd, name), name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; st.st_blksize = BUFSIZ; } if (fin == NULL) { if (errno != 0) { perror_reply(550, name); if (cmd == 0) { LOGCMD("get", name); } } return; } byte_count = -1; if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { reply(550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fin)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; time(&start); send_data(fin, dout, st.st_blksize, st.st_size, restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); if (cmd == 0 && guest && stats) logxfer(name, st.st_size, start); (void) fclose(dout); data = -1; pdata = -1; done: if (cmd == 0) LOGBYTES("get", name, byte_count); (*closefunc)(fin); } void store(name, mode, unique) char *name, *mode; int unique; { FILE *fout, *din; struct stat st; int (*closefunc) __P((FILE *)); if ((unique || guest) && stat(name, &st) == 0 && (name = gunique(name)) == NULL) { LOGCMD(*mode == 'w' ? "put" : "append", name); return; } if (restart_point) mode = "r+"; fout = fopen(name, mode); closefunc = fclose; if (fout == NULL) { perror_reply(553, name); LOGCMD(*mode == 'w' ? "put" : "append", name); return; } byte_count = -1; if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fout)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } /* * We must do this seek to "current" position * because we are changing from reading to * writing. */ if (fseek(fout, 0L, L_INCR) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, (off_t)-1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete."); } (void) fclose(din); data = -1; pdata = -1; done: LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); (*closefunc)(fout); } static FILE * getdatasock(mode) char *mode; { int on = 1, s, t, tries; if (data >= 0) return (fdopen(data, mode)); (void) seteuid((uid_t)0); - s = socket(AF_INET, SOCK_STREAM, 0); + + s = socket(data_dest.su_family, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ - data_source.sin_len = sizeof(struct sockaddr_in); - data_source.sin_family = AF_INET; - data_source.sin_addr = ctrl_addr.sin_addr; + data_source = ctrl_addr; + data_source.su_port = htons(20); /* ftp-data port */ for (tries = 1; ; tries++) { if (bind(s, (struct sockaddr *)&data_source, - sizeof(data_source)) >= 0) + data_source.su_len) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } (void) seteuid((uid_t)pw->pw_uid); #ifdef IP_TOS + if (data_source.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif #ifdef TCP_NOPUSH /* * Turn off push flag to keep sender TCP from sending short packets * at the boundaries of each write(). Should probably do a SO_SNDBUF * to set the send buffer size as well, but that may not be desirable * in heavy-load situations. */ on = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); #endif #ifdef SO_SNDBUF on = 65536; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0) syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m"); #endif return (fdopen(s, mode)); bad: /* Return the real value of errno (close may change it) */ t = errno; (void) seteuid((uid_t)pw->pw_uid); (void) close(s); errno = t; return (NULL); } static FILE * dataconn(name, size, mode) char *name; off_t size; char *mode; { char sizebuf[32]; FILE *file; int retry = 0, tos; file_size = size; byte_count = 0; if (size != (off_t) -1) (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)", size); else *sizebuf = '\0'; if (pdata >= 0) { - struct sockaddr_in from; - int s, fromlen = sizeof(from); + union sockunion from; + int s, fromlen = ctrl_addr.su_len; struct timeval timeout; fd_set set; FD_ZERO(&set); FD_SET(pdata, &set); timeout.tv_usec = 0; timeout.tv_sec = 120; if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 || (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) { reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return (NULL); } (void) close(pdata); pdata = s; #ifdef IP_TOS + if (from.su_family == AF_INET) + { tos = IPTOS_THROUGHPUT; (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)); + } #endif reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (fdopen(pdata, mode)); } if (data >= 0) { reply(125, "Using existing data connection for '%s'%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; file = getdatasock(mode); if (file == NULL) { - reply(425, "Can't create data socket (%s,%d): %s.", - inet_ntoa(data_source.sin_addr), - ntohs(data_source.sin_port), strerror(errno)); + char hostbuf[BUFSIZ], portbuf[BUFSIZ]; + getnameinfo((struct sockaddr *)&data_source, + data_source.su_len, hostbuf, sizeof(hostbuf) - 1, + portbuf, sizeof(portbuf), + NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID); + reply(425, "Can't create data socket (%s,%s): %s.", + hostbuf, portbuf, strerror(errno)); return (NULL); } data = fileno(file); while (connect(data, (struct sockaddr *)&data_dest, - sizeof(data_dest)) < 0) { + data_dest.su_len) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; continue; } perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return (NULL); } reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (file); } /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. * * NB: Form isn't handled. */ static void send_data(instr, outstr, blksize, filesize, isreg) FILE *instr, *outstr; off_t blksize; off_t filesize; int isreg; { int c, cnt, filefd, netfd; char *buf, *bp; size_t len; transflag++; if (setjmp(urgcatch)) { transflag = 0; return; } switch (type) { case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') { if (ferror(outstr)) goto data_err; (void) putc('\r', outstr); } (void) putc(c, outstr); } fflush(outstr); transflag = 0; if (ferror(instr)) goto file_err; if (ferror(outstr)) goto data_err; reply(226, "Transfer complete."); return; case TYPE_I: case TYPE_L: /* * isreg is only set if we are not doing restart and we * are sending a regular file */ netfd = fileno(outstr); filefd = fileno(instr); if (isreg && filesize < (off_t)16 * 1024 * 1024) { buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, (off_t)0); if (buf == MAP_FAILED) { syslog(LOG_WARNING, "mmap(%lu): %m", (unsigned long)filesize); goto oldway; } bp = buf; len = filesize; do { cnt = write(netfd, bp, len); len -= cnt; bp += cnt; if (cnt > 0) byte_count += cnt; } while(cnt > 0 && len > 0); transflag = 0; munmap(buf, (size_t)filesize); if (cnt < 0) goto data_err; reply(226, "Transfer complete."); return; } oldway: if ((buf = malloc((u_int)blksize)) == NULL) { transflag = 0; perror_reply(451, "Local resource failure: malloc"); return; } while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && write(netfd, buf, cnt) == cnt) byte_count += cnt; transflag = 0; (void)free(buf); if (cnt != 0) { if (cnt < 0) goto file_err; goto data_err; } reply(226, "Transfer complete."); return; default: transflag = 0; reply(550, "Unimplemented TYPE %d in send_data", type); return; } data_err: transflag = 0; perror_reply(426, "Data connection"); return; file_err: transflag = 0; perror_reply(551, "Error on input file"); } /* * Transfer data from peer to "outstr" using the appropriate encapulation of * the data subject to Mode, Structure, and Type. * * N.B.: Form isn't handled. */ static int receive_data(instr, outstr) FILE *instr, *outstr; { int c; int cnt, bare_lfs; char buf[BUFSIZ]; transflag++; if (setjmp(urgcatch)) { transflag = 0; return (-1); } bare_lfs = 0; switch (type) { case TYPE_I: case TYPE_L: while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { if (write(fileno(outstr), buf, cnt) != cnt) goto file_err; byte_count += cnt; } if (cnt < 0) goto data_err; transflag = 0; return (0); case TYPE_E: reply(553, "TYPE E not implemented."); transflag = 0; return (-1); case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') bare_lfs++; while (c == '\r') { if (ferror(outstr)) goto data_err; if ((c = getc(instr)) != '\n') { (void) putc ('\r', outstr); if (c == '\0' || c == EOF) goto contin2; } } (void) putc(c, outstr); contin2: ; } fflush(outstr); if (ferror(instr)) goto data_err; if (ferror(outstr)) goto file_err; transflag = 0; if (bare_lfs) { lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); (void)printf(" File may not have transferred correctly.\r\n"); } return (0); default: reply(550, "Unimplemented TYPE %d in receive_data", type); transflag = 0; return (-1); } data_err: transflag = 0; perror_reply(426, "Data Connection"); return (-1); file_err: transflag = 0; perror_reply(452, "Error writing file"); return (-1); } void statfilecmd(filename) char *filename; { FILE *fin; int c; char line[LINE_MAX]; (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); fin = ftpd_popen(line, "r"); lreply(211, "status of %s:", filename); while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(stdout)){ perror_reply(421, "control connection"); (void) ftpd_pclose(fin); dologout(1); /* NOTREACHED */ } if (ferror(fin)) { perror_reply(551, filename); (void) ftpd_pclose(fin); return; } (void) putc('\r', stdout); } (void) putc(c, stdout); } (void) ftpd_pclose(fin); reply(211, "End of Status"); } void statcmd() { - struct sockaddr_in *sin; + union sockunion *su; u_char *a, *p; + char hname[INET6_ADDRSTRLEN]; + int ispassive; lreply(211, "%s FTP server status:", hostname, version); printf(" %s\r\n", version); printf(" Connected to %s", remotehost); - if (!isdigit(remotehost[0])) - printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID)) { + if (strcmp(hname, remotehost) != 0) + printf(" (%s)", hname); + } printf("\r\n"); if (logged_in) { if (guest) printf(" Logged in anonymously\r\n"); else printf(" Logged in as %s\r\n", pw->pw_name); } else if (askpasswd) printf(" Waiting for password\r\n"); else printf(" Waiting for user name\r\n"); printf(" TYPE: %s", typenames[type]); if (type == TYPE_A || type == TYPE_E) printf(", FORM: %s", formnames[form]); if (type == TYPE_L) #if NBBY == 8 printf(" %d", NBBY); #else printf(" %d", bytesize); /* need definition! */ #endif printf("; STRUcture: %s; transfer MODE: %s\r\n", strunames[stru], modenames[mode]); if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { - printf(" in Passive mode"); - sin = &pasv_addr; + ispassive = 1; + su = &pasv_addr; goto printaddr; } else if (usedefault == 0) { - printf(" PORT"); - sin = &data_dest; + ispassive = 0; + su = &data_dest; printaddr: - a = (u_char *) &sin->sin_addr; - p = (u_char *) &sin->sin_port; #define UC(b) (((int) b) & 0xff) - printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), - UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + if (epsvall) { + printf(" EPSV only mode (EPSV ALL)\r\n"); + goto epsvonly; + } + + /* PORT/PASV */ + if (su->su_family == AF_INET) { + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", + ispassive ? "PASV" : "PORT", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + } + + /* LPRT/LPSV */ + { + int alen, af, i; + + switch (su->su_family) { + case AF_INET: + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + alen = sizeof(su->su_sin.sin_addr); + af = 4; + break; + case AF_INET6: + a = (u_char *) &su->su_sin6.sin6_addr; + p = (u_char *) &su->su_sin6.sin6_port; + alen = sizeof(su->su_sin6.sin6_addr); + af = 6; + break; + default: + af = 0; + break; + } + if (af) { + printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", + af, alen); + for (i = 0; i < alen; i++) + printf("%d,", UC(a[i])); + printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); + } + } + +epsvonly:; + /* EPRT/EPSV */ + { + int af; + + switch (su->su_family) { + case AF_INET: + af = 1; + break; + case AF_INET6: + af = 2; + break; + default: + af = 0; + break; + } + if (af) { + if (!getnameinfo((struct sockaddr *)su, su->su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST)) { + printf(" %s |%d|%s|%d|\r\n", + ispassive ? "EPSV" : "EPRT", + af, hname, htons(su->su_port)); + } + } + } #undef UC } else printf(" No data connection\r\n"); reply(211, "End of status"); } void fatal(s) char *s; { reply(451, "Error in server: %s\n", s); reply(221, "Closing connection due to server error."); dologout(0); /* NOTREACHED */ } void #if __STDC__ reply(int n, const char *fmt, ...) #else reply(n, fmt, va_alist) int n; char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)printf("%d ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d ", n); vsyslog(LOG_DEBUG, fmt, ap); } } void #if __STDC__ lreply(int n, const char *fmt, ...) #else lreply(n, fmt, va_alist) int n; char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)printf("%d- ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d- ", n); vsyslog(LOG_DEBUG, fmt, ap); } } static void ack(s) char *s; { reply(250, "%s command successful.", s); } void nack(s) char *s; { reply(502, "%s command not implemented.", s); } /* ARGSUSED */ void yyerror(s) char *s; { char *cp; if ((cp = strchr(cbuf,'\n'))) *cp = '\0'; reply(500, "'%s': command not understood.", cbuf); } void delete(name) char *name; { struct stat st; LOGCMD("delete", name); if (stat(name, &st) < 0) { perror_reply(550, name); return; } if ((st.st_mode&S_IFMT) == S_IFDIR) { if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (unlink(name) < 0) { perror_reply(550, name); return; } done: ack("DELE"); } void cwd(path) char *path; { if (chdir(path) < 0) perror_reply(550, path); else ack("CWD"); } void makedir(name) char *name; { LOGCMD("mkdir", name); if (mkdir(name, 0777) < 0) perror_reply(550, name); else reply(257, "MKD command successful."); } void removedir(name) char *name; { LOGCMD("rmdir", name); if (rmdir(name) < 0) perror_reply(550, name); else ack("RMD"); } void pwd() { char path[MAXPATHLEN + 1]; if (getwd(path) == (char *)NULL) reply(550, "%s.", path); else reply(257, "\"%s\" is current directory.", path); } char * renamefrom(name) char *name; { struct stat st; if (stat(name, &st) < 0) { perror_reply(550, name); return ((char *)0); } reply(350, "File exists, ready for destination name"); return (name); } void renamecmd(from, to) char *from, *to; { struct stat st; LOGCMD2("rename", from, to); if (guest && (stat(to, &st) == 0)) { reply(550, "%s: permission denied", to); return; } if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO"); } static void -dolog(sin) - struct sockaddr_in *sin; +dolog(who) + struct sockaddr *who; { - realhostname(remotehost, sizeof(remotehost) - 1, &sin->sin_addr); + int error; + + realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING if (thishost != firsthost) snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", remotehost, hostname); else #endif snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) { #ifdef VIRTUAL_HOSTING if (thishost != firsthost) syslog(LOG_INFO, "connection from %s (to %s)", remotehost, hostname); else #endif + { + char who_name[MAXHOSTNAMELEN]; + + error = getnameinfo(who, who->sa_len, + who_name, sizeof(who_name) - 1, + NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); syslog(LOG_INFO, "connection from %s (%s)", remotehost, - inet_ntoa(sin->sin_addr)); + error == 0 ? who_name : ""); + } } } /* * Record logout in wtmp file * and exit with supplied status. */ void dologout(status) int status; { /* * Prevent reception of SIGURG from resulting in a resumption * back to the main program loop. */ transflag = 0; if (logged_in) { (void) seteuid((uid_t)0); ftpd_logwtmp(ttyline, "", ""); } /* beware of flushing buffers after a SIGPIPE */ _exit(status); } static void myoob(signo) int signo; { char *cp; /* only process if transfer occurring */ if (!transflag) return; cp = tmpline; if (getline(cp, 7, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } upper(cp); if (strcmp(cp, "ABOR\r\n") == 0) { tmpline[0] = '\0'; reply(426, "Transfer aborted. Data connection closed."); reply(226, "Abort successful"); longjmp(urgcatch, 1); } if (strcmp(cp, "STAT\r\n") == 0) { tmpline[0] = '\0'; if (file_size != (off_t) -1) reply(213, "Status: %qd of %qd bytes transferred", byte_count, file_size); else reply(213, "Status: %qd bytes transferred", byte_count); } } /* * Note: a response of 425 is not mentioned as a possible response to * the PASV command in RFC959. However, it has been blessed as * a legitimate response by Jon Postel in a telephone conversation * with Rick Adams on 25 Jan 89. */ void passive() { int len; char *p, *a; if (pdata >= 0) /* close old port if one set */ close(pdata); - pdata = socket(AF_INET, SOCK_STREAM, 0); + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } (void) seteuid((uid_t)0); #ifdef IP_PORTRANGE - { + if (ctrl_addr.su_family == AF_INET) { int on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; } #endif pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, - sizeof(pasv_addr)) < 0) + pasv_addr.su_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) goto pasv_error; (void) seteuid((uid_t)pw->pw_uid); len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; - a = (char *) &pasv_addr.sin_addr; - p = (char *) &pasv_addr.sin_port; + if (pasv_addr.su_family == AF_INET) + a = (char *) &pasv_addr.su_sin.sin_addr; + else if (pasv_addr.su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + else + goto pasv_error; + + p = (char *) &pasv_addr.su_port; #define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return; pasv_error: (void) seteuid((uid_t)pw->pw_uid); (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } +/* + * Long Passive defined in RFC 1639. + * 228 Entering Long Passive Mode + * (af, hal, h1, h2, h3,..., pal, p1, p2...) + */ + +void +long_passive(cmd, pf) + char *cmd; + int pf; +{ + int len; + char *p, *a; + + if (pdata >= 0) /* close old port if one set */ + close(pdata); + + if (pf != PF_UNSPEC) { + if (ctrl_addr.su_family != pf) { + switch (ctrl_addr.su_family) { + case AF_INET: + pf = 1; + break; + case AF_INET6: + pf = 2; + break; + default: + pf = 0; + break; + } + /* + * XXX + * only EPRT/EPSV ready clients will understand this + */ + if (strcmp(cmd, "EPSV") == 0 && pf) { + reply(522, "Network protocol mismatch, " + "use (%d)", pf); + } else + reply(501, "Network protocol mismatch"); /*XXX*/ + + return; + } + } + + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + + (void) seteuid((uid_t)0); + + pasv_addr = ctrl_addr; + pasv_addr.su_port = 0; + len = pasv_addr.su_len; + + if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) + goto pasv_error; + + (void) seteuid((uid_t)pw->pw_uid); + + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + +#define UC(b) (((int) b) & 0xff) + + if (strcmp(cmd, "LPSV") == 0) { + p = (char *)&pasv_addr.su_port; + switch (pasv_addr.su_family) { + case AF_INET: + a = (char *) &pasv_addr.su_sin.sin_addr; + v4_reply: + reply(228, +"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + 2, UC(p[0]), UC(p[1])); + return; + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + goto v4_reply; + } + a = (char *) &pasv_addr.su_sin6.sin6_addr; + reply(228, +"Entering Long Passive Mode " +"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), + UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), + UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), + 2, UC(p[0]), UC(p[1])); + return; + } + } else if (strcmp(cmd, "EPSV") == 0) { + switch (pasv_addr.su_family) { + case AF_INET: + case AF_INET6: + reply(229, "Entering Extended Passive Mode (|||%d|)", + ntohs(pasv_addr.su_port)); + return; + } + } else { + /* more proper error code? */ + } + +pasv_error: + (void) seteuid((uid_t)pw->pw_uid); + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + /* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. */ static char * gunique(local) char *local; { static char new[MAXPATHLEN]; struct stat st; int count; char *cp; cp = strrchr(local, '/'); if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, cp ? local : "."); return ((char *) 0); } if (cp) *cp = '/'; /* -4 is for the .nn we put on the end below */ (void) snprintf(new, sizeof(new) - 4, "%s", local); cp = new + strlen(new); *cp++ = '.'; for (count = 1; count < 100; count++) { (void)sprintf(cp, "%d", count); if (stat(new, &st) < 0) return (new); } reply(452, "Unique file name cannot be created."); return (NULL); } /* * Format and send reply containing system error number. */ void perror_reply(code, string) int code; char *string; { reply(code, "%s: %s.", string, strerror(errno)); } static char *onefile[] = { "", 0 }; void send_file_list(whichf) char *whichf; { struct stat st; DIR *dirp = NULL; struct dirent *dir; FILE *dout = NULL; char **dirlist, *dirname; int simple = 0; int freeglob = 0; glob_t gl; if (strpbrk(whichf, "~{[*?") != NULL) { int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; memset(&gl, 0, sizeof(gl)); freeglob = 1; if (glob(whichf, flags, 0, &gl)) { reply(550, "not found"); goto out; } else if (gl.gl_pathc == 0) { errno = ENOENT; perror_reply(550, whichf); goto out; } dirlist = gl.gl_pathv; } else { onefile[0] = whichf; dirlist = onefile; simple = 1; } if (setjmp(urgcatch)) { transflag = 0; goto out; } while ((dirname = *dirlist++)) { if (stat(dirname, &st) < 0) { /* * If user typed "ls -l", etc, and the client * used NLST, do what the user meant. */ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) { retrieve(_PATH_LS " %s", dirname); goto out; } perror_reply(550, whichf); if (dout != NULL) { (void) fclose(dout); transflag = 0; data = -1; pdata = -1; } goto out; } if (S_ISREG(st.st_mode)) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } fprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); byte_count += strlen(dirname) + 1; continue; } else if (!S_ISDIR(st.st_mode)) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[MAXPATHLEN]; if (dir->d_name[0] == '.' && dir->d_namlen == 1) continue; if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && dir->d_namlen == 2) continue; snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); /* * We have to do a stat to insure it's * not a directory or special file. */ if (simple || (stat(nbuf, &st) == 0 && S_ISREG(st.st_mode))) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else fprintf(dout, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); byte_count += strlen(nbuf) + 1; } } (void) closedir(dirp); } if (dout == NULL) reply(550, "No files found."); else if (ferror(dout) != 0) perror_reply(550, "Data connection"); else reply(226, "Transfer complete."); transflag = 0; if (dout != NULL) (void) fclose(dout); data = -1; pdata = -1; out: if (freeglob) { freeglob = 0; globfree(&gl); } } void reapchild(signo) int signo; { while (wait3(NULL, WNOHANG, NULL) > 0); } #ifdef OLD_SETPROCTITLE /* * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) * Warning, since this is usually started from inetd.conf, it often doesn't * have much of an environment or arglist to overwrite. */ void #if __STDC__ setproctitle(const char *fmt, ...) #else setproctitle(fmt, va_alist) char *fmt; va_dcl #endif { int i; va_list ap; char *p, *bp, ch; char buf[LINE_MAX]; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)vsnprintf(buf, sizeof(buf), fmt, ap); /* make ps print our process name */ p = Argv[0]; *p++ = '-'; i = strlen(buf); if (i > LastArgv - p - 2) { i = LastArgv - p - 2; buf[i] = '\0'; } bp = buf; while (ch = *bp++) if (ch != '\n' && ch != '\r') *p++ = ch; while (p < LastArgv) *p++ = ' '; } #endif /* OLD_SETPROCTITLE */ static void logxfer(name, size, start) char *name; long size; long start; { char buf[1024]; char path[MAXPATHLEN + 1]; time_t now; if (statfd >= 0 && getwd(path) != NULL) { time(&now); snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%ld!%ld\n", ctime(&now)+4, ident, remotehost, path, name, size, now - start + (now == start)); write(statfd, buf, strlen(buf)); } } diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index d3f2ec56c883..2ac57d13d270 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -1,93 +1,102 @@ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include +#include #include #include #include #include #include #include #include #include "extern.h" static int fd = -1; /* * Modified version of logwtmp that holds wtmp file open * after first call, for use with ftp (which may chroot * after login, but before logout). */ void ftpd_logwtmp(line, name, host) char *line, *name, *host; { struct utmp ut; struct stat buf; if (strlen(host) > UT_HOSTSIZE) { - struct hostent *hp = gethostbyname(host); + struct addrinfo hints, *res; + int error; + static char hostbuf[BUFSIZ]; - if (hp != NULL) { - struct in_addr in; - - memmove(&in, hp->h_addr, sizeof(in)); - host = inet_ntoa(in); - } else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(host, NULL, &hints, &res); + if (error) host = "invalid hostname"; + else { + getnameinfo(res->ai_addr, res->ai_addrlen, + hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + host = hostbuf; + if (strlen(host) > UT_HOSTSIZE) + host[UT_HOSTSIZE] = '\0'; + } } if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) return; if (fstat(fd, &buf) == 0) { (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); (void)time(&ut.ut_time); if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) (void)ftruncate(fd, buf.st_size); } } diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c index 606d17a421d6..82cfd581f8db 100644 --- a/libexec/ftpd/popen.c +++ b/libexec/ftpd/popen.c @@ -1,201 +1,202 @@ /* * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include +#include #include #include #include #include #include #include #include #include "extern.h" #include "pathnames.h" #include #include #include #define MAXUSRARGS 100 #define MAXGLOBARGS 1000 /* * Special version of popen which avoids call to shell. This ensures noone * may create a pipe to a hidden program as a side effect of a list or dir * command. */ static int *pids; static int fds; FILE * ftpd_popen(program, type) char *program, *type; { char *cp; FILE *iop; int argc, gargc, pdes[2], pid; char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS]; if (((*type != 'r') && (*type != 'w')) || type[1]) return (NULL); if (!pids) { if ((fds = getdtablesize()) <= 0) return (NULL); if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL) return (NULL); memset(pids, 0, fds * sizeof(int)); } if (pipe(pdes) < 0) return (NULL); /* break up string into pieces */ for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) { if (!(argv[argc++] = strtok(cp, " \t\n"))) break; } argv[argc - 1] = NULL; /* glob each piece */ gargv[0] = argv[0]; for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) { glob_t gl; int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; memset(&gl, 0, sizeof(gl)); if (glob(argv[argc], flags, NULL, &gl)) gargv[gargc++] = strdup(argv[argc]); else for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1); pop++) gargv[gargc++] = strdup(*pop); globfree(&gl); } gargv[gargc] = NULL; iop = NULL; fflush(NULL); pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork(); switch(pid) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto pfree; /* NOTREACHED */ case 0: /* child */ if (*type == 'r') { if (pdes[1] != STDOUT_FILENO) { dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); } dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ (void)close(pdes[0]); } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); } if (strcmp(gargv[0], _PATH_LS) == 0) { extern int optreset; /* Reset getopt for ls_main() */ optreset = optind = optopt = 1; /* Close syslogging to remove pwd.db missing msgs */ closelog(); /* Trigger to sense new /etc/localtime after chroot */ if (getenv("TZ") == NULL) { setenv("TZ", "", 0); tzset(); unsetenv("TZ"); tzset(); } exit(ls_main(gargc, gargv)); } execv(gargv[0], gargv); _exit(1); } /* parent; assume fdopen can't fail... */ if (*type == 'r') { iop = fdopen(pdes[0], type); (void)close(pdes[1]); } else { iop = fdopen(pdes[1], type); (void)close(pdes[0]); } pids[fileno(iop)] = pid; pfree: for (argc = 1; gargv[argc] != NULL; argc++) free(gargv[argc]); return (iop); } int ftpd_pclose(iop) FILE *iop; { int fdes, omask, status; pid_t pid; /* * pclose returns -1 if stream is not associated with a * `popened' command, or, if already `pclosed'. */ if (pids == 0 || pids[fdes = fileno(iop)] == 0) return (-1); (void)fclose(iop); omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) continue; (void)sigsetmask(omask); pids[fdes] = 0; if (pid < 0) return (pid); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (1); } diff --git a/secure/libexec/telnetd/Makefile b/secure/libexec/telnetd/Makefile index 230e4e79bf53..e13f48dcad78 100644 --- a/secure/libexec/telnetd/Makefile +++ b/secure/libexec/telnetd/Makefile @@ -1,22 +1,22 @@ # $FreeBSD$ # Do not define -DKLUDGELINEMODE, as it does not interact well with many # telnet implementations. PROG= telnetd MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c DPADD= ${LIBUTIL} ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBMP} \ ${LIBCRYPT} LDADD= -lutil -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes -lcrypt -lmp .include .PATH: ${TELNETDIR}/telnetd diff --git a/secure/usr.bin/telnet/Makefile b/secure/usr.bin/telnet/Makefile index b7683443d5a6..f0db4cb8cb28 100644 --- a/secure/usr.bin/telnet/Makefile +++ b/secure/usr.bin/telnet/Makefile @@ -1,16 +1,19 @@ # $FreeBSD$ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -I${TELNETDIR} +CFLAGS+= -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBCRYPT} ${LIBMP} +DPADD+= ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes -lcrypt -lmp +LDADD+= -lipsec .include .PATH: ${TELNETDIR}/telnet diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index bbd82e253aa6..b5a59cc8330e 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -1,21 +1,22 @@ # $FreeBSD$ # $NetBSD: Makefile,v 1.15 1997/10/18 15:31:20 lukem Exp $ # from: @(#)Makefile 8.2 (Berkeley) 4/3/94 # Uncomment the following to provide defaults for gate-ftp operation # #CFLAGS+=-DGATE_SERVER=\"ftp-gw.host\" # -DGATE_PORT=21 PROG= ftp SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c ruserpass.c \ util.c +CFLAGS+=-DINET6 -g LDADD+= -ledit -ltermcap DPADD+= ${LIBEDIT} ${LIBTERMCAP} LINKS= ${BINDIR}/ftp ${BINDIR}/pftp \ ${BINDIR}/ftp ${BINDIR}/gate-ftp MLINKS= ftp.1 pftp.1 \ ftp.1 gate-ftp.1 .include diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h index a8ac97b8c866..7841f7b02c55 100644 --- a/usr.bin/ftp/extern.h +++ b/usr.bin/ftp/extern.h @@ -1,173 +1,173 @@ /* $FreeBSD$ */ /* $NetBSD: extern.h,v 1.17.2.1 1997/11/18 00:59:50 mellon Exp $ */ /*- * Copyright (c) 1994 The Regents of the University of California. * 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)extern.h 8.3 (Berkeley) 10/9/94 */ void abort_remote __P((FILE *)); void abortpt __P((int)); void abortrecv __P((int)); void abortsend __P((int)); void account __P((int, char **)); void alarmtimer __P((int)); int another __P((int *, char ***, const char *)); int auto_fetch __P((int, char **)); void blkfree __P((char **)); void cd __P((int, char **)); void cdup __P((int, char **)); void changetype __P((int, int)); void cmdabort __P((int)); void cmdscanner __P((int)); int command __P((const char *, ...)); #ifndef SMALL unsigned char complete __P((EditLine *, int)); void controlediting __P((void)); #endif /* !SMALL */ int confirm __P((const char *, const char *)); FILE *dataconn __P((const char *)); void delete __P((int, char **)); void disconnect __P((int, char **)); void do_chmod __P((int, char **)); void do_umask __P((int, char **)); void domacro __P((int, char **)); char *domap __P((char *)); void doproxy __P((int, char **)); char *dotrans __P((char *)); int empty __P((fd_set *, int)); void get __P((int, char **)); struct cmd *getcmd __P((const char *)); int getit __P((int, char **, int, const char *)); int getreply __P((int)); int globulize __P((char **)); char *gunique __P((const char *)); void help __P((int, char **)); -char *hookup __P((const char *, int)); +char *hookup __P((const char *, char *)); void idle __P((int, char **)); int initconn __P((void)); void intr __P((void)); void list_vertical __P((StringList *)); void lcd __P((int, char **)); int login __P((const char *, char *, char *)); void lostpeer __P((void)); void lpwd __P((int, char **)); void ls __P((int, char **)); void mabort __P((int)); void macdef __P((int, char **)); void makeargv __P((void)); void makedir __P((int, char **)); void mdelete __P((int, char **)); void mget __P((int, char **)); void mls __P((int, char **)); void modtime __P((int, char **)); void mput __P((int, char **)); char *onoff __P((int)); void newer __P((int, char **)); void page __P((int, char **)); void progressmeter __P((int)); char *prompt __P((void)); void proxabort __P((int)); void proxtrans __P((const char *, const char *, const char *)); void psabort __P((int)); void psummary __P((int)); void pswitch __P((int)); void ptransfer __P((int)); void put __P((int, char **)); void pwd __P((int, char **)); void quit __P((int, char **)); void quote __P((int, char **)); void quote1 __P((const char *, int, char **)); void recvrequest __P((const char *, const char *, const char *, const char *, int, int)); void reget __P((int, char **)); char *remglob __P((char **, int, char **)); off_t remotesize __P((const char *, int)); time_t remotemodtime __P((const char *, int)); void removedir __P((int, char **)); void renamefile __P((int, char **)); void reset __P((int, char **)); void restart __P((int, char **)); void rmthelp __P((int, char **)); void rmtstatus __P((int, char **)); int ruserpass __P((const char *, char **, char **, char **)); void sendrequest __P((const char *, const char *, const char *, int)); void setascii __P((int, char **)); void setbell __P((int, char **)); void setbinary __P((int, char **)); void setcase __P((int, char **)); void setcr __P((int, char **)); void setdebug __P((int, char **)); void setedit __P((int, char **)); void setform __P((int, char **)); void setftmode __P((int, char **)); void setgate __P((int, char **)); void setglob __P((int, char **)); void sethash __P((int, char **)); void setnmap __P((int, char **)); void setntrans __P((int, char **)); void setpassive __P((int, char **)); void setpeer __P((int, char **)); void setport __P((int, char **)); void setpreserve __P((int, char **)); void setprogress __P((int, char **)); void setprompt __P((int, char **)); void setrestrict __P((int, char **)); void setrunique __P((int, char **)); void setstruct __P((int, char **)); void setsunique __P((int, char **)); void settenex __P((int, char **)); void settrace __P((int, char **)); void setttywidth __P((int)); void settype __P((int, char **)); void setverbose __P((int, char **)); void shell __P((int, char **)); void site __P((int, char **)); void sizecmd __P((int, char **)); char *slurpstring __P((void)); void status __P((int, char **)); void syst __P((int, char **)); int togglevar __P((int, char **, int *, const char *)); void usage __P((void)); void user __P((int, char **)); extern struct cmd cmdtab[]; extern FILE *cout; extern int data; extern char *home; extern int proxy; extern char reply_string[]; extern int NCMDS; extern char *__progname; /* from crt0.o */ diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c index 1d73c0f9655e..62b64e9e35dd 100644 --- a/usr.bin/ftp/fetch.c +++ b/usr.bin/ftp/fetch.c @@ -1,647 +1,668 @@ /* $NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason Thorpe and Luke Mewburn. * * 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 the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ #include #ifndef lint __RCSID("$FreeBSD$"); __RCSID_SOURCE("$NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $"); #endif /* not lint */ /* * FTP User Program -- Command line file retrieval */ #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "ftp_var.h" +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + static int url_get __P((const char *, const char *)); void aborthttp __P((int)); #define FTP_URL "ftp://" /* ftp URL prefix */ #define HTTP_URL "http://" /* http URL prefix */ #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ #define HTTP_PROXY "http_proxy" /* env var with http proxy location */ #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) jmp_buf httpabort; /* * Retrieve URL, via the proxy in $proxyvar if necessary. * Modifies the string argument given. * Returns -1 on failure, 0 on success */ static int url_get(origline, proxyenv) const char *origline; const char *proxyenv; { - struct sockaddr_in sin; + struct addrinfo hints; + struct addrinfo *res; + char nameinfo[2 * INET6_ADDRSTRLEN + 1]; int i, out, isftpurl; - u_int16_t port; + char *port; volatile int s; size_t len; char c, *cp, *ep, *portnum, *path, buf[4096]; const char *savefile; char *line, *proxy, *host; volatile sig_t oldintr; off_t hashbytes; + int error; s = -1; proxy = NULL; isftpurl = 0; #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&savefile; (void)&proxy; #endif line = strdup(origline); if (line == NULL) errx(1, "Can't allocate memory to parse URL"); if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) host = line + sizeof(HTTP_URL) - 1; else if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) { host = line + sizeof(FTP_URL) - 1; isftpurl = 1; } else errx(1, "url_get: Invalid URL '%s'", line); path = strchr(host, '/'); /* find path */ if (EMPTYSTRING(path)) { if (isftpurl) goto noftpautologin; warnx("Invalid URL (no `/' after host): %s", origline); goto cleanup_url_get; } *path++ = '\0'; if (EMPTYSTRING(path)) { if (isftpurl) goto noftpautologin; warnx("Invalid URL (no file after host): %s", origline); goto cleanup_url_get; } savefile = strrchr(path, '/'); /* find savefile */ if (savefile != NULL) savefile++; else savefile = path; if (EMPTYSTRING(savefile)) { if (isftpurl) goto noftpautologin; warnx("Invalid URL (no file after directory): %s", origline); goto cleanup_url_get; } if (proxyenv != NULL) { /* use proxy */ proxy = strdup(proxyenv); if (proxy == NULL) errx(1, "Can't allocate memory for proxy URL."); if (strncasecmp(proxy, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) host = proxy + sizeof(HTTP_URL) - 1; else if (strncasecmp(proxy, FTP_URL, sizeof(FTP_URL) - 1) == 0) host = proxy + sizeof(FTP_URL) - 1; else { warnx("Malformed proxy URL: %s", proxyenv); goto cleanup_url_get; } if (EMPTYSTRING(host)) { warnx("Malformed proxy URL: %s", proxyenv); goto cleanup_url_get; } *--path = '/'; /* add / back to real path */ path = strchr(host, '/'); /* remove trailing / on host */ if (! EMPTYSTRING(path)) *path++ = '\0'; path = line; } - portnum = strchr(host, ':'); /* find portnum */ - if (portnum != NULL) + if (*host == '[' && (portnum = strrchr(host, ']'))) { /* IPv6 URL */ *portnum++ = '\0'; - + host++; + if (*portnum == ':') + portnum++; + else + portnum = NULL; + } else { + portnum = strrchr(host, ':'); /* find portnum */ + if (portnum != NULL) + *portnum++ = '\0'; + } + if (debug) printf("host %s, port %s, path %s, save as %s.\n", host, portnum, path, savefile); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - - if (isdigit((unsigned char)host[0])) { - if (inet_aton(host, &sin.sin_addr) == 0) { - warnx("Invalid IP address: %s", host); - goto cleanup_url_get; - } - } else { - struct hostent *hp; - - hp = gethostbyname(host); - if (hp == NULL) { - warnx("%s: %s", host, hstrerror(h_errno)); - goto cleanup_url_get; - } - if (hp->h_addrtype != AF_INET) { - warnx("%s: not an Internet address?", host); - goto cleanup_url_get; - } - memcpy(&sin.sin_addr, hp->h_addr, hp->h_length); - } - if (! EMPTYSTRING(portnum)) { - char *ep; - long nport; - - nport = strtol(portnum, &ep, 10); - if (nport < 1 || nport > 0xffff || *ep != '\0') { - warnx("Invalid port: %s", portnum); - goto cleanup_url_get; - } - port = htons(nport); + port = portnum; } else port = httpport; - sin.sin_port = port; - s = socket(AF_INET, SOCK_STREAM, 0); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res); + if (error) { + warnx("%s: %s", host, gai_strerror(error)); + if (error = EAI_SYSTEM) + warnx("%s: %s", host, strerror(errno)); + goto cleanup_url_get; + } + + while (1) + { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { warn("Can't create socket"); goto cleanup_url_get; } if (dobind && bind(s, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { - warn("Can't bind to %s", inet_ntoa(bindto.sin_addr)); + ((struct sockaddr *)&bindto)->sa_len) == -1) { + getnameinfo((struct sockaddr *)&bindto, + ((struct sockaddr *)&bindto)->sa_len, + nameinfo, sizeof(nameinfo), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX check error? */ + warn("Can't bind to %s", nameinfo); goto cleanup_url_get; } - if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) { - warn("Can't connect to %s", host); + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + close(s); + res = res->ai_next; + if (res) + continue; goto cleanup_url_get; } + break; + } + /* * Construct and send the request. We're expecting a return * status of "200". Proxy requests don't want leading /. */ if (!proxy) printf("Requesting %s\n", origline); else printf("Requesting %s (via %s)\n", origline, proxyenv); len = snprintf(buf, sizeof(buf), "GET %s%s HTTP/1.0\r\n\r\n", proxy ? "" : "/", path); if (write(s, buf, len) < len) { warn("Writing HTTP request"); goto cleanup_url_get; } memset(buf, 0, sizeof(buf)); for (cp = buf; cp < buf + sizeof(buf); ) { if (read(s, cp, 1) != 1) goto improper; if (*cp == '\r') continue; if (*cp == '\n') break; cp++; } buf[sizeof(buf) - 1] = '\0'; /* sanity */ cp = strchr(buf, ' '); if (cp == NULL) goto improper; else cp++; if (strncmp(cp, "200", 3)) { warnx("Error retrieving file: %s", cp); goto cleanup_url_get; } /* * Read the rest of the header. */ memset(buf, 0, sizeof(buf)); c = '\0'; for (cp = buf; cp < buf + sizeof(buf); ) { if (read(s, cp, 1) != 1) goto improper; if (*cp == '\r') continue; if (*cp == '\n' && c == '\n') break; c = *cp; cp++; } buf[sizeof(buf) - 1] = '\0'; /* sanity */ /* * Look for the "Content-length: " header. */ #define CONTENTLEN "Content-Length: " for (cp = buf; *cp != '\0'; cp++) { if (tolower((unsigned char)*cp) == 'c' && strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) break; } if (*cp != '\0') { cp += sizeof(CONTENTLEN) - 1; ep = strchr(cp, '\n'); if (ep == NULL) goto improper; else *ep = '\0'; filesize = strtol(cp, &ep, 10); if (filesize < 1 || *ep != '\0') goto improper; } else filesize = -1; /* Open the output file. */ out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666); if (out < 0) { warn("Can't open %s", savefile); goto cleanup_url_get; } /* Trap signals */ oldintr = NULL; if (setjmp(httpabort)) { if (oldintr) (void)signal(SIGINT, oldintr); goto cleanup_url_get; } oldintr = signal(SIGINT, aborthttp); bytes = 0; hashbytes = mark; progressmeter(-1); /* Finally, suck down the file. */ i = 0; while ((len = read(s, buf, sizeof(buf))) > 0) { bytes += len; for (cp = buf; len > 0; len -= i, cp += i) { if ((i = write(out, cp, len)) == -1) { warn("Writing %s", savefile); goto cleanup_url_get; } else if (i == 0) break; } if (hash && !progress) { while (bytes >= hashbytes) { (void)putchar('#'); hashbytes += mark; } (void)fflush(stdout); } } if (hash && !progress && bytes > 0) { if (bytes < mark) (void)putchar('#'); (void)putchar('\n'); (void)fflush(stdout); } if (len != 0) { warn("Reading from socket"); goto cleanup_url_get; } progressmeter(1); if (verbose) puts("Successfully retrieved file."); (void)signal(SIGINT, oldintr); close(s); close(out); if (proxy) free(proxy); free(line); return (0); noftpautologin: warnx( "Auto-login using ftp URLs isn't supported when using $ftp_proxy"); goto cleanup_url_get; improper: warnx("Improper response from %s", host); cleanup_url_get: if (s != -1) close(s); if (proxy) free(proxy); free(line); return (-1); } /* * Abort a http retrieval */ void aborthttp(notused) int notused; { alarmtimer(0); puts("\nhttp fetch aborted."); (void)fflush(stdout); longjmp(httpabort, 1); } /* * Retrieve multiple files from the command line, transferring * files of the form "host:path", "ftp://host/path" using the * ftp protocol, and files of the form "http://host/path" using * the http protocol. * If path has a trailing "/", then return (-1); * the path will be cd-ed into and the connection remains open, * and the function will return -1 (to indicate the connection * is alive). * If an error occurs the return value will be the offset+1 in * argv[] of the file that caused a problem (i.e, argv[x] * returns x+1) * Otherwise, 0 is returned if all files retrieved successfully. */ int auto_fetch(argc, argv) int argc; char *argv[]; { static char lasthost[MAXHOSTNAMELEN]; char *xargv[5]; char *cp, *line, *host, *dir, *file, *portnum; char *user, *pass; char *ftpproxy, *httpproxy; int rval, xargc; volatile int argpos; int dirhasglob, filehasglob; char rempath[MAXPATHLEN]; argpos = 0; if (setjmp(toplevel)) { if (connected) disconnect(0, NULL); return (argpos + 1); } (void)signal(SIGINT, (sig_t)intr); (void)signal(SIGPIPE, (sig_t)lostpeer); ftpproxy = getenv(FTP_PROXY); httpproxy = getenv(HTTP_PROXY); /* * Loop through as long as there's files to fetch. */ for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) { if (strchr(argv[argpos], ':') == NULL) break; host = dir = file = portnum = user = pass = NULL; /* * We muck with the string, so we make a copy. */ line = strdup(argv[argpos]); if (line == NULL) errx(1, "Can't allocate memory for auto-fetch."); /* * Try HTTP URL-style arguments first. */ if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { if (url_get(line, httpproxy) == -1) rval = argpos + 1; continue; } /* * Try FTP URL-style arguments next. If ftpproxy is * set, use url_get() instead of standard ftp. * Finally, try host:file. */ host = line; if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) { if (ftpproxy) { if (url_get(line, ftpproxy) == -1) rval = argpos + 1; continue; } host += sizeof(FTP_URL) - 1; dir = strchr(host, '/'); /* look for [user:pass@]host[:port] */ pass = strpbrk(host, ":@/"); if (pass == NULL || *pass == '/') { pass = NULL; goto parsed_url; } if (pass == host || *pass == '@') { bad_ftp_url: warnx("Invalid URL: %s", argv[argpos]); rval = argpos + 1; continue; } *pass++ = '\0'; cp = strpbrk(pass, ":@/"); if (cp == NULL || *cp == '/') { portnum = pass; pass = NULL; goto parsed_url; } if (EMPTYSTRING(cp) || *cp == ':') goto bad_ftp_url; *cp++ = '\0'; user = host; if (EMPTYSTRING(user)) goto bad_ftp_url; host = cp; portnum = strchr(host, ':'); if (portnum != NULL) *portnum++ = '\0'; } else { /* classic style `host:file' */ dir = strchr(host, ':'); } parsed_url: if (EMPTYSTRING(host)) { rval = argpos + 1; continue; } /* * If dir is NULL, the file wasn't specified * (URL looked something like ftp://host) */ if (dir != NULL) *dir++ = '\0'; /* * Extract the file and (if present) directory name. */ if (! EMPTYSTRING(dir)) { cp = strrchr(dir, '/'); if (cp != NULL) { *cp++ = '\0'; file = cp; } else { file = dir; dir = NULL; } } if (debug) printf("user %s:%s host %s port %s dir %s file %s\n", user, pass, host, portnum, dir, file); /* * Set up the connection if we don't have one. */ if (strcmp(host, lasthost) != 0) { int oautologin; (void)strcpy(lasthost, host); if (connected) disconnect(0, NULL); xargv[0] = __progname; xargv[1] = host; xargv[2] = NULL; xargc = 2; if (! EMPTYSTRING(portnum)) { xargv[2] = portnum; xargv[3] = NULL; xargc = 3; } oautologin = autologin; if (user != NULL) autologin = 0; setpeer(xargc, xargv); autologin = oautologin; if ((connected == 0) || ((connected == 1) && !login(host, user, pass)) ) { warnx("Can't connect or login to host `%s'", host); rval = argpos + 1; continue; } /* Always use binary transfers. */ setbinary(0, NULL); } /* cd back to '/' */ xargv[0] = "cd"; xargv[1] = "/"; xargv[2] = NULL; cd(2, xargv); if (! dirchange) { rval = argpos + 1; continue; } dirhasglob = filehasglob = 0; if (doglob) { if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL) dirhasglob = 1; if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL) filehasglob = 1; } /* Change directories, if necessary. */ if (! EMPTYSTRING(dir) && !dirhasglob) { xargv[0] = "cd"; xargv[1] = dir; xargv[2] = NULL; cd(2, xargv); if (! dirchange) { rval = argpos + 1; continue; } } if (EMPTYSTRING(file)) { rval = -1; continue; } if (!verbose) printf("Retrieving %s/%s\n", dir ? dir : "", file); if (dirhasglob) { snprintf(rempath, sizeof(rempath), "%s/%s", dir, file); file = rempath; } /* Fetch the file(s). */ xargv[0] = "get"; xargv[1] = file; xargv[2] = NULL; if (dirhasglob || filehasglob) { int ointeractive; ointeractive = interactive; interactive = 0; xargv[0] = "mget"; mget(2, xargv); interactive = ointeractive; } else get(2, xargv); if ((code / 100) != COMPLETE) rval = argpos + 1; } if (connected && rval != -1) disconnect(0, NULL); return (rval); } + +int +isurl(p) + const char *p; +{ + if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 + || strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { + return 1; + } + return 0; +} diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 index 59b371f34b7b..a112709981fe 100644 --- a/usr.bin/ftp/ftp.1 +++ b/usr.bin/ftp/ftp.1 @@ -1,1418 +1,1436 @@ .\" $FreeBSD$ .\" $NetBSD: ftp.1,v 1.21 1997/06/10 21:59:58 lukem Exp $ .\" .\" Copyright (c) 1985, 1989, 1990, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 .\" -.Dd August 18, 1997 +.Dd January 27, 2000 .Dt FTP 1 .Os BSD 4.2 .Sh NAME .Nm ftp , pftp , gate-ftp .Nd .Tn ARPANET file transfer program .Sh SYNOPSIS .Nm .Op Fl adeginptUvV .Op Fl P Ar port .Op Fl s Ar src_addr .Op Ar host Op Ar port .Nm ftp ftp://[\fIuser\fR:\fIpassword\fR@]\fIhost\fR[:\fIport\fR]/\fIfile\fR[/] .Nm ftp http://\fIhost\fR[:\fIport\fR]/\fIfile\fR .Nm ftp \fIhost\fR:[/\fIpath\fR/]\fIfile\fR[/] .Sh DESCRIPTION .Nm is the user interface to the .Tn ARPANET standard File Transfer Protocol. The program allows a user to transfer files to and from a remote network site. +The version supports IPv6 (Internet protocol version 6), as well as IPv4. .Pp The latter three usage formats will fetch a file using either the HTTP or FTP protocols into the current directory. This is ideal for scripts. Refer to .Sx AUTO-FETCHING FILES below for more information. .Pp Options may be specified at the command line, or to the command interpreter. .Bl -tag -width Fl .It Fl a Causes .Nm to bypass normal login procedure, and use an anonymous login instead. .It Fl d Enables debugging. .It Fl e Disables command line editing. .It Fl g Disables file name globbing. .It Fl i Turns off interactive prompting during multiple file transfers. .It Fl n Restrains .Nm from attempting .Dq auto-login upon initial connection. If auto-login is enabled, .Nm will check the .Pa .netrc (see below) file in the user's home directory for an entry describing an account on the remote machine. If no entry exists, .Nm will prompt for the remote machine login name (default is the user identity on the local machine), and, if necessary, prompt for a password and an account with which to login. .It Fl p Enables passive mode operation for use behind connection filtering firewalls. Using the .Nm pftp command has the same effect. .It Fl P Ar port Sets the port number to .Ar port . .It Fl s Ar src_addr Sets the local IP address for all connections to .Ar src_addr , which can be an IP address or a host name. .It Fl t Enables packet tracing. .It Fl U Disable data port range restrictions. .It Fl v Enable verbose mode. This is the default if input is from a terminal. Forces .Nm to show all responses from the remote server, as well as report on data transfer statistics. .It Fl V Disable verbose mode, overriding the default of enabled when input is from a terminal. .El .Pp The client host with which .Nm is to communicate may be specified on the command line. If this is done, .Nm will immediately attempt to establish a connection to an .Tn FTP server on that host; otherwise, .Nm will enter its command interpreter and await instructions from the user. When .Nm is awaiting commands from the user the prompt .Ql ftp> is provided to the user. The following commands are recognized by .Nm ftp : .Bl -tag -width Fl .It Ic \&! Op Ar command Op Ar args Invoke an interactive shell on the local machine. If there are arguments, the first is taken to be a command to execute directly, with the rest of the arguments as its arguments. .It Ic \&$ Ar macro-name Op Ar args Execute the macro .Ar macro-name that was defined with the .Ic macdef command. Arguments are passed to the macro unglobbed. .It Ic account Op Ar passwd Supply a supplemental password required by a remote system for access to resources once a login has been successfully completed. If no argument is included, the user will be prompted for an account password in a non-echoing input mode. .It Ic append Ar local-file Op Ar remote-file Append a local file to a file on the remote machine. If .Ar remote-file is left unspecified, the local file name is used in naming the remote file after being altered by any .Ic ntrans or .Ic nmap setting. File transfer uses the current settings for .Ic type , .Ic format , .Ic mode and .Ic structure . .It Ic ascii Set the file transfer .Ic type to network .Tn ASCII . This is the default type. .It Ic bell Arrange that a bell be sounded after each file transfer command is completed. .It Ic binary Set the file transfer .Ic type to support binary image transfer. .It Ic bye Terminate the .Tn FTP session with the remote server and exit .Nm ftp . An end of file will also terminate the session and exit. .It Ic case Toggle remote computer file name case mapping during .Ic mget commands. When .Ic case is on (default is off), remote computer file names with all letters in upper case are written in the local directory with the letters mapped to lower case. .It Ic \&cd Ar remote-directory Change the working directory on the remote machine to .Ar remote-directory . .It Ic cdup Change the remote machine working directory to the parent of the current remote machine working directory. .It Ic chmod Ar mode file-name Change the permission modes of the file .Ar file-name on the remote system to .Ar mode . .It Ic close Terminate the .Tn FTP session with the remote server, and return to the command interpreter. Any defined macros are erased. .It Ic \&cr Toggle carriage return stripping during ascii type file retrieval. Records are denoted by a carriage return/linefeed sequence during ascii type file transfer. When .Ic \&cr is on (the default), carriage returns are stripped from this sequence to conform with the .Ux single linefeed record delimiter. Records on .Pf non\- Ns Ux remote systems may contain single linefeeds; when an ascii type transfer is made, these linefeeds may be distinguished from a record delimiter only when .Ic \&cr is off. .It Ic delete Ar remote-file Delete the file .Ar remote-file on the remote machine. .It Ic debug Op Ar debug-value Toggle debugging mode. If an optional .Ar debug-value is specified, it is used to set the debugging level. When debugging is on, .Nm prints each command sent to the remote machine, preceded by the string .Ql \-\-> .It Ic dir Op Ar remote-directory Op Ar local-file Print a listing of the contents of a directory on the remote machine. The listing includes any system-dependent information that the server chooses to include; for example, most .Ux systems will produce output from the command .Ql ls \-l . (See also .Ic ls . ) If .Ar remote-directory is left unspecified, the current working directory is used. If interactive prompting is on, .Nm will prompt the user to verify that the last argument is indeed the target local file for receiving .Ic dir output. If no local file is specified, or if .Ar local-file is .Sq Fl , the output is sent to the terminal. .It Ic disconnect A synonym for .Ic close . .It Ic edit Toggle command line editing, and context sensitive command and file completion. This is automatically enabled if input is from a terminal, and disabled otherwise. .It Ic exit A synonym for .Ic bye . .It Ic ftp Ar host Op Ar port A synonym for .Ic open . .It Ic form Ar format Set the file transfer .Ic form to .Ar format . The default format is \*(Lqfile\*(Rq. .It Ic get Ar remote-file Op Ar local-file Retrieve the .Ar remote-file and store it on the local machine. If the local file name is not specified, it is given the same name it has on the remote machine, subject to alteration by the current .Ic case , .Ic ntrans and .Ic nmap settings. The current settings for .Ic type , .Ic form , .Ic mode and .Ic structure are used while transferring the file. .It Ic gate Op Ar host Op Ar port Toggle gate-ftp mode. This will not be permitted if the gate-ftp server hasn't been set (either explicitly by the user, or from the .Ev FTPSERVER environment variable). If .Ar host is given, then gate-ftp mode will be enabled, and the gate-ftp server will be set to .Ar host . If .Ar port is also given, that will be used as the port to connect to on the gate-ftp server. .It Ic glob Toggle filename expansion for .Ic mdelete , .Ic mget and .Ic mput . If globbing is turned off with .Ic glob , the file name arguments are taken literally and not expanded. Globbing for .Ic mput is done as in .Xr csh 1 . For .Ic mdelete and .Ic mget , each remote file name is expanded separately on the remote machine and the lists are not merged. Expansion of a directory name is likely to be different from expansion of the name of an ordinary file: the exact result depends on the foreign operating system and ftp server, and can be previewed by doing .Ql mls remote-files \- Note: .Ic mget and .Ic mput are not meant to transfer entire directory subtrees of files. That can be done by transferring a .Xr tar 1 archive of the subtree (in binary mode). .It Ic hash Op Ar size Toggle hash-sign (``#'') printing for each data block transferred. The size of a data block defaults to 1024 bytes. This can be changed by specifying .Ar size in bytes. .It Ic help Op Ar command Print an informative message about the meaning of .Ar command . If no argument is given, .Nm prints a list of the known commands. .It Ic idle Op Ar seconds Set the inactivity timer on the remote server to .Ar seconds seconds. If .Ar seconds is omitted, the current inactivity timer is printed. .It Ic lcd Op Ar directory Change the working directory on the local machine. If no .Ar directory is specified, the user's home directory is used. .It Ic less Ar file A synonym for .Ic page . .It Ic lpwd Print the working directory on the local machine. .It Ic \&ls Op Ar remote-directory Op Ar local-file Print a list of the files in a directory on the remote machine. If .Ar remote-directory is left unspecified, the current working directory is used. If interactive prompting is on, .Nm will prompt the user to verify that the last argument is indeed the target local file for receiving .Ic ls output. If no local file is specified, or if .Ar local-file is .Fl , the output is sent to the terminal. .It Ic macdef Ar macro-name Define a macro. Subsequent lines are stored as the macro .Ar macro-name ; a null line (consecutive newline characters in a file or carriage returns from the terminal) terminates macro input mode. There is a limit of 16 macros and 4096 total characters in all defined macros. Macros remain defined until a .Ic close command is executed. The macro processor interprets `$' and `\e' as special characters. A `$' followed by a number (or numbers) is replaced by the corresponding argument on the macro invocation command line. A `$' followed by an `i' signals that macro processor that the executing macro is to be looped. On the first pass `$i' is replaced by the first argument on the macro invocation command line, on the second pass it is replaced by the second argument, and so on. A `\e' followed by any character is replaced by that character. Use the `\e' to prevent special treatment of the `$'. .It Ic mdelete Op Ar remote-files Delete the .Ar remote-files on the remote machine. .It Ic mdir Ar remote-files local-file Like .Ic dir , except multiple remote files may be specified. If interactive prompting is on, .Nm will prompt the user to verify that the last argument is indeed the target local file for receiving .Ic mdir output. .It Ic mget Ar remote-files Expand the .Ar remote-files on the remote machine and do a .Ic get for each file name thus produced. See .Ic glob for details on the filename expansion. Resulting file names will then be processed according to .Ic case , .Ic ntrans and .Ic nmap settings. Files are transferred into the local working directory, which can be changed with .Ql lcd directory ; new local directories can be created with .Ql "\&! mkdir directory" . .It Ic mkdir Ar directory-name Make a directory on the remote machine. .It Ic mls Ar remote-files local-file Like .Ic ls , except multiple remote files may be specified, and the .Ar local-file must be specified. If interactive prompting is on, .Nm will prompt the user to verify that the last argument is indeed the target local file for receiving .Ic mls output. .It Ic mode Op Ar mode-name Set the file transfer .Ic mode to .Ar mode-name . The default mode is \*(Lqstream\*(Rq mode. .It Ic modtime Ar file-name Show the last modification time of the file on the remote machine. .It Ic more Ar file A synonym for .Ic page . .It Ic mput Ar local-files Expand wild cards in the list of local files given as arguments and do a .Ic put for each file in the resulting list. See .Ic glob for details of filename expansion. Resulting file names will then be processed according to .Ic ntrans and .Ic nmap settings. .It Ic msend Ar local-files A synonym for .Ic mput . .It Ic newer Ar file-name Get the file only if the modification time of the remote file is more recent that the file on the current system. If the file does not exist on the current system, the remote file is considered .Ic newer . Otherwise, this command is identical to .Ar get . .It Ic nlist Op Ar remote-directory Op Ar local-file A synonym for .Ic ls . .It Ic nmap Op Ar inpattern outpattern Set or unset the filename mapping mechanism. If no arguments are specified, the filename mapping mechanism is unset. If arguments are specified, remote filenames are mapped during .Ic mput commands and .Ic put commands issued without a specified remote target filename. If arguments are specified, local filenames are mapped during .Ic mget commands and .Ic get commands issued without a specified local target filename. This command is useful when connecting to a .No non\- Ns Ux remote computer with different file naming conventions or practices. The mapping follows the pattern set by .Ar inpattern and .Ar outpattern . .Op Ar Inpattern is a template for incoming filenames (which may have already been processed according to the .Ic ntrans and .Ic case settings). Variable templating is accomplished by including the sequences `$1', `$2', ..., `$9' in .Ar inpattern . Use `\\' to prevent this special treatment of the `$' character. All other characters are treated literally, and are used to determine the .Ic nmap .Op Ar inpattern variable values. For example, given .Ar inpattern $1.$2 and the remote file name "mydata.data", $1 would have the value "mydata", and $2 would have the value "data". The .Ar outpattern determines the resulting mapped filename. The sequences `$1', `$2', ...., `$9' are replaced by any value resulting from the .Ar inpattern template. The sequence `$0' is replace by the original filename. Additionally, the sequence .Ql Op Ar seq1 , Ar seq2 is replaced by .Op Ar seq1 if .Ar seq1 is not a null string; otherwise it is replaced by .Ar seq2 . For example, the command .Pp .Bd -literal -offset indent -compact nmap $1.$2.$3 [$1,$2].[$2,file] .Ed .Pp would yield the output filename "myfile.data" for input filenames "myfile.data" and "myfile.data.old", "myfile.file" for the input filename "myfile", and "myfile.myfile" for the input filename ".myfile". Spaces may be included in .Ar outpattern , as in the example: `nmap $1 sed "s/ *$//" > $1' . Use the `\e' character to prevent special treatment of the `$','[',']' and `,' characters. .It Ic ntrans Op Ar inchars Op Ar outchars Set or unset the filename character translation mechanism. If no arguments are specified, the filename character translation mechanism is unset. If arguments are specified, characters in remote filenames are translated during .Ic mput commands and .Ic put commands issued without a specified remote target filename. If arguments are specified, characters in local filenames are translated during .Ic mget commands and .Ic get commands issued without a specified local target filename. This command is useful when connecting to a .No non\- Ns Ux remote computer with different file naming conventions or practices. Characters in a filename matching a character in .Ar inchars are replaced with the corresponding character in .Ar outchars . If the character's position in .Ar inchars is longer than the length of .Ar outchars , the character is deleted from the file name. .It Ic open Ar host Op Ar port Establish a connection to the specified .Ar host .Tn FTP server. An optional port number may be supplied, in which case, .Nm will attempt to contact an .Tn FTP server at that port. If the .Ic auto-login option is on (default), .Nm will also attempt to automatically log the user in to the .Tn FTP server (see below). .It Ic page Ar file Retrieve .Ic file and display with the program defined in .Ev PAGER (which defaults to .Xr less 1 ). .It Ic passive Toggle passive mode. If passive mode is turned on (default is off), the ftp client will send a .Dv PASV command for all data connections instead of the usual .Dv PORT command. The .Dv PASV command requests that the remote server open a port for the data connection and return the address of that port. The remote server listens on that port and the client connects to it. When using the more traditional .Dv PORT command, the client listens on a port and sends that address to the remote server, who connects back to it. Passive mode is useful when using .Nm through a gateway router or host that controls the directionality of traffic. (Note that though ftp servers are required to support the .Dv PASV -command by RFC 1123, some do not.) +command by RFC 1123, some do not. +Please note that if you are connecting to IPv6 ftp server, +the program will use +.Dv EPSV/EPRT +pair and +.Dv LPSV/LPRT +pair, +instead of +.Dv PASV +and +.Dv PORT . +The meaning is the same.) .It Ic preserve Toggle preservation of modification times on retrieved files. .It Ic progress Toggle display of transfer progress bar. The progress bar will be disabled for a transfer that has .Ar local-file as .Sq Fl or a command that starts with .Sq \&| . Refer to .Sx FILE NAMING CONVENTIONS for more information. .It Ic prompt Toggle interactive prompting. Interactive prompting occurs during multiple file transfers to allow the user to selectively retrieve or store files. If prompting is turned off (default is on), any .Ic mget or .Ic mput will transfer all files, and any .Ic mdelete will delete all files. .Pp When prompting is on, the following commands are available at a prompt: .Bl -tag -width 2n -offset indent .It Ic n Do not transfer the file. .It Ic a Answer .Sq yes to the current file, and automatically answer .Sq yes to any remaining files for the current command. .It Ic p Answer .Sq yes to the current file, and turn off prompt mode (as if .Dq prompt off had been given). .El .Pp Any other reponse will answer .Sq yes to the current file. .It Ic proxy Ar ftp-command Execute an ftp command on a secondary control connection. This command allows simultaneous connection to two remote ftp servers for transferring files between the two servers. The first .Ic proxy command should be an .Ic open , to establish the secondary control connection. Enter the command "proxy ?" to see other ftp commands executable on the secondary connection. The following commands behave differently when prefaced by .Ic proxy : .Ic open will not define new macros during the auto-login process, .Ic close will not erase existing macro definitions, .Ic get and .Ic mget transfer files from the host on the primary control connection to the host on the secondary control connection, and .Ic put , .Ic mput and .Ic append transfer files from the host on the secondary control connection to the host on the primary control connection. Third party file transfers depend upon support of the ftp protocol .Dv PASV command by the server on the secondary control connection. .It Ic put Ar local-file Op Ar remote-file Store a local file on the remote machine. If .Ar remote-file is left unspecified, the local file name is used after processing according to any .Ic ntrans or .Ic nmap settings in naming the remote file. File transfer uses the current settings for .Ic type , .Ic format , .Ic mode and .Ic structure . .It Ic pwd Print the name of the current working directory on the remote machine. .It Ic quit A synonym for .Ic bye . .It Ic quote Ar arg1 arg2 ... The arguments specified are sent, verbatim, to the remote .Tn FTP server. .It Ic recv Ar remote-file Op Ar local-file A synonym for .Ic get . .It Ic reget Ar remote-file Op Ar local-file Reget acts like get, except that if .Ar local-file exists and is smaller than .Ar remote-file , .Ar local-file is presumed to be a partially transferred copy of .Ar remote-file and the transfer is continued from the apparent point of failure. This command is useful when transferring very large files over networks that are prone to dropping connections. .It Ic remotehelp Op Ar command-name Request help from the remote .Tn FTP server. If a .Ar command-name is specified it is supplied to the server as well. .It Ic rstatus Op Ar file-name With no arguments, show status of remote machine. If .Ar file-name is specified, show status of .Ar file-name on remote machine. .It Ic rename Op Ar from Op Ar to Rename the file .Ar from on the remote machine, to the file .Ar to . .It Ic reset Clear reply queue. This command re-synchronizes command/reply sequencing with the remote ftp server. Resynchronization may be necessary following a violation of the ftp protocol by the remote server. .It Ic restart Ar marker Restart the immediately following .Ic get or .Ic put at the indicated .Ar marker . On .Ux systems, marker is usually a byte offset into the file. .It Ic restrict Toggle data port range restrictions. When not operating in passive mode, the .Nm client program requests that the remote server open a connection back to the client host on a separate data port. In previous versions, that remote port fell in the range 1024..4999. However, most firewall setups filter that range of TCP ports because other services reside there. The default behavior now is for the client to request that the server connect back to the client using the port range 49152..65535. Firewall administrators can chose to allow TCP connections in that range, if they deem it to not be a security risk. .It Ic rmdir Ar directory-name Delete a directory on the remote machine. .It Ic runique Toggle storing of files on the local system with unique filenames. If a file already exists with a name equal to the target local filename for a .Ic get or .Ic mget command, a ".1" is appended to the name. If the resulting name matches another existing file, a ".2" is appended to the original name. If this process continues up to ".99", an error message is printed, and the transfer does not take place. The generated unique filename will be reported. Note that .Ic runique will not affect local files generated from a shell command (see below). The default value is off. .It Ic send Ar local-file Op Ar remote-file A synonym for .Ic put . .It Ic sendport Toggle the use of .Dv PORT commands. By default, .Nm will attempt to use a .Dv PORT command when establishing a connection for each data transfer. The use of .Dv PORT commands can prevent delays when performing multiple file transfers. If the .Dv PORT command fails, .Nm will use the default data port. When the use of .Dv PORT commands is disabled, no attempt will be made to use .Dv PORT commands for each data transfer. This is useful for certain .Tn FTP implementations which do ignore .Dv PORT commands but, incorrectly, indicate they've been accepted. .It Ic site Ar arg1 arg2 ... The arguments specified are sent, verbatim, to the remote .Tn FTP server as a .Dv SITE command. .It Ic size Ar file-name Return size of .Ar file-name on remote machine. .It Ic status Show the current status of .Nm ftp . .It Ic struct Op Ar struct-name Set the file transfer .Ar structure to .Ar struct-name . By default \*(Lqstream\*(Rq structure is used. .It Ic sunique Toggle storing of files on remote machine under unique file names. Remote ftp server must support ftp protocol .Dv STOU command for successful completion. The remote server will report unique name. Default value is off. .It Ic system Show the type of operating system running on the remote machine. .It Ic tenex Set the file transfer type to that needed to talk to .Tn TENEX machines. .It Ic trace Toggle packet tracing. .It Ic type Op Ar type-name Set the file transfer .Ic type to .Ar type-name . If no type is specified, the current type is printed. The default type is network .Tn ASCII . .It Ic umask Op Ar newmask Set the default umask on the remote server to .Ar newmask . If .Ar newmask is omitted, the current umask is printed. .It Xo .Ic user Ar user-name .Op Ar password Op Ar account .Xc Identify yourself to the remote .Tn FTP server. If the .Ar password is not specified and the server requires it, .Nm will prompt the user for it (after disabling local echo). If an .Ar account field is not specified, and the .Tn FTP server requires it, the user will be prompted for it. If an .Ar account field is specified, an account command will be relayed to the remote server after the login sequence is completed if the remote server did not require it for logging in. Unless .Nm is invoked with \*(Lqauto-login\*(Rq disabled, this process is done automatically on initial connection to the .Tn FTP server. .It Ic verbose Toggle verbose mode. In verbose mode, all responses from the .Tn FTP server are displayed to the user. In addition, if verbose is on, when a file transfer completes, statistics regarding the efficiency of the transfer are reported. By default, verbose is on. .It Ic ? Op Ar command A synonym for .Ic help . .El .Pp Command arguments which have embedded spaces may be quoted with quote `"' marks. .Pp Commands which toggle settings can take an explicit .Ic on or .Ic off argument to force the setting appropriately. .Pp If .Nm receives a .Dv SIGINFO (see the .Dq status argument of .Xr stty 1 ) signal whilst a transfer is in progress, the current transfer rate statistics will be written to the standard error output, in the same format as the standard completion message. .Sh AUTO-FETCHING FILES In addition to standard commands, this version of .Nm supports an auto-fetch feature. To enable auto-fetch, simply pass the list of hostnames/files on the command line. .Pp The following formats are valid syntax for an auto-fetch element: .Bl -tag -width "host:/file" .It host:/file .Dq Classic ftp format .It ftp://[user:password@]host[:port]/file An ftp URL, retrieved using the ftp protocol if .Ev ftp_proxy isn't defined. Otherwise, transfer using http via the proxy defined in .Ev ftp_proxy . If .Ar user:password@ is given and .Ev ftp_proxy isn't defined, login as .Ar user with a password of .Ar password . .It http://host[:port]/file An HTTP URL, retrieved using the http protocol. If .Ev http_proxy is defined, it is used as a URL to an HTTP proxy server. .El .Pp If a classic format or a ftp URL format has a trailing .Sq / , then .Nm will connect to the site and .Ic cd to the directory given as the path, and leave the user in interactive mode ready for further input. .Pp If successive auto-fetch ftp elements refer to the same host, then the connection is maintained between transfers, reducing overhead on connection creation and deletion. .Pp If .Ic file contains a glob character and globbing is enabled, (see .Ic glob ) , then the equivalent of .Ic "mget file" is performed. .Pp If the directory component of .Ic file contains no globbing characters, it is stored in the current directory as the .Xr basename 1 of .Ic file . Otherwise, the remote name is used as the local name. .Sh ABORTING A FILE TRANSFER To abort a file transfer, use the terminal interrupt key (usually Ctrl-C). Sending transfers will be immediately halted. Receiving transfers will be halted by sending a ftp protocol .Dv ABOR command to the remote server, and discarding any further data received. The speed at which this is accomplished depends upon the remote server's support for .Dv ABOR processing. If the remote server does not support the .Dv ABOR command, an .Ql ftp> prompt will not appear until the remote server has completed sending the requested file. .Pp The terminal interrupt key sequence will be ignored when .Nm has completed any local processing and is awaiting a reply from the remote server. A long delay in this mode may result from the ABOR processing described above, or from unexpected behavior by the remote server, including violations of the ftp protocol. If the delay results from unexpected remote server behavior, the local .Nm program must be killed by hand. .Sh FILE NAMING CONVENTIONS Files specified as arguments to .Nm commands are processed according to the following rules. .Bl -enum .It If the file name .Sq Fl is specified, the .Ar stdin (for reading) or .Ar stdout (for writing) is used. .It If the first character of the file name is .Sq \&| , the remainder of the argument is interpreted as a shell command. .Nm then forks a shell, using .Xr popen 3 with the argument supplied, and reads (writes) from the stdin (stdout). If the shell command includes spaces, the argument must be quoted; e.g. \*(Lq" ls -lt"\*(Rq. A particularly useful example of this mechanism is: \*(Lqdir \&|more\*(Rq. .It Failing the above checks, if .Dq globbing is enabled, local file names are expanded according to the rules used in the .Xr csh 1 ; c.f. the .Ic glob command. If the .Nm command expects a single local file (e.g. .Ic put ) , only the first filename generated by the "globbing" operation is used. .It For .Ic mget commands and .Ic get commands with unspecified local file names, the local filename is the remote filename, which may be altered by a .Ic case , .Ic ntrans , or .Ic nmap setting. The resulting filename may then be altered if .Ic runique is on. .It For .Ic mput commands and .Ic put commands with unspecified remote file names, the remote filename is the local filename, which may be altered by a .Ic ntrans or .Ic nmap setting. The resulting filename may then be altered by the remote server if .Ic sunique is on. .El .Sh FILE TRANSFER PARAMETERS The FTP specification specifies many parameters which may affect a file transfer. The .Ic type may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary), \*(Lqebcdic\*(Rq and \*(Lqlocal byte size\*(Rq (for .Tn PDP Ns -10's and .Tn PDP Ns -20's mostly). .Nm supports the ascii and image types of file transfer, plus local byte size 8 for .Ic tenex mode transfers. .Pp .Nm supports only the default values for the remaining file transfer parameters: .Ic mode , .Ic form and .Ic struct . .Sh THE .netrc FILE The .Pa .netrc file contains login and initialization information used by the auto-login process. It resides in the user's home directory. The following tokens are recognized; they may be separated by spaces, tabs, or new-lines: .Bl -tag -width password .It Ic machine Ar name Identify a remote machine .Ar name . The auto-login process searches the .Pa .netrc file for a .Ic machine token that matches the remote machine specified on the .Nm command line or as an .Ic open command argument. Once a match is made, the subsequent .Pa .netrc tokens are processed, stopping when the end of file is reached or another .Ic machine or a .Ic default token is encountered. .It Ic default This is the same as .Ic machine .Ar name except that .Ic default matches any name. There can be only one .Ic default token, and it must be after all .Ic machine tokens. This is normally used as: .Pp .Dl default login anonymous password user@site .Pp thereby giving the user .Ar automatic anonymous ftp login to machines not specified in .Pa .netrc . This can be overridden by using the .Fl n flag to disable auto-login. .It Ic login Ar name Identify a user on the remote machine. If this token is present, the auto-login process will initiate a login using the specified .Ar name . .It Ic password Ar string Supply a password. If this token is present, the auto-login process will supply the specified string if the remote server requires a password as part of the login process. Note that if this token is present in the .Pa .netrc file for any user other than .Ar anonymous , .Nm will abort the auto-login process if the .Pa .netrc is readable by anyone besides the user. .It Ic account Ar string Supply an additional account password. If this token is present, the auto-login process will supply the specified string if the remote server requires an additional account password, or the auto-login process will initiate an .Dv ACCT command if it does not. .It Ic macdef Ar name Define a macro. This token functions like the .Nm .Ic macdef command functions. A macro is defined with the specified name; its contents begin with the next .Pa .netrc line and continue until a null line (consecutive new-line characters) is encountered. If a macro named .Ic init is defined, it is automatically executed as the last step in the auto-login process. .El .Sh COMMAND LINE EDITING .Nm supports interactive command line editing, via the .Xr editline 3 library. It is enabled with the .Ic edit command, and is enabled by default if input is from a tty. Previous lines can be recalled and edited with the arrow keys, and other GNU Emacs-style editing keys may be used as well. .Pp The .Xr editline 3 library is configured with a .Pa .editrc file - refer to .Xr editrc 5 for more information. .Pp An extra key binding is available to .Nm to provide context sensitive command and filename completion (including remote file completion). To use this, bind a key to the .Xr editline 3 command .Ic ftp-complete . By default, this is bound to the TAB key. .Sh ENVIRONMENT .Nm utilizes the following environment variables. .Bl -tag -width "FTP_PASSIVE_MODE" .It Ev FTP_PASSIVE_MODE If this variable is set to something else than .Sq NO , .Nm will use passive mode by default. .It Ev FTPSERVER Host to use as gate-ftp server when .Ic gate is enabled. .It Ev FTPSERVERPORT Port to use when connecting to gate-ftp server when .Ic gate is enabled. Default is port returned by a .Fn getservbyname lookup of .Dq ftpgate/tcp . .It Ev HOME For default location of a .Pa .netrc file, if one exists. .It Ev PAGER Used by .Ic page to display files. .It Ev SHELL For default shell. .It Ev ftp_proxy URL of FTP proxy to use when making FTP URL requests (if not defined, use the standard ftp protocol). .It Ev http_proxy URL of HTTP proxy to use when making HTTP URL requests. .El .Sh SEE ALSO .Xr getservbyname 3 , .Xr editrc 5 , .Xr services 5 , .Xr ftpd 8 .Sh NOTES The .Xr pftp 1 and .Xr gate-ftp 1 commands are links to .Nm ftp . .Sh HISTORY The .Nm command appeared in .Bx 4.2 . .Pp Various features such as command line editing, context sensitive command and file completion, dynamic progress bar, automatic fetching of files, ftp and http URLs, and modification time preservation were implemented in .Nx 1.3 by Luke Mewburn, with assistance from Jason Thorpe. +.Pp +IPv6 support was added by WIDE/KAME Project. .Sh BUGS Correct execution of many commands depends upon proper behavior by the remote server. .Pp An error in the treatment of carriage returns in the .Bx 4.2 ascii-mode transfer code has been corrected. This correction may result in incorrect transfers of binary files to and from .Bx 4.2 servers using the ascii type. Avoid this problem by using the binary image type. +.Pp +Proxying functionalities, such as +.Ev ftp_proxy , +may not work for IPv6 connection. diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index d2edd014d54b..3c7da3feb2cf 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -1,1610 +1,1876 @@ /* $FreeBSD$ */ /* $NetBSD: ftp.c,v 1.29.2.1 1997/11/18 01:01:04 mellon Exp $ */ /* * Copyright (c) 1985, 1989, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #include #ifndef lint #if 0 static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; #else __RCSID("$FreeBSD$"); __RCSID_SOURCE("$NetBSD: ftp.c,v 1.29.2.1 1997/11/18 01:01:04 mellon Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __STDC__ #include #else #include #endif #include "ftp_var.h" -struct sockaddr_in hisctladdr; -struct sockaddr_in data_addr; +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + +extern int h_errno; + int data = -1; int abrtflag = 0; jmp_buf ptabort; int ptabflg; int ptflag = 0; -struct sockaddr_in myctladdr; - FILE *cin, *cout; +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port + +union sockunion myctladdr, hisctladdr, data_addr; + char * hookup(host, port) const char *host; - int port; + char *port; { - struct hostent *hp = NULL; - int s, len, tos; + int s, len, tos, error; + struct addrinfo hints, *res, *res0; static char hostnamebuf[MAXHOSTNAMELEN]; - memset((void *)&hisctladdr, 0, sizeof(hisctladdr)); - if (inet_aton(host, &hisctladdr.sin_addr) != 0) { - hisctladdr.sin_family = AF_INET; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("%s: %s", host, gai_strerror(error)); + if (error == EAI_SYSTEM) + warnx("%s: %s", host, strerror(errno)); + code = -1; + return (0); + } + + res = res0; + if (res->ai_canonname) + hostname = res->ai_canonname; + else { (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf)); - } else { - hp = gethostbyname(host); - if (hp == NULL) { - warnx("%s: %s", host, hstrerror(h_errno)); - code = -1; - return ((char *) 0); - } - hisctladdr.sin_family = hp->h_addrtype; - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(hisctladdr.sin_addr))); - (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf)); + hostname = hostnamebuf; } - hostnamebuf[sizeof(hostnamebuf) - 1] = '\0'; - hostname = hostnamebuf; - hisctladdr.sin_port = port; while (1) { - if ((s = socket(hisctladdr.sin_family, SOCK_STREAM, 0)) == -1) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s < 0) { warn("socket"); code = -1; return (0); } - if (dobind && bind(s, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { + if (dobind && + bind(s, (struct sockaddr *)&bindto, + ((struct sockaddr *)&bindto)->sa_len) == -1) { warn("bind"); - code = -1; - goto bad; + goto next; } - if (connect(s, (struct sockaddr *)&hisctladdr, - sizeof(hisctladdr)) == 0) + if (connect(s, res->ai_addr, res->ai_addrlen) == 0) break; - if (hp && *++hp->h_addr_list) { - warnc(errno, "connect to address %s", - inet_ntoa(hisctladdr.sin_addr)); - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(hisctladdr.sin_addr))); - printf("Trying %s...\n", - inet_ntoa(hisctladdr.sin_addr)); + next: + if (res->ai_next) { + char hname[INET6_ADDRSTRLEN]; + getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + warn("connect to address %s", hname); + res = res->ai_next; + getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + printf("Trying %s...\n", hname); (void)close(s); continue; } warn("connect"); code = -1; goto bad; } + memcpy(&hisctladdr, res->ai_addr, res->ai_addrlen); len = sizeof(myctladdr); if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) { warn("getsockname"); code = -1; goto bad; } #ifdef IP_TOS - tos = IPTOS_LOWDELAY; + if (myctladdr.su_family == AF_INET) + { + tos = IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif cin = fdopen(s, "r"); cout = fdopen(s, "w"); if (cin == NULL || cout == NULL) { warnx("fdopen failed."); if (cin) (void)fclose(cin); if (cout) (void)fclose(cout); code = -1; goto bad; } if (verbose) printf("Connected to %s.\n", hostname); if (getreply(0) > 2) { /* read startup message from server */ if (cin) (void)fclose(cin); if (cout) (void)fclose(cout); code = -1; goto bad; } #ifdef SO_OOBINLINE { int on = 1; if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0 && debug) { warn("setsockopt"); } } #endif /* SO_OOBINLINE */ return (hostname); bad: (void)close(s); return ((char *)0); } void cmdabort(notused) int notused; { alarmtimer(0); putchar('\n'); (void)fflush(stdout); abrtflag++; if (ptflag) longjmp(ptabort, 1); } /*VARARGS*/ int #ifdef __STDC__ command(const char *fmt, ...) #else command(va_alist) va_dcl #endif { va_list ap; int r; sig_t oldintr; #ifndef __STDC__ const char *fmt; #endif abrtflag = 0; if (debug) { fputs("---> ", stdout); #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); fmt = va_arg(ap, const char *); #endif if (strncmp("PASS ", fmt, 5) == 0) fputs("PASS XXXX", stdout); else if (strncmp("ACCT ", fmt, 5) == 0) fputs("ACCT XXXX", stdout); else vprintf(fmt, ap); va_end(ap); putchar('\n'); (void)fflush(stdout); } if (cout == NULL) { warnx("No control connection for command."); code = -1; return (0); } oldintr = signal(SIGINT, cmdabort); #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); fmt = va_arg(ap, char *); #endif vfprintf(cout, fmt, ap); va_end(ap); fputs("\r\n", cout); (void)fflush(cout); cpend = 1; r = getreply(!strcmp(fmt, "QUIT")); if (abrtflag && oldintr != SIG_IGN) (*oldintr)(SIGINT); (void)signal(SIGINT, oldintr); return (r); } char reply_string[BUFSIZ]; /* first line of previous reply */ int getreply(expecteof) int expecteof; { char current_line[BUFSIZ]; /* last line of previous reply */ int c, n, line; int dig; int originalcode = 0, continuation = 0; sig_t oldintr; int pflag = 0; char *cp, *pt = pasv; oldintr = signal(SIGINT, cmdabort); for (line = 0 ;; line++) { dig = n = code = 0; cp = current_line; while ((c = getc(cin)) != '\n') { if (c == IAC) { /* handle telnet commands */ switch (c = getc(cin)) { case WILL: case WONT: c = getc(cin); fprintf(cout, "%c%c%c", IAC, DONT, c); (void)fflush(cout); break; case DO: case DONT: c = getc(cin); fprintf(cout, "%c%c%c", IAC, WONT, c); (void)fflush(cout); break; default: break; } continue; } dig++; if (c == EOF) { if (expecteof) { (void)signal(SIGINT, oldintr); code = 221; return (0); } lostpeer(); if (verbose) { puts( "421 Service not available, remote server has closed connection."); (void)fflush(stdout); } code = 421; return (4); } if (c != '\r' && (verbose > 0 || (verbose > -1 && n == '5' && dig > 4))) { if (proxflag && (dig == 1 || (dig == 5 && verbose == 0))) printf("%s:", hostname); (void)putchar(c); } if (dig < 4 && isdigit((unsigned char)c)) code = code * 10 + (c - '0'); - if (!pflag && code == 227) - pflag = 1; - if (dig > 4 && pflag == 1 && isdigit((unsigned char)c)) + switch (pflag) { + case 0: + if (code == 227 || code == 228) { + /* result for PASV/LPSV */ + pflag = 1; + /* fall through */ + } else if (code == 229) { + /* result for EPSV */ + pflag = 1; + pflag = 100; + break; + } else + break; + case 1: + if (!(dig > 4 && isdigit((unsigned char)c))) + break; pflag = 2; - if (pflag == 2) { + /* fall through */ + case 2: if (c != '\r' && c != ')' && pt < &pasv[sizeof(pasv)-1]) *pt++ = c; else { *pt = '\0'; pflag = 3; } + break; + case 100: + if (dig > 4 && c == '(') + pflag = 2; + break; } if (dig == 4 && c == '-') { if (continuation) code = 0; continuation++; } if (n == 0) n = c; if (cp < ¤t_line[sizeof(current_line) - 1]) *cp++ = c; } if (verbose > 0 || (verbose > -1 && n == '5')) { (void)putchar(c); (void)fflush (stdout); } if (line == 0) { size_t len = cp - current_line; if (len > sizeof(reply_string)) len = sizeof(reply_string); (void)strncpy(reply_string, current_line, len); reply_string[len] = '\0'; } if (continuation && code != originalcode) { if (originalcode == 0) originalcode = code; continue; } *cp = '\0'; if (n != '1') cpend = 0; (void)signal(SIGINT, oldintr); if (code == 421 || originalcode == 421) lostpeer(); if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN) (*oldintr)(SIGINT); return (n - '0'); } } int empty(mask, sec) fd_set *mask; int sec; { struct timeval t; t.tv_sec = (long) sec; t.tv_usec = 0; return (select(32, mask, (fd_set *) 0, (fd_set *) 0, &t)); } jmp_buf sendabort; void abortsend(notused) int notused; { alarmtimer(0); mflag = 0; abrtflag = 0; puts("\nsend aborted\nwaiting for remote to finish abort."); (void)fflush(stdout); longjmp(sendabort, 1); } void sendrequest(cmd, local, remote, printnames) const char *cmd, *local, *remote; int printnames; { struct stat st; int c, d; FILE *fin, *dout; int (*closefunc) __P((FILE *)); sig_t oldinti, oldintr, oldintp; volatile off_t hashbytes; char *lmode, buf[BUFSIZ], *bufp; int oprogress; #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&fin; (void)&dout; (void)&closefunc; (void)&oldinti; (void)&oldintr; (void)&oldintp; (void)&lmode; #endif hashbytes = mark; direction = "sent"; dout = NULL; bytes = 0; filesize = -1; oprogress = progress; if (verbose && printnames) { if (local && *local != '-') printf("local: %s ", local); if (remote) printf("remote: %s\n", remote); } if (proxy) { proxtrans(cmd, local, remote); return; } if (curtype != type) changetype(type, 0); closefunc = NULL; oldintr = NULL; oldintp = NULL; oldinti = NULL; lmode = "w"; if (setjmp(sendabort)) { while (cpend) { (void)getreply(0); } if (data >= 0) { (void)close(data); data = -1; } if (oldintr) (void)signal(SIGINT, oldintr); if (oldintp) (void)signal(SIGPIPE, oldintp); if (oldinti) (void)signal(SIGINFO, oldinti); code = -1; goto cleanupsend; } oldintr = signal(SIGINT, abortsend); oldinti = signal(SIGINFO, psummary); if (strcmp(local, "-") == 0) { fin = stdin; progress = 0; } else if (*local == '|') { oldintp = signal(SIGPIPE, SIG_IGN); fin = popen(local + 1, "r"); if (fin == NULL) { warn("%s", local + 1); (void)signal(SIGINT, oldintr); (void)signal(SIGPIPE, oldintp); (void)signal(SIGINFO, oldinti); code = -1; goto cleanupsend; } progress = 0; closefunc = pclose; } else { fin = fopen(local, "r"); if (fin == NULL) { warn("local: %s", local); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; goto cleanupsend; } closefunc = fclose; if (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { printf("%s: not a plain file.\n", local); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); fclose(fin); code = -1; goto cleanupsend; } filesize = st.st_size; } if (initconn()) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); code = -1; if (closefunc != NULL) (*closefunc)(fin); goto cleanupsend; } if (setjmp(sendabort)) goto abort; if (restart_point && (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { int rc; rc = -1; switch (curtype) { case TYPE_A: rc = fseek(fin, (long) restart_point, SEEK_SET); break; case TYPE_I: case TYPE_L: rc = lseek(fileno(fin), restart_point, SEEK_SET); break; } if (rc < 0) { warn("local: %s", local); if (closefunc != NULL) (*closefunc)(fin); goto cleanupsend; } if (command("REST %qd", (long long) restart_point) != CONTINUE) { if (closefunc != NULL) (*closefunc)(fin); goto cleanupsend; } lmode = "r+w"; } if (remote) { if (command("%s %s", cmd, remote) != PRELIM) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); if (closefunc != NULL) (*closefunc)(fin); goto cleanupsend; } } else if (command("%s", cmd) != PRELIM) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); if (closefunc != NULL) (*closefunc)(fin); goto cleanupsend; } dout = dataconn(lmode); if (dout == NULL) goto abort; progressmeter(-1); oldintp = signal(SIGPIPE, SIG_IGN); switch (curtype) { case TYPE_I: case TYPE_L: errno = d = 0; while ((c = read(fileno(fin), buf, sizeof(buf))) > 0) { bytes += c; for (bufp = buf; c > 0; c -= d, bufp += d) if ((d = write(fileno(dout), bufp, c)) <= 0) break; if (hash && (!progress || filesize < 0) ) { while (bytes >= hashbytes) { (void)putchar('#'); hashbytes += mark; } (void)fflush(stdout); } } if (hash && (!progress || filesize < 0) && bytes > 0) { if (bytes < mark) (void)putchar('#'); (void)putchar('\n'); (void)fflush(stdout); } if (c < 0) warn("local: %s", local); if (d < 0) { if (errno != EPIPE) warn("netout"); bytes = -1; } break; case TYPE_A: while ((c = getc(fin)) != EOF) { if (c == '\n') { while (hash && (!progress || filesize < 0) && (bytes >= hashbytes)) { (void)putchar('#'); (void)fflush(stdout); hashbytes += mark; } if (ferror(dout)) break; (void)putc('\r', dout); bytes++; } (void)putc(c, dout); bytes++; #if 0 /* this violates RFC */ if (c == '\r') { (void)putc('\0', dout); bytes++; } #endif } if (hash && (!progress || filesize < 0)) { if (bytes < hashbytes) (void)putchar('#'); (void)putchar('\n'); (void)fflush(stdout); } if (ferror(fin)) warn("local: %s", local); if (ferror(dout)) { if (errno != EPIPE) warn("netout"); bytes = -1; } break; } progressmeter(1); if (closefunc != NULL) (*closefunc)(fin); (void)fclose(dout); (void)getreply(0); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); if (bytes > 0) ptransfer(0); goto cleanupsend; abort: (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); if (!cpend) { code = -1; return; } if (data >= 0) { (void)close(data); data = -1; } if (dout) (void)fclose(dout); (void)getreply(0); code = -1; if (closefunc != NULL && fin != NULL) (*closefunc)(fin); if (bytes > 0) ptransfer(0); cleanupsend: progress = oprogress; restart_point = 0; } jmp_buf recvabort; void abortrecv(notused) int notused; { alarmtimer(0); mflag = 0; abrtflag = 0; puts("\nreceive aborted\nwaiting for remote to finish abort."); (void)fflush(stdout); longjmp(recvabort, 1); } void recvrequest(cmd, local, remote, lmode, printnames, ignorespecial) const char *cmd, *local, *remote, *lmode; int printnames, ignorespecial; { FILE *fout, *din; int (*closefunc) __P((FILE *)); sig_t oldinti, oldintr, oldintp; int c, d; volatile int is_retr, tcrflag, bare_lfs; static size_t bufsize; static char *buf; volatile off_t hashbytes; struct stat st; time_t mtime; struct timeval tval[2]; int oprogress; int opreserve; #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&local; (void)&fout; (void)&din; (void)&closefunc; (void)&oldinti; (void)&oldintr; (void)&oldintp; #endif fout = NULL; din = NULL; oldinti = NULL; hashbytes = mark; direction = "received"; bytes = 0; bare_lfs = 0; filesize = -1; oprogress = progress; opreserve = preserve; is_retr = (strcmp(cmd, "RETR") == 0); if (is_retr && verbose && printnames) { if (local && (ignorespecial || *local != '-')) printf("local: %s ", local); if (remote) printf("remote: %s\n", remote); } if (proxy && is_retr) { proxtrans(cmd, local, remote); return; } closefunc = NULL; oldintr = NULL; oldintp = NULL; tcrflag = !crflag && is_retr; if (setjmp(recvabort)) { while (cpend) { (void)getreply(0); } if (data >= 0) { (void)close(data); data = -1; } if (oldintr) (void)signal(SIGINT, oldintr); if (oldinti) (void)signal(SIGINFO, oldinti); progress = oprogress; preserve = opreserve; code = -1; return; } oldintr = signal(SIGINT, abortrecv); oldinti = signal(SIGINFO, psummary); if (ignorespecial || (strcmp(local, "-") && *local != '|')) { if (access(local, W_OK) < 0) { char *dir = strrchr(local, '/'); if (errno != ENOENT && errno != EACCES) { warn("local: %s", local); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } if (dir != NULL) *dir = 0; d = access(dir == local ? "/" : dir ? local : ".", W_OK); if (dir != NULL) *dir = '/'; if (d < 0) { warn("local: %s", local); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } if (!runique && errno == EACCES && chmod(local, 0600) < 0) { warn("local: %s", local); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } if (runique && errno == EACCES && (local = gunique(local)) == NULL) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } } else if (runique && (local = gunique(local)) == NULL) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } } if (!is_retr) { if (curtype != TYPE_A) changetype(TYPE_A, 0); } else { if (curtype != type) changetype(type, 0); filesize = remotesize(remote, 0); } if (initconn()) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); code = -1; return; } if (setjmp(recvabort)) goto abort; if (is_retr && restart_point && command("REST %qd", (long long) restart_point) != CONTINUE) return; if (remote) { if (command("%s %s", cmd, remote) != PRELIM) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); return; } } else { if (command("%s", cmd) != PRELIM) { (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); return; } } din = dataconn("r"); if (din == NULL) goto abort; if (!ignorespecial && strcmp(local, "-") == 0) { fout = stdout; progress = 0; preserve = 0; } else if (!ignorespecial && *local == '|') { oldintp = signal(SIGPIPE, SIG_IGN); fout = popen(local + 1, "w"); if (fout == NULL) { warn("%s", local+1); goto abort; } progress = 0; preserve = 0; closefunc = pclose; } else { fout = fopen(local, lmode); if (fout == NULL) { warn("local: %s", local); goto abort; } closefunc = fclose; } if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0) st.st_blksize = BUFSIZ; if (st.st_blksize > bufsize) { if (buf) (void)free(buf); buf = malloc((unsigned)st.st_blksize); if (buf == NULL) { warn("malloc"); bufsize = 0; goto abort; } bufsize = st.st_blksize; } if (!S_ISREG(st.st_mode)) { progress = 0; preserve = 0; } progressmeter(-1); switch (curtype) { case TYPE_I: case TYPE_L: if (is_retr && restart_point && lseek(fileno(fout), restart_point, SEEK_SET) < 0) { warn("local: %s", local); progress = oprogress; preserve = opreserve; if (closefunc != NULL) (*closefunc)(fout); return; } errno = d = 0; while ((c = read(fileno(din), buf, bufsize)) > 0) { if ((d = write(fileno(fout), buf, c)) != c) break; bytes += c; if (hash && (!progress || filesize < 0)) { while (bytes >= hashbytes) { (void)putchar('#'); hashbytes += mark; } (void)fflush(stdout); } } if (hash && (!progress || filesize < 0) && bytes > 0) { if (bytes < mark) (void)putchar('#'); (void)putchar('\n'); (void)fflush(stdout); } if (c < 0) { if (errno != EPIPE) warn("netin"); bytes = -1; } if (d < c) { if (d < 0) warn("local: %s", local); else warnx("%s: short write", local); } break; case TYPE_A: if (is_retr && restart_point) { int ch; long i, n; if (fseek(fout, 0L, SEEK_SET) < 0) goto done; n = (long)restart_point; for (i = 0; i++ < n;) { if ((ch = getc(fout)) == EOF) goto done; if (ch == '\n') i++; } if (fseek(fout, 0L, SEEK_CUR) < 0) { done: warn("local: %s", local); progress = oprogress; preserve = opreserve; if (closefunc != NULL) (*closefunc)(fout); return; } } while ((c = getc(din)) != EOF) { if (c == '\n') bare_lfs++; while (c == '\r') { while (hash && (!progress || filesize < 0) && (bytes >= hashbytes)) { (void)putchar('#'); (void)fflush(stdout); hashbytes += mark; } bytes++; if ((c = getc(din)) != '\n' || tcrflag) { if (ferror(fout)) goto break2; (void)putc('\r', fout); if (c == '\0') { bytes++; goto contin2; } if (c == EOF) goto contin2; } } (void)putc(c, fout); bytes++; contin2: ; } break2: if (bare_lfs) { printf( "WARNING! %d bare linefeeds received in ASCII mode.\n", bare_lfs); puts("File may not have transferred correctly."); } if (hash && (!progress || filesize < 0)) { if (bytes < hashbytes) (void)putchar('#'); (void)putchar('\n'); (void)fflush(stdout); } if (ferror(din)) { if (errno != EPIPE) warn("netin"); bytes = -1; } if (ferror(fout)) warn("local: %s", local); break; } progressmeter(1); progress = oprogress; preserve = opreserve; if (closefunc != NULL) (*closefunc)(fout); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); if (oldintp) (void)signal(SIGPIPE, oldintp); (void)fclose(din); (void)getreply(0); if (bytes >= 0 && is_retr) { if (bytes > 0) ptransfer(0); if (preserve && (closefunc == fclose)) { mtime = remotemodtime(remote, 0); if (mtime != -1) { (void)gettimeofday(&tval[0], (struct timezone *)0); tval[1].tv_sec = mtime; tval[1].tv_usec = 0; if (utimes(local, tval) == -1) { printf( "Can't change modification time on %s to %s", local, asctime(localtime(&mtime))); } } } } return; abort: /* abort using RFC959 recommended IP,SYNC sequence */ progress = oprogress; preserve = opreserve; if (oldintp) (void)signal(SIGPIPE, oldintp); (void)signal(SIGINT, SIG_IGN); if (!cpend) { code = -1; (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); return; } abort_remote(din); code = -1; if (data >= 0) { (void)close(data); data = -1; } if (closefunc != NULL && fout != NULL) (*closefunc)(fout); if (din) (void)fclose(din); if (bytes > 0) ptransfer(0); (void)signal(SIGINT, oldintr); (void)signal(SIGINFO, oldinti); } /* * Need to start a listen on the data channel before we send the command, * otherwise the server's connect may fail. */ int initconn() { char *p, *a; int result, len, tmpno = 0; int on = 1; - int a0, a1, a2, a3, p0, p1; - int ports; + int error, ports; + u_int af; + u_int hal, h[16]; + u_int pal, prt[2]; + char *pasvcmd; + +#ifdef INET6 + if (myctladdr.su_family == AF_INET6 + && (IN6_IS_ADDR_LINKLOCAL(&myctladdr.su_sin6.sin6_addr) + || IN6_IS_ADDR_SITELOCAL(&myctladdr.su_sin6.sin6_addr))) { + warnx("use of scoped address can be troublesome"); + } +#endif if (passivemode) { - data = socket(AF_INET, SOCK_STREAM, 0); + data_addr = myctladdr; + data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { warn("socket"); return (1); } if (dobind && bind(data, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { + ((struct sockaddr *)&bindto)->sa_len) == -1) { warn("bind"); goto bad; } if ((options & SO_DEBUG) && setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0) warn("setsockopt (ignored)"); - if (command("PASV") != COMPLETE) { + switch (data_addr.su_family) { + case AF_INET: + result = command(pasvcmd = "EPSV"); + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + result = COMPLETE + 1; + } + if (result != COMPLETE) + result = command(pasvcmd = "PASV"); + break; +#ifdef INET6 + case AF_INET6: + result = command(pasvcmd = "EPSV"); + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + result = COMPLETE + 1; + } + if (result != COMPLETE) + result = command(pasvcmd = "LPSV"); + break; +#endif + default: + result = COMPLETE + 1; + } + if (result != COMPLETE) { puts("Passive mode refused."); goto bad; } +#define pack2(var, offset) \ + (((var[(offset) + 0] & 0xff) << 8) | ((var[(offset) + 1] & 0xff) << 0)) +#define pack4(var, offset) \ + (((var[(offset) + 0] & 0xff) << 24) | ((var[(offset) + 1] & 0xff) << 16) \ + | ((var[(offset) + 2] & 0xff) << 8) | ((var[(offset) + 3] & 0xff) << 0)) /* * What we've got at this point is a string of comma * separated one-byte unsigned integer values. + * In PASV case, * The first four are the an IP address. The fifth is * the MSB of the port number, the sixth is the LSB. * From that we'll prepare a sockaddr_in. + * In other case, the format is more complicated. */ + if (strcmp(pasvcmd, "PASV") == 0) { + if (code / 10 == 22 && code != 227) { + puts("wrong server: return code must be 227"); + error = 1; + goto bad; + } + error = sscanf(pasv, "%d,%d,%d,%d,%d,%d", + &h[0], &h[1], &h[2], &h[3], + &prt[0], &prt[1]); + if (error == 6) { + error = 0; + data_addr.su_sin.sin_addr.s_addr = + htonl(pack4(h, 0)); + } else + error = 1; + } else if (strcmp(pasvcmd, "LPSV") == 0) { + if (code / 10 == 22 && code != 228) { + puts("wrong server: return code must be 228"); + error = 1; + goto bad; + } + switch (data_addr.su_family) { + case AF_INET: + error = sscanf(pasv, +"%d,%d,%d,%d,%d,%d,%d,%d,%d", + &af, &hal, + &h[0], &h[1], &h[2], &h[3], + &pal, &prt[0], &prt[1]); + if (error == 9 && af == 4 && hal == 4 && pal == 2) { + error = 0; + data_addr.su_sin.sin_addr.s_addr = + htonl(pack4(h, 0)); + } else + error = 1; + break; +#ifdef INET6 + case AF_INET6: + error = sscanf(pasv, +"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &af, &hal, + &h[0], &h[1], &h[2], &h[3], + &h[4], &h[5], &h[6], &h[7], + &h[8], &h[9], &h[10], &h[11], + &h[12], &h[13], &h[14], &h[15], + &pal, &prt[0], &prt[1]); + if (error != 21 || af != 6 || hal != 16 || pal != 2) { + error = 1; + break; + } - if (sscanf(pasv, "%d,%d,%d,%d,%d,%d", - &a0, &a1, &a2, &a3, &p0, &p1) != 6) { + error = 0; + { + u_int32_t *p32; + p32 = (u_int32_t *)&data_addr.su_sin6.sin6_addr; + p32[0] = htonl(pack4(h, 0)); + p32[1] = htonl(pack4(h, 4)); + p32[2] = htonl(pack4(h, 8)); + p32[3] = htonl(pack4(h, 12)); + } + break; +#endif + default: + error = 1; + } + } else if (strcmp(pasvcmd, "EPSV") == 0) { + char delim[4]; + char *tcpport; + + prt[0] = 0; + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + error = 1; + goto bad; + } + error = sscanf(pasv, "%c%c%c%d%c", + &delim[0], &delim[1], &delim[2], + &prt[1], &delim[3]); + if (error != 5) { + error = 1; + goto epsv_done; + } + if (delim[0] != delim[1] || delim[0] != delim[2] + || delim[0] != delim[3]) { + error = 1; + goto epsv_done; + } + + data_addr = hisctladdr; + /* quickhack */ + prt[0] = (prt[1] & 0xff00) >> 8; + prt[1] &= 0xff; + error = 0; +epsv_done: + } else + error = 1; + + if (error) { puts( "Passive mode address scan failure. Shouldn't happen!"); goto bad; - } + }; - memset(&data_addr, 0, sizeof(data_addr)); - data_addr.sin_family = AF_INET; - a = (char *)&data_addr.sin_addr.s_addr; - a[0] = a0 & 0xff; - a[1] = a1 & 0xff; - a[2] = a2 & 0xff; - a[3] = a3 & 0xff; - p = (char *)&data_addr.sin_port; - p[0] = p0 & 0xff; - p[1] = p1 & 0xff; + data_addr.su_port = htons(pack2(prt, 0)); if (connect(data, (struct sockaddr *)&data_addr, - sizeof(data_addr)) < 0) { + data_addr.su_len) < 0) { warn("connect"); goto bad; } #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (0); } noport: data_addr = myctladdr; if (sendport) - data_addr.sin_port = 0; /* let system pick one */ + data_addr.su_port = 0; /* let system pick one */ if (data != -1) (void)close(data); - data = socket(AF_INET, SOCK_STREAM, 0); + data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { warn("socket"); if (tmpno) sendport = 1; return (1); } if (!sendport) if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { warn("setsockopt (reuse address)"); goto bad; } #ifdef IP_PORTRANGE + if (data_addr.su_family == AF_INET) + { + ports = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(data, IPPROTO_IP, IP_PORTRANGE, (char *)&ports, sizeof(ports)) < 0) warn("setsockopt PORTRANGE (ignored)"); + } +#endif +#ifdef INET6 +#ifdef IPV6_PORTRANGE + if (data_addr.su_family == AF_INET6) { + ports = restricted_data_ports ? IPV6_PORTRANGE_HIGH + : IPV6_PORTRANGE_DEFAULT; + if (setsockopt(data, IPPROTO_IPV6, IPV6_PORTRANGE, + (char *)&ports, sizeof(ports)) < 0) + warn("setsockopt PORTRANGE (ignored)"); + } +#endif #endif - if (bind(data, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0) { + if (bind(data, (struct sockaddr *)&data_addr, data_addr.su_len) < 0) { warn("bind"); goto bad; } if (options & SO_DEBUG && setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0) warn("setsockopt (ignored)"); len = sizeof(data_addr); if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) { warn("getsockname"); goto bad; } if (listen(data, 1) < 0) warn("listen"); if (sendport) { - a = (char *)&data_addr.sin_addr; - p = (char *)&data_addr.sin_port; + char hname[INET6_ADDRSTRLEN]; + int af; + struct sockaddr_in data_addr4; + union sockunion *daddr; + +#ifdef INET6 + if (data_addr.su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&data_addr.su_sin6.sin6_addr)) { + memset(&data_addr4, 0, sizeof(data_addr4)); + data_addr4.sin_len = sizeof(struct sockaddr_in); + data_addr4.sin_family = AF_INET; + data_addr4.sin_port = data_addr.su_port; + memcpy((caddr_t)&data_addr4.sin_addr, + (caddr_t)&data_addr.su_sin6.sin6_addr.s6_addr[12], + sizeof(struct in_addr)); + daddr = (union sockunion *)&data_addr4; + } else +#endif + daddr = &data_addr; + + + #define UC(b) (((int)b)&0xff) - result = - command("PORT %d,%d,%d,%d,%d,%d", - UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(p[0]), UC(p[1])); + + switch (daddr->su_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + af = (daddr->su_family == AF_INET) ? 1 : 2; + if (getnameinfo((struct sockaddr *)daddr, + daddr->su_len, hname, + sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID)) { + result = ERROR; + } else { + result = command("EPRT |%d|%s|%d|", + af, hname, ntohs(daddr->su_port)); + } + break; + default: + result = COMPLETE + 1; + break; + } + if (result == COMPLETE) + goto skip_port; + + p = (char *)&daddr->su_port; + switch (daddr->su_family) { + case AF_INET: + a = (char *)&daddr->su_sin.sin_addr; + result = command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), + UC(p[0]), UC(p[1])); + break; +#ifdef INET6 + case AF_INET6: + a = (char *)&daddr->su_sin6.sin6_addr; + result = command( +"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + 6, 16, + UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), + UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]), + UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]), + UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]), + 2, UC(p[0]), UC(p[1])); + break; +#endif + default: + result = COMPLETE + 1; /* xxx */ + } + skip_port: + if (result == ERROR && sendport == -1) { sendport = 0; tmpno = 1; goto noport; } return (result != COMPLETE); } if (tmpno) sendport = 1; #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (0); bad: (void)close(data), data = -1; if (tmpno) sendport = 1; return (1); } FILE * dataconn(lmode) const char *lmode; { - struct sockaddr_in from; + union sockunion from; int s, fromlen, tos; - fromlen = sizeof(from); + fromlen = myctladdr.su_len; if (passivemode) return (fdopen(data, lmode)); s = accept(data, (struct sockaddr *) &from, &fromlen); if (s < 0) { warn("accept"); (void)close(data), data = -1; return (NULL); } (void)close(data); data = s; #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { tos = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (fdopen(data, lmode)); } void psummary(notused) int notused; { if (bytes > 0) ptransfer(1); } void psabort(notused) int notused; { alarmtimer(0); abrtflag++; } void pswitch(flag) int flag; { sig_t oldintr; static struct comvars { int connect; char name[MAXHOSTNAMELEN]; - struct sockaddr_in mctl; - struct sockaddr_in hctl; + union sockunion mctl; + union sockunion hctl; FILE *in; FILE *out; int tpe; int curtpe; int cpnd; int sunqe; int runqe; int mcse; int ntflg; char nti[17]; char nto[17]; int mapflg; char mi[MAXPATHLEN]; char mo[MAXPATHLEN]; } proxstruct, tmpstruct; struct comvars *ip, *op; abrtflag = 0; oldintr = signal(SIGINT, psabort); if (flag) { if (proxy) return; ip = &tmpstruct; op = &proxstruct; proxy++; } else { if (!proxy) return; ip = &proxstruct; op = &tmpstruct; proxy = 0; } ip->connect = connected; connected = op->connect; if (hostname) { (void)strncpy(ip->name, hostname, sizeof(ip->name) - 1); ip->name[sizeof(ip->name) - 1] = '\0'; } else ip->name[0] = '\0'; hostname = op->name; ip->hctl = hisctladdr; hisctladdr = op->hctl; ip->mctl = myctladdr; myctladdr = op->mctl; ip->in = cin; cin = op->in; ip->out = cout; cout = op->out; ip->tpe = type; type = op->tpe; ip->curtpe = curtype; curtype = op->curtpe; ip->cpnd = cpend; cpend = op->cpnd; ip->sunqe = sunique; sunique = op->sunqe; ip->runqe = runique; runique = op->runqe; ip->mcse = mcase; mcase = op->mcse; ip->ntflg = ntflag; ntflag = op->ntflg; (void)strncpy(ip->nti, ntin, sizeof(ip->nti) - 1); (ip->nti)[sizeof(ip->nti) - 1] = '\0'; (void)strcpy(ntin, op->nti); (void)strncpy(ip->nto, ntout, sizeof(ip->nto) - 1); (ip->nto)[sizeof(ip->nto) - 1] = '\0'; (void)strcpy(ntout, op->nto); ip->mapflg = mapflag; mapflag = op->mapflg; (void)strncpy(ip->mi, mapin, sizeof(ip->mi) - 1); (ip->mi)[sizeof(ip->mi) - 1] = '\0'; (void)strcpy(mapin, op->mi); (void)strncpy(ip->mo, mapout, sizeof(ip->mo) - 1); (ip->mo)[sizeof(ip->mo) - 1] = '\0'; (void)strcpy(mapout, op->mo); (void)signal(SIGINT, oldintr); if (abrtflag) { abrtflag = 0; (*oldintr)(SIGINT); } } void abortpt(notused) int notused; { alarmtimer(0); putchar('\n'); (void)fflush(stdout); ptabflg++; mflag = 0; abrtflag = 0; longjmp(ptabort, 1); } void proxtrans(cmd, local, remote) const char *cmd, *local, *remote; { sig_t oldintr; int prox_type, nfnd; volatile int secndflag; char *cmd2; fd_set mask; #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&oldintr; (void)&cmd2; #endif oldintr = NULL; secndflag = 0; if (strcmp(cmd, "RETR")) cmd2 = "RETR"; else cmd2 = runique ? "STOU" : "STOR"; if ((prox_type = type) == 0) { if (unix_server && unix_proxy) prox_type = TYPE_I; else prox_type = TYPE_A; } if (curtype != prox_type) changetype(prox_type, 1); if (command("PASV") != COMPLETE) { puts("proxy server does not support third party transfers."); return; } pswitch(0); if (!connected) { puts("No primary connection."); pswitch(1); code = -1; return; } if (curtype != prox_type) changetype(prox_type, 1); if (command("PORT %s", pasv) != COMPLETE) { pswitch(1); return; } if (setjmp(ptabort)) goto abort; oldintr = signal(SIGINT, abortpt); if (command("%s %s", cmd, remote) != PRELIM) { (void)signal(SIGINT, oldintr); pswitch(1); return; } sleep(2); pswitch(1); secndflag++; if (command("%s %s", cmd2, local) != PRELIM) goto abort; ptflag++; (void)getreply(0); pswitch(0); (void)getreply(0); (void)signal(SIGINT, oldintr); pswitch(1); ptflag = 0; printf("local: %s remote: %s\n", local, remote); return; abort: (void)signal(SIGINT, SIG_IGN); ptflag = 0; if (strcmp(cmd, "RETR") && !proxy) pswitch(1); else if (!strcmp(cmd, "RETR") && proxy) pswitch(0); if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ if (command("%s %s", cmd2, local) != PRELIM) { pswitch(0); if (cpend) abort_remote((FILE *) NULL); } pswitch(1); if (ptabflg) code = -1; (void)signal(SIGINT, oldintr); return; } if (cpend) abort_remote((FILE *) NULL); pswitch(!proxy); if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ if (command("%s %s", cmd2, local) != PRELIM) { pswitch(0); if (cpend) abort_remote((FILE *) NULL); pswitch(1); if (ptabflg) code = -1; (void)signal(SIGINT, oldintr); return; } } if (cpend) abort_remote((FILE *) NULL); pswitch(!proxy); if (cpend) { FD_ZERO(&mask); FD_SET(fileno(cin), &mask); if ((nfnd = empty(&mask, 10)) <= 0) { if (nfnd < 0) { warn("abort"); } if (ptabflg) code = -1; lostpeer(); } (void)getreply(0); (void)getreply(0); } if (proxy) pswitch(0); pswitch(1); if (ptabflg) code = -1; (void)signal(SIGINT, oldintr); } void reset(argc, argv) int argc; char *argv[]; { fd_set mask; int nfnd = 1; FD_ZERO(&mask); while (nfnd > 0) { FD_SET(fileno(cin), &mask); if ((nfnd = empty(&mask, 0)) < 0) { warn("reset"); code = -1; lostpeer(); } else if (nfnd) { (void)getreply(0); } } } char * gunique(local) const char *local; { static char new[MAXPATHLEN]; char *cp = strrchr(local, '/'); int d, count=0; char ext = '1'; if (cp) *cp = '\0'; d = access(cp == local ? "/" : cp ? local : ".", W_OK); if (cp) *cp = '/'; if (d < 0) { warn("local: %s", local); return ((char *) 0); } (void)strcpy(new, local); cp = new + strlen(new); *cp++ = '.'; while (!d) { if (++count == 100) { puts("runique: can't find unique file name."); return ((char *) 0); } *cp++ = ext; *cp = '\0'; if (ext == '9') ext = '0'; else ext++; if ((d = access(new, F_OK)) < 0) break; if (ext != '0') cp--; else if (*(cp - 2) == '.') *(cp - 1) = '1'; else { *(cp - 2) = *(cp - 2) + 1; cp--; } } return (new); } void abort_remote(din) FILE *din; { char buf[BUFSIZ]; int nfnd; fd_set mask; if (cout == NULL) { warnx("Lost control connection for abort."); if (ptabflg) code = -1; lostpeer(); return; } /* * send IAC in urgent mode instead of DM because 4.3BSD places oob mark * after urgent byte rather than before as is protocol now */ sprintf(buf, "%c%c%c", IAC, IP, IAC); if (send(fileno(cout), buf, 3, MSG_OOB) != 3) warn("abort"); fprintf(cout, "%cABOR\r\n", DM); (void)fflush(cout); FD_ZERO(&mask); FD_SET(fileno(cin), &mask); if (din) { FD_SET(fileno(din), &mask); } if ((nfnd = empty(&mask, 10)) <= 0) { if (nfnd < 0) { warn("abort"); } if (ptabflg) code = -1; lostpeer(); } if (din && FD_ISSET(fileno(din), &mask)) { while (read(fileno(din), buf, BUFSIZ) > 0) /* LOOP */; } if (getreply(0) == ERROR && code == 552) { /* 552 needed for nic style abort */ (void)getreply(0); } (void)getreply(0); } diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h index d26d22df7f14..cd9b0bddb916 100644 --- a/usr.bin/ftp/ftp_var.h +++ b/usr.bin/ftp/ftp_var.h @@ -1,185 +1,186 @@ /* $FreeBSD$ */ /* $NetBSD: ftp_var.h,v 1.20.2.1 1997/11/18 01:01:37 mellon Exp $ */ /* * Copyright (c) 1985, 1989, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)ftp_var.h 8.4 (Berkeley) 10/9/94 */ /* * FTP global variables. */ #include +#include #include #include #include #ifndef SMALL #include #endif /* !SMALL */ #include "extern.h" #define HASHBYTES 1024 #define FTPBUFLEN MAXPATHLEN + 200 #define STALLTIME 5 /* # of seconds of no xfer before "stalling" */ #define FTP_PORT 21 /* default if ! getservbyname("ftp/tcp") */ #define HTTP_PORT 80 /* default if ! getservbyname("http/tcp") */ #ifndef GATE_PORT #define GATE_PORT 21 /* default if ! getservbyname("ftpgate/tcp") */ #endif #ifndef GATE_SERVER #define GATE_SERVER "" /* default server */ #endif #define PAGER "more" /* default pager if $PAGER isn't set */ /* * Options and other state info. */ int trace; /* trace packets exchanged */ int hash; /* print # for each buffer transferred */ int mark; /* number of bytes between hashes */ int sendport; /* use PORT cmd for each data connection */ int verbose; /* print messages coming back from server */ int connected; /* 1 = connected to server, -1 = logged in */ int fromatty; /* input is from a terminal */ int interactive; /* interactively prompt on m* cmds */ int confirmrest; /* confirm rest of current m* cmd */ int debug; /* debugging level */ int bell; /* ring bell on cmd completion */ int doglob; /* glob local file names */ int autologin; /* establish user account on connection */ int proxy; /* proxy server connection active */ int proxflag; /* proxy connection exists */ int gatemode; /* use gate-ftp */ char *gateserver; /* server to use for gate-ftp */ int sunique; /* store files on server with unique name */ int runique; /* store local files with unique name */ int mcase; /* map upper to lower case for mget names */ int ntflag; /* use ntin ntout tables for name translation */ int mapflag; /* use mapin mapout templates on file names */ int preserve; /* preserve modification time on files */ int progress; /* display transfer progress bar */ int code; /* return/reply code for ftp command */ int crflag; /* if 1, strip car. rets. on ascii gets */ -char pasv[64]; /* passive port for proxy data connection */ +char pasv[BUFSIZ]; /* passive port for proxy data connection */ int passivemode; /* passive mode enabled */ int restricted_data_ports; /* enable quarantine FTP area */ char *altarg; /* argv[1] with no shell-like preprocessing */ char ntin[17]; /* input translation table */ char ntout[17]; /* output translation table */ char mapin[MAXPATHLEN]; /* input map template */ char mapout[MAXPATHLEN]; /* output map template */ char typename[32]; /* name of file transfer type */ int type; /* requested file transfer type */ int curtype; /* current file transfer type */ char structname[32]; /* name of file transfer structure */ int stru; /* file transfer structure */ char formname[32]; /* name of file transfer format */ int form; /* file transfer format */ char modename[32]; /* name of file transfer mode */ int mode; /* file transfer mode */ char bytename[32]; /* local byte size in ascii */ int bytesize; /* local byte size in binary */ int anonftp; /* automatic anonymous login */ int dirchange; /* remote directory changed by cd command */ int ttywidth; /* width of tty */ char *tmpdir; /* temporary directory */ #ifndef SMALL int editing; /* command line editing enabled */ EditLine *el; /* editline(3) status structure */ History *hist; /* editline(3) history structure */ char *cursor_pos; /* cursor position we're looking for */ size_t cursor_argc; /* location of cursor in margv */ size_t cursor_argo; /* offset of cursor in margv[cursor_argc] */ #endif /* !SMALL */ off_t bytes; /* current # of bytes read */ off_t filesize; /* size of file being transferred */ char *direction; /* direction transfer is occurring */ off_t restart_point; /* offset to restart transfer */ char *hostname; /* name of host connected to */ int unix_server; /* server is unix, can use binary for ascii */ int unix_proxy; /* proxy is unix, can use binary for ascii */ -u_int16_t ftpport; /* port number to use for ftp connections */ -u_int16_t httpport; /* port number to use for http connections */ -u_int16_t gateport; /* port number to use for gateftp connections */ +char *ftpport; /* port number to use for ftp connections */ +char *httpport; /* port number to use for http connections */ +char *gateport; /* port number to use for gateftp connections */ int dobind; /* bind to specific address */ -struct sockaddr_in bindto; /* address to bind to */ +struct sockaddr_storage bindto; /* address to bind to */ jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ char line[FTPBUFLEN]; /* input line buffer */ char *stringbase; /* current scan point in line buffer */ char argbuf[FTPBUFLEN]; /* argument storage buffer */ char *argbase; /* current storage point in arg buffer */ StringList *marg_sl; /* stringlist containing margv */ int margc; /* count of arguments on input line */ #define margv (marg_sl->sl_str) /* args parsed from input line */ int cpend; /* flag: if != 0, then pending server reply */ int mflag; /* flag: if != 0, then active multi command */ int options; /* used during socket creation */ /* * Format of command table. */ struct cmd { char *c_name; /* name of command */ char *c_help; /* help string */ char c_bell; /* give bell when command completes */ char c_conn; /* must be connected to use command */ char c_proxy; /* proxy server may execute */ #ifndef SMALL char *c_complete; /* context sensitive completion list */ #endif /* !SMALL */ void (*c_handler) __P((int, char **)); /* function to call */ }; struct macel { char mac_name[9]; /* macro name */ char *mac_start; /* start of macro in macbuf */ char *mac_end; /* end of macro in macbuf */ }; int macnum; /* number of defined macros */ struct macel macros[16]; char macbuf[4096]; diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c index 371730849e34..aeb74878c8a7 100644 --- a/usr.bin/ftp/main.c +++ b/usr.bin/ftp/main.c @@ -1,723 +1,708 @@ /* $FreeBSD$ */ /* $NetBSD: main.c,v 1.26 1997/10/14 16:31:22 christos Exp $ */ /* * Copyright (c) 1985, 1989, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\ \tThe Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; #else __RCSID("$FreeBSD$"); __RCSID_SOURCE("$NetBSD: main.c,v 1.26 1997/10/14 16:31:22 christos Exp $"); #endif #endif /* not lint */ /* * FTP User Program -- Command Interface. */ #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "ftp_var.h" #include "pathnames.h" int main __P((int, char **)); int main(argc, argv) int argc; char *argv[]; { struct servent *sp; int ch, top, rval; long port; struct passwd *pw = NULL; char *cp, *ep, homedir[MAXPATHLEN], *s; int dumbterm; char *src_addr = NULL; (void) setlocale(LC_ALL, ""); - sp = getservbyname("ftp", "tcp"); - if (sp == 0) - ftpport = htons(FTP_PORT); /* good fallback */ - else - ftpport = sp->s_port; - sp = getservbyname("http", "tcp"); - if (sp == 0) - httpport = htons(HTTP_PORT); /* good fallback */ - else - httpport = sp->s_port; - gateport = 0; + ftpport = "ftp"; + httpport = "http"; + gateport = NULL; cp = getenv("FTPSERVERPORT"); - if (cp != NULL) { - port = strtol(cp, &ep, 10); - if (port < 1 || port > 0xffff || *ep != '\0') - warnx("bad FTPSERVERPORT port number: %s (ignored)", - cp); - else - gateport = htons(port); - } - if (gateport == 0) { - sp = getservbyname("ftpgate", "tcp"); - if (sp == 0) - gateport = htons(GATE_PORT); - else - gateport = sp->s_port; - } + if (cp != NULL) + gateport = cp; + if (!gateport) + gateport = "ftpgate"; doglob = 1; interactive = 1; autologin = 1; passivemode = 0; restricted_data_ports = 1; preserve = 1; verbose = 0; progress = 0; gatemode = 0; #ifndef SMALL editing = 0; el = NULL; hist = NULL; #endif mark = HASHBYTES; marg_sl = sl_init(); if ((tmpdir = getenv("TMPDIR")) == NULL) tmpdir = _PATH_TMP; cp = strrchr(argv[0], '/'); cp = (cp == NULL) ? argv[0] : cp + 1; if ((s = getenv("FTP_PASSIVE_MODE")) != NULL && strcasecmp(s, "no") != 0) passivemode = 1; if (strcmp(cp, "pftp") == 0) passivemode = 1; else if (strcmp(cp, "gate-ftp") == 0) gatemode = 1; gateserver = getenv("FTPSERVER"); if (gateserver == NULL || *gateserver == '\0') gateserver = GATE_SERVER; if (gatemode) { if (*gateserver == '\0') { warnx( "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); gatemode = 0; } } cp = getenv("TERM"); if (cp == NULL || strcmp(cp, "dumb") == 0) dumbterm = 1; else dumbterm = 0; fromatty = isatty(fileno(stdin)); if (fromatty) { verbose = 1; /* verbose if from a tty */ #ifndef SMALL if (! dumbterm) editing = 1; /* editing mode on if tty is usable */ #endif } if (isatty(fileno(stdout)) && !dumbterm) progress = 1; /* progress bar on if tty is usable */ while ((ch = getopt(argc, argv, "adeginpP:s:tUvV")) != -1) { switch (ch) { case 'a': anonftp = 1; break; case 'd': options |= SO_DEBUG; debug++; break; case 'e': #ifndef SMALL editing = 0; #endif break; case 'g': doglob = 0; break; case 'i': interactive = 0; break; case 'n': autologin = 0; break; case 'p': passivemode = 1; break; case 'P': - port = strtol(optarg, &ep, 10); - if (port < 1 || port > 0xffff || *ep != '\0') - warnx("bad port number: %s (ignored)", optarg); - else - ftpport = htons(port); + ftpport = optarg; break; case 's': dobind = 1; src_addr = optarg; break; case 't': trace = 1; break; case 'U': restricted_data_ports = 0; break; case 'v': verbose = 1; break; case 'V': verbose = 0; break; default: usage(); } } argc -= optind; argv += optind; cpend = 0; /* no pending replies */ proxy = 0; /* proxy not active */ crflag = 1; /* strip c.r. on ascii gets */ sendport = -1; /* not using ports */ if (dobind) { - memset((void *)&bindto, 0, sizeof(bindto)); - if (inet_aton(src_addr, &bindto.sin_addr) == 1) - bindto.sin_family = AF_INET; - else { - struct hostent *hp = gethostbyname(src_addr); - if (hp == NULL) - errx(1, "%s: %s", src_addr, hstrerror(h_errno)); - bindto.sin_family = hp->h_addrtype; - memcpy(&bindto.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(bindto.sin_addr))); + struct addrinfo hints; + struct addrinfo *res; + char *ftpdataport = "ftp-data"; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, NULL, &hints, &res); + if (error) { + fprintf(stderr, "%s: %s", src_addr, + gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "%s", strerror(errno)); + exit(1); } + memcpy(&bindto, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } /* * Set up the home directory in case we're globbing. */ cp = getlogin(); if (cp != NULL) { pw = getpwnam(cp); } if (pw == NULL) pw = getpwuid(getuid()); if (pw != NULL) { home = homedir; (void)strcpy(home, pw->pw_dir); } setttywidth(0); (void)signal(SIGWINCH, setttywidth); #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&argc; (void)&argv; #endif if (argc > 0) { - if (strchr(argv[0], ':') != NULL) { + if (isurl(argv[0])) { anonftp = 1; /* Handle "automatic" transfers. */ rval = auto_fetch(argc, argv); if (rval >= 0) /* -1 == connected and cd-ed */ exit(rval); } else { char *xargv[4], **xargp = xargv; #ifdef __GNUC__ /* XXX: to shut up gcc warnings */ (void)&xargp; #endif if (setjmp(toplevel)) exit(0); (void)signal(SIGINT, (sig_t)intr); (void)signal(SIGPIPE, (sig_t)lostpeer); *xargp++ = __progname; *xargp++ = argv[0]; /* host */ if (argc > 1) *xargp++ = argv[1]; /* port */ *xargp = NULL; setpeer(xargp-xargv, xargv); } } #ifndef SMALL controlediting(); #endif /* !SMALL */ top = setjmp(toplevel) == 0; if (top) { (void)signal(SIGINT, (sig_t)intr); (void)signal(SIGPIPE, (sig_t)lostpeer); } for (;;) { cmdscanner(top); top = 1; } } void intr() { alarmtimer(0); longjmp(toplevel, 1); } void lostpeer() { alarmtimer(0); if (connected) { if (cout != NULL) { (void)shutdown(fileno(cout), 1+1); (void)fclose(cout); cout = NULL; } if (data >= 0) { (void)shutdown(data, 1+1); (void)close(data); data = -1; } connected = 0; } pswitch(1); if (connected) { if (cout != NULL) { (void)shutdown(fileno(cout), 1+1); (void)fclose(cout); cout = NULL; } connected = 0; } proxflag = 0; pswitch(0); } /* * Generate a prompt */ char * prompt() { return ("ftp> "); } /* * Command parser. */ void cmdscanner(top) int top; { struct cmd *c; int num; if (!top #ifndef SMALL && !editing #endif /* !SMALL */ ) (void)putchar('\n'); for (;;) { #ifndef SMALL if (!editing) { #endif /* !SMALL */ if (fromatty) { fputs(prompt(), stdout); (void)fflush(stdout); } if (fgets(line, sizeof(line), stdin) == NULL) quit(0, 0); num = strlen(line); if (num == 0) break; if (line[--num] == '\n') { if (num == 0) break; line[num] = '\0'; } else if (num == sizeof(line) - 2) { puts("sorry, input line too long."); while ((num = getchar()) != '\n' && num != EOF) /* void */; break; } /* else it was a line without a newline */ #ifndef SMALL } else { const char *buf; cursor_pos = NULL; if ((buf = el_gets(el, &num)) == NULL || num == 0) quit(0, 0); if (line[--num] == '\n') { if (num == 0) break; } else if (num >= sizeof(line)) { puts("sorry, input line too long."); break; } memcpy(line, buf, num); line[num] = '\0'; history(hist, H_ENTER, buf); } #endif /* !SMALL */ makeargv(); if (margc == 0) continue; #if 0 && !defined(SMALL) /* XXX: don't want el_parse */ /* * el_parse returns -1 to signal that it's not been handled * internally. */ if (el_parse(el, margc, margv) != -1) continue; #endif /* !SMALL */ c = getcmd(margv[0]); if (c == (struct cmd *)-1) { puts("?Ambiguous command."); continue; } if (c == 0) { puts("?Invalid command."); continue; } if (c->c_conn && !connected) { puts("Not connected."); continue; } confirmrest = 0; (*c->c_handler)(margc, margv); if (bell && c->c_bell) (void)putchar('\007'); if (c->c_handler != help) break; } (void)signal(SIGINT, (sig_t)intr); (void)signal(SIGPIPE, (sig_t)lostpeer); } struct cmd * getcmd(name) const char *name; { const char *p, *q; struct cmd *c, *found; int nmatches, longest; if (name == NULL) return (0); longest = 0; nmatches = 0; found = 0; for (c = cmdtab; (p = c->c_name) != NULL; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return ((struct cmd *)-1); return (found); } /* * Slice a string up into argc/argv. */ int slrflag; void makeargv() { char *argp; stringbase = line; /* scan from first of buffer */ argbase = argbuf; /* store from first of buffer */ slrflag = 0; marg_sl->sl_cur = 0; /* reset to start of marg_sl */ for (margc = 0; ; margc++) { argp = slurpstring(); sl_add(marg_sl, argp); if (argp == NULL) break; } #ifndef SMALL if (cursor_pos == line) { cursor_argc = 0; cursor_argo = 0; } else if (cursor_pos != NULL) { cursor_argc = margc; cursor_argo = strlen(margv[margc-1]); } #endif /* !SMALL */ } #ifdef SMALL #define INC_CHKCURSOR(x) (x)++ #else /* !SMALL */ #define INC_CHKCURSOR(x) { (x)++ ; \ if (x == cursor_pos) { \ cursor_argc = margc; \ cursor_argo = ap-argbase; \ cursor_pos = NULL; \ } } #endif /* !SMALL */ /* * Parse string into argbuf; * implemented with FSM to * handle quoting and strings */ char * slurpstring() { int got_one = 0; char *sb = stringbase; char *ap = argbase; char *tmp = argbase; /* will return this if token found */ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ switch (slrflag) { /* and $ as token for macro invoke */ case 0: slrflag++; INC_CHKCURSOR(stringbase); return ((*sb == '!') ? "!" : "$"); /* NOTREACHED */ case 1: slrflag++; altarg = stringbase; break; default: break; } } S0: switch (*sb) { case '\0': goto OUT; case ' ': case '\t': INC_CHKCURSOR(sb); goto S0; default: switch (slrflag) { case 0: slrflag++; break; case 1: slrflag++; altarg = sb; break; default: break; } goto S1; } S1: switch (*sb) { case ' ': case '\t': case '\0': goto OUT; /* end of token */ case '\\': INC_CHKCURSOR(sb); goto S2; /* slurp next character */ case '"': INC_CHKCURSOR(sb); goto S3; /* slurp quoted string */ default: *ap = *sb; /* add character to token */ ap++; INC_CHKCURSOR(sb); got_one = 1; goto S1; } S2: switch (*sb) { case '\0': goto OUT; default: *ap = *sb; ap++; INC_CHKCURSOR(sb); got_one = 1; goto S1; } S3: switch (*sb) { case '\0': goto OUT; case '"': INC_CHKCURSOR(sb); goto S1; default: *ap = *sb; ap++; INC_CHKCURSOR(sb); got_one = 1; goto S3; } OUT: if (got_one) *ap++ = '\0'; argbase = ap; /* update storage pointer */ stringbase = sb; /* update scan pointer */ if (got_one) { return (tmp); } switch (slrflag) { case 0: slrflag++; break; case 1: slrflag++; altarg = (char *) 0; break; default: break; } return ((char *)0); } /* * Help command. * Call each command handler with argc == 0 and argv[0] == name. */ void help(argc, argv) int argc; char *argv[]; { struct cmd *c; if (argc == 1) { StringList *buf; buf = sl_init(); printf("%sommands may be abbreviated. Commands are:\n\n", proxy ? "Proxy c" : "C"); for (c = cmdtab; c < &cmdtab[NCMDS]; c++) if (c->c_name && (!proxy || c->c_proxy)) sl_add(buf, c->c_name); list_vertical(buf); sl_free(buf, 0); return; } #define HELPINDENT ((int) sizeof("disconnect")) while (--argc > 0) { char *arg; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) printf("?Ambiguous help command %s\n", arg); else if (c == (struct cmd *)0) printf("?Invalid help command %s\n", arg); else printf("%-*s\t%s\n", HELPINDENT, c->c_name, c->c_help); } } void usage() { (void)fprintf(stderr, "usage: %s [-adeginptUvV] [-P port] [-s src_addr] [host [port]]\n" " %s host:path[/]\n" " %s ftp://host[:port]/path[/]\n" " %s http://host[:port]/file\n", __progname, __progname, __progname, __progname); exit(1); } diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c index a34ca3424dbd..a62565c85f0e 100644 --- a/usr.bin/ftp/util.c +++ b/usr.bin/ftp/util.c @@ -1,858 +1,847 @@ /* $FreeBSD$ */ /* $NetBSD: util.c,v 1.16.2.1 1997/11/18 01:02:33 mellon Exp $ */ /* * Copyright (c) 1985, 1989, 1993, 1994 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. */ #include #ifndef lint __RCSID("$FreeBSD$"); __RCSID_SOURCE("$NetBSD: util.c,v 1.16.2.1 1997/11/18 01:02:33 mellon Exp $"); #endif /* not lint */ /* * FTP User Program -- Misc support routines */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftp_var.h" #include "pathnames.h" #ifndef SECSPERHOUR #define SECSPERHOUR (60*60) #endif /* * Connect to peer server and * auto-login, if possible. */ void setpeer(argc, argv) int argc; char *argv[]; { char *host; - u_int16_t port; + char *port; if (connected) { printf("Already connected to %s, use close first.\n", hostname); code = -1; return; } if (argc < 2) (void)another(&argc, &argv, "to"); if (argc < 2 || argc > 3) { printf("usage: %s host-name [port]\n", argv[0]); code = -1; return; } if (gatemode) port = gateport; else port = ftpport; - if (argc > 2) { - char *ep; - long nport; - - nport = strtol(argv[2], &ep, 10); - if (nport < 1 || nport > 0xffff || *ep != '\0') { - printf("%s: bad port number '%s'.\n", argv[0], argv[2]); - printf("usage: %s host-name [port]\n", argv[0]); - code = -1; - return; - } - port = htons(nport); - } + if (argc > 2) + port = strdup(argv[2]); if (gatemode) { if (gateserver == NULL || *gateserver == '\0') errx(1, "gateserver not defined (shouldn't happen)"); host = hookup(gateserver, port); } else host = hookup(argv[1], port); if (host) { int overbose; if (gatemode) { if (command("PASSERVE %s", argv[1]) != COMPLETE) return; if (verbose) printf("Connected via pass-through server %s\n", gateserver); } connected = 1; /* * Set up defaults for FTP. */ (void)strcpy(typename, "ascii"), type = TYPE_A; curtype = TYPE_A; (void)strcpy(formname, "non-print"), form = FORM_N; (void)strcpy(modename, "stream"), mode = MODE_S; (void)strcpy(structname, "file"), stru = STRU_F; (void)strcpy(bytename, "8"), bytesize = 8; if (autologin) (void)login(argv[1], NULL, NULL); overbose = verbose; if (debug == 0) verbose = -1; if (command("SYST") == COMPLETE && overbose) { char *cp, c; c = 0; cp = strchr(reply_string+4, ' '); if (cp == NULL) cp = strchr(reply_string+4, '\r'); if (cp) { if (cp[-1] == '.') cp--; c = *cp; *cp = '\0'; } printf("Remote system type is %s.\n", reply_string + 4); if (cp) *cp = c; } if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { if (proxy) unix_proxy = 1; else unix_server = 1; /* * Set type to 0 (not specified by user), * meaning binary by default, but don't bother * telling server. We can use binary * for text files unless changed by the user. */ type = 0; (void)strcpy(typename, "binary"); if (overbose) printf("Using %s mode to transfer files.\n", typename); } else { if (proxy) unix_proxy = 0; else unix_server = 0; if (overbose && !strncmp(reply_string, "215 TOPS20", 10)) puts( "Remember to set tenex mode when transferring binary files from this machine."); } verbose = overbose; } } /* * login to remote host, using given username & password if supplied */ int login(host, user, pass) const char *host; char *user, *pass; { char tmp[80]; char *acct; char anonpass[MAXLOGNAME + 1 + MAXHOSTNAMELEN]; /* "user@hostname" */ char hostname[MAXHOSTNAMELEN]; struct passwd *pw; int n, aflag = 0; acct = NULL; if (user == NULL) { if (ruserpass(host, &user, &pass, &acct) < 0) { code = -1; return (0); } } /* * Set up arguments for an anonymous FTP session, if necessary. */ if ((user == NULL || pass == NULL) && anonftp) { memset(anonpass, 0, sizeof(anonpass)); memset(hostname, 0, sizeof(hostname)); /* * Set up anonymous login password. */ if ((user = getlogin()) == NULL) { if ((pw = getpwuid(getuid())) == NULL) user = "anonymous"; else user = pw->pw_name; } gethostname(hostname, MAXHOSTNAMELEN); #ifndef DONT_CHEAT_ANONPASS /* * Every anonymous FTP server I've encountered * will accept the string "username@", and will * append the hostname itself. We do this by default * since many servers are picky about not having * a FQDN in the anonymous password. - thorpej@netbsd.org */ snprintf(anonpass, sizeof(anonpass) - 1, "%s@", user); #else snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s", user, hp->h_name); #endif pass = anonpass; user = "anonymous"; /* as per RFC 1635 */ } while (user == NULL) { char *myname = getlogin(); if (myname == NULL && (pw = getpwuid(getuid())) != NULL) myname = pw->pw_name; if (myname) printf("Name (%s:%s): ", host, myname); else printf("Name (%s): ", host); if (fgets(tmp, sizeof(tmp) - 1, stdin) == NULL) return (0); tmp[strlen(tmp) - 1] = '\0'; if (*tmp == '\0') user = myname; else user = tmp; } n = command("USER %s", user); if (n == CONTINUE) { if (pass == NULL) pass = getpass("Password:"); n = command("PASS %s", pass); } if (n == CONTINUE) { aflag++; if (acct == NULL) acct = getpass("Account:"); n = command("ACCT %s", acct); } if ((n != COMPLETE) || (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) { warnx("Login failed."); return (0); } if (proxy) return (1); connected = -1; for (n = 0; n < macnum; ++n) { if (!strcmp("init", macros[n].mac_name)) { (void)strcpy(line, "$init"); makeargv(); domacro(margc, margv); break; } } return (1); } /* * `another' gets another argument, and stores the new argc and argv. * It reverts to the top level (via main.c's intr()) on EOF/error. * * Returns false if no new arguments have been added. */ int another(pargc, pargv, prompt) int *pargc; char ***pargv; const char *prompt; { int len = strlen(line), ret; if (len >= sizeof(line) - 3) { puts("sorry, arguments too long."); intr(); } printf("(%s) ", prompt); line[len++] = ' '; if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) intr(); len += strlen(&line[len]); if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; makeargv(); ret = margc > *pargc; *pargc = margc; *pargv = margv; return (ret); } /* * glob files given in argv[] from the remote server. * if errbuf isn't NULL, store error messages there instead * of writing to the screen. */ char * remglob(argv, doswitch, errbuf) char *argv[]; int doswitch; char **errbuf; { char temp[MAXPATHLEN]; static char buf[MAXPATHLEN]; static FILE *ftemp = NULL; static char **args; int oldverbose, oldhash, fd; char *cp, *mode; if (!mflag) { if (!doglob) args = NULL; else { if (ftemp) { (void)fclose(ftemp); ftemp = NULL; } } return (NULL); } if (!doglob) { if (args == NULL) args = argv; if ((cp = *++args) == NULL) args = NULL; return (cp); } if (ftemp == NULL) { (void)snprintf(temp, sizeof(temp), "%s/%s", tmpdir, TMPFILE); if ((fd = mkstemp(temp)) < 0) { warn("unable to create temporary file %s", temp); return (NULL); } close(fd); oldverbose = verbose; verbose = (errbuf != NULL) ? -1 : 0; oldhash = hash; hash = 0; if (doswitch) pswitch(!proxy); for (mode = "w"; *++argv != NULL; mode = "a") recvrequest("NLST", temp, *argv, mode, 0, 0); if ((code / 100) != COMPLETE) { if (errbuf != NULL) *errbuf = reply_string; } if (doswitch) pswitch(!proxy); verbose = oldverbose; hash = oldhash; ftemp = fopen(temp, "r"); (void)unlink(temp); if (ftemp == NULL) { if (errbuf == NULL) puts("can't find list of remote files, oops."); else *errbuf = "can't find list of remote files, oops."; return (NULL); } } if (fgets(buf, sizeof(buf), ftemp) == NULL) { (void)fclose(ftemp); ftemp = NULL; return (NULL); } if ((cp = strchr(buf, '\n')) != NULL) *cp = '\0'; return (buf); } int confirm(cmd, file) const char *cmd, *file; { char line[BUFSIZ]; if (!interactive || confirmrest) return (1); printf("%s %s? ", cmd, file); (void)fflush(stdout); if (fgets(line, sizeof(line), stdin) == NULL) return (0); switch (tolower((unsigned char)*line)) { case 'n': return (0); case 'p': interactive = 0; puts("Interactive mode: off."); break; case 'a': confirmrest = 1; printf("Prompting off for duration of %s.\n", cmd); break; } return (1); } /* * Glob a local file name specification with * the expectation of a single return value. * Can't control multiple values being expanded * from the expression, we return only the first. */ int globulize(cpp) char **cpp; { glob_t gl; int flags; if (!doglob) return (1); flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; memset(&gl, 0, sizeof(gl)); if (glob(*cpp, flags, NULL, &gl) || gl.gl_pathc == 0) { warnx("%s: not found", *cpp); globfree(&gl); return (0); } /* XXX: caller should check if *cpp changed, and * free(*cpp) if that is the case */ *cpp = strdup(gl.gl_pathv[0]); globfree(&gl); return (1); } /* * determine size of remote file */ off_t remotesize(file, noisy) const char *file; int noisy; { int overbose; off_t size; overbose = verbose; size = -1; if (debug == 0) verbose = -1; if (command("SIZE %s", file) == COMPLETE) { char *cp, *ep; cp = strchr(reply_string, ' '); if (cp != NULL) { cp++; size = strtoq(cp, &ep, 10); if (*ep != '\0' && !isspace((unsigned char)*ep)) size = -1; } } else if (noisy && debug == 0) puts(reply_string); verbose = overbose; return (size); } /* * determine last modification time (in GMT) of remote file */ time_t remotemodtime(file, noisy) const char *file; int noisy; { struct tm timebuf; time_t rtime; int len, month, ocode, overbose, y2kbug, year; char *fmt; char mtbuf[17]; overbose = verbose; ocode = code; rtime = -1; if (debug == 0) verbose = -1; if (command("MDTM %s", file) == COMPLETE) { /* * Parse the time string, which is expected to be 14 * characters long. Some broken servers send tm_year * formatted with "19%02d", which produces an incorrect * (but parsable) 15 characters for years >= 2000. * Scan for invalid trailing junk by accepting up to 16 * characters. */ if (sscanf(reply_string, "%*s %16s", mtbuf) == 1) { fmt = NULL; len = strlen(mtbuf); y2kbug = 0; if (len == 15 && strncmp(mtbuf, "19", 2) == 0) { fmt = "19%03d%02d%02d%02d%02d%02d"; y2kbug = 1; } else if (len == 14) fmt = "%04d%02d%02d%02d%02d%02d"; if (fmt != NULL) memset(&timebuf, 0, sizeof(timebuf)); if (sscanf(mtbuf, fmt, &year, &month, &timebuf.tm_mday, &timebuf.tm_hour, &timebuf.tm_min, &timebuf.tm_sec) == 6) { timebuf.tm_isdst = -1; timebuf.tm_mon = month - 1; if (y2kbug) timebuf.tm_year = year; else timebuf.tm_year = year - 1900; rtime = mktime(&timebuf); } } if (rtime == -1 && (noisy || debug != 0)) printf("Can't convert %s to a time.\n", mtbuf); else rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */ } else if (noisy && debug == 0) puts(reply_string); verbose = overbose; if (rtime == -1) code = ocode; return (rtime); } void updateprogressmeter __P((int)); void updateprogressmeter(dummy) int dummy; { static pid_t pgrp = -1; int ctty_pgrp; if (pgrp == -1) pgrp = getpgrp(); /* * print progress bar only if we are foreground process. */ if (ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && ctty_pgrp == (int)pgrp) progressmeter(0); } /* * Display a transfer progress bar if progress is non-zero. * SIGALRM is hijacked for use by this function. * - Before the transfer, set filesize to size of file (or -1 if unknown), * and call with flag = -1. This starts the once per second timer, * and a call to updateprogressmeter() upon SIGALRM. * - During the transfer, updateprogressmeter will call progressmeter * with flag = 0 * - After the transfer, call with flag = 1 */ static struct timeval start; void progressmeter(flag) int flag; { /* * List of order of magnitude prefixes. * The last is `P', as 2^64 = 16384 Petabytes */ static const char prefixes[] = " KMGTP"; static struct timeval lastupdate; static off_t lastsize; struct timeval now, td, wait; off_t cursize, abbrevsize; double elapsed; int ratio, barlength, i, len; off_t remaining; char buf[256]; len = 0; if (flag == -1) { (void)gettimeofday(&start, (struct timezone *)0); lastupdate = start; lastsize = restart_point; } (void)gettimeofday(&now, (struct timezone *)0); if (!progress || filesize <= 0) return; cursize = bytes + restart_point; ratio = cursize * 100 / filesize; ratio = MAX(ratio, 0); ratio = MIN(ratio, 100); len += snprintf(buf + len, sizeof(buf) - len, "\r%3d%% ", ratio); barlength = ttywidth - 30; if (barlength > 0) { i = barlength * ratio / 100; len += snprintf(buf + len, sizeof(buf) - len, "|%.*s%*s|", i, "*****************************************************************************" "*****************************************************************************", barlength - i, ""); } i = 0; abbrevsize = cursize; while (abbrevsize >= 100000 && i < sizeof(prefixes)) { i++; abbrevsize >>= 10; } len += snprintf(buf + len, sizeof(buf) - len, " %5qd %c%c ", (long long)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : 'B'); timersub(&now, &lastupdate, &wait); if (cursize > lastsize) { lastupdate = now; lastsize = cursize; if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */ start.tv_sec += wait.tv_sec; start.tv_usec += wait.tv_usec; } wait.tv_sec = 0; } timersub(&now, &start, &td); elapsed = td.tv_sec + (td.tv_usec / 1000000.0); if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { len += snprintf(buf + len, sizeof(buf) - len, " --:-- ETA"); } else if (wait.tv_sec >= STALLTIME) { len += snprintf(buf + len, sizeof(buf) - len, " - stalled -"); } else { remaining = ((filesize - restart_point) / (bytes / elapsed) - elapsed); if (remaining >= 100 * SECSPERHOUR) len += snprintf(buf + len, sizeof(buf) - len, " --:-- ETA"); else { i = remaining / SECSPERHOUR; if (i) len += snprintf(buf + len, sizeof(buf) - len, "%2d:", i); else len += snprintf(buf + len, sizeof(buf) - len, " "); i = remaining % SECSPERHOUR; len += snprintf(buf + len, sizeof(buf) - len, "%02d:%02d ETA", i / 60, i % 60); } } (void)write(STDOUT_FILENO, buf, len); if (flag == -1) { (void)signal(SIGALRM, updateprogressmeter); alarmtimer(1); /* set alarm timer for 1 Hz */ } else if (flag == 1) { alarmtimer(0); (void)putchar('\n'); } fflush(stdout); } /* * Display transfer statistics. * Requires start to be initialised by progressmeter(-1), * direction to be defined by xfer routines, and filesize and bytes * to be updated by xfer routines * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR * instead of STDOUT. */ void ptransfer(siginfo) int siginfo; { struct timeval now, td; double elapsed; off_t bs; int meg, remaining, hh, len; char buf[100]; if (!verbose && !siginfo) return; (void)gettimeofday(&now, (struct timezone *)0); timersub(&now, &start, &td); elapsed = td.tv_sec + (td.tv_usec / 1000000.0); bs = bytes / (elapsed == 0.0 ? 1 : elapsed); meg = 0; if (bs > (1024 * 1024)) meg = 1; len = 0; len += snprintf(buf + len, sizeof(buf) - len, "%qd byte%s %s in %.2f seconds (%.2f %sB/s)\n", (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed, bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K"); if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 && bytes + restart_point <= filesize) { remaining = (int)((filesize - restart_point) / (bytes / elapsed) - elapsed); hh = remaining / SECSPERHOUR; remaining %= SECSPERHOUR; len--; /* decrement len to overwrite \n */ len += snprintf(buf + len, sizeof(buf) - len, " ETA: %02d:%02d:%02d\n", hh, remaining / 60, remaining % 60); } (void)write(siginfo ? STDERR_FILENO : STDOUT_FILENO, buf, len); } /* * List words in stringlist, vertically arranged */ void list_vertical(sl) StringList *sl; { int i, j, w; int columns, width, lines, items; char *p; width = items = 0; for (i = 0 ; i < sl->sl_cur ; i++) { w = strlen(sl->sl_str[i]); if (w > width) width = w; } width = (width + 8) &~ 7; columns = ttywidth / width; if (columns == 0) columns = 1; lines = (sl->sl_cur + columns - 1) / columns; for (i = 0; i < lines; i++) { for (j = 0; j < columns; j++) { p = sl->sl_str[j * lines + i]; if (p) fputs(p, stdout); if (j * lines + i + lines >= sl->sl_cur) { putchar('\n'); break; } w = strlen(p); while (w < width) { w = (w + 8) &~ 7; (void)putchar('\t'); } } } } /* * Update the global ttywidth value, using TIOCGWINSZ. */ void setttywidth(a) int a; { struct winsize winsize; if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) ttywidth = winsize.ws_col; else ttywidth = 80; } /* * Set the SIGALRM interval timer for wait seconds, 0 to disable. */ void alarmtimer(wait) int wait; { struct itimerval itv; itv.it_value.tv_sec = wait; itv.it_value.tv_usec = 0; itv.it_interval = itv.it_value; setitimer(ITIMER_REAL, &itv, NULL); } /* * Setup or cleanup EditLine structures */ #ifndef SMALL void controlediting() { if (editing && el == NULL && hist == NULL) { el = el_init(__progname, stdin, stdout); /* init editline */ hist = history_init(); /* init the builtin history */ history(hist, H_EVENT, 100); /* remember 100 events */ el_set(el, EL_HIST, history, hist); /* use history */ el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ el_set(el, EL_PROMPT, prompt); /* set the prompt function */ /* add local file completion, bind to TAB */ el_set(el, EL_ADDFN, "ftp-complete", "Context sensitive argument completion", complete); el_set(el, EL_BIND, "^I", "ftp-complete", NULL); el_source(el, NULL); /* read ~/.editrc */ el_set(el, EL_SIGNAL, 1); } else if (!editing) { if (hist) { history_end(hist); hist = NULL; } if (el) { el_end(el); el = NULL; } } } #endif /* !SMALL */ diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile index 658a2b7fb298..b4faf30769ad 100644 --- a/usr.bin/telnet/Makefile +++ b/usr.bin/telnet/Makefile @@ -1,61 +1,63 @@ # # Copyright (c) 1990 The Regents of the University of California. # 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 the University of # California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. # # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ # PROG= telnet CFLAGS+=-DKLUDGELINEMODE -DUSE_TERMIO #-DAUTHENTICATION -DENCRYPTION CFLAGS+=-DENV_HACK CFLAGS+=-DSKEY CFLAGS+=-I${.CURDIR}/../../lib +CFLAGS+=-DIPSEC -DINET6 #CFLAGS+= -DKRB4 -DPADD= ${LIBTERMCAP} ${LIBTELNET} -LDADD= -ltermcap -ltelnet +DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBIPSEC} +LDADD= -ltermcap -ltelnet -lipsec #DPADD+= ${LIBKRB} ${LIBDES} #LDADD+= -lkrb -ldes SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c telnet.c \ terminal.c tn3270.c utilities.c # These are the sources that have encryption stuff in them. CRYPT_SRC= authenc.c commands.c externs.h main.c network.c CRYPT_SRC+= ring.c ring.h telnet.c terminal.c utilities.c Makefile NOCRYPT_DIR=${.CURDIR}/Nocrypt .include nocrypt: @echo "Encryption code already removed." diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index a4677e0135e4..a640becaa168 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -1,2859 +1,2981 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static char sccsid[] = "@(#)commands.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #if defined(unix) #include #if defined(CRAY) || defined(sysV88) #include #endif #include #else #include #endif /* defined(unix) */ #include #include #ifdef CRAY #include #endif /* CRAY */ #include #include #include #include #include #include #include #include #include #include "general.h" #include "ring.h" #include "externs.h" #include "defines.h" #include "types.h" #if !defined(CRAY) && !defined(sysV88) #include # if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix) # include # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include - +#include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif MAXHOSTNAMELEN #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ char *hostname; static char _hostname[MAXHOSTNAMELEN]; extern char *getenv(); extern int isprefix(); extern char **genget(); extern int Ambiguous(); static call(); typedef struct { char *name; /* command name */ char *help; /* help string (NULL for no help) */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ } Command; static char line[256]; static char saveline[256]; static int margc; static char *margv[20]; #if defined(SKEY) #include #define PATH_SKEY "/usr/bin/key" int skey_calc(argc, argv) int argc; char **argv; { int status; if(argc != 3) { printf("%s sequence challenge\n", argv[0]); return; } switch(fork()) { case 0: execv(PATH_SKEY, argv); exit (1); case -1: perror("fork"); break; default: (void) wait(&status); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (0); } } #endif static void makeargv() { register char *cp, *cp2, c; register char **argp = margv; margc = 0; cp = line; if (*cp == '!') { /* Special case shell escape */ strcpy(saveline, line); /* save for shell command */ *argp++ = "!"; /* No room in string to get this */ margc++; cp++; } while (c = *cp) { register int inquote = 0; while (isspace(c)) c = *++cp; if (c == '\0') break; *argp++ = cp; margc += 1; for (cp2 = cp; c != '\0'; c = *++cp) { if (inquote) { if (c == inquote) { inquote = 0; continue; } } else { if (c == '\\') { if ((c = *++cp) == '\0') break; } else if (c == '"') { inquote = '"'; continue; } else if (c == '\'') { inquote = '\''; continue; } else if (isspace(c)) break; } *cp2++ = c; } *cp2 = '\0'; if (c == '\0') break; cp++; } *argp++ = 0; } /* * Make a character string into a number. * * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). */ static special(s) register char *s; { register char c; char b; switch (*s) { case '^': b = *++s; if (b == '?') { c = b | 0x40; /* DEL */ } else { c = b & 0x1f; } break; default: c = *s; break; } return c; } /* * Construct a control character sequence * for a special character. */ static char * control(c) register cc_t c; { static char buf[5]; /* * The only way I could get the Sun 3.5 compiler * to shut up about * if ((unsigned int)c >= 0x80) * was to assign "c" to an unsigned int variable... * Arggg.... */ register unsigned int uic = (unsigned int)c; if (uic == 0x7f) return ("^?"); if (c == (cc_t)_POSIX_VDISABLE) { return "off"; } if (uic >= 0x80) { buf[0] = '\\'; buf[1] = ((c>>6)&07) + '0'; buf[2] = ((c>>3)&07) + '0'; buf[3] = (c&07) + '0'; buf[4] = 0; } else if (uic >= 0x20) { buf[0] = c; buf[1] = 0; } else { buf[0] = '^'; buf[1] = '@'+c; buf[2] = 0; } return (buf); } /* * The following are data structures and routines for * the "send" command. * */ struct sendlist { char *name; /* How user refers to it (case independent) */ char *help; /* Help information (0 ==> no help) */ int needconnect; /* Need to be connected */ int narg; /* Number of arguments */ int (*handler)(); /* Routine to perform (for special ops) */ int nbyte; /* Number of bytes to send this command */ int what; /* Character to be sent (<0 ==> special) */ }; static int send_esc P((void)), send_help P((void)), send_docmd P((char *)), send_dontcmd P((char *)), send_willcmd P((char *)), send_wontcmd P((char *)); static struct sendlist Sendlist[] = { { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, { "break", 0, 1, 0, 0, 2, BREAK }, { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, { "intp", 0, 1, 0, 0, 2, IP }, { "interrupt", 0, 1, 0, 0, 2, IP }, { "intr", 0, 1, 0, 0, 2, IP }, { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, { "?", "Display send options", 0, 0, send_help, 0, 0 }, { "help", 0, 0, 0, send_help, 0, 0 }, { "do", 0, 0, 1, send_docmd, 3, 0 }, { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, { "will", 0, 0, 1, send_willcmd, 3, 0 }, { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, { 0 } }; #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ sizeof(struct sendlist))) static int sendcmd(argc, argv) int argc; char **argv; { int count; /* how many bytes we are going to need to send */ int i; int question = 0; /* was at least one argument a question */ struct sendlist *s; /* pointer to current command */ int success = 0; int needconnect = 0; if (argc < 2) { printf("need at least one argument for 'send' command\n"); printf("'send ?' for help\n"); return 0; } /* * First, validate all the send arguments. * In addition, we see how much space we are going to need, and * whether or not we will be doing a "SYNCH" operation (which * flushes the network queue). */ count = 0; for (i = 1; i < argc; i++) { s = GETSEND(argv[i]); if (s == 0) { printf("Unknown send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } else if (Ambiguous(s)) { printf("Ambiguous send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } if (i + s->narg >= argc) { fprintf(stderr, "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n", s->narg, s->narg == 1 ? "" : "s", s->name, s->name); return 0; } count += s->nbyte; if (s->handler == send_help) { send_help(); return 0; } i += s->narg; needconnect += s->needconnect; } if (!connected && needconnect) { printf("?Need to be connected first.\n"); printf("'send ?' for help\n"); return 0; } /* Now, do we have enough room? */ if (NETROOM() < count) { printf("There is not enough room in the buffer TO the network\n"); printf("to process your request. Nothing will be done.\n"); printf("('send synch' will throw away most data in the network\n"); printf("buffer, if this might help.)\n"); return 0; } /* OK, they are all OK, now go through again and actually send */ count = 0; for (i = 1; i < argc; i++) { if ((s = GETSEND(argv[i])) == 0) { fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); (void) quit(); /*NOTREACHED*/ } if (s->handler) { count++; success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0, (s->narg > 1) ? argv[i+2] : 0); i += s->narg; } else { NET2ADD(IAC, s->what); printoption("SENT", IAC, s->what); } } return (count == success); } static int send_esc() { NETADD(escape); return 1; } static int send_docmd(name) char *name; { return(send_tncmd(send_do, "do", name)); } static int send_dontcmd(name) char *name; { return(send_tncmd(send_dont, "dont", name)); } static int send_willcmd(name) char *name; { return(send_tncmd(send_will, "will", name)); } static int send_wontcmd(name) char *name; { return(send_tncmd(send_wont, "wont", name)); } int send_tncmd(func, cmd, name) void (*func)(); char *cmd, *name; { char **cpp; extern char *telopts[]; register int val = 0; if (isprefix(name, "help") || isprefix(name, "?")) { register int col, len; printf("Usage: send %s \n", cmd); printf("\"value\" must be from 0 to 255\n"); printf("Valid options are:\n\t"); col = 8; for (cpp = telopts; *cpp; cpp++) { len = strlen(*cpp) + 3; if (col + len > 65) { printf("\n\t"); col = 8; } printf(" \"%s\"", *cpp); col += len; } printf("\n"); return 0; } cpp = (char **)genget(name, telopts, sizeof(char *)); if (Ambiguous(cpp)) { fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n", name, cmd); return 0; } if (cpp) { val = cpp - telopts; } else { register char *cp = name; while (*cp >= '0' && *cp <= '9') { val *= 10; val += *cp - '0'; cp++; } if (*cp != 0) { fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n", name, cmd); return 0; } else if (val < 0 || val > 255) { fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n", name, cmd); return 0; } } if (!connected) { printf("?Need to be connected first.\n"); return 0; } (*func)(val, 1); return 1; } static int send_help() { struct sendlist *s; /* pointer to current command */ for (s = Sendlist; s->name; s++) { if (s->help) printf("%-15s %s\n", s->name, s->help); } return(0); } /* * The following are the routines and data structures referred * to by the arguments to the "toggle" command. */ static int lclchars() { donelclchars = 1; return 1; } static int togdebug() { #ifndef NOT43 if (net > 0 && (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { perror("setsockopt (SO_DEBUG)"); } #else /* NOT43 */ if (debug) { if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) perror("setsockopt (SO_DEBUG)"); } else printf("Cannot turn off socket debugging\n"); #endif /* NOT43 */ return 1; } static int togcrlf() { if (crlf) { printf("Will send carriage returns as telnet .\n"); } else { printf("Will send carriage returns as telnet .\n"); } return 1; } int binmode; static int togbinary(val) int val; { donebinarytoggle = 1; if (val >= 0) { binmode = val; } else { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { binmode = 1; } else if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { binmode = 0; } val = binmode ? 0 : 1; } if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { printf("Already operating in binary mode with remote host.\n"); } else { printf("Negotiating binary mode with remote host.\n"); tel_enter_binary(3); } } else { if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { printf("Already in network ascii mode with remote host.\n"); } else { printf("Negotiating network ascii mode with remote host.\n"); tel_leave_binary(3); } } return 1; } static int togrbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_do(TELOPT_BINARY)) { printf("Already receiving in binary mode.\n"); } else { printf("Negotiating binary mode on input.\n"); tel_enter_binary(1); } } else { if (my_want_state_is_dont(TELOPT_BINARY)) { printf("Already receiving in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on input.\n"); tel_leave_binary(1); } } return 1; } static int togxbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY)) { printf("Already transmitting in binary mode.\n"); } else { printf("Negotiating binary mode on output.\n"); tel_enter_binary(2); } } else { if (my_want_state_is_wont(TELOPT_BINARY)) { printf("Already transmitting in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on output.\n"); tel_leave_binary(2); } } return 1; } static int togglehelp P((void)); #if defined(AUTHENTICATION) extern int auth_togdebug P((int)); #endif struct togglelist { char *name; /* name of toggle */ char *help; /* help message */ int (*handler)(); /* routine to do actual setting */ int *variable; char *actionexplanation; }; static struct togglelist Togglelist[] = { { "autoflush", "flushing of output when sending interrupt characters", 0, &autoflush, "flush output when sending interrupt characters" }, { "autosynch", "automatic sending of interrupt characters in urgent mode", 0, &autosynch, "send interrupt characters in urgent mode" }, #if defined(AUTHENTICATION) { "autologin", "automatic sending of login and/or authentication info", 0, &autologin, "send login name and/or authentication information" }, { "authdebug", "Toggle authentication debugging", auth_togdebug, 0, "print authentication debugging information" }, #endif { "skiprc", "don't read ~/.telnetrc file", 0, &skiprc, "skip reading of ~/.telnetrc file" }, { "binary", "sending and receiving of binary data", togbinary, 0, 0 }, { "inbinary", "receiving of binary data", togrbinary, 0, 0 }, { "outbinary", "sending of binary data", togxbinary, 0, 0 }, { "crlf", "sending carriage returns as telnet ", togcrlf, &crlf, 0 }, { "crmod", "mapping of received carriage returns", 0, &crmod, "map carriage return on output" }, { "localchars", "local recognition of certain control characters", lclchars, &localchars, "recognize certain control characters" }, { " ", "", 0 }, /* empty line */ #if defined(unix) && defined(TN3270) { "apitrace", "(debugging) toggle tracing of API transactions", 0, &apitrace, "trace API transactions" }, { "cursesdata", "(debugging) toggle printing of hexadecimal curses data", 0, &cursesdata, "print hexadecimal representation of curses data" }, #endif /* defined(unix) && defined(TN3270) */ { "debug", "debugging", togdebug, &debug, "turn on socket level debugging" }, { "netdata", "printing of hexadecimal network data (debugging)", 0, &netdata, "print hexadecimal representation of network traffic" }, { "prettydump", "output of \"netdata\" to user readable format (debugging)", 0, &prettydump, "print user readable output for \"netdata\"" }, { "options", "viewing of options processing (debugging)", 0, &showoptions, "show option processing" }, #if defined(unix) { "termdata", "(debugging) toggle printing of hexadecimal terminal data", 0, &termdata, "print hexadecimal representation of terminal traffic" }, #endif /* defined(unix) */ { "?", 0, togglehelp }, { "help", 0, togglehelp }, { 0 } }; static int togglehelp() { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s toggle %s\n", c->name, c->help); else printf("\n"); } } printf("\n"); printf("%-15s %s\n", "?", "display help information"); return 0; } static void settogglehelp(set) int set; { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s %s\n", c->name, set ? "enable" : "disable", c->help); else printf("\n"); } } } #define GETTOGGLE(name) (struct togglelist *) \ genget(name, (char **) Togglelist, sizeof(struct togglelist)) static int toggle(argc, argv) int argc; char *argv[]; { int retval = 1; char *name; struct togglelist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); return 0; } argc--; argv++; while (argc--) { name = *argv++; c = GETTOGGLE(name); if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", name); return 0; } else if (c == 0) { fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", name); return 0; } else { if (c->variable) { *c->variable = !*c->variable; /* invert it */ if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) { retval &= (*c->handler)(-1); } } } return retval; } /* * The following perform the "set" command. */ #ifdef USE_TERMIO struct termio new_tc = { 0 }; #endif struct setlist { char *name; /* name */ char *help; /* help information */ void (*handler)(); cc_t *charp; /* where it is located at */ }; static struct setlist Setlist[] = { #ifdef KLUDGELINEMODE { "echo", "character to toggle local echoing on/off", 0, &echoc }, #endif { "escape", "character to escape back to telnet command mode", 0, &escape }, { "rlogin", "rlogin escape character", 0, &rlogin }, { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, { " ", "" }, { " ", "The following need 'localchars' to be toggled true", 0, 0 }, { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp }, { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp }, { "quit", "character to cause an Abort process", 0, termQuitCharp }, { "eof", "character to cause an EOF ", 0, termEofCharp }, { " ", "" }, { " ", "The following are for local editing in linemode", 0, 0 }, { "erase", "character to use to erase a character", 0, termEraseCharp }, { "kill", "character to use to erase a line", 0, termKillCharp }, { "lnext", "character to use for literal next", 0, termLiteralNextCharp }, { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, { "reprint", "character to use for line reprint", 0, termRprntCharp }, { "worderase", "character to use to erase a word", 0, termWerasCharp }, { "start", "character to use for XON", 0, termStartCharp }, { "stop", "character to use for XOFF", 0, termStopCharp }, { "forw1", "alternate end of line character", 0, termForw1Charp }, { "forw2", "alternate end of line character", 0, termForw2Charp }, { "ayt", "alternate AYT character", 0, termAytCharp }, { 0 } }; #if defined(CRAY) && !defined(__STDC__) /* Work around compiler bug in pcc 4.1.5 */ void _setlist_init() { #ifndef KLUDGELINEMODE #define N 5 #else #define N 6 #endif Setlist[N+0].charp = &termFlushChar; Setlist[N+1].charp = &termIntChar; Setlist[N+2].charp = &termQuitChar; Setlist[N+3].charp = &termEofChar; Setlist[N+6].charp = &termEraseChar; Setlist[N+7].charp = &termKillChar; Setlist[N+8].charp = &termLiteralNextChar; Setlist[N+9].charp = &termSuspChar; Setlist[N+10].charp = &termRprntChar; Setlist[N+11].charp = &termWerasChar; Setlist[N+12].charp = &termStartChar; Setlist[N+13].charp = &termStopChar; Setlist[N+14].charp = &termForw1Char; Setlist[N+15].charp = &termForw2Char; Setlist[N+16].charp = &termAytChar; #undef N } #endif /* defined(CRAY) && !defined(__STDC__) */ static struct setlist * getset(name) char *name; { return (struct setlist *) genget(name, (char **) Setlist, sizeof(struct setlist)); } void set_escape_char(s) char *s; { if (rlogin != _POSIX_VDISABLE) { rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet rlogin escape character is '%s'.\n", control(rlogin)); } else { escape = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet escape character is '%s'.\n", control(escape)); } } static int setcmd(argc, argv) int argc; char *argv[]; { int value; struct setlist *ct; struct togglelist *c; if (argc < 2 || argc > 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(1); printf("%-15s %s\n", "?", "display help information"); return 0; } ct = getset(argv[1]); if (ct == 0) { c = GETTOGGLE(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", argv[1]); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } if (c->variable) { if ((argc == 2) || (strcmp("on", argv[2]) == 0)) *c->variable = 1; else if (strcmp("off", argv[2]) == 0) *c->variable = 0; else { printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n"); return 0; } if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(1); } else if (argc != 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } else if (ct->handler) { (*ct->handler)(argv[2]); printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp); } else { if (strcmp("off", argv[2])) { value = special(argv[2]); } else { value = _POSIX_VDISABLE; } *(ct->charp) = (cc_t)value; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } slc_check(); return 1; } static int unsetcmd(argc, argv) int argc; char *argv[]; { struct setlist *ct; struct togglelist *c; register char *name; if (argc < 2) { fprintf(stderr, "Need an argument to 'unset' command. 'unset ?' for help.\n"); return 0; } if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(0); printf("%-15s %s\n", "?", "display help information"); return 0; } argc--; argv++; while (argc--) { name = *argv++; ct = getset(name); if (ct == 0) { c = GETTOGGLE(name); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n", name); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } if (c->variable) { *c->variable = 0; if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(0); } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } else if (ct->handler) { (*ct->handler)(0); printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp); } else { *(ct->charp) = _POSIX_VDISABLE; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } } return 1; } /* * The following are the data structures and routines for the * 'mode' command. */ #ifdef KLUDGELINEMODE extern int kludgelinemode; static int dokludgemode() { kludgelinemode = 1; send_wont(TELOPT_LINEMODE, 1); send_dont(TELOPT_SGA, 1); send_dont(TELOPT_ECHO, 1); } #endif static int dolinemode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_dont(TELOPT_SGA, 1); #endif send_will(TELOPT_LINEMODE, 1); send_dont(TELOPT_ECHO, 1); return 1; } static int docharmode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_do(TELOPT_SGA, 1); else #endif send_wont(TELOPT_LINEMODE, 1); send_do(TELOPT_ECHO, 1); return 1; } static int dolmmode(bit, on) int bit, on; { unsigned char c; extern int linemode; if (my_want_state_is_wont(TELOPT_LINEMODE)) { printf("?Need to have LINEMODE option enabled first.\n"); printf("'mode ?' for help.\n"); return 0; } if (on) c = (linemode | bit); else c = (linemode & ~bit); lm_mode(&c, 1, 1); return 1; } int setmode(bit) { return dolmmode(bit, 1); } int clearmode(bit) { return dolmmode(bit, 0); } struct modelist { char *name; /* command name */ char *help; /* help string */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ int arg1; }; extern int modehelp(); static struct modelist ModeList[] = { { "character", "Disable LINEMODE option", docharmode, 1 }, #ifdef KLUDGELINEMODE { "", "(or disable obsolete line-by-line mode)", 0 }, #endif { "line", "Enable LINEMODE option", dolinemode, 1 }, #ifdef KLUDGELINEMODE { "", "(or enable obsolete line-by-line mode)", 0 }, #endif { "", "", 0 }, { "", "These require the LINEMODE option to be enabled", 0 }, { "isig", "Enable signal trapping", setmode, 1, MODE_TRAPSIG }, { "+isig", 0, setmode, 1, MODE_TRAPSIG }, { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG }, { "edit", "Enable character editing", setmode, 1, MODE_EDIT }, { "+edit", 0, setmode, 1, MODE_EDIT }, { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT }, { "softtabs", "Enable tab expansion", setmode, 1, MODE_SOFT_TAB }, { "+softtabs", 0, setmode, 1, MODE_SOFT_TAB }, { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB }, { "litecho", "Enable literal character echo", setmode, 1, MODE_LIT_ECHO }, { "+litecho", 0, setmode, 1, MODE_LIT_ECHO }, { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO }, { "help", 0, modehelp, 0 }, #ifdef KLUDGELINEMODE { "kludgeline", 0, dokludgemode, 1 }, #endif { "", "", 0 }, { "?", "Print help information", modehelp, 0 }, { 0 }, }; int modehelp() { struct modelist *mt; printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); for (mt = ModeList; mt->name; mt++) { if (mt->help) { if (*mt->help) printf("%-15s %s\n", mt->name, mt->help); else printf("\n"); } } return 0; } #define GETMODECMD(name) (struct modelist *) \ genget(name, (char **) ModeList, sizeof(struct modelist)) static int modecmd(argc, argv) int argc; char *argv[]; { struct modelist *mt; if (argc != 2) { printf("'mode' command requires an argument\n"); printf("'mode ?' for help.\n"); } else if ((mt = GETMODECMD(argv[1])) == 0) { fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); } else if (Ambiguous(mt)) { fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); } else if (mt->needconnect && !connected) { printf("?Need to be connected first.\n"); printf("'mode ?' for help.\n"); } else if (mt->handler) { return (*mt->handler)(mt->arg1); } return 0; } /* * The following data structures and routines implement the * "display" command. */ static int display(argc, argv) int argc; char *argv[]; { struct togglelist *tl; struct setlist *sl; #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ if (*tl->variable) { \ printf("will"); \ } else { \ printf("won't"); \ } \ printf(" %s.\n", tl->actionexplanation); \ } #define doset(sl) if (sl->name && *sl->name != ' ') { \ if (sl->handler == 0) \ printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \ else \ printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \ } if (argc == 1) { for (tl = Togglelist; tl->name; tl++) { dotog(tl); } printf("\n"); for (sl = Setlist; sl->name; sl++) { doset(sl); } } else { int i; for (i = 1; i < argc; i++) { sl = getset(argv[i]); tl = GETTOGGLE(argv[i]); if (Ambiguous(sl) || Ambiguous(tl)) { printf("?Ambiguous argument '%s'.\n", argv[i]); return 0; } else if (!sl && !tl) { printf("?Unknown argument '%s'.\n", argv[i]); return 0; } else { if (tl) { dotog(tl); } if (sl) { doset(sl); } } } } /*@*/optionstatus(); return 1; #undef doset #undef dotog } /* * The following are the data structures, and many of the routines, * relating to command processing. */ /* * Set the escape character. */ static int setescape(argc, argv) int argc; char *argv[]; { register char *arg; char buf[50]; printf( "Deprecated usage - please use 'set escape%s%s' in the future.\n", (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); if (argc > 2) arg = argv[1]; else { printf("new escape character: "); (void) fgets(buf, sizeof(buf), stdin); arg = buf; } if (arg[0] != '\0') escape = arg[0]; if (!In3270) { printf("Escape character is '%s'.\n", control(escape)); } (void) fflush(stdout); return 1; } /*VARARGS*/ static int togcrmod() { crmod = !crmod; printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); (void) fflush(stdout); return 1; } /*VARARGS*/ int suspend() { #ifdef SIGTSTP setcommandmode(); { long oldrows, oldcols, newrows, newcols, err; err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; (void) kill(0, SIGTSTP); /* * If we didn't get the window size before the SUSPEND, but we * can get them now (???), then send the NAWS to make sure that * we are set up for the right window size. */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } } /* reget parameters in case they were changed */ TerminalSaveState(); setconnmode(0); #else printf("Suspend is not supported. Try the '!' command instead\n"); #endif return 1; } #if !defined(TN3270) /*ARGSUSED*/ int shell(argc, argv) int argc; char *argv[]; { long oldrows, oldcols, newrows, newcols, err; setcommandmode(); err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; switch(vfork()) { case -1: perror("Fork failed\n"); break; case 0: { /* * Fire up the shell in the child. */ register char *shellp, *shellname; extern char *rindex(); shellp = getenv("SHELL"); if (shellp == NULL) shellp = "/bin/sh"; if ((shellname = rindex(shellp, '/')) == 0) shellname = shellp; else shellname++; if (argc > 1) execl(shellp, shellname, "-c", &saveline[1], 0); else execl(shellp, shellname, 0); perror("Execl"); _exit(1); } default: (void)wait((int *)0); /* Wait for the shell to complete */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } break; } return 1; } #else /* !defined(TN3270) */ extern int shell(); #endif /* !defined(TN3270) */ /*VARARGS*/ static bye(argc, argv) int argc; /* Number of arguments */ char *argv[]; /* arguments */ { extern int resettermname; if (connected) { (void) shutdown(net, 2); printf("Connection closed.\n"); (void) NetClose(net); connected = 0; resettermname = 1; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ /* reset options */ tninit(); #if defined(TN3270) SetIn3270(); /* Get out of 3270 mode */ #endif /* defined(TN3270) */ } if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { longjmp(toplevel, 1); /* NOTREACHED */ } return 1; /* Keep lint, etc., happy */ } /*VARARGS*/ quit() { (void) call(bye, "bye", "fromquit", 0); Exit(0); /*NOTREACHED*/ } /*VARARGS*/ int logout() { send_do(TELOPT_LOGOUT, 1); (void) netflush(); return 1; } /* * The SLC command. */ struct slclist { char *name; char *help; void (*handler)(); int arg; }; static void slc_help(); struct slclist SlcList[] = { { "export", "Use local special character definitions", slc_mode_export, 0 }, { "import", "Use remote special character definitions", slc_mode_import, 1 }, { "check", "Verify remote special character definitions", slc_mode_import, 0 }, { "help", 0, slc_help, 0 }, { "?", "Print help information", slc_help, 0 }, { 0 }, }; static void slc_help() { struct slclist *c; for (c = SlcList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct slclist * getslc(name) char *name; { return (struct slclist *) genget(name, (char **) SlcList, sizeof(struct slclist)); } static slccmd(argc, argv) int argc; char *argv[]; { struct slclist *c; if (argc != 2) { fprintf(stderr, "Need an argument to 'slc' command. 'slc ?' for help.\n"); return 0; } c = getslc(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]); return 0; } (*c->handler)(c->arg); slcstate(); return 1; } /* * The ENVIRON command. */ struct envlist { char *name; char *help; void (*handler)(); int narg; }; extern struct env_lst * env_define P((unsigned char *, unsigned char *)); extern void env_undefine P((unsigned char *)), env_export P((unsigned char *)), env_unexport P((unsigned char *)), env_send P((unsigned char *)), #if defined(OLD_ENVIRON) && defined(ENV_HACK) env_varval P((unsigned char *)), #endif env_list P((void)); static void env_help P((void)); struct envlist EnvList[] = { { "define", "Define an environment variable", (void (*)())env_define, 2 }, { "undefine", "Undefine an environment variable", env_undefine, 1 }, { "export", "Mark an environment variable for automatic export", env_export, 1 }, { "unexport", "Don't mark an environment variable for automatic export", env_unexport, 1 }, { "send", "Send an environment variable", env_send, 1 }, { "list", "List the current environment variables", env_list, 0 }, #if defined(OLD_ENVIRON) && defined(ENV_HACK) { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", env_varval, 1 }, #endif { "help", 0, env_help, 0 }, { "?", "Print help information", env_help, 0 }, { 0 }, }; static void env_help() { struct envlist *c; for (c = EnvList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct envlist * getenvcmd(name) char *name; { return (struct envlist *) genget(name, (char **) EnvList, sizeof(struct envlist)); } env_cmd(argc, argv) int argc; char *argv[]; { struct envlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'environ' command. 'environ ?' for help.\n"); return 0; } c = getenvcmd(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } (*c->handler)(argv[2], argv[3]); return 1; } struct env_lst { struct env_lst *next; /* pointer to next structure */ struct env_lst *prev; /* pointer to previous structure */ unsigned char *var; /* pointer to variable name */ unsigned char *value; /* pointer to variable value */ int export; /* 1 -> export with default list of variables */ int welldefined; /* A well defined variable */ }; struct env_lst envlisthead; struct env_lst * env_find(var) unsigned char *var; { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { if (strcmp((char *)ep->var, (char *)var) == 0) return(ep); } return(NULL); } void env_init() { extern char **environ; register char **epp, *cp; register struct env_lst *ep; extern char *index(); for (epp = environ; *epp; epp++) { if (cp = index(*epp, '=')) { *cp = '\0'; ep = env_define((unsigned char *)*epp, (unsigned char *)cp+1); ep->export = 0; *cp = '='; } } /* * Special case for DISPLAY variable. If it is ":0.0" or * "unix:0.0", we have to get rid of "unix" and insert our * hostname. */ if ((ep = env_find("DISPLAY")) && ((*ep->value == ':') || (strncmp((char *)ep->value, "unix:", 5) == 0))) { char hbuf[256+1]; char *cp2 = index((char *)ep->value, ':'); gethostname(hbuf, 256); hbuf[256] = '\0'; cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1); sprintf((char *)cp, "%s%s", hbuf, cp2); free(ep->value); ep->value = (unsigned char *)cp; } /* * If USER is not defined, but LOGNAME is, then add * USER with the value from LOGNAME. By default, we * don't export the USER variable. */ if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) { env_define((unsigned char *)"USER", ep->value); env_unexport((unsigned char *)"USER"); } env_export((unsigned char *)"DISPLAY"); env_export((unsigned char *)"PRINTER"); } struct env_lst * env_define(var, value) unsigned char *var, *value; { register struct env_lst *ep; if (ep = env_find(var)) { if (ep->var) free(ep->var); if (ep->value) free(ep->value); } else { ep = (struct env_lst *)malloc(sizeof(struct env_lst)); ep->next = envlisthead.next; envlisthead.next = ep; ep->prev = &envlisthead; if (ep->next) ep->next->prev = ep; } ep->welldefined = opt_welldefined(var); ep->export = 1; ep->var = (unsigned char *)strdup((char *)var); ep->value = (unsigned char *)strdup((char *)value); return(ep); } void env_undefine(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) { ep->prev->next = ep->next; if (ep->next) ep->next->prev = ep->prev; if (ep->var) free(ep->var); if (ep->value) free(ep->value); free(ep); } } void env_export(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) ep->export = 1; } void env_unexport(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) ep->export = 0; } void env_send(var) unsigned char *var; { register struct env_lst *ep; if (my_state_is_wont(TELOPT_NEW_ENVIRON) #ifdef OLD_ENVIRON && my_state_is_wont(TELOPT_OLD_ENVIRON) #endif ) { fprintf(stderr, "Cannot send '%s': Telnet ENVIRON option not enabled\n", var); return; } ep = env_find(var); if (ep == 0) { fprintf(stderr, "Cannot send '%s': variable not defined\n", var); return; } env_opt_start_info(); env_opt_add(ep->var); env_opt_end(0); } void env_list() { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { printf("%c %-20s %s\n", ep->export ? '*' : ' ', ep->var, ep->value); } } unsigned char * env_default(init, welldefined) int init; { static struct env_lst *nep = NULL; if (init) { nep = &envlisthead; return; } if (nep) { while (nep = nep->next) { if (nep->export && (nep->welldefined == welldefined)) return(nep->var); } } return(NULL); } unsigned char * env_getvalue(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) return(ep->value); return(NULL); } #if defined(OLD_ENVIRON) && defined(ENV_HACK) void env_varval(what) unsigned char *what; { extern int old_env_var, old_env_value, env_auto; int len = strlen((char *)what); if (len == 0) goto unknown; if (strncasecmp((char *)what, "status", len) == 0) { if (env_auto) printf("%s%s", "VAR and VALUE are/will be ", "determined automatically\n"); if (old_env_var == OLD_ENV_VAR) printf("VAR and VALUE set to correct definitions\n"); else printf("VAR and VALUE definitions are reversed\n"); } else if (strncasecmp((char *)what, "auto", len) == 0) { env_auto = 1; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else if (strncasecmp((char *)what, "right", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VAR; old_env_value = OLD_ENV_VALUE; } else if (strncasecmp((char *)what, "wrong", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else { unknown: printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n"); } } #endif #if defined(AUTHENTICATION) /* * The AUTHENTICATE command. */ struct authlist { char *name; char *help; int (*handler)(); int narg; }; extern int auth_enable P((int)), auth_disable P((int)), auth_status P((void)); static int auth_help P((void)); struct authlist AuthList[] = { { "status", "Display current status of authentication information", auth_status, 0 }, { "disable", "Disable an authentication type ('auth disable ?' for more)", auth_disable, 1 }, { "enable", "Enable an authentication type ('auth enable ?' for more)", auth_enable, 1 }, { "help", 0, auth_help, 0 }, { "?", "Print help information", auth_help, 0 }, { 0 }, }; static int auth_help() { struct authlist *c; for (c = AuthList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } auth_cmd(argc, argv) int argc; char *argv[]; { struct authlist *c; c = (struct authlist *) genget(argv[1], (char **) AuthList, sizeof(struct authlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } return((*c->handler)(argv[2], argv[3])); } #endif #if defined(unix) && defined(TN3270) static void filestuff(fd) int fd; { int res; #ifdef F_GETOWN setconnmode(0); res = fcntl(fd, F_GETOWN, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } printf("\tOwner is %d.\n", res); #endif setconnmode(0); res = fcntl(fd, F_GETFL, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } #ifdef notdef printf("\tFlags are 0x%x: %s\n", res, decodeflags(res)); #endif } #endif /* defined(unix) && defined(TN3270) */ /* * Print status about the connection. */ /*ARGSUSED*/ static status(argc, argv) int argc; char *argv[]; { if (connected) { printf("Connected to %s.\n", hostname); if ((argc < 2) || strcmp(argv[1], "notmuch")) { int mode = getconnmode(); if (my_want_state_is_will(TELOPT_LINEMODE)) { printf("Operating with LINEMODE option\n"); printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No"); printf("%s catching of signals\n", (mode&MODE_TRAPSIG) ? "Local" : "No"); slcstate(); #ifdef KLUDGELINEMODE } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) { printf("Operating in obsolete linemode\n"); #endif } else { printf("Operating in single character mode\n"); if (localchars) printf("Catching signals locally\n"); } printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote"); if (my_want_state_is_will(TELOPT_LFLOW)) printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No"); } } else { printf("No connection.\n"); } # if !defined(TN3270) printf("Escape character is '%s'.\n", control(escape)); (void) fflush(stdout); # else /* !defined(TN3270) */ if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { printf("Escape character is '%s'.\n", control(escape)); } # if defined(unix) if ((argc >= 2) && !strcmp(argv[1], "everything")) { printf("SIGIO received %d time%s.\n", sigiocount, (sigiocount == 1)? "":"s"); if (In3270) { printf("Process ID %d, process group %d.\n", getpid(), getpgrp(getpid())); printf("Terminal input:\n"); filestuff(tin); printf("Terminal output:\n"); filestuff(tout); printf("Network socket:\n"); filestuff(net); } } if (In3270 && transcom) { printf("Transparent mode command is '%s'.\n", transcom); } # endif /* defined(unix) */ (void) fflush(stdout); if (In3270) { return 0; } # endif /* defined(TN3270) */ return 1; } #ifdef SIGINFO /* * Function that gets called when SIGINFO is received. */ ayt_status() { (void) call(status, "status", "notmuch", 0); } #endif +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif + int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - bzero((char *)&sin, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); setuid(getuid()); return 0; } if (argc < 2) { (void) strcpy(line, "open "); printf("(to) "); (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin); makeargv(); argc = margc; argv = margv; } cmd = *argv; --argc; ++argv; while (argc) { if (isprefix(*argv, "help") || isprefix(*argv, "?")) goto usage; if (strcmp(*argv, "-l") == 0) { --argc; ++argv; if (argc == 0) goto usage; user = *argv++; --argc; continue; } if (strcmp(*argv, "-a") == 0) { --argc; ++argv; autologin = 1; continue; } if (strcmp(*argv, "-s") == 0) { --argc; ++argv; if (argc == 0) goto usage; src_addr = *argv++; --argc; continue; } if (hostp == 0) { hostp = *argv++; --argc; continue; } if (portp == 0) { portp = *argv++; --argc; continue; } usage: printf("usage: telnet [-l user] [-a] [-s src_addr] host-name [port]\n"); setuid(getuid()); return 0; } if (hostp == 0) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); - return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); + setuid(getuid()); + return 0; + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { setuid(getuid()); + freeaddrinfo(res); return 0; - } else if (temp == -1) { + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], - MIN(host->h_length, sizeof(sin.sin_addr))); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, - MIN(host->h_length, sizeof(sin.sin_addr))); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; - } - telnetport = 1; - } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = IPTOS_LOWDELAY; if (tos && (setsockopt(net, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) && (errno != ENOPROTOOPT)) perror("telnet: setsockopt (IP_TOS) (ignored)"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { perror("setsockopt (SO_DEBUG)"); } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memcpy((caddr_t)&sin.sin_addr, host->h_addr_list[0], - MIN(host->h_length, sizeof(sin.sin_addr))); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } connected++; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; user = getenv("USER"); if (user == NULL || (pw = getpwnam(user)) && pw->pw_uid != getuid()) { if (pw = getpwuid(getuid())) user = pw->pw_name; else user = NULL; } } if (user) { env_define((unsigned char *)"USER", (unsigned char *)user); env_export((unsigned char *)"USER"); } (void) call(status, "status", "notmuch", 0); if (setjmp(peerdied) == 0) telnet(user); (void) NetClose(net); ExitString("Connection closed by foreign host.\n",1); /*NOTREACHED*/ } #define HELPINDENT (sizeof ("connect")) static char openhelp[] = "connect to a site", closehelp[] = "close current connection", logouthelp[] = "forcibly logout remote user and close the connection", quithelp[] = "exit telnet", statushelp[] = "print status information", helphelp[] = "print help information", sendhelp[] = "transmit special characters ('send ?' for more)", sethelp[] = "set operating parameters ('set ?' for more)", unsethelp[] = "unset operating parameters ('unset ?' for more)", togglestring[] ="toggle operating parameters ('toggle ?' for more)", slchelp[] = "change state of special charaters ('slc ?' for more)", displayhelp[] = "display operating parameters", #if defined(TN3270) && defined(unix) transcomhelp[] = "specify Unix command for transparent mode pipe", #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) authhelp[] = "turn on (off) authentication ('auth ?' for more)", #endif #if defined(unix) zhelp[] = "suspend telnet", #endif /* defined(unix) */ #if defined(SKEY) skeyhelp[] = "compute response to s/key challenge", #endif shellhelp[] = "invoke a subshell", envhelp[] = "change environment variables ('environ ?' for more)", modestring[] = "try to enter line or character mode ('mode ?' for more)"; static int help(); static Command cmdtab[] = { { "close", closehelp, bye, 1 }, { "logout", logouthelp, logout, 1 }, { "display", displayhelp, display, 0 }, { "mode", modestring, modecmd, 0 }, { "telnet", openhelp, tn, 0 }, { "open", openhelp, tn, 0 }, { "quit", quithelp, quit, 0 }, { "send", sendhelp, sendcmd, 0 }, { "set", sethelp, setcmd, 0 }, { "unset", unsethelp, unsetcmd, 0 }, { "status", statushelp, status, 0 }, { "toggle", togglestring, toggle, 0 }, { "slc", slchelp, slccmd, 0 }, #if defined(TN3270) && defined(unix) { "transcom", transcomhelp, settranscom, 0 }, #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) { "auth", authhelp, auth_cmd, 0 }, #endif #if defined(unix) { "z", zhelp, suspend, 0 }, #endif /* defined(unix) */ #if defined(TN3270) { "!", shellhelp, shell, 1 }, #else { "!", shellhelp, shell, 0 }, #endif { "environ", envhelp, env_cmd, 0 }, { "?", helphelp, help, 0 }, #if defined(SKEY) { "skey", skeyhelp, skey_calc, 0 }, #endif 0 }; static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; static char escapehelp[] = "deprecated command -- use 'set escape' instead"; static Command cmdtab2[] = { { "help", 0, help, 0 }, { "escape", escapehelp, setescape, 0 }, { "crmod", crmodhelp, togcrmod, 0 }, 0 }; /* * Call routine with argc, argv set from args (terminated by 0). */ /*VARARGS1*/ static call(va_alist) va_dcl { va_list ap; typedef int (*intrtn_t)(); intrtn_t routine; char *args[100]; int argno = 0; va_start(ap); routine = (va_arg(ap, intrtn_t)); while ((args[argno++] = va_arg(ap, char *)) != 0) { ; } va_end(ap); return (*routine)(argno-1, args); } static Command * getcmd(name) char *name; { Command *cm; if (cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))) return cm; return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); } void command(top, tbuf, cnt) int top; char *tbuf; int cnt; { register Command *c; setcommandmode(); if (!top) { putchar('\n'); #if defined(unix) } else { (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); #endif /* defined(unix) */ } for (;;) { if (rlogin == _POSIX_VDISABLE) printf("%s> ", prompt); if (tbuf) { register char *cp; cp = line; while (cnt > 0 && (*cp++ = *tbuf++) != '\n') cnt--; tbuf = 0; if (cp == line || *--cp != '\n' || cp == line) goto getline; *cp = '\0'; if (rlogin == _POSIX_VDISABLE) printf("%s\n", line); } else { getline: if (rlogin != _POSIX_VDISABLE) printf("%s> ", prompt); if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin) || ferror(stdin)) { (void) quit(); /*NOTREACHED*/ } break; } } if (line[0] == 0) break; makeargv(); if (margv[0] == 0) { break; } c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command\n"); continue; } if (c == 0) { printf("?Invalid command\n"); continue; } if (c->needconnect && !connected) { printf("?Need to be connected first.\n"); continue; } if ((*c->handler)(margc, margv)) { break; } } if (!top) { if (!connected) { longjmp(toplevel, 1); /*NOTREACHED*/ } #if defined(TN3270) if (shell_active == 0) { setconnmode(0); } #else /* defined(TN3270) */ setconnmode(0); #endif /* defined(TN3270) */ } } /* * Help command. */ static help(argc, argv) int argc; char *argv[]; { register Command *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) if (c->help) { printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); } return 0; } while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (Ambiguous(c)) printf("?Ambiguous help command %s\n", arg); else if (c == (Command *)0) printf("?Invalid help command %s\n", arg); else printf("%s\n", c->help); } return 0; } static char *rcname = 0; static char rcbuf[128]; cmdrc(m1, m2) char *m1, *m2; { register Command *c; FILE *rcfile; int gotmachine = 0; int l1 = strlen(m1); int l2 = strlen(m2); char m1save[64]; if (skiprc) return; strcpy(m1save, m1); m1 = m1save; if (rcname == 0) { rcname = getenv("HOME"); if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf)) strcpy(rcbuf, rcname); else rcbuf[0] = '\0'; strcat(rcbuf, "/.telnetrc"); rcname = rcbuf; } if ((rcfile = fopen(rcname, "r")) == 0) { return; } for (;;) { if (fgets(line, sizeof(line), rcfile) == NULL) break; if (line[0] == 0) break; if (line[0] == '#') continue; if (gotmachine) { if (!isspace(line[0])) gotmachine = 0; } if (gotmachine == 0) { if (isspace(line[0])) continue; if (strncasecmp(line, m1, l1) == 0) strncpy(line, &line[l1], sizeof(line) - l1); else if (strncasecmp(line, m2, l2) == 0) strncpy(line, &line[l2], sizeof(line) - l2); else if (strncasecmp(line, "DEFAULT", 7) == 0) strncpy(line, &line[7], sizeof(line) - 7); else continue; if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n') continue; gotmachine = 1; } makeargv(); if (margv[0] == 0) continue; c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command: %s\n", margv[0]); continue; } if (c == 0) { printf("?Invalid command: %s\n", margv[0]); continue; } /* * This should never happen... */ if (c->needconnect && !connected) { printf("?Need to be connected first for %s.\n", margv[0]); continue; } (*c->handler)(margc, margv); } fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst * If the leading ! is present, it is a * strict source route, otherwise it is * assmed to be a loose source route. * * We fill in the source route option as * hop1,hop2,hop3...dest * and return a pointer to hop1, which will * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a * pointer to a pointer to a character array * that should be filled in with the option. * * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the * path is unknown, and *cpp is set to point to the bad * hostname. * * *cpp: If *cpp was equal to NULL, it will be filled * in with a pointer to our static area that has * the option filled in. This will be 32bit aligned. * * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* * Verify the arguments, and make sure we have * at least 7 bytes for the option. */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in * the begining of the option. */ #ifndef sysV88 if (*cp == '!') { cp++; *lsrp++ = IPOPT_SSRR; } else *lsrp++ = IPOPT_LSRR; #else if (*cp == '!') { cp++; ipopt.io_type = IPOPT_SSRR; } else ipopt.io_type = IPOPT_LSRR; #endif if (*cp != '@') return((unsigned long)-1); #ifndef sysV88 lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; c = *cp2; cp2++) { if (c == ',') { *cp2++ = '\0'; if (*cp2 == '@') cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; break; } if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if (host = gethostbyname(cp)) { -#if defined(h_addr) - memcpy((caddr_t)&sin_addr, host->h_addr_list[0], - MIN(host->h_length,sizeof(sin_addr))); -#else - memcpy((caddr_t)&sin_addr, host->h_addr, - MIN(host->h_length,sizeof(sin_addr))); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memcpy(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else break; /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) + return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ *lenp = lsrp - *cpp; #else ipopt.io_len = lsrp - *cpp; if (ipopt.io_len <= 5) { /* Is 3 better ? */ *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h index a8c3f24f57fd..10e89acdee36 100644 --- a/usr.bin/telnet/externs.h +++ b/usr.bin/telnet/externs.h @@ -1,479 +1,489 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. * * @(#)externs.h 8.2 (Berkeley) 12/15/93 + * $FreeBSD$ */ #ifndef BSD # define BSD 43 #endif /* * ucb stdio.h defines BSD as something wierd */ #if defined(sun) && defined(__svr4__) #define BSD 43 #endif #ifndef USE_TERMIO # if BSD > 43 || defined(SYSV_TERMIO) # define USE_TERMIO # endif #endif #include #include #if defined(CRAY) && !defined(NO_BSD_SETJMP) #include #endif #ifndef FILIO_H #include #else #include #endif #ifdef CRAY # include #endif /* CRAY */ #ifdef USE_TERMIO # ifndef VINTR # ifdef SYSV_TERMIO # include # else # include # define termio termios # endif # endif #endif #if defined(NO_CC_T) || !defined(USE_TERMIO) # if !defined(USE_TERMIO) typedef char cc_t; # else typedef unsigned char cc_t; # endif #endif #ifndef NO_STRING_H #include #endif #include +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ # endif # ifdef VDISABLE # define _POSIX_VDISABLE VDISABLE # else # define _POSIX_VDISABLE ((cc_t)'\377') # endif #endif #define SUBBUFSIZE 256 #ifndef CRAY extern int errno; /* outside this world */ #endif /* !CRAY */ #if !defined(P) # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ In3270, /* Are we in 3270 mode? */ telnetport, /* Are we connected to the telnet port? */ localflow, /* Flow control handled locally */ restartany, /* If flow control, restart output on any character */ localchars, /* we recognize interrupt/quit */ donelclchars, /* the user has set "localchars" */ showoptions, net, /* Network file descriptor */ tin, /* Terminal input file descriptor */ tout, /* Terminal output file descriptor */ crlf, /* Should '\r' be mapped to (or )? */ autoflush, /* flush output when interrupting? */ autosynch, /* send interrupt characters with SYNCH? */ SYNCHing, /* Is the stream in telnet SYNCH mode? */ donebinarytoggle, /* the user has put us in binary */ dontlecho, /* do we suppress local echoing right now? */ crmod, netdata, /* Print out network data flow */ prettydump, /* Print "netdata" output in user readable format */ #if defined(unix) #if defined(TN3270) cursesdata, /* Print out curses data flow */ apitrace, /* Trace API transactions */ #endif /* defined(TN3270) */ termdata, /* Print out terminal data flow */ #endif /* defined(unix) */ debug, /* Debug level */ doaddrlookup, /* do a reverse lookup? */ clienteof; /* Client received EOF */ extern cc_t escape; /* Escape to command mode */ extern cc_t rlogin; /* Rlogin mode escape character */ #ifdef KLUDGELINEMODE extern cc_t echoc; /* Toggle local echoing */ #endif extern char *prompt; /* Prompt for command. */ extern char doopt[], dont[], will[], wont[], options[], /* All the little options */ *hostname; /* Who are we connected to? */ /* * We keep track of each side of the option negotiation. */ #define MY_STATE_WILL 0x01 #define MY_WANT_STATE_WILL 0x02 #define MY_STATE_DO 0x04 #define MY_WANT_STATE_DO 0x08 /* * Macros to check the current state of things */ #define my_state_is_do(opt) (options[opt]&MY_STATE_DO) #define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) #define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) #define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) #define my_state_is_dont(opt) (!my_state_is_do(opt)) #define my_state_is_wont(opt) (!my_state_is_will(opt)) #define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) #define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) #define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;} #define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;} #define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;} #define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;} #define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;} #define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;} #define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;} #define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;} /* * Make everything symetrical */ #define HIS_STATE_WILL MY_STATE_DO #define HIS_WANT_STATE_WILL MY_WANT_STATE_DO #define HIS_STATE_DO MY_STATE_WILL #define HIS_WANT_STATE_DO MY_WANT_STATE_WILL #define his_state_is_do my_state_is_will #define his_state_is_will my_state_is_do #define his_want_state_is_do my_want_state_is_will #define his_want_state_is_will my_want_state_is_do #define his_state_is_dont my_state_is_wont #define his_state_is_wont my_state_is_dont #define his_want_state_is_dont my_want_state_is_wont #define his_want_state_is_wont my_want_state_is_dont #define set_his_state_do set_my_state_will #define set_his_state_will set_my_state_do #define set_his_want_state_do set_my_want_state_will #define set_his_want_state_will set_my_want_state_do #define set_his_state_dont set_my_state_wont #define set_his_state_wont set_my_state_dont #define set_his_want_state_dont set_my_want_state_wont #define set_his_want_state_wont set_my_want_state_dont extern FILE *NetTrace; /* Where debugging output goes */ extern unsigned char NetTraceFile[]; /* Name of file where debugging output goes */ extern void SetNetTrace P((char *)); /* Function to change where debugging goes */ extern jmp_buf peerdied, toplevel; /* For error conditions. */ extern void command P((int, char *, int)), Dump P((int, unsigned char *, int)), init_3270 P((void)), printoption P((char *, int, int)), printsub P((int, unsigned char *, int)), sendnaws P((void)), setconnmode P((int)), setcommandmode P((void)), setneturg P((void)), sys_telnet_init P((void)), telnet P((char *)), tel_enter_binary P((int)), TerminalFlushOutput P((void)), TerminalNewMode P((int)), TerminalRestoreState P((void)), TerminalSaveState P((void)), tninit P((void)), upcase P((char *)), willoption P((int)), wontoption P((int)); extern void send_do P((int, int)), send_dont P((int, int)), send_will P((int, int)), send_wont P((int, int)); extern void lm_will P((unsigned char *, int)), lm_wont P((unsigned char *, int)), lm_do P((unsigned char *, int)), lm_dont P((unsigned char *, int)), lm_mode P((unsigned char *, int, int)); extern void slc_init P((void)), slcstate P((void)), slc_mode_export P((void)), slc_mode_import P((int)), slc_import P((int)), slc_export P((void)), slc P((unsigned char *, int)), slc_check P((void)), slc_start_reply P((void)), slc_add_reply P((int, int, int)), slc_end_reply P((void)); extern int slc_update P((void)); extern void env_opt P((unsigned char *, int)), env_opt_start P((void)), env_opt_start_info P((void)), env_opt_add P((unsigned char *)), env_opt_end P((int)); extern unsigned char *env_default P((int, int)), *env_getvalue P((unsigned char *)); extern int get_status P((void)), dosynch P((void)); extern cc_t *tcval P((int)); #ifndef USE_TERMIO extern struct tchars ntc; extern struct ltchars nltc; extern struct sgttyb nttyb; # define termEofChar ntc.t_eofc # define termEraseChar nttyb.sg_erase # define termFlushChar nltc.t_flushc # define termIntChar ntc.t_intrc # define termKillChar nttyb.sg_kill # define termLiteralNextChar nltc.t_lnextc # define termQuitChar ntc.t_quitc # define termSuspChar nltc.t_suspc # define termRprntChar nltc.t_rprntc # define termWerasChar nltc.t_werasc # define termStartChar ntc.t_startc # define termStopChar ntc.t_stopc # define termForw1Char ntc.t_brkc extern cc_t termForw2Char; extern cc_t termAytChar; # define termEofCharp (cc_t *)&ntc.t_eofc # define termEraseCharp (cc_t *)&nttyb.sg_erase # define termFlushCharp (cc_t *)&nltc.t_flushc # define termIntCharp (cc_t *)&ntc.t_intrc # define termKillCharp (cc_t *)&nttyb.sg_kill # define termLiteralNextCharp (cc_t *)&nltc.t_lnextc # define termQuitCharp (cc_t *)&ntc.t_quitc # define termSuspCharp (cc_t *)&nltc.t_suspc # define termRprntCharp (cc_t *)&nltc.t_rprntc # define termWerasCharp (cc_t *)&nltc.t_werasc # define termStartCharp (cc_t *)&ntc.t_startc # define termStopCharp (cc_t *)&ntc.t_stopc # define termForw1Charp (cc_t *)&ntc.t_brkc # define termForw2Charp (cc_t *)&termForw2Char # define termAytCharp (cc_t *)&termAytChar # else extern struct termio new_tc; # define termEofChar new_tc.c_cc[VEOF] # define termEraseChar new_tc.c_cc[VERASE] # define termIntChar new_tc.c_cc[VINTR] # define termKillChar new_tc.c_cc[VKILL] # define termQuitChar new_tc.c_cc[VQUIT] # ifndef VSUSP extern cc_t termSuspChar; # else # define termSuspChar new_tc.c_cc[VSUSP] # endif # if defined(VFLUSHO) && !defined(VDISCARD) # define VDISCARD VFLUSHO # endif # ifndef VDISCARD extern cc_t termFlushChar; # else # define termFlushChar new_tc.c_cc[VDISCARD] # endif # ifndef VWERASE extern cc_t termWerasChar; # else # define termWerasChar new_tc.c_cc[VWERASE] # endif # ifndef VREPRINT extern cc_t termRprntChar; # else # define termRprntChar new_tc.c_cc[VREPRINT] # endif # ifndef VLNEXT extern cc_t termLiteralNextChar; # else # define termLiteralNextChar new_tc.c_cc[VLNEXT] # endif # ifndef VSTART extern cc_t termStartChar; # else # define termStartChar new_tc.c_cc[VSTART] # endif # ifndef VSTOP extern cc_t termStopChar; # else # define termStopChar new_tc.c_cc[VSTOP] # endif # ifndef VEOL extern cc_t termForw1Char; # else # define termForw1Char new_tc.c_cc[VEOL] # endif # ifndef VEOL2 extern cc_t termForw2Char; # else # define termForw2Char new_tc.c_cc[VEOL] # endif # ifndef VSTATUS extern cc_t termAytChar; #else # define termAytChar new_tc.c_cc[VSTATUS] #endif # if !defined(CRAY) || defined(__STDC__) # define termEofCharp &termEofChar # define termEraseCharp &termEraseChar # define termIntCharp &termIntChar # define termKillCharp &termKillChar # define termQuitCharp &termQuitChar # define termSuspCharp &termSuspChar # define termFlushCharp &termFlushChar # define termWerasCharp &termWerasChar # define termRprntCharp &termRprntChar # define termLiteralNextCharp &termLiteralNextChar # define termStartCharp &termStartChar # define termStopCharp &termStopChar # define termForw1Charp &termForw1Char # define termForw2Charp &termForw2Char # define termAytCharp &termAytChar # else /* Work around a compiler bug */ # define termEofCharp 0 # define termEraseCharp 0 # define termIntCharp 0 # define termKillCharp 0 # define termQuitCharp 0 # define termSuspCharp 0 # define termFlushCharp 0 # define termWerasCharp 0 # define termRprntCharp 0 # define termLiteralNextCharp 0 # define termStartCharp 0 # define termStopCharp 0 # define termForw1Charp 0 # define termForw2Charp 0 # define termAytCharp 0 # endif #endif /* Ring buffer structures which are shared */ extern Ring netoring, netiring, ttyoring, ttyiring; /* Tn3270 section */ #if defined(TN3270) extern int HaveInput, /* Whether an asynchronous I/O indication came in */ noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */ noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */ sigiocount, /* Count of SIGIO receptions */ shell_active; /* Subshell is active */ extern char *Ibackp, /* Oldest byte of 3270 data */ Ibuf[], /* 3270 buffer */ *Ifrontp, /* Where next 3270 byte goes */ tline[], *transcom; /* Transparent command */ extern int settranscom P((int, char**)); extern void inputAvailable P((int)); #endif /* defined(TN3270) */ diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c index 298389c7eafd..9126738c848b 100644 --- a/usr.bin/telnet/main.c +++ b/usr.bin/telnet/main.c @@ -1,326 +1,366 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. + * + * $FreeBSD$ */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include +#include #include "ring.h" #include "externs.h" #include "defines.h" /* These values need to be the same as defined in libtelnet/kerberos5.c */ /* Either define them in both places, or put in some common header file. */ #define OPTS_FORWARD_CREDS 0x00000002 #define OPTS_FORWARDABLE_CREDS 0x00000001 #if 0 #define FORWARD #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ void tninit() { init_terminal(); init_network(); init_telnet(); init_sys(); #if defined(TN3270) init_3270(); #endif } void usage() { fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) # ifdef AUTHENTICATION "[-noasynch] [-noasynctty]\n\t" "[-noasyncnet] [-r] [-s src_addr] [-t transcom] ", # else "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t" "[-s src_addr] [-t transcom]", # endif #else "[-r] [-s src_addr] ", +#endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" #endif "[host-name [port]]" ); exit(1); } /* * main. Parse arguments, invoke the protocol or command parser. */ main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int ch; char *user, *strrchr(); char *src_addr = NULL; #ifdef FORWARD extern int forward_flags; #endif /* FORWARD */ tninit(); /* Clear out things */ #if defined(CRAY) && !defined(__STDC__) _setlist_init(); /* Work around compiler bug */ #endif TerminalSaveState(); if (prompt = strrchr(argv[0], '/')) ++prompt; else prompt = argv[0]; user = NULL; rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != -1) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { + switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; case 'E': rlogin = escape = _POSIX_VDISABLE; break; case 'K': #ifdef AUTHENTICATION autologin = 0; #endif break; case 'L': eight |= 2; /* binary output only */ break; case 'N': doaddrlookup = 0; break; case 'S': { #ifdef HAS_GETTOS extern int tos; if ((tos = parsetos(optarg, "tcp")) < 0) fprintf(stderr, "%s%s%s%s\n", prompt, ": Bad TOS argument '", optarg, "; will try to use default TOS"); #else fprintf(stderr, "%s: Warning: -S ignored, no parsetos() support.\n", prompt); #endif } break; case 'X': #ifdef AUTHENTICATION auth_disable_name(optarg); #endif break; case 'a': autologin = 1; break; case 'c': skiprc = 1; break; case 'd': debug = 1; break; case 'e': set_escape_char(optarg); break; case 'f': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; #else fprintf(stderr, "%s: Warning: -f ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'F': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; forward_flags |= OPTS_FORWARDABLE_CREDS; #else fprintf(stderr, "%s: Warning: -F ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'k': #if defined(AUTHENTICATION) && defined(KRB4) { extern char *dest_realm, dst_realm_buf[], dst_realm_sz; dest_realm = dst_realm_buf; (void)strncpy(dest_realm, optarg, dst_realm_sz); } #else fprintf(stderr, "%s: Warning: -k ignored, no Kerberos V4 support.\n", prompt); #endif break; case 'l': autologin = 1; user = optarg; break; case 'n': #if defined(TN3270) && defined(unix) /* distinguish between "-n oasynch" and "-noasynch" */ if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == 'n' && argv[optind - 1][2] == 'o') { if (!strcmp(optarg, "oasynch")) { noasynchtty = 1; noasynchnet = 1; } else if (!strcmp(optarg, "oasynchtty")) noasynchtty = 1; else if (!strcmp(optarg, "oasynchnet")) noasynchnet = 1; } else #endif /* defined(TN3270) && defined(unix) */ SetNetTrace(optarg); break; case 'r': rlogin = '~'; break; case 's': src_addr = optarg; break; case 't': #if defined(TN3270) && defined(unix) transcom = tline; (void)strcpy(transcom, optarg); #else fprintf(stderr, "%s: Warning: -t ignored, no TN3270 support.\n", prompt); #endif break; case 'x': fprintf(stderr, "%s: Warning: -x ignored, no ENCRYPT support.\n", prompt); break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); /* NOTREACHED */ } } if (autologin == -1) autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1; argc -= optind; argv += optind; if (argc) { char *args[9], **argp = args; if (argc > 2) usage(); *argp++ = prompt; if (user) { *argp++ = "-l"; *argp++ = user; } if (src_addr) { *argp++ = "-s"; *argp++ = src_addr; } *argp++ = argv[0]; /* host */ if (argc > 1) *argp++ = argv[1]; /* port */ *argp = 0; if (setjmp(toplevel) != 0) Exit(0); if (tn(argp - args, args) == 1) return (0); else return (1); } (void)setjmp(toplevel); for (;;) { #ifdef TN3270 if (shell_active) shell_continue(); else #endif command(1, 0, 0); } } diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1 index ca2e968be6fd..dfa4fcfe75a9 100644 --- a/usr.bin/telnet/telnet.1 +++ b/usr.bin/telnet/telnet.1 @@ -1,1403 +1,1406 @@ .\" Copyright (c) 1983, 1990, 1993 .\" The Regents of the University of California. 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 the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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. .\" .\" @(#)telnet.1 8.5 (Berkeley) 3/1/94 .\" $FreeBSD$ .\" -.Dd March 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME .Nm telnet .Nd user interface to the .Tn TELNET protocol .Sh SYNOPSIS .Nm .Op Fl 8EFKLNacdfrx .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl e Ar escapechar .Op Fl k Ar realm .Op Fl l Ar user .Op Fl n Ar tracefile .Op Fl s Ar src_addr .Oo .Ar host .Op Ar port .Oc .Sh DESCRIPTION The .Nm command is used to communicate with another host using the .Tn TELNET protocol. If .Nm is invoked without the .Ar host argument, it enters command mode, indicated by its prompt .Pq Nm telnet\&> . In this mode, it accepts and executes the commands listed below. If it is invoked with arguments, it performs an .Ic open command with those arguments. .Pp Options: .Bl -tag -width indent .It Fl 8 Specifies an 8-bit data path. This causes an attempt to negotiate the .Dv TELNET BINARY option on both input and output. .It Fl E Stops any character from being recognized as an escape character. .It Fl F If Kerberos V5 authentication is being used, the .Fl F option allows the local credentials to be forwarded to the remote system, including any credentials that have already been forwarded into the local environment. .It Fl K Specifies no automatic login to the remote system. .It Fl L Specifies an 8-bit data path on output. This causes the .Dv BINARY option to be negotiated on output. .It Fl N Prevents IP address to name lookup when destination host is given as an IP address. .It Fl S Ar tos Sets the IP type-of-service (TOS) option for the telnet connection to the value .Ar tos, which can be a numeric TOS value or, on systems that support it, a symbolic TOS name found in the .Pa /etc/iptos file. .It Fl X Ar atype Disables the .Ar atype type of authentication. .It Fl a Attempt automatic login. Currently, this sends the user name via the .Ev USER variable of the .Ev ENVIRON option if supported by the remote system. The name used is that of the current user as returned by .Xr getlogin 2 if it agrees with the current user ID, otherwise it is the name associated with the user ID. .It Fl c Disables the reading of the user's .Pa \&.telnetrc file. (See the .Ic toggle skiprc command on this man page.) .It Fl d Sets the initial value of the .Ic debug toggle to .Dv TRUE . .It Fl e Ar escapechar Sets the initial .Nm escape character to .Ar escapechar . If .Ar escapechar is omitted, then there will be no escape character. .It Fl f If Kerberos V5 authentication is being used, the .Fl f option allows the local credentials to be forwarded to the remote system. .It Fl k Ar realm If Kerberos authentication is being used, the .Fl k option requests that .Nm obtain tickets for the remote host in realm .Ar realm instead of the remote host's realm, as determined by .Xr krb_realmofhost 3 . .It Fl l Ar user When connecting to the remote system, if the remote system understands the .Ev ENVIRON option, then .Ar user will be sent to the remote system as the value for the variable .Ev USER . This option implies the .Fl a option. This option may also be used with the .Ic open command. .It Fl n Ar tracefile Opens .Ar tracefile for recording trace information. See the .Ic set tracefile command below. .It Fl r Specifies a user interface similar to .Xr rlogin 1 . In this mode, the escape character is set to the tilde (~) character, unless modified by the .Fl e option. .It Fl s Ar src_addr Set the source IP address for the .Nm connection to .Ar src_addr , which can be an IP address or a host name. .It Fl x Turns on encryption of the data stream if possible. This option is not available outside of the United States and Canada. .It Ar host Indicates the official name, an alias, or the Internet address of a remote host. .It Ar port Indicates a port number (address of an application). If a number is not specified, the default .Nm port is used. .El .Pp When in rlogin mode, a line of the form ~. disconnects from the remote host; ~ is the .Nm escape character. Similarly, the line ~^Z suspends the .Nm session. The line ~^] escapes to the normal .Nm escape prompt. .Pp Once a connection has been opened, .Nm will attempt to enable the .Dv TELNET LINEMODE option. If this fails, then .Nm will revert to one of two input modes: either \*(Lqcharacter at a time\*(Rq or \*(Lqold line by line\*(Rq depending on what the remote system supports. .Pp When .Dv LINEMODE is enabled, character processing is done on the local system, under the control of the remote system. When input editing or character echoing is to be disabled, the remote system will relay that information. The remote system will also relay changes to any special characters that happen on the remote system, so that they can take effect on the local system. .Pp In \*(Lqcharacter at a time\*(Rq mode, most text typed is immediately sent to the remote host for processing. .Pp In \*(Lqold line by line\*(Rq mode, all text is echoed locally, and (normally) only completed lines are sent to the remote host. The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used to turn off and on the local echo (this would mostly be used to enter passwords without the password being echoed). .Pp If the .Dv LINEMODE option is enabled, or if the .Ic localchars toggle is .Dv TRUE (the default for \*(Lqold line by line\*(Rq; see below), the user's .Ic quit , .Ic intr , and .Ic flush characters are trapped locally, and sent as .Tn TELNET protocol sequences to the remote side. If .Dv LINEMODE has ever been enabled, then the user's .Ic susp and .Ic eof are also sent as .Tn TELNET protocol sequences, and .Ic quit is sent as a .Dv TELNET ABORT instead of .Dv BREAK . There are options (see .Ic toggle .Ic autoflush and .Ic toggle .Ic autosynch below) which cause this action to flush subsequent output to the terminal (until the remote host acknowledges the .Tn TELNET sequence) and flush previous terminal input (in the case of .Ic quit and .Ic intr ) . .Pp While connected to a remote host, .Nm command mode may be entered by typing the .Nm \*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq). When in command mode, the normal terminal editing conventions are available. .Pp The following .Nm commands are available. Only enough of each command to uniquely identify it need be typed (this is also true for arguments to the .Ic mode , .Ic set , .Ic toggle , .Ic unset , .Ic slc , .Ic environ , and .Ic display commands). .Pp .Bl -tag -width "mode type" .It Ic auth Ar argument ... The auth command manipulates the information sent through the .Dv TELNET AUTHENTICATE option. Valid arguments for the .Ic auth command are: .Bl -tag -width "disable type" .It Ic disable Ar type Disables the specified type of authentication. To obtain a list of available types, use the .Ic auth disable \&? command. .It Ic enable Ar type Enables the specified type of authentication. To obtain a list of available types, use the .Ic auth enable \&? command. .It Ic status Lists the current status of the various types of authentication. .El .It Ic close Close a .Tn TELNET session and return to command mode. .It Ic display Ar argument ... Displays all, or some, of the .Ic set and .Ic toggle values (see below). .It Ic encrypt Ar argument ... The encrypt command manipulates the information sent through the .Dv TELNET ENCRYPT option. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside of the United States and Canada. .Pp Valid arguments for the .Ic encrypt command are: .Bl -tag -width Ar .It Ic disable Ar type Ic [input|output] Disables the specified type of encryption. If you omit the input and output, both input and output are disabled. To obtain a list of available types, use the .Ic encrypt disable \&? command. .It Ic enable Ar type Ic [input|output] Enables the specified type of encryption. If you omit input and output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic input This is the same as the .Ic encrypt start input command. .It Ic -input This is the same as the .Ic encrypt stop input command. .It Ic output This is the same as the .Ic encrypt start output command. .It Ic -output This is the same as the .Ic encrypt stop output command. .It Ic start Ic [input|output] Attempts to start encryption. If you omit .Ic input and .Ic output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic status Lists the current status of encryption. .It Ic stop Ic [input|output] Stops encryption. If you omit input and output, encryption is on both input and output. .It Ic type Ar type Sets the default type of encryption to be used with later .Ic encrypt start or .Ic encrypt stop commands. .El .It Ic environ Ar arguments... The .Ic environ command is used to manipulate the variables that my be sent through the .Dv TELNET ENVIRON option. The initial set of variables is taken from the users environment, with only the .Ev DISPLAY and .Ev PRINTER variables being exported by default. The .Ev USER variable is also exported if the .Fl a or .Fl l options are used. .Pp Valid arguments for the .Ic environ command are: .Bl -tag -width Fl .It Ic define Ar variable value Define the variable .Ar variable to have a value of .Ar value. Any variables defined by this command are automatically exported. The .Ar value may be enclosed in single or double quotes so that tabs and spaces may be included. .It Ic undefine Ar variable Remove .Ar variable from the list of environment variables. .It Ic export Ar variable Mark the variable .Ar variable to be exported to the remote side. .It Ic unexport Ar variable Mark the variable .Ar variable to not be exported unless explicitly asked for by the remote side. .It Ic list List the current set of environment variables. Those marked with a .Cm * will be sent automatically, other variables will only be sent if explicitly requested. .It Ic \&? Prints out help information for the .Ic environ command. .El .It Ic logout Sends the .Dv TELNET LOGOUT option to the remote side. This command is similar to a .Ic close command; however, if the remote side does not support the .Dv LOGOUT option, nothing happens. If, however, the remote side does support the .Dv LOGOUT option, this command should cause the remote side to close the .Tn TELNET connection. If the remote side also supports the concept of suspending a user's session for later reattachment, the logout argument indicates that you should terminate the session immediately. .It Ic mode Ar type .Ar Type is one of several options, depending on the state of the .Tn TELNET session. The remote host is asked for permission to go into the requested mode. If the remote host is capable of entering that mode, the requested mode will be entered. .Bl -tag -width Ar .It Ic character Disable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then enter \*(Lqcharacter at a time\*(Rq mode. .It Ic line Enable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then attempt to enter \*(Lqold-line-by-line\*(Rq mode. .It Ic isig Pq Ic \-isig Attempt to enable (disable) the .Dv TRAPSIG mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic edit Pq Ic \-edit Attempt to enable (disable) the .Dv EDIT mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic softtabs Pq Ic \-softtabs Attempt to enable (disable) the .Dv SOFT_TAB mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic litecho Pq Ic \-litecho Attempt to enable (disable) the .Dv LIT_ECHO mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic \&? Prints out help information for the .Ic mode command. .El .It Xo .Ic open Ar host .Op Fl l Ar user .Oo Op Fl .Ar port Oc .Xc Open a connection to the named host. If no port number is specified, .Nm will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Fl l option may be used to specify the user name to be passed to the remote system via the .Ev ENVIRON option. When connecting to a non-standard port, .Nm omits any automatic initiation of .Tn TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done. After establishing a connection, the file .Pa \&.telnetrc in the users home directory is opened. Lines beginning with a # are comment lines. Blank lines are ignored. Lines that begin without white space are the start of a machine entry. The first thing on the line is the name of the machine that is being connected to. The rest of the line, and successive lines that begin with white space are assumed to be .Nm commands and are processed as if they had been typed in manually to the .Nm command prompt. .It Ic quit Close any open .Tn TELNET session and exit .Nm telnet . An end of file (in command mode) will also close a session and exit. .It Ic send Ar arguments Sends one or more special character sequences to the remote host. The following are the arguments which may be specified (more than one argument may be specified at a time): .Pp .Bl -tag -width escape .It Ic abort Sends the .Dv TELNET ABORT (Abort processes) sequence. .It Ic ao Sends the .Dv TELNET AO (Abort Output) sequence, which should cause the remote system to flush all output .Em from the remote system .Em to the user's terminal. .It Ic ayt Sends the .Dv TELNET AYT (Are You There) sequence, to which the remote system may or may not choose to respond. .It Ic brk Sends the .Dv TELNET BRK (Break) sequence, which may have significance to the remote system. .It Ic ec Sends the .Dv TELNET EC (Erase Character) sequence, which should cause the remote system to erase the last character entered. .It Ic el Sends the .Dv TELNET EL (Erase Line) sequence, which should cause the remote system to erase the line currently being entered. .It Ic eof Sends the .Dv TELNET EOF (End Of File) sequence. .It Ic eor Sends the .Dv TELNET EOR (End of Record) sequence. .It Ic escape Sends the current .Nm escape character (initially \*(Lq^\*(Rq). .It Ic ga Sends the .Dv TELNET GA (Go Ahead) sequence, which likely has no significance to the remote system. .It Ic getstatus If the remote side supports the .Dv TELNET STATUS command, .Ic getstatus will send the subnegotiation to request that the server send its current option status. .It Ic ip Sends the .Dv TELNET IP (Interrupt Process) sequence, which should cause the remote system to abort the currently running process. .It Ic nop Sends the .Dv TELNET NOP (No OPeration) sequence. .It Ic susp Sends the .Dv TELNET SUSP (SUSPend process) sequence. .It Ic synch Sends the .Dv TELNET SYNCH sequence. This sequence causes the remote system to discard all previously typed (but not yet read) input. This sequence is sent as .Tn TCP urgent data (and may not work if the remote system is a .Bx 4.2 system -- if it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal). .It Ic do Ar cmd .It Ic dont Ar cmd .It Ic will Ar cmd .It Ic wont Ar cmd Sends the .Dv TELNET DO .Ar cmd sequence. .Ar Cmd can be either a decimal number between 0 and 255, or a symbolic name for a specific .Dv TELNET command. .Ar Cmd can also be either .Ic help or .Ic \&? to print out help information, including a list of known symbolic names. .It Ic \&? Prints out help information for the .Ic send command. .El .It Ic set Ar argument value .It Ic unset Ar argument value The .Ic set command will set any one of a number of .Nm variables to a specific value or to .Dv TRUE . The special value .Ic off turns off the function associated with the variable, this is equivalent to using the .Ic unset command. The .Ic unset command will disable or set to .Dv FALSE any of the specified functions. The values of variables may be interrogated with the .Ic display command. The variables which may be set or unset, but not toggled, are listed here. In addition, any of the variables for the .Ic toggle command may be explicitly set or unset using the .Ic set and .Ic unset commands. .Bl -tag -width escape .It Ic ayt If .Tn TELNET is in localchars mode, or .Dv LINEMODE is enabled, and the status character is typed, a .Dv TELNET AYT sequence (see .Ic send ayt preceding) is sent to the remote host. The initial value for the \*(LqAre You There\*(Rq character is the terminal's status character. .It Ic echo This is the value (initially \*(Lq^E\*(Rq) which, when in \*(Lqline by line\*(Rq mode, toggles between doing local echoing of entered characters (for normal processing), and suppressing echoing of entered characters (for entering, say, a password). .It Ic eof If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, entering this character as the first character on a line will cause this character to be sent to the remote system. The initial value of the eof character is taken to be the terminal's .Ic eof character. .It Ic erase If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Sy and if .Nm is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EC sequence (see .Ic send .Ic ec above) is sent to the remote system. The initial value for the erase character is taken to be the terminal's .Ic erase character. .It Ic escape This is the .Nm escape character (initially \*(Lq^[\*(Rq) which causes entry into .Nm command mode (when connected to a remote system). .It Ic flushoutput If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic flushoutput character is typed, a .Dv TELNET AO sequence (see .Ic send .Ic ao above) is sent to the remote host. The initial value for the flush character is taken to be the terminal's .Ic flush character. .It Ic forw1 .It Ic forw2 If .Nm is operating in .Dv LINEMODE , these are the characters that, when typed, cause partial lines to be forwarded to the remote system. The initial value for the forwarding characters are taken from the terminal's eol and eol2 characters. .It Ic interrupt If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic interrupt character is typed, a .Dv TELNET IP sequence (see .Ic send .Ic ip above) is sent to the remote host. The initial value for the interrupt character is taken to be the terminal's .Ic intr character. .It Ic kill If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Ic and if .Nm is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EL sequence (see .Ic send .Ic el above) is sent to the remote system. The initial value for the kill character is taken to be the terminal's .Ic kill character. .It Ic lnext If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic lnext character. The initial value for the lnext character is taken to be the terminal's .Ic lnext character. .It Ic quit If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic quit character is typed, a .Dv TELNET BRK sequence (see .Ic send .Ic brk above) is sent to the remote host. The initial value for the quit character is taken to be the terminal's .Ic quit character. .It Ic reprint If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic reprint character. The initial value for the reprint character is taken to be the terminal's .Ic reprint character. .It Ic rlogin This is the rlogin escape character. If set, the normal .Nm escape character is ignored unless it is preceded by this character at the beginning of a line. This character, at the beginning of a line followed by a "." closes the connection; when followed by a ^Z it suspends the .Nm command. The initial state is to disable the .Nm rlogin escape character. .It Ic start If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic start character. The initial value for the start character is taken to be the terminal's .Ic start character. .It Ic stop If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic stop character. The initial value for the stop character is taken to be the terminal's .Ic stop character. .It Ic susp If .Nm is in .Ic localchars mode, or .Dv LINEMODE is enabled, and the .Ic suspend character is typed, a .Dv TELNET SUSP sequence (see .Ic send .Ic susp above) is sent to the remote host. The initial value for the suspend character is taken to be the terminal's .Ic suspend character. .It Ic tracefile This is the file to which the output, caused by .Ic netdata or .Ic option tracing being .Dv TRUE , will be written. If it is set to .Dq Fl , then tracing information will be written to standard output (the default). .It Ic worderase If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic worderase character. The initial value for the worderase character is taken to be the terminal's .Ic worderase character. .It Ic \&? Displays the legal .Ic set .Pq Ic unset commands. .El .It Ic skey Ar sequence challenge The .Ic skey command computes a response to the S/Key challenge. .It Ic slc Ar state The .Ic slc command (Set Local Characters) is used to set or change the state of the special characters when the .Dv TELNET LINEMODE option has been enabled. Special characters are characters that get mapped to .Tn TELNET commands sequences (like .Ic ip or .Ic quit ) or line editing characters (like .Ic erase and .Ic kill ) . By default, the local special characters are exported. .Bl -tag -width Fl .It Ic check Verify the current settings for the current special characters. The remote side is requested to send all the current special character settings, and if there are any discrepancies with the local side, the local side will switch to the remote value. .It Ic export Switch to the local defaults for the special characters. The local default characters are those of the local terminal at the time when .Nm was started. .It Ic import Switch to the remote defaults for the special characters. The remote default characters are those of the remote system at the time when the .Tn TELNET connection was established. .It Ic \&? Prints out help information for the .Ic slc command. .El .It Ic status Show the current status of .Nm telnet . This includes the peer one is connected to, as well as the current mode. .It Ic toggle Ar arguments ... Toggle (between .Dv TRUE and .Dv FALSE ) various flags that control how .Nm responds to events. These flags may be set explicitly to .Dv TRUE or .Dv FALSE using the .Ic set and .Ic unset commands listed above. More than one argument may be specified. The state of these flags may be interrogated with the .Ic display command. Valid arguments are: .Bl -tag -width Ar .It Ic authdebug Turns on debugging information for the authentication code. .It Ic autoflush If .Ic autoflush and .Ic localchars are both .Dv TRUE , then when the .Ic ao , or .Ic quit characters are recognized (and transformed into .Tn TELNET sequences; see .Ic set above for details), .Nm refuses to display any data on the user's terminal until the remote system acknowledges (via a .Dv TELNET TIMING MARK option) that it has processed those .Tn TELNET sequences. The initial value for this toggle is .Dv TRUE if the terminal user had not done an "stty noflsh", otherwise .Dv FALSE (see .Xr stty 1 ) . .It Ic autodecrypt When the .Dv TELNET ENCRYPT option is negotiated, by default the actual encryption (decryption) of the data stream does not start automatically. The autoencrypt (autodecrypt) command states that encryption of the output (input) stream should be enabled as soon as possible. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside the United States and Canada. .It Ic autologin If the remote side supports the .Dv TELNET AUTHENTICATION option .Nm attempts to use it to perform automatic authentication. If the .Dv AUTHENTICATION option is not supported, the user's login name are propagated through the .Dv TELNET ENVIRON option. This command is the same as specifying .Fl a option on the .Ic open command. .It Ic autosynch If .Ic autosynch and .Ic localchars are both .Dv TRUE , then when either the .Ic intr or .Ic quit characters is typed (see .Ic set above for descriptions of the .Ic intr and .Ic quit characters), the resulting .Tn TELNET sequence sent is followed by the .Dv TELNET SYNCH sequence. This procedure .Ic should cause the remote system to begin throwing away all previously typed input until both of the .Tn TELNET sequences have been read and acted upon. The initial value of this toggle is .Dv FALSE . .It Ic binary Enable or disable the .Dv TELNET BINARY option on both input and output. .It Ic inbinary Enable or disable the .Dv TELNET BINARY option on input. .It Ic outbinary Enable or disable the .Dv TELNET BINARY option on output. .It Ic crlf If this is .Dv TRUE , then carriage returns will be sent as .Li . If this is .Dv FALSE , then carriage returns will be send as .Li . The initial value for this toggle is .Dv FALSE . .It Ic crmod Toggle carriage return mode. When this mode is enabled, most carriage return characters received from the remote host will be mapped into a carriage return followed by a line feed. This mode does not affect those characters typed by the user, only those received from the remote host. This mode is not very useful unless the remote host only sends carriage return, but never line feed. The initial value for this toggle is .Dv FALSE . .It Ic debug Toggles socket level debugging (useful only to the .Ic super user ) . The initial value for this toggle is .Dv FALSE . .It Ic encdebug Turns on debugging information for the encryption code. .It Ic localchars If this is .Dv TRUE , then the .Ic flush , .Ic interrupt , .Ic quit , .Ic erase , and .Ic kill characters (see .Ic set above) are recognized locally, and transformed into (hopefully) appropriate .Tn TELNET control sequences (respectively .Ic ao , .Ic ip , .Ic brk , .Ic ec , and .Ic el ; see .Ic send above). The initial value for this toggle is .Dv TRUE in \*(Lqold line by line\*(Rq mode, and .Dv FALSE in \*(Lqcharacter at a time\*(Rq mode. When the .Dv LINEMODE option is enabled, the value of .Ic localchars is ignored, and assumed to always be .Dv TRUE . If .Dv LINEMODE has ever been enabled, then .Ic quit is sent as .Ic abort , and .Ic eof and .Ic suspend are sent as .Ic eof and .Ic susp (see .Ic send above). .It Ic netdata Toggles the display of all network data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic options Toggles the display of some internal .Nm protocol processing (having to do with .Tn TELNET options). The initial value for this toggle is .Dv FALSE . .It Ic prettydump When the .Ic netdata toggle is enabled, if .Ic prettydump is enabled the output from the .Ic netdata command will be formatted in a more user readable format. Spaces are put between each character in the output, and the beginning of any .Nm escape sequence is preceded by a '*' to aid in locating them. .It Ic skiprc When the skiprc toggle is .Dv TRUE , .Nm skips the reading of the .Pa \&.telnetrc file in the users home directory when connections are opened. The initial value for this toggle is .Dv FALSE. .It Ic termdata Toggles the display of all terminal data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic verbose_encrypt When the .Ic verbose_encrypt toggle is .Dv TRUE , .Nm prints out a message each time encryption is enabled or disabled. The initial value for this toggle is .Dv FALSE. Note: Because of export controls, data encryption is not supported outside of the United States and Canada. .It Ic \&? Displays the legal .Ic toggle commands. .El .It Ic z Suspend .Nm telnet . This command only works when the user is using the .Xr csh 1 . .It Ic \&! Op Ar command Execute a single command in a subshell on the local system. If .Ar command is omitted, then an interactive subshell is invoked. .It Ic \&? Op Ar command Get help. With no arguments, .Nm prints a help summary. If .Ar command is specified, .Nm will print the help information for just that command. .El .Sh ENVIRONMENT .Nm uses at least the .Ev HOME , .Ev SHELL , .Ev DISPLAY , and .Ev TERM environment variables. Other environment variables may be propagated to the other side via the .Dv TELNET ENVIRON option. .Sh SEE ALSO .Xr rlogin 1 , .Xr rsh 1 , .Xr hosts 5 , .Xr nologin 5 , .Xr telnetd 8 .Sh FILES .Bl -tag -width ~/.telnetrc -compact .It Pa ~/.telnetrc user customized telnet startup values .El .Sh HISTORY The .Nm command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in \*(Lqold line by line\*(Rq mode. .Pp In \*(Lqold line by line\*(Rq mode or .Dv LINEMODE the terminal's .Ic eof character is only recognized (and sent to the remote system) when it is the first character on a line. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index abe3b5bd3d9b..5e7433d87691 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,160 +1,161 @@ # From: @(#)Makefile 5.20 (Berkeley) 6/12/93 # $FreeBSD$ # XXX MISSING: mkproto SUBDIR= IPXrouted \ ac \ accton \ adduser \ amd \ ancontrol \ arp \ atm \ bootparamd \ burncd \ cdcontrol \ chkgrp \ chown \ chroot \ ckdist \ config \ cron \ crunch \ ctm \ dev_mkdb \ diskpart \ edquota \ + faithd \ fdcontrol \ fdformat \ fdwrite \ gifconfig \ ifmcstat \ inetd \ iostat \ ipftest \ ipresend \ ipsend \ iptest \ jail \ kbdcontrol \ kbdmap \ kernbb \ keyserv \ lpr \ mailwrapper \ manctl \ memcontrol \ mergemaster \ mixer \ moused \ mrouted \ mtest \ mtree \ ndp \ newsyslog \ ngctl \ ntp \ nghook \ pccard \ pciconf \ periodic \ pkg_install \ pnpinfo \ portmap \ ppp \ pppctl \ pppd \ pppstats \ prefix \ procctl \ pstat \ pw \ pwd_mkdb \ quot \ quotaon \ rarpd \ repquota \ rip6query \ rmt \ route6d \ rpc.lockd \ rpc.statd \ rpc.umntall \ rpc.yppasswdd \ rpc.ypupdated \ rpc.ypxfrd \ rrenumd \ rtadvd \ rtprio \ rtsold \ rwhod \ sa \ setkey \ sliplogin \ slstat \ spray \ syslogd \ tcpdchk \ tcpdmatch \ tcpdump \ timed \ traceroute \ traceroute6 \ trpt \ tzsetup \ usbd \ usbdevs \ vidcontrol \ vipw \ vnconfig \ watch \ xten \ yp_mkdb \ ypbind \ yppoll \ yppush \ ypserv \ ypset \ zic .if !defined(NO_BIND) SUBDIR+=named \ named.reload \ named.restart \ ndc \ nslookup \ nsupdate .endif .if !defined(NO_SENDMAIL) SUBDIR+=mailstats \ makemap \ praliases \ sendmail .endif .if ${MACHINE_ARCH} == "alpha" SUBDIR+=elf2exe .endif .if ${MACHINE_ARCH} == "i386" SUBDIR+=apm \ apmd \ boot0cfg \ btxld \ i4b \ kgmon \ kgzip \ lptcontrol \ mptable \ pcvt \ rndcontrol \ sgsc \ sicontrol \ spkrtest \ stallion \ wicontrol \ wlconfig .endif .include diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile new file mode 100644 index 000000000000..c40c21de0c44 --- /dev/null +++ b/usr.sbin/faithd/Makefile @@ -0,0 +1,24 @@ +# Copyright (c) 1996 WIDE Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modifications, are permitted provided that the above copyright notice +# and this paragraph are duplicated in all such forms and that any +# documentation, advertising materials, and other materials related to +# such distribution and use acknowledge that the software was developed +# by the WIDE Project, Japan. The name of the Project may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' +# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT +# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE. +# $FreeBSD$ + +PROG= faithd +SRCS= faithd.c tcp.c ftp.c rsh.c +MAN8= faithd.8 +#CFLAGS+= -DFAITH4 +CFLAGS+= -Wall +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README new file mode 100644 index 000000000000..f8e47532dd33 --- /dev/null +++ b/usr.sbin/faithd/README @@ -0,0 +1,140 @@ +Configuring FAITH IPv6-to-IPv4 TCP relay + +Kazu Yamamoto and Jun-ichiro itojun Hagino +$Id: README,v 1.1.1.1 1999/08/08 23:29:27 itojun Exp $ +$FreeBSD$ + + +Introduction +============ + +FAITH is a IPv6-to-IPv4 TCP relay. It performs tcp relay just as some of +firewall-oriented gateway does, but between IPv6 and IPv4 with address +translation. +TCP connections has to be made from IPv6 node to IPv4 node. FAITH will +not relay connections for the opposite direction. +To perform relays, FAITH daemon needs to be executed on a router between +your local IPv6 site and outside IPv4 network. The daemon needs to be +invoked per each TCP services (TCP port number). + + IPv4 node "dest" = 123.4.5.6 + | + [[[[ outside IPv4 ocean ]]]] + | + node that runs FAITH-daemon (usually a router) + | + ==+=====+===+==== IPv6, or IPv4/v6 network in your site ^ + | | | connection + clients IPv6 node "src" | + +You will have to allocate an IPv6 address prefix to map IPv4 addresses into. +The following description uses 3ffe:0501:1234:ffff:: as example. +Please use a prefix which belongs to your site. +FAITH will make it possible to make a IPv6 TCP connection From IPv6 node +"src", toward IPv4 node "dest", by specifying FAITH-mapped address +3ffe:0501:1234:ffff::123.4.5.6 +(which is, 3ffe:0501:1234:ffff:0000:0000:7b04:0506). +The address mapping can be performed by hand:-), by speical nameserver on +the network, or by special resolver on the source node. + + +Setup +===== + +The following example assumes: +- You have assigned 3ffe:0501:1234:ffff:: as FAITH adderss prefix. +- You are willing to provide IPv6-to IPv4 TCP relay for telnet. + +<> + +(1) If you have IPv6 TCP server for the "telnet" service, i.e. telnetd via + inet6d, disable that daemon. Comment out the line from "inet6d.conf" + and send the HUP signal to "inet6d". + +(2) Execute sysctl as root to enable FAITH support in the kernel. + + # sysctl -w net.inet6.ip6.keepfaith=1 + +(3) Route packets toward FAITH prefix into "faith0" interface. + + # ifconfig faith0 up + # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 -interface faith0 + + or, on platforms that has problem with "-interface": + # ifconfig faith0 up + # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 \ + fe80:q::xxxx:yyyy:zzzz:wwww + (the last one is link-local address assigned for faith0) + +(4) Execute "faithd" by root as follows: + + # faithd telnet /usr/local/v6/libexec/telnetd telnetd + + 1st argument is a service name you are willing to provide TCP relay. + (it can be specified either by number "23" or by string "telnet") + 2nd argument is a path name for local IPv6 TCP server. If there is a + connection toward the router itself, this program will be invoked. + 3rd and the following arguments are arguments for the local IPv6 TCP + server. (3rd argument is typically the program name without its path.) + + More examples: + + # faithd login /usr/local/v6/libexec/rlogin rlogind + # faithd shell /usr/local/v6/libexec/rshd rshd + # faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l + # faithd ssh + + +<> + +(4) Make sure that packets whose destinations match the prefix can +reach from the IPv6 host to the translating router. + +<> + +There are two ways to translate IPv4 address to IPv6 address: + (a) Faked by DNS + (b) Faked by /etc/hosts. + +(5.a) Install "newbie" and set up FAITH mode. See kit/ports/newbie of + KAME package. KAME package is obtained from www.kame.net. + +(5.b) Add an entry into /etc/hosts so that you can resolve hostname into +faked IPv6 addrss. For example, add the following line for www.freebsd.org: + + 3ffe:0501:1234:ffff::204.216.27.21 www.freebsd.org + +<> + +(6) To see if "faithd" works, watch "/var/log/daemon". Note: please +setup "/etc/syslog.conf" so that LOG_DAEMON messages are to be stored +in "/var/log/daemon". + + + daemon.* /var/log/daemon + + +Advanced configuration +====================== + +If you would like to restrict IPv4 destination for translation, you may +want to do the following: + + # route add -inet6 3ffe:0501:1234:ffff::123.0.0.0 -prefixlen 104 \ + -interface faith0 + +By this way, you can restrict IPv4 destination to 123.0.0.0/8. +You may also want to reject packets toward 3ffe:0501:1234:ffff::/64 which +is not in 3ffe:0501:1234:ffff::123.0.0.0/104. This will be left as excerside +for the reader. + +By doing this, you will be able to provide your IPv4 web server to outside +IPv6 customers, without risks of unwanted open relays. + + [[[[ IPv6 network outside ]]]] | + | | connection + node that runs FAITH-daemon (usually a router) v + | + ========+======== IPv4/v6 network in your site + | (123.0.0.0/8) + IPv4 web server diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8 new file mode 100644 index 000000000000..2f62ed3e5f2a --- /dev/null +++ b/usr.sbin/faithd/faithd.8 @@ -0,0 +1,256 @@ +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" 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. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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: faithd.8,v 1.3 1999/10/07 04:22:14 itojun Exp $ +.\" $FreeBSD$ +.\" +.Dd January 27, 2000 +.Dt FAITHD 8 +.Os KAME +.Sh NAME +.Nm faithd +.Nd FAITH IPv6/v4 translator daemon +.Sh SYNOPSIS +.Nm faithd +.Op Fl dp +.Oo +.Ar service +.Oo +.Ar serverpath +.Op Ar serverargs +.Oc +.Oc +.Sh DESCRIPTION +.Nm +provides IPv6/v4 TCP relay for the specified +.Ar service . +.Pp +.Nm +must be invoked on IPv4/v6 dual stack router. +The router must be configured to capture all the TCP traffic +toward reserved IPv6 address prefix, by using +.Xr route 8 +and +.Xr sysctl 8 +commands. +.Nm +will daemonize itself on invocation. +.Pp +.Nm +will listen to TCPv6 port +.Ar service . +If TCPv6 traffic to port +.Ar service +is found, +.Nm +will relay the TCPv6 traffic to TCPv4. +Destination for relayed TCPv4 connection will be determined by the +last 4 octets of the original IPv6 destination. +For example, if +.Li 3ffe:0501:4819:ffff:: +is reserved for +.Nm faithd , +and the TCPv6 destination address is +.Li 3ffe:0501:4819:ffff::0a01:0101 , +the traffic will be relayed to IPv4 destination +.Li 10.1.1.1 . +.Pp +If +.Ar service +is not given, +.Li telnet +is assumed, and +.Nm +will relay TCP traffic on TCP port +.Li telnet . +With +.Ar service , +.Nm +will work as TCP relaying daemon for specified +.Ar service +as described above. +.Pp +Since +.Nm +listens to TCP port +.Ar service , +it is not possible to run local TCP daemons for port +.Ar service +on the router, using +.Xr inetd 8 +or other standard mechanisms. +By specifying +.Ar serverpath +to +.Nm faithd , +you can run local daemons on the router. +.Nm +will invoke local daemon at +.Ar serverpath +if the destination address is local interface address, +and will perform translation to IPv4 TCP in other cases. +You can also specify +.Ar serverargs +for the arguments for the local daemon. +.Pp +To use +.Nm +translation service, +an IPv6 address prefix must be reserved for mapping IPv4 addresses into. +Kernel must be properly configured to route all the TCP connection +toward the reserved IPv6 address prefix into the +.Dv faith +pseudo interface, by using +.Xr route 8 +command. +Also, +.Xr sysctl 8 +should be used to configure +.Dv net.inet6.ip6.keepfaith +to +.Dv 1 . +.Pp +If +.Fl d +is given, debugging information will be generated using +.Xr syslog 3 . +If +.Fl p +is given, +.Nm +will use privileged TCP port number as source port, +for IPv4 TCP connection toward final destination. +For relaying +.Xr ftp 1 +and +.Xr rlogin 1 , +.Fl p +is not necessary as special program code is supplied. +.Pp +.Nm +will relay both normal and out-of-band TCP data. +It is capable of emulating TCP half close as well. +.Nm +includes special support for protocols used by +.Xr ftp 1 +and +.Xr rlogin 1 . +When translating FTP protocol, +.Nm +translates network level addresses in +.Li PORT/LPRT/EPRT +and +.Li PASV/LPSV/EPSV +commands. +For RLOGIN protocol, +.Nm +will relay back connection from +.Xr rlogind 8 +on the server to +.Xr rlogin 1 +on client. +.Pp +Inactive sessions will be disconnected in 30 minutes, +to avoid stale sessions from chewing up resources. +This may be inappropriate for some of the services +.Po +should this be configurable? +.Pc . +.\" +.Sh EXAMPLES +Before invoking +.Nm faithd , +.Xr faith 4 +interface has to be configured properly. +.Pp +To translate +.Li telnet +service, and provide no local telnet service, invoke +.Nm +as either of the following: +.Bd -literal -offset +# faithd +# faithd telnet +.Ed +.Pp +If you would like to provide local telnet service via +.Xr telnetd 8 +on +.Pa /usr/local/v6/libexec/telnetd , +user the following command line: +.Bd -literal -offset +# faithd telnet /usr/local/v6/libexec/telnetd telnetd +.Ed +.Pp +If you would like to pass extra arguments to the local daemon: +.Bd -literal -offset +# faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l +.Ed +.Pp +Here are some other examples: +.Bd -literal -offset +# faithd login /usr/local/v6/libexec/rlogin rlogind +# faithd shell /usr/local/v6/libexec/rshd rshd +# faithd sshd +.Ed +.\" +.Sh RETURN VALUES +.Nm +exits with +.Dv EXIT_SUCCESS +.Pq 0 +on success, and +.Dv EXIT_FAILURE +.Pq 1 +on error. +.\" +.Sh SEE ALSO +.Xr faith 4 , +.Xr route 8 , +.Xr sysctl 8 +.Rs +.%A Jun-ichiro itojun Hagino +.%A Kazu Yamamoto +.%T "An IPv6-to-IPv4 transport relay translator" +.%R internet draft +.%N draft-ietf-ngtrans-tcpudp-relay-00.txt +.%O work in progress material +.Re +.\" +.Sh SECURITY NOTICE +It is very insecure to use +.Xr rhosts 5 +and other IP-address based authentication, for connections relayed by +.Nm +.Po +and any other TCP relaying services +.Pc . +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE Hydrangea IPv6 protocol stack kit. diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c new file mode 100644 index 000000000000..ee6c8dfefc10 --- /dev/null +++ b/usr.sbin/faithd/faithd.c @@ -0,0 +1,837 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +/* + * User level translator from IPv6 to IPv4. + * + * Usage: faithd [ ...] + * e.g. faithd telnet /usr/local/v6/sbin/telnetd telnetd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef IFT_FAITH +# define USE_ROUTE +# include +# include +# include +#endif + +#include +#include +#include + +#ifdef FAITH4 +#include +#include +#ifndef FAITH_NS +#define FAITH_NS "FAITH_NS" +#endif +#endif + +#include "faithd.h" + +char *serverpath = NULL; +char *serverarg[MAXARGV + 1]; +static char *faithdname = NULL; +char logname[BUFSIZ]; +char procname[BUFSIZ]; +struct myaddrs { + struct myaddrs *next; + struct sockaddr *addr; +}; +struct myaddrs *myaddrs = NULL; +static char *service; +#ifdef USE_ROUTE +static int sockfd = 0; +#endif +int dflag = 0; +static int pflag = 0; + +int main __P((int, char **)); +static void play_service __P((int)); +static void play_child __P((int, struct sockaddr *)); +static int faith_prefix __P((struct sockaddr *)); +static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *)); +#ifdef FAITH4 +static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *)); +#endif +static void sig_child __P((int)); +static void sig_terminate __P((int)); +static void start_daemon __P((void)); +static unsigned int if_maxindex __P((void)); +static void grab_myaddrs __P((void)); +static void free_myaddrs __P((void)); +static void update_myaddrs __P((void)); +static void usage __P((void)); + +int +main(int argc, char *argv[]) +{ + struct addrinfo hints, *res; + int s_wld, error, i, serverargc, on = 1; + int family = AF_INET6; + int c; +#ifdef FAITH_NS + char *ns; +#endif /* FAITH_NS */ + extern int optind; + extern char *optarg; + + /* + * Initializing stuff + */ + + faithdname = strrchr(argv[0], '/'); + if (faithdname) + faithdname++; + else + faithdname = argv[0]; + + while ((c = getopt(argc, argv, "dp46")) != -1) { + switch (c) { + case 'd': + dflag++; + break; + case 'p': + pflag++; + break; +#ifdef FAITH4 + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; +#endif + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef FAITH_NS + if ((ns = getenv(FAITH_NS)) != NULL) { + struct sockaddr_storage ss; + struct addrinfo hints, *res; + char serv[NI_MAXSERV]; + + memset(&ss, 0, sizeof(ss)); + memset(&hints, 0, sizeof(hints)); + snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT); + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(ns, serv, &hints, &res) == 0) { + res_init(); + memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen); + _res.nscount = 1; + } + } +#endif /* FAITH_NS */ + +#ifdef USE_ROUTE + grab_myaddrs(); +#endif + + switch (argc) { + case 0: + serverpath = DEFAULT_PATH; + serverarg[0] = DEFAULT_NAME; + serverarg[1] = NULL; + service = DEFAULT_PORT_NAME; + break; + default: + serverargc = argc - NUMARG; + if (serverargc > MAXARGV) + exit_error("too many augments"); + + serverpath = malloc(strlen(argv[NUMPRG])); + strcpy(serverpath, argv[NUMPRG]); + for (i = 0; i < serverargc; i++) { + serverarg[i] = malloc(strlen(argv[i + NUMARG])); + strcpy(serverarg[i], argv[i + NUMARG]); + } + serverarg[i] = NULL; + /* fall throuth */ + case 1: /* no local service */ + service = argv[NUMPRT]; + break; + } + + /* + * Opening wild card socket for this service. + */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + if (error) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); + if (error == EAI_SYSTEM) + exit_error("getaddrinfo: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s_wld == -1) + exit_error("socket: %s", ERRSTR); + +#ifdef IPV6_FAITH + if (res->ai_family == AF_INET6) { + error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + } +#endif +#ifdef FAITH4 +#ifdef IP_FAITH + if (res->ai_family == AF_INET) { + error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IP_FAITH): %s", ERRSTR); + } +#endif +#endif /* FAITH4 */ + + error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_REUSEADDR): %s", ERRSTR); + + error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + + error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen); + if (error == -1) + exit_error("bind: %s", ERRSTR); + + error = listen(s_wld, 5); + if (error == -1) + exit_error("listen: %s", ERRSTR); + +#ifdef USE_ROUTE + sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); + if (sockfd < 0) { + exit_error("socket(PF_ROUTE): %s", ERRSTR); + /*NOTREACHED*/ + } +#endif + + /* + * Everything is OK. + */ + + start_daemon(); + + snprintf(logname, sizeof(logname), "faithd %s", service); + snprintf(procname, sizeof(procname), "accepting port %s", service); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + syslog(LOG_INFO, "Staring faith daemon for %s port", service); + + play_service(s_wld); + /*NOTRECHED*/ + exit(1); /*pacify gcc*/ +} + +static void +play_service(int s_wld) +{ + struct sockaddr_storage srcaddr; + int len; + int s_src; + pid_t child_pid; + fd_set rfds; + int error; + int maxfd; + + /* + * Wait, accept, fork, faith.... + */ +again: + setproctitle(procname); + + FD_ZERO(&rfds); + FD_SET(s_wld, &rfds); + maxfd = s_wld; +#ifdef USE_ROUTE + if (sockfd) { + FD_SET(sockfd, &rfds); + maxfd = (maxfd < sockfd) ? sockfd : maxfd; + } +#endif + + error = select(maxfd + 1, &rfds, NULL, NULL, NULL); + if (error < 0) { + if (errno == EINTR) + goto again; + exit_failure("select: %s", ERRSTR); + /*NOTREACHED*/ + } + +#ifdef USE_ROUTE + if (FD_ISSET(sockfd, &rfds)) { + update_myaddrs(); + } +#endif + if (FD_ISSET(s_wld, &rfds)) { + len = sizeof(srcaddr); + s_src = accept(s_wld, (struct sockaddr *)&srcaddr, + &len); + if (s_src == -1) + exit_failure("socket: %s", ERRSTR); + + child_pid = fork(); + + if (child_pid == 0) { + /* child process */ + close(s_wld); + closelog(); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + play_child(s_src, (struct sockaddr *)&srcaddr); + exit_failure("should never reach here"); + } else { + /* parent process */ + close(s_src); + if (child_pid == -1) + syslog(LOG_ERR, "can't fork"); + } + } + goto again; +} + +static void +play_child(int s_src, struct sockaddr *srcaddr) +{ + struct sockaddr_storage dstaddr6; + struct sockaddr_storage dstaddr4; + char src[MAXHOSTNAMELEN]; + char dst6[MAXHOSTNAMELEN]; + char dst4[MAXHOSTNAMELEN]; + int len = sizeof(dstaddr6); + int s_dst, error, hport, nresvport, on = 1; + struct timeval tv; + struct sockaddr *sa4; + + tv.tv_sec = 1; + tv.tv_usec = 0; + + getnameinfo(srcaddr, srcaddr->sa_len, + src, sizeof(src), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "accepted a client from %s", src); + + error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len); + if (error == -1) + exit_failure("getsockname: %s", ERRSTR); + + getnameinfo((struct sockaddr *)&dstaddr6, len, + dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "the client is connecting to %s", dst6); + + if (!faith_prefix((struct sockaddr *)&dstaddr6)) { + if (serverpath) { + /* + * Local service + */ + syslog(LOG_INFO, "executing local %s", serverpath); + dup2(s_src, 0); + close(s_src); + dup2(0, 1); + dup2(0, 2); + execv(serverpath, serverarg); + syslog(LOG_ERR, "execv %s: %s", serverpath, ERRSTR); + _exit(EXIT_FAILURE); + } else { + close(s_src); + exit_success("no local service for %s", service); + } + } + + /* + * Act as a translator + */ + + switch (((struct sockaddr *)&dstaddr6)->sa_family) { + case AF_INET6: + if (!map6to4((struct sockaddr_in6 *)&dstaddr6, + (struct sockaddr_in *)&dstaddr4)) { + close(s_src); + exit_error("map6to4 failed"); + } + syslog(LOG_INFO, "translating from v6 to v4"); + break; +#ifdef FAITH4 + case AF_INET: + if (!map4to6((struct sockaddr_in *)&dstaddr6, + (struct sockaddr_in6 *)&dstaddr4)) { + close(s_src); + exit_error("map4to6 failed"); + } + syslog(LOG_INFO, "translating from v4 to v6"); + break; +#endif + default: + close(s_src); + exit_error("family not supported"); + /*NOTREACHED*/ + } + + sa4 = (struct sockaddr *)&dstaddr4; + getnameinfo(sa4, sa4->sa_len, + dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "the translator is connecting to %s", dst4); + + setproctitle("port %s, %s -> %s", service, src, dst4); + + if (sa4->sa_family == AF_INET6) + hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port); + else /* AF_INET */ + hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port); + + switch (hport) { + case RLOGIN_PORT: + case RSH_PORT: + s_dst = rresvport_af(&nresvport, sa4->sa_family); + break; + default: + if (pflag) + s_dst = rresvport_af(&nresvport, sa4->sa_family); + else + s_dst = socket(sa4->sa_family, SOCK_STREAM, 0); + break; + } + if (s_dst == -1) + exit_failure("socket: %s", ERRSTR); + + error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + + error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (error == -1) + exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (error == -1) + exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + + error = connect(s_dst, sa4, sa4->sa_len); + if (error == -1) + exit_failure("connect: %s", ERRSTR); + + switch (hport) { + case FTP_PORT: + ftp_relay(s_src, s_dst); + break; + case RSH_PORT: + rsh_relay(s_src, s_dst); + break; + default: + tcp_relay(s_src, s_dst, service); + break; + } + + /* NOTREACHED */ +} + +/* 0: non faith, 1: faith */ +static int +faith_prefix(struct sockaddr *dst) +{ +#ifndef USE_ROUTE + int mib[4], size; + struct in6_addr faith_prefix; + struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst; + + if (dst->sa_family != AF_INET6) + return 0; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_FAITH_PREFIX; + size = sizeof(struct in6_addr); + if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) + exit_error("sysctl: %s", ERRSTR); + + if (memcmp(dst, &faith_prefix, + sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) { + return 1; + } + return 0; +#else + struct myaddrs *p; + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin4; + struct sockaddr_in6 *dst6; + struct sockaddr_in *dst4; + struct sockaddr_in dstmap; + + dst6 = (struct sockaddr_in6 *)dst; + if (dst->sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) { + /* ugly... */ + memset(&dstmap, 0, sizeof(dstmap)); + dstmap.sin_family = AF_INET; + dstmap.sin_len = sizeof(dstmap); + memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12], + sizeof(dstmap.sin_addr)); + dst = (struct sockaddr *)&dstmap; + } + + dst6 = (struct sockaddr_in6 *)dst; + dst4 = (struct sockaddr_in *)dst; + + for (p = myaddrs; p; p = p->next) { + sin6 = (struct sockaddr_in6 *)p->addr; + sin4 = (struct sockaddr_in *)p->addr; + + if (p->addr->sa_len != dst->sa_len + || p->addr->sa_family != dst->sa_family) + continue; + + switch (dst->sa_family) { + case AF_INET6: + if (sin6->sin6_scope_id == dst6->sin6_scope_id + && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr)) + return 0; + break; + case AF_INET: + if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr) + return 0; + break; + } + } + return 1; +#endif +} + +/* 0: non faith, 1: faith */ +static int +map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4) +{ + memset(dst4, 0, sizeof(*dst4)); + dst4->sin_len = sizeof(*dst4); + dst4->sin_family = AF_INET; + dst4->sin_port = dst6->sin6_port; + memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12], + sizeof(dst4->sin_addr)); + + if (dst4->sin_addr.s_addr == INADDR_ANY + || dst4->sin_addr.s_addr == INADDR_BROADCAST + || IN_MULTICAST(dst4->sin_addr.s_addr)) + return 0; + + return 1; +} + +#ifdef FAITH4 +/* 0: non faith, 1: faith */ +static int +map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + struct addrinfo hints, *res; + int ai_errno; + + if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host), + serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0) + return 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) { + syslog(LOG_INFO, "%s %s: %s", host, serv, gai_strerror(ai_errno)); + if (ai_errno == EAI_SYSTEM) + syslog(LOG_INFO, "%s %s: %s", host, serv, + strerror(errno)); + return 0; + } + + memcpy(dst6, res->ai_addr, res->ai_addrlen); + + freeaddrinfo(res); + + return 1; +} +#endif /* FAITH4 */ + +static void +sig_child(int sig) +{ + int status; + pid_t pid; + + pid = wait3(&status, WNOHANG, (struct rusage *)0); + if (pid && status) + syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); +} + +void +sig_terminate(int sig) +{ + syslog(LOG_INFO, "Terminating faith daemon"); + exit(EXIT_SUCCESS); +} + +static void +start_daemon(void) +{ + if (daemon(0, 0) == -1) + exit_error("daemon: %s", ERRSTR); + + if (signal(SIGCHLD, sig_child) == SIG_ERR) + exit_failure("signal CHLD: %s", ERRSTR); + + if (signal(SIGTERM, sig_terminate) == SIG_ERR) + exit_failure("signal TERM: %s", ERRSTR); +} + +void +exit_error(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fprintf(stderr, "%s\n", buf); + exit(EXIT_FAILURE); +} + +void +exit_failure(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + syslog(LOG_ERR, buf); + exit(EXIT_FAILURE); +} + +void +exit_success(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + syslog(LOG_INFO, buf); + exit(EXIT_SUCCESS); +} + +#ifdef USE_ROUTE +static unsigned int +if_maxindex() +{ + struct if_nameindex *p, *p0; + unsigned int max = 0; + + p0 = if_nameindex(); + for (p = p0; p && p->if_index && p->if_name; p++) { + if (max < p->if_index) + max = p->if_index; + } + if_freenameindex(p0); + return max; +} + +static void +grab_myaddrs() +{ + int s; + unsigned int maxif; + struct ifreq *iflist; + struct ifconf ifconf; + struct ifreq *ifr, *ifr_end; + struct myaddrs *p; + struct sockaddr_in6 *sin6; + + maxif = if_maxindex() + 1; + iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */ + if (!iflist) { + exit_failure("not enough core"); + /*NOTREACHED*/ + } + + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + exit_failure("socket(SOCK_DGRAM)"); + /*NOTREACHED*/ + } + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.ifc_req = iflist; + ifconf.ifc_len = maxif * BUFSIZ; /* XXX */ + if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) { + exit_failure("ioctl(SIOCGIFCONF)"); + /*NOTREACHED*/ + } + close(s); + + /* Look for this interface in the list */ + ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len); + for (ifr = ifconf.ifc_req; + ifr < ifr_end; + ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + + ifr->ifr_addr.sa_len)) { + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + case AF_INET6: + p = (struct myaddrs *)malloc(sizeof(struct myaddrs) + + ifr->ifr_addr.sa_len); + if (!p) { + exit_failure("not enough core"); + /*NOTREACHED*/ + } + memcpy(p + 1, &ifr->ifr_addr, ifr->ifr_addr.sa_len); + p->next = myaddrs; + p->addr = (struct sockaddr *)(p + 1); +#ifdef __KAME__ + if (ifr->ifr_addr.sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)p->addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) + || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } + } +#endif + myaddrs = p; + if (dflag) { + char hbuf[NI_MAXHOST]; + getnameinfo(p->addr, p->addr->sa_len, + hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST); + syslog(LOG_INFO, "my interface: %s %s", hbuf, ifr->ifr_name); + } + break; + default: + break; + } + } + + free(iflist); +} + +static void +free_myaddrs() +{ + struct myaddrs *p, *q; + + p = myaddrs; + while (p) { + q = p->next; + free(p); + p = q; + } + myaddrs = NULL; +} + +static void +update_myaddrs() +{ + char msg[BUFSIZ]; + int len; + struct rt_msghdr *rtm; + + len = read(sockfd, msg, sizeof(msg)); + if (len < 0) { + syslog(LOG_ERR, "read(PF_ROUTE) failed"); + return; + } + rtm = (struct rt_msghdr *)msg; + if (len < 4 || len < rtm->rtm_msglen) { + syslog(LOG_ERR, "read(PF_ROUTE) short read"); + return; + } + if (rtm->rtm_version != RTM_VERSION) { + syslog(LOG_ERR, "routing socket version mismatch"); + close(sockfd); + sockfd = 0; + return; + } + switch (rtm->rtm_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + break; + default: + return; + } + /* XXX more filters here? */ + + syslog(LOG_INFO, "update interface address list"); + free_myaddrs(); + grab_myaddrs(); +} +#endif /*USE_ROUTE*/ + +static void +usage() +{ + fprintf(stderr, "usage: %s [-dp] [service [serverpath [serverargs]]]\n", + faithdname); + exit(0); +} diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h new file mode 100644 index 000000000000..55566edc030c --- /dev/null +++ b/usr.sbin/faithd/faithd.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +extern char logname[]; +extern int dflag; + +extern void tcp_relay __P((int, int, const char *)); +extern void ftp_relay __P((int, int)); +extern int ftp_active __P((int, int, int *, int *)); +extern int ftp_passive __P((int, int, int *, int *)); +extern void rsh_relay __P((int, int)); +extern void rsh_dual_relay __P((int, int)); +extern void exit_error __P((const char *fmt, ...)); +extern void exit_success __P((const char *fmt, ...)); +extern void exit_failure __P((const char *fmt, ...)); + +#define DEFAULT_PORT_NAME "telnet" +#define DEFAULT_PATH "/usr/local/v6/libexec/telnetd" +#define DEFAULT_NAME "telnetd" + +#define FTP_PORT 21 +#define RLOGIN_PORT 513 +#define RSH_PORT 514 + +#define RETURN_SUCCESS 0 +#define RETURN_FAILURE 1 + +#define YES 1 +#define NO 0 + +#define MSS 2048 +#define MAXARGV 20 + +#define NUMPRT 0 +#define NUMPRG 1 +#define NUMARG 2 + +#define UC(b) (((int)b)&0xff) + +#define ERRSTR strerror(errno) + +#define FAITH_TIMEOUT (30 * 60) /*second*/ diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c new file mode 100644 index 000000000000..fcb25e32de57 --- /dev/null +++ b/usr.sbin/faithd/ftp.c @@ -0,0 +1,1139 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +static char rbuf[MSS]; +static char sbuf[MSS]; +static int passivemode = 0; +static int wport4 = -1; /* listen() to active */ +static int wport6 = -1; /* listen() to passive */ +static int port4 = -1; /* active: inbound passive: outbound */ +static int port6 = -1; /* active: outbound passive: inbound */ +static struct sockaddr_storage data4; /* server data address */ +static struct sockaddr_storage data6; /* client data address */ +static int epsvall = 0; + +#ifdef FAITH4 +enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV }; +#else +enum state { NONE, LPRT, EPRT, LPSV, EPSV }; +#endif + +static int ftp_activeconn __P((void)); +static int ftp_passiveconn __P((void)); +static int ftp_copy __P((int, int)); +static int ftp_copyresult __P((int, int, enum state)); +static int ftp_copycommand __P((int, int, enum state *)); + +void +ftp_relay(int ctl6, int ctl4) +{ + fd_set readfds; + int error; + enum state state = NONE; + struct timeval tv; + + syslog(LOG_INFO, "starting ftp control connection"); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(ctl4, &readfds); + FD_SET(ctl6, &readfds); + if (0 <= port4) + FD_SET(port4, &readfds); + if (0 <= port6) + FD_SET(port6, &readfds); +#if 0 + if (0 <= wport4) + FD_SET(wport4, &readfds); + if (0 <= wport6) + FD_SET(wport6, &readfds); +#endif + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select: %s", ERRSTR); + else if (error == 0) + exit_failure("connection timeout"); + + /* + * The order of the following checks does (slightly) matter. + * It is important to visit all checks (do not use "continue"), + * otherwise some of the pipe may become full and we cannot + * relay correctly. + */ + if (FD_ISSET(ctl6, &readfds)) { + /* + * copy control connection from the client. + * command translation is necessary. + */ + error = ftp_copycommand(ctl6, ctl4, &state); + + switch (error) { + case -1: + goto bad; + case 0: + close(ctl4); + close(ctl6); + exit_success("terminating ftp control connection"); + /*NOTREACHED*/ + default: + break; + } + } + if (FD_ISSET(ctl4, &readfds)) { + /* + * copy control connection from the server + * translation of result code is necessary. + */ + error = ftp_copyresult(ctl4, ctl6, state); + + switch (error) { + case -1: + goto bad; + case 0: + close(ctl4); + close(ctl6); + exit_success("terminating ftp control connection"); + /*NOTREACHED*/ + default: + break; + } + } + if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) { + /* + * copy data connection. + * no special treatment necessary. + */ + if (FD_ISSET(port4, &readfds)) + error = ftp_copy(port4, port6); + switch (error) { + case -1: + goto bad; + case 0: + close(port4); + close(port6); + port4 = port6 = -1; + syslog(LOG_INFO, "terminating data connection"); + break; + default: + break; + } + } + if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) { + /* + * copy data connection. + * no special treatment necessary. + */ + if (FD_ISSET(port6, &readfds)) + error = ftp_copy(port6, port4); + switch (error) { + case -1: + goto bad; + case 0: + close(port4); + close(port6); + port4 = port6 = -1; + syslog(LOG_INFO, "terminating data connection"); + break; + default: + break; + } + } +#if 0 + if (wport4 && FD_ISSET(wport4, &readfds)) { + /* + * establish active data connection from the server. + */ + ftp_activeconn(); + } + if (wport6 && FD_ISSET(wport6, &readfds)) { + /* + * establish passive data connection from the client. + */ + ftp_passiveconn(); + } +#endif + } + + bad: + exit_failure(ERRSTR); +} + +static int +ftp_activeconn() +{ + int n; + int error; + fd_set set; + struct timeval timeout; + struct sockaddr *sa; + + /* get active connection from server */ + FD_ZERO(&set); + FD_SET(wport4, &set); + timeout.tv_sec = 120; + timeout.tv_usec = -1; + n = sizeof(data4); + if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 + || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) { + close(wport4); + wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + + /* ask active connection to client */ + sa = (struct sockaddr *)&data6; + port6 = socket(sa->sa_family, SOCK_STREAM, 0); + if (port6 == -1) { + close(port4); + close(wport4); + port4 = wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + error = connect(port6, sa, sa->sa_len); + if (port6 == -1) { + close(port6); + close(port4); + close(wport4); + port6 = port4 = wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + + syslog(LOG_INFO, "active mode data connection established"); + return 0; +} + +static int +ftp_passiveconn() +{ + int n; + int error; + fd_set set; + struct timeval timeout; + struct sockaddr *sa; + + /* get passive connection from client */ + FD_ZERO(&set); + FD_SET(wport6, &set); + timeout.tv_sec = 120; + timeout.tv_usec = 0; + n = sizeof(data6); + if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 + || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) { + close(wport6); + wport6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + + /* ask passive connection to server */ + sa = (struct sockaddr *)&data4; + port4 = socket(sa->sa_family, SOCK_STREAM, 0); + if (port4 == -1) { + close(wport6); + close(port6); + wport6 = port6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + error = connect(port4, sa, sa->sa_len); + if (port4 == -1) { + close(wport6); + close(port4); + close(port6); + wport6 = port4 = port6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + + syslog(LOG_INFO, "passive mode data connection established"); + return 0; +} + +static int +ftp_copy(int src, int dst) +{ + int error, atmark; + int n; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + switch (n) { + case -1: + case 0: + return n; + default: + write(dst, rbuf, n); + return n; + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} + +static int +ftp_copyresult(int src, int dst, enum state state) +{ + int error, atmark; + int n; + char *param; + int code; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + if (n <= 0) + return n; + rbuf[n] = '\0'; + + /* + * parse argument + */ + { + char *p; + int i; + + p = rbuf; + for (i = 0; i < 3; i++) { + if (!isdigit(*p)) { + /* invalid reply */ + write(dst, rbuf, n); + return n; + } + p++; + } + if (!isspace(*p)) { + /* invalid reply */ + write(dst, rbuf, n); + return n; + } + code = atoi(rbuf); + param = p; + /* param points to first non-command token, if any */ + while (*param && isspace(*param)) + param++; + if (!*param) + param = NULL; + } + + switch (state) { + case NONE: + if (!passivemode && rbuf[0] == '1') { + if (ftp_activeconn() < 0) { + n = snprintf(rbuf, sizeof(rbuf), + "425 Cannot open data connetion\r\n"); + } + } + write(dst, rbuf, n); + return n; + case LPRT: + case EPRT: + /* expecting "200 PORT command successful." */ + if (code == 200) { + char *p; + + p = strstr(rbuf, "PORT"); + if (p) { + p[0] = (state == LPRT) ? 'L' : 'E'; + p[1] = 'P'; + } + } else { + close(wport4); + wport4 = -1; + } + write(dst, rbuf, n); + return n; +#ifdef FAITH4 + case PORT: + /* expecting "200 EPRT command successful." */ + if (code == 200) { + char *p; + + p = strstr(rbuf, "EPRT"); + if (p) { + p[0] = 'P'; + p[1] = 'O'; + } + } else { + close(wport4); + wport4 = -1; + } + write(dst, rbuf, n); + return n; +#endif + case LPSV: + case EPSV: + /* expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" */ + if (code != 227) { +passivefail0: + close(wport6); + wport6 = -1; + write(dst, rbuf, n); + return n; + } + + { + unsigned int ho[4], po[2]; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + u_short port; + char *p; + + /* + * PASV result -> LPSV/EPSV result + */ + p = param; + while (*p && *p != '(') + p++; + if (!*p) + goto passivefail0; /*XXX*/ + p++; + n = sscanf(p, "%u,%u,%u,%u,%u,%u", + &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); + if (n != 6) + goto passivefail0; /*XXX*/ + + /* keep PORT parameter */ + memset(&data4, 0, sizeof(data4)); + sin = (struct sockaddr_in *)&data4; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = 0; + for (n = 0; n < 4; n++) { + sin->sin_addr.s_addr |= + htonl((ho[n] & 0xff) << ((3 - n) * 8)); + } + sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + + /* get ready for passive data connection */ + memset(&data6, 0, sizeof(data6)); + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0); + if (wport6 == -1) { +passivefail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate from PASV\r\n"); + write(src, sbuf, n); + return n; + } +#ifdef IPV6_FAITH + { + int on = 1; + error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH, + &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + } +#endif + error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + error = listen(wport6, 1); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + + /* transmit LPSV or EPSV */ + /* + * addr from dst, port from wport6 + */ + n = sizeof(data6); + error = getsockname(wport6, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + sin6 = (struct sockaddr_in6 *)&data6; + port = sin6->sin6_port; + + n = sizeof(data6); + error = getsockname(dst, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_port = port; + + if (state == LPSV) { + char *a, *p; + + a = (char *)&sin6->sin6_addr; + p = (char *)&sin6->sin6_port; + n = snprintf(sbuf, sizeof(sbuf), +"228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n", + 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), + UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), + UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), + 2, UC(p[0]), UC(p[1])); + write(dst, sbuf, n); + passivemode = 1; + return n; + } else { + n = snprintf(sbuf, sizeof(sbuf), +"229 Entering Extended Passive Mode (|||%d|)\r\n", + ntohs(sin6->sin6_port)); + write(dst, sbuf, n); + passivemode = 1; + return n; + } + } +#ifdef FAITH4 + case PASV: + /* expecting "229 Entering Extended Passive Mode (|||x|)" */ + if (code != 229) { +passivefail1: + close(wport6); + wport6 = -1; + write(dst, rbuf, n); + return n; + } + + { + u_short port; + char *p; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + /* + * EPSV result -> PORT result + */ + p = param; + while (*p && *p != '(') + p++; + if (!*p) + goto passivefail1; /*XXX*/ + p++; + n = sscanf(p, "|||%hu|", &port); + if (n != 1) + goto passivefail1; /*XXX*/ + + /* keep EPRT parameter */ + n = sizeof(data4); + error = getpeername(src, (struct sockaddr *)&data4, &n); + if (error == -1) + goto passivefail1; /*XXX*/ + sin6 = (struct sockaddr_in6 *)&data4; + sin6->sin6_port = htons(port); + + /* get ready for passive data connection */ + memset(&data6, 0, sizeof(data6)); + sin = (struct sockaddr_in *)&data6; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + wport6 = socket(sin->sin_family, SOCK_STREAM, 0); + if (wport6 == -1) { +passivefail2: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate from EPSV\r\n"); + write(src, sbuf, n); + return n; + } +#ifdef IP_FAITH + { + int on = 1; + error = setsockopt(wport6, IPPROTO_IP, IP_FAITH, + &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IP_FAITH): %s", ERRSTR); + } +#endif + error = bind(wport6, (struct sockaddr *)sin, sin->sin_len); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + error = listen(wport6, 1); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + + /* transmit PORT */ + /* + * addr from dst, port from wport6 + */ + n = sizeof(data6); + error = getsockname(wport6, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + sin = (struct sockaddr_in *)&data6; + port = sin->sin_port; + + n = sizeof(data6); + error = getsockname(dst, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + sin = (struct sockaddr_in *)&data6; + sin->sin_port = port; + + { + char *a, *p; + + a = (char *)&sin->sin_addr; + p = (char *)&sin->sin_port; + n = snprintf(sbuf, sizeof(sbuf), +"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + write(dst, sbuf, n); + passivemode = 1; + return n; + } + } +#endif /* FAITH4 */ + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} + +static int +ftp_copycommand(int src, int dst, enum state *state) +{ + int error, atmark; + int n; + unsigned int af, hal, ho[16], pal, po[2]; + char *a, *p; + char cmd[5], *param; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + enum state nstate; + char ch; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + if (n <= 0) + return n; + rbuf[n] = '\0'; + + if (n < 4) { + write(dst, rbuf, n); + return n; + } + + /* + * parse argument + */ + { + char *p, *q; + int i; + + p = rbuf; + q = cmd; + for (i = 0; i < 4; i++) { + if (!isalpha(*p)) { + /* invalid command */ + write(dst, rbuf, n); + return n; + } + *q++ = islower(*p) ? toupper(*p) : *p; + p++; + } + if (!isspace(*p)) { + /* invalid command */ + write(dst, rbuf, n); + return n; + } + *q = '\0'; + param = p; + /* param points to first non-command token, if any */ + while (*param && isspace(*param)) + param++; + if (!*param) + param = NULL; + } + + *state = NONE; + + if (strcmp(cmd, "LPRT") == 0 && param) { + /* + * LPRT -> PORT + */ + nstate = LPRT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + n = sscanf(param, +"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3], + &ho[4], &ho[5], &ho[6], &ho[7], + &ho[8], &ho[9], &ho[10], &ho[11], + &ho[12], &ho[13], &ho[14], &ho[15], + &pal, &po[0], &po[1]); + if (n != 21 || af != 6 || hal != 16|| pal != 2) { + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to LPRT\r\n"); + write(src, sbuf, n); + return n; + } + + /* keep LPRT parameter */ + memset(&data6, 0, sizeof(data6)); + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + for (n = 0; n < 16; n++) + sin6->sin6_addr.s6_addr[n] = ho[n]; + sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + +sendport: + /* get ready for active data connection */ + n = sizeof(data4); + error = getsockname(dst, (struct sockaddr *)&data4, &n); + if (error == -1) { +lprtfail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate to PORT\r\n"); + write(src, sbuf, n); + return n; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET) + goto lprtfail; + sin = (struct sockaddr_in *)&data4; + sin->sin_port = 0; + wport4 = socket(sin->sin_family, SOCK_STREAM, 0); + if (wport4 == -1) + goto lprtfail; + error = bind(wport4, (struct sockaddr *)sin, sin->sin_len); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + error = listen(wport4, 1); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + + /* transmit PORT */ + n = sizeof(data4); + error = getsockname(wport4, (struct sockaddr *)&data4, &n); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + sin = (struct sockaddr_in *)&data4; + a = (char *)&sin->sin_addr; + p = (char *)&sin->sin_port; + n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + write(dst, sbuf, n); + *state = nstate; + passivemode = 0; + return n; + } else if (strcmp(cmd, "EPRT") == 0 && param) { + /* + * EPRT -> PORT + */ + char *afp, *hostp, *portp; + struct addrinfo hints, *res; + + nstate = EPRT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + p = param; + ch = *p++; /* boundary character */ + afp = p; + while (*p && *p != ch) + p++; + if (!*p) { +eprtparamfail: + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + *p++ = '\0'; + hostp = p; + while (*p && *p != ch) + p++; + if (!*p) + goto eprtparamfail; + *p++ = '\0'; + portp = p; + while (*p && *p != ch) + p++; + if (!*p) + goto eprtparamfail; + *p++ = '\0'; + + n = sscanf(afp, "%d", &af); + if (n != 1 || af != 2) { + n = snprintf(sbuf, sizeof(sbuf), + "501 unsupported address family to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(hostp, portp, &hints, &res); + if (error) { + n = snprintf(sbuf, sizeof(sbuf), + "501 EPRT: %s", gai_strerror(error)); + if (error == EAI_SYSTEM) + n += snprintf(sbuf, sizeof(sbuf), + ": %s", strerror(errno)); + n += snprintf(sbuf, sizeof(sbuf), "\r\n"); + + write(src, sbuf, n); + return n; + } + if (res->ai_next) { + n = snprintf(sbuf, sizeof(sbuf), + "501 EPRT: %s resolved to multiple addresses\r\n", hostp); + write(src, sbuf, n); + return n; + } + + memcpy(&data6, res->ai_addr, res->ai_addrlen); + + goto sendport; + } else if (strcmp(cmd, "LPSV") == 0 && !param) { + /* + * LPSV -> PASV + */ + nstate = LPSV; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + /* transmit PASV */ + n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); + write(dst, sbuf, n); + *state = LPSV; + passivemode = 0; /* to be set to 1 later */ + return n; + } else if (strcmp(cmd, "EPSV") == 0 && !param) { + /* + * EPSV -> PASV + */ + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); + write(dst, sbuf, n); + *state = EPSV; + passivemode = 0; /* to be set to 1 later */ + return n; + } else if (strcmp(cmd, "EPSV") == 0 && param + && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) { + /* + * EPSV ALL + */ + epsvall = 1; + n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n"); + write(src, sbuf, n); + return n; +#ifdef FAITH4 + } else if (strcmp(cmd, "PORT") == 0 && param) { + /* + * PORT -> EPRT + */ + char host[NI_MAXHOST], serv[NI_MAXSERV]; + + nstate = PORT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + p = param; + n = sscanf(p, "%u,%u,%u,%u,%u,%u", + &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); + if (n != 6) { + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to PORT\r\n"); + write(src, sbuf, n); + return n; + } + + memset(&data6, 0, sizeof(data6)); + sin = (struct sockaddr_in *)&data6; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = htonl( + ((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) | + ((ho[2] & 0xff) << 8) | (ho[3] & 0xff)); + sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + + /* get ready for active data connection */ + n = sizeof(data4); + error = getsockname(dst, (struct sockaddr *)&data4, &n); + if (error == -1) { +portfail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET6) + goto portfail; + + ((struct sockaddr_in6 *)&data4)->sin6_port = 0; + sa = (struct sockaddr *)&data4; + wport4 = socket(sa->sa_family, SOCK_STREAM, 0); + if (wport4 == -1) + goto portfail; + error = bind(wport4, sa, sa->sa_len); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + error = listen(wport4, 1); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + + /* transmit EPRT */ + n = sizeof(data4); + error = getsockname(wport4, (struct sockaddr *)&data4, &n); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + af = 2; + sa = (struct sockaddr *)&data4; + if (getnameinfo(sa, sa->sa_len, host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) { + close(wport4); + wport4 = -1; + goto portfail; + } + n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv); + write(dst, sbuf, n); + *state = nstate; + passivemode = 0; + return n; + } else if (strcmp(cmd, "PASV") == 0 && !param) { + /* + * PASV -> EPSV + */ + + nstate = PASV; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + /* transmit EPSV */ + n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n"); + write(dst, sbuf, n); + *state = PASV; + passivemode = 0; /* to be set to 1 later */ + return n; +#else /* FAITH4 */ + } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) { + /* + * reject PORT/PASV + */ + n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd); + write(src, sbuf, n); + return n; +#endif /* FAITH4 */ + } else if (passivemode + && (strcmp(cmd, "STOR") == 0 + || strcmp(cmd, "STOU") == 0 + || strcmp(cmd, "RETR") == 0 + || strcmp(cmd, "LIST") == 0 + || strcmp(cmd, "NLST") == 0 + || strcmp(cmd, "APPE") == 0)) { + /* + * commands with data transfer. need to care about passive + * mode data connection. + */ + + if (ftp_passiveconn() < 0) { + n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n"); + write(src, sbuf, n); + } else { + /* simply relay the command */ + write(dst, rbuf, n); + } + + *state = NONE; + return n; + } else { + /* simply relay it */ + *state = NONE; + write(dst, rbuf, n); + return n; + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} diff --git a/usr.sbin/faithd/rsh.c b/usr.sbin/faithd/rsh.c new file mode 100644 index 000000000000..c6e83572be9a --- /dev/null +++ b/usr.sbin/faithd/rsh.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +char rshbuf[MSS]; + +int s_ctl, s_ctl6, s_rcv, s_snd; +int half; + +void +rsh_relay(int s_src, int s_dst) +{ + ssize_t n; + fd_set readfds; + int error; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(s_src, &readfds); + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select %d: %s", s_src, ERRSTR); + else if (error == 0) + exit_failure("connecion timeout"); + + n = read(s_src, rshbuf, sizeof(rshbuf)); + if (rshbuf[0] != 0) { + rsh_dual_relay(s_src, s_dst); + /* NOTREACHED */ + } + write(s_dst, rshbuf, n); + tcp_relay(s_src, s_dst, "rsh"); + /* NOTREACHED */ +} + +static void +relay(int src, int dst) +{ + int error; + ssize_t n; + int atmark; + + error = ioctl(s_rcv, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(s_rcv, rshbuf, 1); + if (n == 1) + send(s_snd, rshbuf, 1, MSG_OOB); + return; + } + + n = read(s_rcv, rshbuf, sizeof(rshbuf)); + + switch (n) { + case -1: + exit_failure(ERRSTR); + case 0: + if (s_rcv == src) { + /* half close */ + shutdown(dst, 1); + half = YES; + break; + } + close(src); + close(dst); + close(s_ctl); + close(s_ctl6); + exit_success("terminating rsh/contorol connections"); + break; + default: + write(s_snd, rshbuf, n); + } +} + +void +rsh_dual_relay(int s_src, int s_dst) +{ + fd_set readfds; + int len, s_wld, error; + struct sockaddr_storage ctladdr6; + struct sockaddr_storage ctladdr; + int port6 = 0, lport, lport6; + char *p; + struct timeval tv; + struct sockaddr *sa; + + half = NO; + s_rcv = s_src; + s_snd = s_dst; + syslog(LOG_INFO, "starting rsh connection"); + + for (p = rshbuf; *p; p++) + port6 = port6 * 10 + *p - '0'; + + len = sizeof(ctladdr6); + getpeername(s_src, (struct sockaddr *)&ctladdr6, &len); + if (((struct sockaddr *)&ctladdr6)->sa_family == AF_INET6) + ((struct sockaddr_in6 *)&ctladdr6)->sin6_port = htons(port6); + else + ((struct sockaddr_in *)&ctladdr6)->sin_port = htons(port6); + + s_wld = rresvport(&lport); + if (s_wld == -1) goto bad; + error = listen(s_wld, 1); + if (error == -1) goto bad; + snprintf(rshbuf, sizeof(rshbuf), "%d", lport); + write(s_dst, rshbuf, strlen(rshbuf)+1); + + len = sizeof(ctladdr); + s_ctl = accept(s_wld, (struct sockaddr *)&ctladdr, &len); + if (s_ctl == -1) goto bad; + close(s_wld); + + sa = (struct sockaddr *)&ctladdr6; + s_ctl6 = rresvport_af(&lport6, sa->sa_family); + if (s_ctl6 == -1) goto bad; + error = connect(s_ctl6, sa, sa->sa_len); + if (error == -1) goto bad; + + syslog(LOG_INFO, "starting rsh control connection"); + + for (;;) { + FD_ZERO(&readfds); + if (half == NO) + FD_SET(s_src, &readfds); + FD_SET(s_dst, &readfds); + FD_SET(s_ctl, &readfds); + FD_SET(s_ctl6, &readfds); + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select 4 sockets: %s", ERRSTR); + else if (error == 0) + exit_failure("connecion timeout"); + + if (half == NO && FD_ISSET(s_src, &readfds)) { + s_rcv = s_src; + s_snd = s_dst; + relay(s_src, s_dst); + } + if (FD_ISSET(s_dst, &readfds)) { + s_rcv = s_dst; + s_snd = s_src; + relay(s_src, s_dst); + } + if (FD_ISSET(s_ctl, &readfds)) { + s_rcv = s_ctl; + s_snd = s_ctl6; + relay(s_src, s_dst); + } + if (FD_ISSET(s_ctl6, &readfds)) { + s_rcv = s_ctl6; + s_snd = s_ctl; + relay(s_src, s_dst); + } + } + /* NOTREACHED */ + + bad: + exit_failure(ERRSTR); +} diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c new file mode 100644 index 000000000000..e1e1b328c2b1 --- /dev/null +++ b/usr.sbin/faithd/tcp.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * 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. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 THE PROJECT OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +static char tcpbuf[16*1024]; + /* bigger than MSS and may be lesser than window size */ +static int tblen, tboff, oob_exists; +static fd_set readfds, writefds, exceptfds; +static char atmark_buf[2]; +static pid_t cpid = (pid_t)0; +static pid_t ppid = (pid_t)0; +static time_t child_lastactive = (time_t)0; +static time_t parent_lastactive = (time_t)0; + +static void sig_ctimeout __P((int)); +static void sig_child __P((int)); +static void notify_inactive __P((void)); +static void notify_active __P((void)); +static void send_data __P((int, int, const char *, int)); +static void relay __P((int, int, const char *, int)); + +/* + * Inactivity timer: + * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4) + * second if traffic is active. if traffic is inactive, don't send SIGUSR1. + * - parent side (ppid == 0) will check the last SIGUSR1 it have seen. + */ +static void +sig_ctimeout(int sig) +{ + /* parent side: record notification from the child */ + if (dflag) + syslog(LOG_DEBUG, "activity timer from child"); + child_lastactive = time(NULL); +} + +/* parent will terminate if child dies. */ +static void +sig_child(int sig) +{ + int status; + pid_t pid; + + pid = wait3(&status, WNOHANG, (struct rusage *)0); + if (pid && status) + syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); + exit_failure("terminate connection due to child termination"); +} + +static void +notify_inactive() +{ + time_t t; + + /* only on parent side... */ + if (ppid) + return; + + /* parent side should check for timeout. */ + t = time(NULL); + if (dflag) { + syslog(LOG_DEBUG, "parent side %sactive, child side %sactive", + (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "", + (FAITH_TIMEOUT < t - child_lastactive) ? "in" : ""); + } + + if (FAITH_TIMEOUT < t - child_lastactive + && FAITH_TIMEOUT < t - parent_lastactive) { + /* both side timeouted */ + signal(SIGCHLD, SIG_DFL); + kill(cpid, SIGTERM); + wait(NULL); + exit_failure("connection timeout"); + /* NOTREACHED */ + } +} + +static void +notify_active() +{ + if (ppid) { + /* child side: notify parent of active traffic */ + time_t t; + t = time(NULL); + if (FAITH_TIMEOUT / 4 < t - child_lastactive) { + if (kill(ppid, SIGUSR1) < 0) { + exit_failure("terminate connection due to parent termination"); + /* NOTREACHED */ + } + child_lastactive = t; + } + } else { + /* parent side */ + parent_lastactive = time(NULL); + } +} + +static void +send_data(int s_rcv, int s_snd, const char *service, int direction) +{ + int cc; + + if (oob_exists) { + cc = send(s_snd, atmark_buf, 1, MSG_OOB); + if (cc == -1) + goto retry_or_err; + oob_exists = 0; + FD_SET(s_rcv, &exceptfds); + } + + for (; tboff < tblen; tboff += cc) { + cc = write(s_snd, tcpbuf + tboff, tblen - tboff); + if (cc < 0) + goto retry_or_err; + } +#ifdef DEBUG + if (tblen) { + if (tblen >= sizeof(tcpbuf)) + tblen = sizeof(tcpbuf) - 1; + tcpbuf[tblen] = '\0'; + syslog(LOG_DEBUG, "from %s (%dbytes): %s", + direction == 1 ? "client" : "server", tblen, tcpbuf); + } +#endif /* DEBUG */ + tblen = 0; tboff = 0; + FD_CLR(s_snd, &writefds); + FD_SET(s_rcv, &readfds); + return; + retry_or_err: + if (errno != EAGAIN) + exit_failure("writing relay data failed: %s", ERRSTR); + FD_SET(s_snd, &writefds); +} + +static void +relay(int s_rcv, int s_snd, const char *service, int direction) +{ + int atmark, error, maxfd; + struct timeval tv; + fd_set oreadfds, owritefds, oexceptfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + fcntl(s_snd, F_SETFD, O_NONBLOCK); + oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds; + FD_SET(s_rcv, &readfds); FD_SET(s_rcv, &exceptfds); + oob_exists = 0; + maxfd = (s_rcv > s_snd) ? s_rcv : s_snd; + + for (;;) { + tv.tv_sec = FAITH_TIMEOUT / 4; + tv.tv_usec = 0; + oreadfds = readfds; + owritefds = writefds; + oexceptfds = exceptfds; + error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv); + if (error == -1) { + if (errno == EINTR) + continue; + exit_failure("select: %s", ERRSTR); + } else if (error == 0) { + readfds = oreadfds; + writefds = owritefds; + exceptfds = oexceptfds; + notify_inactive(); + continue; + } + + /* activity notification */ + notify_active(); + + if (FD_ISSET(s_rcv, &exceptfds)) { + error = ioctl(s_rcv, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + int cc; + oob_read_retry: + cc = read(s_rcv, atmark_buf, 1); + if (cc == 1) { + FD_CLR(s_rcv, &exceptfds); + FD_SET(s_snd, &writefds); + oob_exists = 1; + } else if (cc == -1) { + if (errno == EINTR) + goto oob_read_retry; + exit_failure("reading oob data failed" + ": %s", + ERRSTR); + } + } + } + if (FD_ISSET(s_rcv, &readfds)) { + relaydata_read_retry: + tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf)); + tboff = 0; + + switch (tblen) { + case -1: + if (errno == EINTR) + goto relaydata_read_retry; + exit_failure("reading relay data failed: %s", + ERRSTR); + /* NOTREACHED */ + case 0: + /* to close opposite-direction relay process */ + shutdown(s_snd, 0); + + close(s_rcv); + close(s_snd); + exit_success("terminating %s relay", service); + /* NOTREACHED */ + default: + FD_CLR(s_rcv, &readfds); + FD_SET(s_snd, &writefds); + break; + } + } + if (FD_ISSET(s_snd, &writefds)) + send_data(s_rcv, s_snd, service, direction); + } +} + +void +tcp_relay(int s_src, int s_dst, const char *service) +{ + syslog(LOG_INFO, "starting %s relay", service); + + child_lastactive = parent_lastactive = time(NULL); + + cpid = fork(); + switch (cpid) { + case -1: + exit_failure("tcp_relay: can't fork grand child: %s", ERRSTR); + /* NOTREACHED */ + case 0: + /* child process: relay going traffic */ + ppid = getppid(); + /* this is child so reopen log */ + closelog(); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + relay(s_src, s_dst, service, 1); + /* NOTREACHED */ + default: + /* parent process: relay coming traffic */ + ppid = (pid_t)0; + signal(SIGUSR1, sig_ctimeout); + signal(SIGCHLD, sig_child); + relay(s_dst, s_src, service, 0); + /* NOTREACHED */ + } +} diff --git a/usr.sbin/faithd/test/faithd.rb b/usr.sbin/faithd/test/faithd.rb new file mode 100644 index 000000000000..683b5041adc1 --- /dev/null +++ b/usr.sbin/faithd/test/faithd.rb @@ -0,0 +1,312 @@ +# faithd, ruby version. requires v6-enabled ruby. +# +# highly experimental (not working right at all) and very limited +# functionality. +# +# $Id: faithd.rb,v 1.1.1.1 1999/08/08 23:29:31 itojun Exp $ +# $FreeBSD$ + +require "socket" +require "thread" + +# XXX should be derived from system headers +IPPROTO_IPV6 = 41 +IPV6_FAITH = 29 +DEBUG = true +DEBUG_LOOPBACK = true + +# TODO: OOB data handling +def tcpcopy(s1, s2, m) + STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG + buf = "" + while TRUE + begin + buf = s1.sysread(100) + s2.syswrite(buf) + rescue EOFError + break + rescue IOError + break + end + end + STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG + s1.shutdown(0) + s2.shutdown(1) +end + +def relay_ftp_passiveconn(s6, s4, dport6, dport4) + Thread.start do + d6 = TCPserver.open("::", dport6).accept + d4 = TCPsocket.open(s4.getpeer[3], dport4) + t = [] + t[0] = Thread.start do + tcpcopy(d6, d4) + end + t[1] = Thread.start do + tcpcopy(d4, d6) + end + for i in t + i.join + end + d4.close + d6.close + end +end + +def ftp_parse_2428(line) + if (line[0] != line[line.length - 1]) + return nil + end + t = line.split(line[0 .. 0]) # as string + if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/) + return nil + end + return t[1 .. 3] +end + +def relay_ftp_command(s6, s4, state) + STDERR.print "relay_ftp_command start\n" if DEBUG + while TRUE + begin + STDERR.print "s6.gets\n" if DEBUG + line = s6.gets + STDERR.print "line is #{line}\n" if DEBUG + if line == nil + return nil + end + + # translate then copy + STDERR.print "line is #{line}\n" if DEBUG + if (line =~ /^EPSV\r\n/i) + STDERR.print "EPSV -> PASV\n" if DEBUG + line = "PASV\n" + state = "EPSV" + elsif (line =~ /^EPRT\s+(.+)\r\n/i) + t = ftp_parse_2428($1) + if t == nil + s6.puts "501 illegal parameter to EPRT\r\n" + next + end + + # some tricks should be here + s6.puts "501 illegal parameter to EPRT\r\n" + next + end + STDERR.print "fail: send #{line} as is\n" if DEBUG + s4.puts(line) + break + rescue EOFError + return nil + rescue IOError + return nil + end + end + STDERR.print "relay_ftp_command finish\n" if DEBUG + return state +end + +def relay_ftp_status(s4, s6, state) + STDERR.print "relay_ftp_status start\n" if DEBUG + while TRUE + begin + line = s4.gets + if line == nil + return nil + end + + # translate then copy + s6.puts(line) + + next if line =~ /^\d\d\d-/ + next if line !~ /^\d/ + + # special post-processing + case line + when /^221 / # result to QUIT + s4.shutdown(0) + s6.shutdown(1) + end + + break if (line =~ /^\d\d\d /) + rescue EOFError + return nil + rescue IOError + return nil + end + end + STDERR.print "relay_ftp_status finish\n" if DEBUG + return state +end + +def relay_ftp(sock, name) + STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG + while TRUE + STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG + s = sock.accept + STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG + Thread.start do + threads = [] + STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG + s6 = s + dest6 = s.addr[3] + if !DEBUG_LOOPBACK + t = s.getsockname.unpack("x8 x12 C4") + dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" + port4 = s.addr[1] + else + dest4 = "127.0.0.1" + port4 = "ftp" + end + if DEBUG + STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG + end + STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG + s4 = TCPsocket.open(dest4, port4) + STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG + state = 0 + while TRUE + # translate status line + state = relay_ftp_status(s4, s6, state) + break if state == nil + # translate command line + state = relay_ftp_command(s6, s4, state) + break if state == nil + end + STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG + s4.close + STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG + s6.close + STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG + end + end + STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG +end + +def relay_tcp(sock, name) + STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG + while TRUE + STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG + s = sock.accept + STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG + Thread.start do + threads = [] + STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG + s6 = s + dest6 = s.addr[3] + if !DEBUG_LOOPBACK + t = s.getsockname.unpack("x8 x12 C4") + dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" + port4 = s.addr[1] + else + dest4 = "127.0.0.1" + port4 = "telnet" + end + if DEBUG + STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG + end + STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG + s4 = TCPsocket.open(dest4, port4) + STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG + [0, 1].each do |i| + threads[i] = Thread.start do + if (i == 0) + tcpcopy(s6, s4) + else + tcpcopy(s4, s6) + end + end + end + STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG + for i in threads + STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG + i.join + STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG + end + STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG + s4.close + STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG + s6.close + STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG + end + end + STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG +end + +def usage() + STDERR.print "usage: #{$0} [-f] port...\n" +end + +#------------------------------------------------------------ + +$mode = "tcp" + +while ARGV[0] =~ /^-/ do + case ARGV[0] + when /^-f/ + $mode = "ftp" + else + usage() + exit 0 + end + ARGV.shift +end + +if ARGV.length == 0 + usage() + exit 1 +end + +ftpport = Socket.getservbyname("ftp") + +res = [] +for port in ARGV + t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM, + nil, Socket::AI_PASSIVE) + if (t.size <= 0) + STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n" + exit 1 + end + res += t +end + +sockpool = [] +names = [] +listenthreads = [] + +res.each do |i| + s = TCPserver.new(i[3], i[1]) + n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ") + if i[6] == IPPROTO_IPV6 + s.setsockopt(i[6], IPV6_FAITH, 1) + end + s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) + sockpool.push s + names.push n +end + +if DEBUG + (0 .. sockpool.size - 1).each do |i| + STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG + end +end + +(0 .. sockpool.size - 1).each do |i| + listenthreads[i] = Thread.start do + if DEBUG + STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG + end + STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG + case $mode + when "tcp" + relay_tcp(sockpool[i], names[i]) + when "ftp" + relay_ftp(sockpool[i], names[i]) + end + end +end + +for i in listenthreads + i.join +end + +exit 0