diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /remote_cmds | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'remote_cmds')
88 files changed, 28689 insertions, 0 deletions
diff --git a/remote_cmds/APPLE_LICENSE b/remote_cmds/APPLE_LICENSE new file mode 100644 index 0000000..e7aa7d0 --- /dev/null +++ b/remote_cmds/APPLE_LICENSE @@ -0,0 +1,370 @@ + APPLE PUBLIC SOURCE LICENSE + Version 1.0 - March 16, 1999 + +Please read this License carefully before downloading this software. +By downloading and using this software, you are agreeing to be bound +by the terms of this License. If you do not or cannot agree to the +terms of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other + work which Apple Computer, Inc. ("Apple") publicly announces as + subject to this Apple Public Source License and which contains a + notice placed by Apple identifying such program or work as "Original + Code" and stating that it is subject to the terms of this Apple + Public Source License version 1.0 (or subsequent version thereof), + as it may be revised from time to time by Apple ("License"). As + used in this License: + +1.1 "Applicable Patents" mean: (a) in the case where Apple is the + grantor of rights, (i) patents or patent applications that are now + or hereafter acquired, owned by or assigned to Apple and (ii) whose + claims cover subject matter contained in the Original Code, but only + to the extent necessary to use, reproduce and/or distribute the + Original Code without infringement; and (b) in the case where You + are the grantor of rights, (i) patents and patent applications that + are now or hereafter acquired, owned by or assigned to You and (ii) + whose claims cover subject matter in Your Modifications, taken alone + or in combination with Original Code. + +1.2 "Covered Code" means the Original Code, Modifications, the + combination of Original Code and any Modifications, and/or any + respective portions thereof. + +1.3 "Deploy" means to use, sublicense or distribute Covered Code other + than for Your internal research and development (R&D), and includes + without limitation, any and all internal use or distribution of + Covered Code within Your business or organization except for R&D + use, as well as direct or indirect sublicensing or distribution of + Covered Code by You to any third party in any form or manner. + +1.4 "Larger Work" means a work which combines Covered Code or portions + thereof with code not governed by the terms of this License. + +1.5 "Modifications" mean any addition to, deletion from, and/or change + to, the substance and/or structure of Covered Code. When code is + released as a series of files, a Modification is: (a) any addition + to or deletion from the contents of a file containing Covered Code; + and/or (b) any new file or other representation of computer program + statements that contains any part of Covered Code. + +1.6 "Original Code" means the Source Code of a program or other work + as originally made available by Apple under this License, including + the Source Code of any updates or upgrades to such programs or works + made available by Apple under this License, and that has been + expressly identified by Apple as such in the header file(s) of such + work. + +1.7 "Source Code" means the human readable form of a program or other + work that is suitable for making modifications to it, including all + modules it contains, plus any associated interface definition files, + scripts used to control compilation and installation of an + executable (object code). + +1.8 "You" or "Your" means an individual or a legal entity exercising + rights under this License. For legal entities, "You" or "Your" + includes any entity which controls, is controlled by, or is under + common control with, You, where "control" means (a) the power, + direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (b) ownership of fifty + percent (50%) or more of the outstanding shares or beneficial + ownership of such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms + and conditions of this License, Apple hereby grants You, effective + on the date You accept this License and download the Original Code, + a world-wide, royalty-free, non-exclusive license, to the extent of + Apple's Applicable Patents and copyrights covering the Original + Code, to do the following: + +2.1 You may use, copy, modify and distribute Original Code, with or + without Modifications, solely for Your internal research and + development, provided that You must in each instance: + +(a) retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code +that refer to this License; + +(b) include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer +or impose any terms on such Source Code that alter or restrict this +License or the recipients' rights hereunder, except as permitted under +Section 6; and + +(c) completely and accurately document all Modifications that you have +made and the date of each such Modification, designate the version of +the Original Code you used, prominently include a file carrying such +information with the Modifications, and duplicate the notice in +Exhibit A in each file of the Source Code of all such Modifications. + +2.2 You may Deploy Covered Code, provided that You must in each + instance: + +(a) satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) make all Your Deployed Modifications publicly available in Source +Code form via electronic distribution (e.g. download from a web site) +under the terms of this License and subject to the license grants set +forth in Section 3 below, and any additional terms You may choose to +offer under Section 6. You must continue to make the Source Code of +Your Deployed Modifications available for as long as you Deploy the +Covered Code or twelve (12) months from the date of initial +Deployment, whichever is longer; + +(c) must notify Apple and other third parties of how to obtain Your +Deployed Modifications by filling out and submitting the required +information found at +http://www.apple.com/publicsource/modifications.html; and + +(d) if you Deploy Covered Code in object code, executable form only, +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is +available under the terms of this License with information on how and +where to obtain such Source Code. + +3. Your Grants. In consideration of, and as a condition to, the + licenses granted to You under this License: + +(a) You hereby grant to Apple and all third parties a non-exclusive, +royalty-free license, under Your Applicable Patents and other +intellectual property rights owned or controlled by You, to use, +reproduce, modify, distribute and Deploy Your Modifications of the +same scope and extent as Apple's licenses under Sections 2.1 and 2.2; +and + +(b) You hereby grant to Apple and its subsidiaries a non-exclusive, +worldwide, royalty-free, perpetual and irrevocable license, under Your +Applicable Patents and other intellectual property rights owned or +controlled by You, to use, reproduce, execute, compile, display, +perform, modify or have modified (for Apple and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form, through +multiple tiers of distribution. + +4. Larger Works. You may create a Larger Work by combining Covered + Code with other code not governed by the terms of this License and + distribute the Larger Work as a single product. In each such + instance, You must make sure the requirements of this License are + fulfilled for the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in + Section 2, no other patent rights, express or implied, are granted + by Apple herein. Modifications and/or Larger Works may require + additional patent licenses from Apple which Apple may grant in its + sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee + for, warranty, support, indemnity or liability obligations and/or + other rights consistent with the scope of the license granted herein + ("Additional Terms") to one or more recipients of Covered + Code. However, You may do so only on Your own behalf and as Your + sole responsibility, and not on behalf of Apple. You must obtain the + recipient's agreement that any such Additional Terms are offered by + You alone, and You hereby agree to indemnify, defend and hold Apple + harmless for any liability incurred by or claims asserted against + Apple by reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new + versions of this License from time to time. Each version will be + given a distinguishing version number. Once Original Code has been + published under a particular version of this License, You may + continue to use it under the terms of that version. You may also + choose to use such Original Code under the terms of any subsequent + version of this License published by Apple. No one other than Apple + has the right to modify the terms applicable to Covered Code created + under this License. + +8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or + in part pre-release, untested, or not fully tested works. The + Original Code may contain errors that could cause failures or loss + of data, and may be incomplete or contain inaccuracies. You + expressly acknowledge and agree that use of the Original Code, or + any portion thereof, is at Your sole and entire risk. THE ORIGINAL + CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT + OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF + SECTIONS 8 AND 9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY + REFERRED TO AS "APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR + CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY OR + SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT THAT + THE FUNCTIONS CONTAINED IN THE ORIGINAL CODE WILL MEET YOUR + REQUIREMENTS, OR THAT THE OPERATION OF THE ORIGINAL CODE WILL BE + UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE ORIGINAL CODE + WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN + BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A + WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. You + acknowledge that the Original Code is not intended for use in the + operation of nuclear facilities, aircraft navigation, communication + systems, or air traffic control machines in which case the failure + of the Original Code could lead to death, personal injury, or severe + physical or environmental damage. + +9. Liability. + +9.1 Infringement. If any of the Original Code becomes the subject of + a claim of infringement ("Affected Original Code"), Apple may, at + its sole discretion and option: (a) attempt to procure the rights + necessary for You to continue using the Affected Original Code; (b) + modify the Affected Original Code so that it is no longer + infringing; or (c) terminate Your rights to use the Affected + Original Code, effective immediately upon Apple's posting of a + notice to such effect on the Apple web site that is used for + implementation of this License. + +9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE + LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR + INABILITY TO USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER + UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), + PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF + THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF + ESSENTIAL PURPOSE OF ANY REMEDY. In no event shall Apple's total + liability to You for all damages under this License exceed the + amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the + trademarks or trade names "Apple", "Apple Computer", "Mac OS X", + "Mac OS X Server" or any other trademarks or trade names belonging + to Apple (collectively "Apple Marks") and no Apple Marks may be + used to endorse or promote products derived from the Original Code + other than as permitted by and in strict compliance at all times + with Apple's third party trademark usage guidelines which are + posted at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Apple retains all rights, title and interest in and to + the Original Code and any Modifications made by or on behalf of + Apple ("Apple Modifications"), and such Apple Modifications will + not be automatically subject to this License. Apple may, at its + sole discretion, choose to license such Apple Modifications under + this License, or on different terms from those contained in this + License or may choose not to license them at all. Apple's + development, use, reproduction, modification, sublicensing and + distribution of Covered Code will not be subject to this License. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will + terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; (b) immediately in the event of +the circumstances described in Sections 9.1 and/or 13.6(b); or (c) +automatically without notice from Apple if You, at any time during the +term of this License, commence an action for patent infringement +against Apple. + +12.2 Effect of Termination. Upon termination, You agree to + immediately stop any further use, reproduction, modification and + distribution of the Covered Code, or Affected Original Code in the + case of termination under Section 9.1, and to destroy all copies of + the Covered Code or Affected Original Code (in the case of + termination under Section 9.1) that are in your possession or + control. All sublicenses to the Covered Code which have been + properly granted prior to termination shall survive any termination + of this License. Provisions which, by their nature, should remain + in effect beyond the termination of this License shall survive, + including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and + 13. Neither party will be liable to the other for compensation, + indemnity or damages of any sort solely as a result of terminating + this License in accordance with its terms, and termination of this + License will be without prejudice to any other right or remedy of + either party. + +13. Miscellaneous. + +13.1 Export Law Assurances. You may not use or otherwise export or + re-export the Original Code except as authorized by United States + law and the laws of the jurisdiction in which the Original Code was + obtained. In particular, but without limitation, the Original Code + may not be exported or re-exported (a) into (or to a national or + resident of) any U.S. embargoed country or (b) to anyone on the + U.S. Treasury Department's list of Specially Designated Nationals + or the U.S. Department of Commerce's Table of Denial Orders. By + using the Original Code, You represent and warrant that You are not + located in, under control of, or a national or resident of any such + country or on any such list. + +13.2 Government End Users. The Covered Code is a "commercial item" as + defined in FAR 2.101. Government software and technical data + rights in the Covered Code include only those rights customarily + provided to the public as defined in this License. This customary + commercial license in technical data and software is provided in + accordance with FAR 12.211 (Technical Data) and 12.212 (Computer + Software) and, for Department of Defense purchases, DFAR + 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 + (Rights in Commercial Computer Software or Computer Software + Documentation). Accordingly, all U.S. Government End Users acquire + Covered Code with only those rights set forth herein. + +13.3 Relationship of Parties. This License will not be construed as + creating an agency, partnership, joint venture or any other form of + legal association between You and Apple, and You will not represent + to the contrary, whether expressly, by implication, appearance or + otherwise. + +13.4 Independent Development. Nothing in this License will impair + Apple's right to acquire, license, develop, have others develop for + it, market and/or distribute technology or products that perform + the same or similar functions as, or otherwise compete with, + Modifications, Larger Works, technology or products that You may + develop, produce, market or distribute. + +13.5 Waiver; Construction. Failure by Apple to enforce any provision + of this License will not be deemed a waiver of future enforcement + of that or any other provision. Any law or regulation which + provides that the language of a contract shall be construed against + the drafter will not apply to this License. + +13.6 Severability. (a) If for any reason a court of competent + jurisdiction finds any provision of this License, or portion + thereof, to be unenforceable, that provision of the License will be + enforced to the maximum extent permissible so as to effect the + economic benefits and intent of the parties, and the remainder of + this License will continue in full force and effect. (b) + Notwithstanding the foregoing, if applicable law prohibits or + restricts You from fully and/or specifically complying with + Sections 2 and/or 3 or prevents the enforceability of either of + those Sections, this License will immediately terminate and You + must immediately discontinue any use of the Covered Code and + destroy all copies of it that are in your possession or control. + +13.7 Dispute Resolution. Any litigation or other dispute resolution + between You and Apple relating to this License shall take place in + the Northern District of California, and You and Apple hereby + consent to the personal jurisdiction of, and venue in, the state + and federal courts within that District with respect to this + License. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly + excluded. + +13.8 Entire Agreement; Governing Law. This License constitutes the + entire agreement between the parties with respect to the subject + matter hereof. This License shall be governed by the laws of the + United States and the State of California, except that body of + California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights +Reserved. This file contains Original Code and/or Modifications of +Original Code as defined in and that are subject to the Apple Public +Source License Version 1.0 (the 'License'). You may not use this file +except in compliance with the License. Please obtain a copy of the +License at http://www.apple.com/publicsource and read it before using +this file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the +License for the specific language governing rights and limitations +under the License." diff --git a/remote_cmds/Makefile b/remote_cmds/Makefile new file mode 100644 index 0000000..7347ea8 --- /dev/null +++ b/remote_cmds/Makefile @@ -0,0 +1,12 @@ +Project = remote_cmds + +ifeq "$(RC_TARGET_CONFIG)" "iPhone" +SubProjects = telnetd.tproj +else +SubProjects = \ + logger.tproj\ + talk.tproj talkd.tproj telnet.tproj telnetd.tproj tftp.tproj\ + tftpd.tproj wall.tproj +endif + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/logger.tproj/Makefile b/remote_cmds/logger.tproj/Makefile new file mode 100644 index 0000000..002cb56 --- /dev/null +++ b/remote_cmds/logger.tproj/Makefile @@ -0,0 +1,11 @@ +Project = logger +Install_Dir = /usr/bin + +CFILES = logger.c +MANPAGES = logger.1 + +Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic +Extra_CC_Flags += -D__FBSDID=__RCSID +Extra_LD_Flags = -dead_strip + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/logger.tproj/logger.1 b/remote_cmds/logger.tproj/logger.1 new file mode 100644 index 0000000..3a942e8 --- /dev/null +++ b/remote_cmds/logger.tproj/logger.1 @@ -0,0 +1,102 @@ +.\" $OpenBSD: logger.1,v 1.2 1996/06/26 05:35:58 deraadt Exp $ +.\" $NetBSD: logger.1,v 1.4 1994/12/22 06:26:59 jtc Exp $ +.\" +.\" 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. +.\" +.\" @(#)logger.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt LOGGER 1 +.Os BSD 4.3 +.Sh NAME +.Nm logger +.Nd make entries in the system log +.Sh SYNOPSIS +.Nm logger +.Op Fl is +.Op Fl f Ar file +.Op Fl p Ar pri +.Op Fl t Ar tag +.Op Ar message ... +.Sh DESCRIPTION +.Nm Logger +provides a shell command interface to the +.Xr syslog 3 +system log module. +.Pp +Options: +.Pp +.Bl -tag -width "message" +.It Fl i +Log the process id of the logger process +with each line. +.It Fl s +Log the message to standard error, as well as the system log. +.It Fl f Ar file +Log the specified file. +.It Fl p Ar pri +Enter the message with the specified priority. +The priority may be specified numerically or as a ``facility.level'' +pair. +For example, ``\-p local3.info'' logs the message(s) as +.Ar info Ns rmational +level in the +.Ar local3 +facility. +The default is ``user.notice.'' +.It Fl t Ar tag +Mark every line in the log with the specified +.Ar tag . +.It Ar message +Write the message to log; if not specified, and the +.Fl f +flag is not +provided, standard input is logged. +.El +.Pp +The +.Nm logger +utility exits 0 on success, and >0 if an error occurs. +.Sh EXAMPLES +.Bd -literal -offset indent -compact +logger System rebooted + +logger \-p local0.notice \-t HOSTIDM \-f /dev/idmc +.Ed +.Sh SEE ALSO +.Xr syslog 3 , +.Xr syslogd 8 +.Sh STANDARDS +The +.Nm logger +utility conforms to +.St -p1003.2-92 . diff --git a/remote_cmds/logger.tproj/logger.c b/remote_cmds/logger.tproj/logger.c new file mode 100644 index 0000000..e3d9203 --- /dev/null +++ b/remote_cmds/logger.tproj/logger.c @@ -0,0 +1,329 @@ +/* + * 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. + */ + +#ifndef lint +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)logger.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/logger/logger.c,v 1.17 2008/02/05 17:34:44 obrien Exp $"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <err.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define SYSLOG_NAMES +#include <syslog.h> + +int decode(char *, CODE *); +int pencode(char *); +#ifndef __APPLE__ +static void logmessage(int, const char *, const char *, const char *); +#endif +static void usage(void); + +struct socks { + int sock; + int addrlen; + struct sockaddr_storage addr; +}; + +#ifdef INET6 +int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ +#else +int family = PF_INET; /* protocol family (IPv4 only) */ +#endif +int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ + +/* + * logger -- read and log utility + * + * Reads from an input and arranges to write the result on the system + * log. + */ +int +main(int argc, char *argv[]) +{ + int ch, logflags, pri; + char *tag, *host, buf[1024]; + const char *svcname; + + tag = NULL; + host = NULL; + svcname = "syslog"; + pri = LOG_USER | LOG_NOTICE; + logflags = 0; + unsetenv("TZ"); +#ifdef __APPLE__ + while ((ch = getopt(argc, argv, "f:ip:st:")) != -1) +#else + while ((ch = getopt(argc, argv, "46Af:h:iP:p:st:")) != -1) +#endif + switch((char)ch) { +#ifndef __APPLE__ + case '4': + family = PF_INET; + break; +#ifdef INET6 + case '6': + family = PF_INET6; + break; +#endif + case 'A': + send_to_all++; + break; +#endif /* __APPLE__ */ + case 'f': /* file to log */ + if (freopen(optarg, "r", stdin) == NULL) + err(1, "%s", optarg); + break; +#ifndef __APPLE__ + case 'h': /* hostname to deliver to */ + host = optarg; + break; +#endif /* __APPLE__ */ + case 'i': /* log process id also */ + logflags |= LOG_PID; + break; +#ifndef __APPLE__ + case 'P': /* service name or port number */ + svcname = optarg; + break; +#endif /* __APPLE__ */ + case 'p': /* priority */ + pri = pencode(optarg); + break; + case 's': /* log to standard error */ + logflags |= LOG_PERROR; + break; + case 't': /* tag */ + tag = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* setup for logging */ + openlog(tag ? tag : getlogin(), logflags, 0); + (void) fclose(stdout); + + /* log input line if appropriate */ + if (argc > 0) { + char *p, *endp; + size_t len; + + for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) { + len = strlen(*argv); + if (p + len > endp && p > buf) { +#ifdef __APPLE__ + syslog(pri, "%s", buf); +#else + logmessage(pri, host, svcname, buf); +#endif + p = buf; + } + if (len > sizeof(buf) - 1) +#ifdef __APPLE__ + syslog(pri, "%s", *argv++); +#else + logmessage(pri, host, svcname, *argv++); +#endif + else { + if (p != buf) + *p++ = ' '; + bcopy(*argv++, p, len); + *(p += len) = '\0'; + } + } + if (p != buf) +#ifdef __APPLE__ + syslog(pri, "%s", buf); +#else + logmessage(pri, host, svcname, buf); +#endif + } else + while (fgets(buf, sizeof(buf), stdin) != NULL) +#ifdef __APPLE__ + syslog(pri, "%s", buf); +#else + logmessage(pri, host, svcname, buf); +#endif + exit(0); +} + +#ifndef __APPLE__ +/* + * Send the message to syslog, either on the local host, or on a remote host + */ +void +logmessage(int pri, const char *host, const char *svcname, const char *buf) +{ + static struct socks *socks; + static int nsock = 0; + struct addrinfo hints, *res, *r; + char *line; + int maxs, len, sock, error, i, lsent; + + if (host == NULL) { + syslog(pri, "%s", buf); + return; + } + + if (nsock <= 0) { /* set up socket stuff */ + /* resolve hostname */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + error = getaddrinfo(host, svcname, &hints, &res); + if (error == EAI_SERVICE) { + warnx("%s/udp: unknown service", svcname); + error = getaddrinfo(host, "514", &hints, &res); + } + if (error) + errx(1, "%s: %s", gai_strerror(error), host); + /* count max number of sockets we may open */ + for (maxs = 0, r = res; r; r = r->ai_next, maxs++); + socks = malloc(maxs * sizeof(struct socks)); + if (!socks) + errx(1, "couldn't allocate memory for sockets"); + for (r = res; r; r = r->ai_next) { + sock = socket(r->ai_family, r->ai_socktype, + r->ai_protocol); + if (sock < 0) + continue; + memcpy(&socks[nsock].addr, r->ai_addr, r->ai_addrlen); + socks[nsock].addrlen = r->ai_addrlen; + socks[nsock++].sock = sock; + } + freeaddrinfo(res); + if (nsock <= 0) + errx(1, "socket"); + } + + if ((len = asprintf(&line, "<%d>%s", pri, buf)) == -1) + errx(1, "asprintf"); + + lsent = -1; + for (i = 0; i < nsock; ++i) { + lsent = sendto(socks[i].sock, line, len, 0, + (struct sockaddr *)&socks[i].addr, + socks[i].addrlen); + if (lsent == len && !send_to_all) + break; + } + if (lsent != len) { + if (lsent == -1) + warn ("sendto"); + else + warnx ("sendto: short send - %d bytes", lsent); + } + + free(line); +} +#endif /* __APPLE__ */ + +/* + * Decode a symbolic name to a numeric value + */ +int +pencode(char *s) +{ + char *save; + int fac, lev; + + for (save = s; *s && *s != '.'; ++s); + if (*s) { + *s = '\0'; + fac = decode(save, facilitynames); + if (fac < 0) + errx(1, "unknown facility name: %s", save); + *s++ = '.'; + } + else { + fac = 0; + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) + errx(1, "unknown priority name: %s", save); + return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); +} + +int +decode(char *name, CODE *codetab) +{ + CODE *c; + + if (isdigit(*name)) + return (atoi(name)); + + for (c = codetab; c->c_name; c++) + if (!strcasecmp(name, c->c_name)) + return (c->c_val); + + return (-1); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: %s\n", +#ifdef __APPLE__ + "logger [-is] [-f file] [-p pri] [-t tag]\n" +#else + "logger [-46Ais] [-f file] [-h host] [-P port] [-p pri] [-t tag]\n" +#endif + " [message ...]" + ); + exit(1); +} diff --git a/remote_cmds/talk.tproj/Makefile b/remote_cmds/talk.tproj/Makefile new file mode 100644 index 0000000..47e3631 --- /dev/null +++ b/remote_cmds/talk.tproj/Makefile @@ -0,0 +1,14 @@ +Project = talk +Install_Dir = /usr/bin + +HFILES = talk.h talk_ctl.h +CFILES = ctl.c ctl_transact.c display.c get_addrs.c get_names.c\ + get_iface.c init_disp.c invite.c io.c look_up.c msgs.c talk.c +MANPAGES = talk.1 + +Extra_CC_Flags = -Wall -Werror -fPIE +Extra_LD_Flags = -dead_strip -pie + +Extra_LD_Libraries = -lcurses + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/talk.tproj/ctl.c b/remote_cmds/talk.tproj/ctl.c new file mode 100644 index 0000000..a5b42cc --- /dev/null +++ b/remote_cmds/talk.tproj/ctl.c @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/ctl.c,v 1.10 2005/02/09 09:13:36 stefanf Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)ctl.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +/* + * This file handles haggling with the various talk daemons to + * get a socket to talk to. sockt is opened and connected in + * the progress + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <string.h> + +#include "talk.h" + +struct sockaddr_in daemon_addr = { sizeof(daemon_addr), AF_INET }; +struct sockaddr_in ctl_addr = { sizeof(ctl_addr), AF_INET }; +struct sockaddr_in my_addr = { sizeof(my_addr), AF_INET }; + + /* inet addresses of the two machines */ +struct in_addr my_machine_addr; +struct in_addr his_machine_addr; + +u_short daemon_port; /* port number of the talk daemon */ + +int ctl_sockt; +int sockt; +int invitation_waiting = 0; + +CTL_MSG msg; + +void +open_sockt() +{ + socklen_t length; + + (void)memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_len = sizeof(my_addr); + my_addr.sin_addr = my_machine_addr; + my_addr.sin_port = 0; + sockt = socket(AF_INET, SOCK_STREAM, 0); + if (sockt == -1) + p_error("Bad socket"); + if (bind(sockt, (struct sockaddr *)&my_addr, sizeof(my_addr)) != 0) + p_error("Binding local socket"); + length = sizeof(my_addr); + if (getsockname(sockt, (struct sockaddr *)&my_addr, &length) == -1) + p_error("Bad address for socket"); +} + +/* open the ctl socket */ +void +open_ctl() +{ + socklen_t length; + + (void)memset(&ctl_addr, 0, sizeof(ctl_addr)); + ctl_addr.sin_family = AF_INET; + ctl_addr.sin_len = sizeof(my_addr); + ctl_addr.sin_port = 0; + ctl_addr.sin_addr = my_machine_addr; + ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0); + if (ctl_sockt == -1) + p_error("Bad socket"); + if (bind(ctl_sockt, + (struct sockaddr *)&ctl_addr, sizeof(ctl_addr)) != 0) + p_error("Couldn't bind to control socket"); + length = sizeof(ctl_addr); + if (getsockname(ctl_sockt, + (struct sockaddr *)&ctl_addr, &length) == -1) + p_error("Bad address for ctl socket"); +} + +/* print_addr is a debug print routine */ +void +print_addr(addr) + struct sockaddr_in addr; +{ + int i; + + printf("addr = %lx, port = %o, family = %o zero = ", + (u_long)addr.sin_addr.s_addr, addr.sin_port, addr.sin_family); + for (i = 0; i<8;i++) + printf("%o ", (int)addr.sin_zero[i]); + putchar('\n'); +} diff --git a/remote_cmds/talk.tproj/ctl_transact.c b/remote_cmds/talk.tproj/ctl_transact.c new file mode 100644 index 0000000..1679250 --- /dev/null +++ b/remote_cmds/talk.tproj/ctl_transact.c @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> +#ifdef __APPLE__ +#include <sys/time.h> +#endif + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/ctl_transact.c,v 1.7 2002/02/18 20:35:26 mike Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)ctl_transact.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> + +#include "talk.h" +#include "talk_ctl.h" + +#define CTL_WAIT 2 /* time to wait for a response, in seconds */ + +/* + * SOCKDGRAM is unreliable, so we must repeat messages if we have + * not recieved an acknowledgement within a reasonable amount + * of time + */ +void +ctl_transact(target, lmsg, type, rp) + struct in_addr target; + CTL_MSG lmsg; + int type; + CTL_RESPONSE *rp; +{ + fd_set read_mask, ctl_mask; + int nready = 0, cc; + struct timeval wait; + + lmsg.type = type; + daemon_addr.sin_addr = target; + daemon_addr.sin_port = daemon_port; + FD_ZERO(&ctl_mask); + FD_SET(ctl_sockt, &ctl_mask); + + /* + * Keep sending the message until a response of + * the proper type is obtained. + */ + do { + wait.tv_sec = CTL_WAIT; + wait.tv_usec = 0; + /* resend message until a response is obtained */ + do { + cc = sendto(ctl_sockt, (char *)&lmsg, sizeof (lmsg), 0, + (struct sockaddr *)&daemon_addr, + sizeof (daemon_addr)); + if (cc != sizeof (lmsg)) { + if (errno == EINTR) + continue; + p_error("Error on write to talk daemon"); + } + read_mask = ctl_mask; + nready = select(ctl_sockt + 1, &read_mask, 0, 0, &wait); + if (nready < 0) { + if (errno == EINTR) + continue; + p_error("Error waiting for daemon response"); + } + } while (nready == 0); + /* + * Keep reading while there are queued messages + * (this is not necessary, it just saves extra + * request/acknowledgements being sent) + */ + do { + cc = recv(ctl_sockt, (char *)rp, sizeof (*rp), 0); + if (cc < 0) { + if (errno == EINTR) + continue; + p_error("Error on read from talk daemon"); + } + read_mask = ctl_mask; + /* an immediate poll */ + timerclear(&wait); + nready = select(ctl_sockt + 1, &read_mask, 0, 0, &wait); + } while (nready > 0 && (rp->vers != TALK_VERSION || + rp->type != type)); + } while (rp->vers != TALK_VERSION || rp->type != type); + rp->id_num = ntohl(rp->id_num); + rp->addr.sa_family = ntohs(rp->addr.sa_family); +} diff --git a/remote_cmds/talk.tproj/display.c b/remote_cmds/talk.tproj/display.c new file mode 100644 index 0000000..2815ec1 --- /dev/null +++ b/remote_cmds/talk.tproj/display.c @@ -0,0 +1,198 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/display.c,v 1.11 2003/07/04 20:44:25 luigi Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +/* + * The window 'manager', initializes curses and handles the actual + * displaying of text + */ +#include <ctype.h> + +#include "talk.h" + +xwin_t my_win; +xwin_t his_win; +WINDOW *line_win; + +int curses_initialized = 0; + +/* + * max HAS to be a function, it is called with + * an argument of the form --foo at least once. + */ +int +max(a,b) + int a, b; +{ + + return (a > b ? a : b); +} + +/* + * Display some text on somebody's window, processing some control + * characters while we are at it. + */ +void +display(win, text, size) + xwin_t *win; + char *text; + int size; +{ + int i; + char cch; + + for (i = 0; i < size; i++) { + if (*text == '\n' || *text == '\r') { + waddch(win->x_win, '\n'); + getyx(win->x_win, win->x_line, win->x_col); + text++; + continue; + } + if (*text == 004 && win == &my_win) { + /* control-D clears the screen */ + werase(my_win.x_win); + getyx(my_win.x_win, my_win.x_line, my_win.x_col); + wrefresh(my_win.x_win); + werase(his_win.x_win); + getyx(his_win.x_win, his_win.x_line, his_win.x_col); + wrefresh(his_win.x_win); + text++; + continue; + } + + /* erase character */ + if ( *text == win->cerase + || *text == 010 /* BS */ + || *text == 0177 /* DEL */ + ) { + wmove(win->x_win, win->x_line, max(--win->x_col, 0)); + getyx(win->x_win, win->x_line, win->x_col); + waddch(win->x_win, ' '); + wmove(win->x_win, win->x_line, win->x_col); + getyx(win->x_win, win->x_line, win->x_col); + text++; + continue; + } + /* + * On word erase search backwards until we find + * the beginning of a word or the beginning of + * the line. + */ + if ( *text == win->werase + || *text == 027 /* ^W */ + ) { + int endcol, xcol, ii, c; + + endcol = win->x_col; + xcol = endcol - 1; + while (xcol >= 0) { + c = readwin(win->x_win, win->x_line, xcol); + if (c != ' ') + break; + xcol--; + } + while (xcol >= 0) { + c = readwin(win->x_win, win->x_line, xcol); + if (c == ' ') + break; + xcol--; + } + wmove(win->x_win, win->x_line, xcol + 1); + for (ii = xcol + 1; ii < endcol; ii++) + waddch(win->x_win, ' '); + wmove(win->x_win, win->x_line, xcol + 1); + getyx(win->x_win, win->x_line, win->x_col); + text++; + continue; + } + /* line kill */ + if ( *text == win->kill + || *text == 025 /* ^U */ + ) { + wmove(win->x_win, win->x_line, 0); + wclrtoeol(win->x_win); + getyx(win->x_win, win->x_line, win->x_col); + text++; + continue; + } + if (*text == '\f') { + if (win == &my_win) + wrefresh(curscr); + text++; + continue; + } + if (*text == '\7') { + write(STDOUT_FILENO, text, 1); + text++; + continue; + } + if (!isprint((unsigned char)*text) && *text != '\t') { + waddch(win->x_win, '^'); + getyx(win->x_win, win->x_line, win->x_col); + cch = (*text & 63) + 64; + waddch(win->x_win, cch); + } else + waddch(win->x_win, (unsigned char)*text); + getyx(win->x_win, win->x_line, win->x_col); + text++; + } + wrefresh(win->x_win); +} + +/* + * Read the character at the indicated position in win + */ +int +readwin(win, line, col) + WINDOW *win; + int line; + int col; +{ + int oldline, oldcol; + int c; + + getyx(win, oldline, oldcol); + wmove(win, line, col); + c = winch(win); + wmove(win, oldline, oldcol); + return (c); +} diff --git a/remote_cmds/talk.tproj/get_addrs.c b/remote_cmds/talk.tproj/get_addrs.c new file mode 100644 index 0000000..e5a71ac --- /dev/null +++ b/remote_cmds/talk.tproj/get_addrs.c @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/get_addrs.c,v 1.5 2001/12/11 23:51:14 markm Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)get_addrs.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#include <err.h> +#include <netdb.h> +#include <string.h> + +#include "talk.h" +#include "talk_ctl.h" + +void +get_addrs(my_machine_name, his_machine_name) + char *my_machine_name __unused, *his_machine_name; +{ + struct hostent *hp; + struct servent *sp; + + msg.pid = htonl(getpid()); + + hp = gethostbyname(his_machine_name); + if (hp == NULL) + errx(1, "%s: %s", his_machine_name, hstrerror(h_errno)); + bcopy(hp->h_addr, (char *) &his_machine_addr, hp->h_length); + if (get_iface(&his_machine_addr, &my_machine_addr) == -1) + err(1, "failed to find my interface address"); + /* find the server's port */ + sp = getservbyname("ntalk", "udp"); + if (sp == 0) + errx(1, "ntalk/udp: service is not registered"); + daemon_port = sp->s_port; +} diff --git a/remote_cmds/talk.tproj/get_iface.c b/remote_cmds/talk.tproj/get_iface.c new file mode 100644 index 0000000..79b7d30 --- /dev/null +++ b/remote_cmds/talk.tproj/get_iface.c @@ -0,0 +1,103 @@ +/* + * Copyright 1994, 1995 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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 <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/get_iface.c,v 1.10 2005/03/11 14:17:12 stefanf Exp $"); +#endif + +/* + * From: + * Id: find_interface.c,v 1.1 1995/08/14 16:08:39 wollman Exp + */ + +#include <errno.h> +#include <string.h> + +#include "talk.h" + +/* + * Try to find the interface address that is used to route an IP + * packet to a remote peer. + */ + +int +get_iface(dst, iface) + struct in_addr *dst; + struct in_addr *iface; +{ + static struct sockaddr_in local; + struct sockaddr_in remote; + socklen_t namelen; + int s, rv; + + memcpy(&remote.sin_addr, dst, sizeof remote.sin_addr); + remote.sin_port = htons(60000); + remote.sin_family = AF_INET; + remote.sin_len = sizeof remote; + + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(60000); + local.sin_family = AF_INET; + local.sin_len = sizeof local; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + return -1; + + do { + rv = bind(s, (struct sockaddr *)&local, sizeof local); + local.sin_port = htons(ntohs(local.sin_port) + 1); + } while(rv < 0 && errno == EADDRINUSE); + + if (rv < 0) { + close(s); + return -1; + } + + do { + rv = connect(s, (struct sockaddr *)&remote, sizeof remote); + remote.sin_port = htons(ntohs(remote.sin_port) + 1); + } while(rv < 0 && errno == EADDRINUSE); + + if (rv < 0) { + close(s); + return -1; + } + + namelen = sizeof local; + rv = getsockname(s, (struct sockaddr *)&local, &namelen); + close(s); + if (rv < 0) + return -1; + + memcpy(iface, &local.sin_addr, sizeof local.sin_addr); + return 0; +} diff --git a/remote_cmds/talk.tproj/get_names.c b/remote_cmds/talk.tproj/get_names.c new file mode 100644 index 0000000..24acab1 --- /dev/null +++ b/remote_cmds/talk.tproj/get_names.c @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/get_names.c,v 1.9 2004/06/14 22:34:13 bms Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)get_names.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#include <sys/param.h> + +#include <err.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> + +#include "talk.h" + +extern CTL_MSG msg; + +static void +usage(void) +{ + fprintf(stderr, "usage: talk person [ttyname]\n"); + exit(1); +} + +/* + * Determine the local and remote user, tty, and machines + */ +void +get_names(argc, argv) + int argc; + char *argv[]; +{ + char hostname[MAXHOSTNAMELEN]; + char *his_name, *my_name; + char *my_machine_name, *his_machine_name; + const char *his_tty; + char *cp; + + if (argc < 2 ) + usage(); + if (!isatty(0)) + errx(1, "standard input must be a tty, not a pipe or a file"); + if ((my_name = getlogin()) == NULL) { + struct passwd *pw; + + if ((pw = getpwuid(getuid())) == NULL) + errx(1, "you don't exist. Go away"); + my_name = pw->pw_name; + } + gethostname(hostname, sizeof (hostname)); + my_machine_name = hostname; + /* check for, and strip out, the machine name of the target */ + for (cp = argv[1]; *cp && !index("@:!", *cp); cp++) + ; + if (*cp == '\0') { + /* this is a local to local talk */ + his_name = argv[1]; + my_machine_name = his_machine_name = "localhost"; + } else { + if (*cp++ == '@') { + /* user@host */ + his_name = argv[1]; + his_machine_name = cp; + } else { + /* host!user or host:user */ + his_name = cp; + his_machine_name = argv[1]; + } + *--cp = '\0'; + } + if (argc > 2) + his_tty = argv[2]; /* tty name is arg 2 */ + else + his_tty = ""; + get_addrs(my_machine_name, his_machine_name); + /* + * Initialize the message template. + */ + msg.vers = TALK_VERSION; + msg.addr.sa_family = htons(AF_INET); + msg.ctl_addr.sa_family = htons(AF_INET); + msg.id_num = htonl(0); + strncpy(msg.l_name, my_name, NAME_SIZE); + msg.l_name[NAME_SIZE - 1] = '\0'; + strncpy(msg.r_name, his_name, NAME_SIZE); + msg.r_name[NAME_SIZE - 1] = '\0'; + strncpy(msg.r_tty, his_tty, TTY_SIZE); + msg.r_tty[TTY_SIZE - 1] = '\0'; +} diff --git a/remote_cmds/talk.tproj/init_disp.c b/remote_cmds/talk.tproj/init_disp.c new file mode 100644 index 0000000..c6a02ef --- /dev/null +++ b/remote_cmds/talk.tproj/init_disp.c @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/init_disp.c,v 1.14 2004/04/19 21:37:28 cognet Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)init_disp.c 8.2 (Berkeley) 2/16/94"; +#endif +#endif /* __APPLE__ */ + +/* + * Initialization code for the display package, + * as well as the signal handling routines. + */ + +#include <sys/stat.h> +#ifdef __APPLE__ +#include <sys/ioctl.h> +#endif + +#include <err.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> + +#include "talk.h" + +extern volatile sig_atomic_t gotwinch; + +/* + * Make sure the callee can write to the screen + */ +void +check_writeable() +{ + char *tty; + struct stat sb; + + if ((tty = ttyname(STDERR_FILENO)) == NULL) + err(1, "ttyname"); + if (stat(tty, &sb) < 0) + err(1, "%s", tty); + if (!(sb.st_mode & S_IWGRP)) + errx(1, "The callee cannot write to this terminal, use \"mesg y\"."); +} + +/* + * Set up curses, catch the appropriate signals, + * and build the various windows. + */ +void +init_display() +{ + struct sigaction sa; + + if (initscr() == NULL) + errx(1, "Terminal type unset or lacking necessary features."); + (void) sigaction(SIGTSTP, (struct sigaction *)0, &sa); + sigaddset(&sa.sa_mask, SIGALRM); + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + curses_initialized = 1; + clear(); + refresh(); + noecho(); + crmode(); + signal(SIGINT, sig_sent); + signal(SIGPIPE, sig_sent); + signal(SIGWINCH, sig_winch); + /* curses takes care of ^Z */ + my_win.x_nlines = LINES / 2; + my_win.x_ncols = COLS; + my_win.x_win = newwin(my_win.x_nlines, my_win.x_ncols, 0, 0); + idlok(my_win.x_win, TRUE); + scrollok(my_win.x_win, TRUE); + wclear(my_win.x_win); + + his_win.x_nlines = LINES / 2 - 1; + his_win.x_ncols = COLS; + his_win.x_win = newwin(his_win.x_nlines, his_win.x_ncols, + my_win.x_nlines+1, 0); + idlok(my_win.x_win, TRUE); + scrollok(his_win.x_win, TRUE); + wclear(his_win.x_win); + + line_win = newwin(1, COLS, my_win.x_nlines, 0); +#if defined(hline) || defined(whline) || defined(NCURSES_VERSION) + whline(line_win, 0, COLS); +#else + box(line_win, '-', '-'); +#endif + wrefresh(line_win); + /* let them know we are working on it */ + current_state = "No connection yet"; +} + +/* + * Trade edit characters with the other talk. By agreement + * the first three characters each talk transmits after + * connection are the three edit characters. + */ +void +set_edit_chars() +{ + char buf[3]; + int cc; + struct termios tio; + + tcgetattr(0, &tio); + my_win.cerase = tio.c_cc[VERASE]; + my_win.kill = tio.c_cc[VKILL]; + my_win.werase = tio.c_cc[VWERASE]; + if (my_win.cerase == (char)_POSIX_VDISABLE) + my_win.kill = CERASE; + if (my_win.kill == (char)_POSIX_VDISABLE) + my_win.kill = CKILL; + if (my_win.werase == (char)_POSIX_VDISABLE) + my_win.werase = CWERASE; + buf[0] = my_win.cerase; + buf[1] = my_win.kill; + buf[2] = my_win.werase; + cc = write(sockt, buf, sizeof(buf)); + if (cc != sizeof(buf) ) + p_error("Lost the connection"); + cc = read(sockt, buf, sizeof(buf)); + if (cc != sizeof(buf) ) + p_error("Lost the connection"); + his_win.cerase = buf[0]; + his_win.kill = buf[1]; + his_win.werase = buf[2]; +} + +/* ARGSUSED */ +void +sig_sent(signo) + int signo __unused; +{ + + message("Connection closing. Exiting"); + quit(); +} + +void +sig_winch(int dummy) +{ + + gotwinch = 1; +} + +/* + * All done talking...hang up the phone and reset terminal thingy's + */ +void +quit() +{ + + if (curses_initialized) { + wmove(his_win.x_win, his_win.x_nlines-1, 0); + wclrtoeol(his_win.x_win); + wrefresh(his_win.x_win); + endwin(); + } + if (invitation_waiting) + send_delete(); + exit(0); +} + +/* + * If we get SIGWINCH, recompute both window sizes and refresh things. + */ +void +resize_display(void) +{ + struct winsize ws; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0 || + (ws.ws_row == LINES && ws.ws_col == COLS)) + return; + + /* Update curses' internal state with new window size. */ + resizeterm(ws.ws_row, ws.ws_col); + + /* + * Resize each window but wait to refresh the screen until + * everything has been drawn so the cursor is in the right spot. + */ + my_win.x_nlines = LINES / 2; + my_win.x_ncols = COLS; + wresize(my_win.x_win, my_win.x_nlines, my_win.x_ncols); + mvwin(my_win.x_win, 0, 0); + clearok(my_win.x_win, TRUE); + + his_win.x_nlines = LINES / 2 - 1; + his_win.x_ncols = COLS; + wresize(his_win.x_win, his_win.x_nlines, his_win.x_ncols); + mvwin(his_win.x_win, my_win.x_nlines + 1, 0); + clearok(his_win.x_win, TRUE); + + wresize(line_win, 1, COLS); + mvwin(line_win, my_win.x_nlines, 0); +#if defined(NCURSES_VERSION) || defined(whline) + whline(line_win, '-', COLS); +#else + wmove(line_win, my_win.x_nlines, 0); + box(line_win, '-', '-'); +#endif + + /* Now redraw the screen. */ + wrefresh(his_win.x_win); + wrefresh(line_win); + wrefresh(my_win.x_win); +} diff --git a/remote_cmds/talk.tproj/invite.c b/remote_cmds/talk.tproj/invite.c new file mode 100644 index 0000000..b8562b2 --- /dev/null +++ b/remote_cmds/talk.tproj/invite.c @@ -0,0 +1,202 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/invite.c,v 1.9 2003/01/01 18:49:00 schweikh Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)invite.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <protocols/talkd.h> + +#include <err.h> +#include <errno.h> +#include <setjmp.h> +#include <signal.h> + +#include "talk_ctl.h" +#include "talk.h" + +/* + * There wasn't an invitation waiting, so send a request containing + * our sockt address to the remote talk daemon so it can invite + * him + */ + +/* + * The msg.id's for the invitations + * on the local and remote machines. + * These are used to delete the + * invitations. + */ +int local_id, remote_id; +jmp_buf invitebuf; + +void +invite_remote() +{ + int new_sockt; + struct itimerval itimer; + CTL_RESPONSE response; + + itimer.it_value.tv_sec = RING_WAIT; + itimer.it_value.tv_usec = 0; + itimer.it_interval = itimer.it_value; + if (listen(sockt, 5) != 0) + p_error("Error on attempt to listen for caller"); +#ifdef MSG_EOR + /* copy new style sockaddr to old, swap family (short in old) */ + msg.addr = *(struct osockaddr *)&my_addr; /* XXX new to old style*/ + msg.addr.sa_family = htons(my_addr.sin_family); +#else + msg.addr = *(struct sockaddr *)&my_addr; +#endif + msg.id_num = htonl(-1); /* an impossible id_num */ + invitation_waiting = 1; + announce_invite(); + /* + * Shut off the automatic messages for a while, + * so we can use the interupt timer to resend the invitation + */ + end_msgs(); + setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0); + message("Waiting for your party to respond"); + signal(SIGALRM, re_invite); + (void) setjmp(invitebuf); + while ((new_sockt = accept(sockt, 0, 0)) < 0) { + if (errno == EINTR) + continue; + p_error("Unable to connect with your party"); + } + close(sockt); + sockt = new_sockt; + + /* + * Have the daemons delete the invitations now that we + * have connected. + */ + current_state = "Waiting for your party to respond"; + start_msgs(); + + msg.id_num = htonl(local_id); + ctl_transact(my_machine_addr, msg, DELETE, &response); + msg.id_num = htonl(remote_id); + ctl_transact(his_machine_addr, msg, DELETE, &response); + invitation_waiting = 0; +} + +/* + * Routine called on interupt to re-invite the callee + */ +/* ARGSUSED */ +void +re_invite(signo) + int signo __unused; +{ + + message("Ringing your party again"); + waddch(my_win.x_win, '\n'); + if (current_line < my_win.x_nlines - 1) + current_line++; + /* force a re-announce */ + msg.id_num = htonl(remote_id + 1); + announce_invite(); + longjmp(invitebuf, 1); +} + +static const char *answers[] = { + "answer #0", /* SUCCESS */ + "Your party is not logged on", /* NOT_HERE */ + "Target machine is too confused to talk to us", /* FAILED */ + "Target machine does not recognize us", /* MACHINE_UNKNOWN */ + "Your party is refusing messages", /* PERMISSION_REFUSED */ + "Target machine can not handle remote talk", /* UNKNOWN_REQUEST */ + "Target machine indicates protocol mismatch", /* BADVERSION */ + "Target machine indicates protocol botch (addr)",/* BADADDR */ + "Target machine indicates protocol botch (ctl_addr)",/* BADCTLADDR */ +}; +#define NANSWERS (sizeof (answers) / sizeof (answers[0])) + +/* + * Transmit the invitation and process the response + */ +void +announce_invite() +{ + CTL_RESPONSE response; + + current_state = "Trying to connect to your party's talk daemon"; + ctl_transact(his_machine_addr, msg, ANNOUNCE, &response); + remote_id = response.id_num; + if (response.answer != SUCCESS) { + if (response.answer < NANSWERS) + message(answers[response.answer]); + quit(); + } + /* leave the actual invitation on my talk daemon */ + current_state = "Trying to connect to local talk daemon"; + ctl_transact(my_machine_addr, msg, LEAVE_INVITE, &response); + local_id = response.id_num; +} + +/* + * Tell the daemon to remove your invitation + */ +void +send_delete() +{ + + msg.type = DELETE; + /* + * This is just an extra clean up, so just send it + * and don't wait for an answer + */ + msg.id_num = htonl(remote_id); + daemon_addr.sin_addr = his_machine_addr; + if (sendto(ctl_sockt, &msg, sizeof (msg), 0, + (struct sockaddr *)&daemon_addr, + sizeof (daemon_addr)) != sizeof(msg)) + warn("send_delete (remote)"); + msg.id_num = htonl(local_id); + daemon_addr.sin_addr = my_machine_addr; + if (sendto(ctl_sockt, &msg, sizeof (msg), 0, + (struct sockaddr *)&daemon_addr, + sizeof (daemon_addr)) != sizeof (msg)) + warn("send_delete (local)"); +} diff --git a/remote_cmds/talk.tproj/io.c b/remote_cmds/talk.tproj/io.c new file mode 100644 index 0000000..644c28b --- /dev/null +++ b/remote_cmds/talk.tproj/io.c @@ -0,0 +1,188 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/io.c,v 1.16 2004/05/10 15:52:16 cognet Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +/* + * This file contains the I/O handling and the exchange of + * edit characters. This connection itself is established in + * ctl.c + */ + +#include <sys/filio.h> +#ifdef __APPLE__ +#include <sys/ioctl.h> +#endif + +#include <errno.h> +#include <signal.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "talk.h" +#include "talk_ctl.h" + +#define A_LONG_TIME 10000000 + +volatile sig_atomic_t gotwinch = 0; + +/* + * The routine to do the actual talking + */ +void +talk() +{ + struct hostent *hp, *hp2; + int nb; + fd_set read_set, read_template; + char buf[BUFSIZ], **addr, *his_machine_name; + struct timeval wait; + + his_machine_name = NULL; + hp = gethostbyaddr((const char *)&his_machine_addr.s_addr, + sizeof(his_machine_addr.s_addr), AF_INET); + if (hp != NULL) { + hp2 = gethostbyname(hp->h_name); + if (hp2 != NULL && hp2->h_addrtype == AF_INET && + hp2->h_length == sizeof(his_machine_addr)) + for (addr = hp2->h_addr_list; *addr != NULL; addr++) + if (memcmp(*addr, &his_machine_addr, + sizeof(his_machine_addr)) == 0) { + his_machine_name = strdup(hp->h_name); + break; + } + } + if (his_machine_name == NULL) + his_machine_name = strdup(inet_ntoa(his_machine_addr)); + snprintf(buf, sizeof(buf), "Connection established with %s@%s.", + msg.r_name, his_machine_name); + free(his_machine_name); + message(buf); + write(STDOUT_FILENO, "\007\007\007", 3); + + current_line = 0; + + /* + * Wait on both the other process (sockt_mask) and + * standard input ( STDIN_MASK ) + */ + FD_ZERO(&read_template); + FD_SET(sockt, &read_template); + FD_SET(fileno(stdin), &read_template); + for (;;) { + read_set = read_template; + wait.tv_sec = A_LONG_TIME; + wait.tv_usec = 0; + nb = select(sockt + 1, &read_set, 0, 0, &wait); + if (gotwinch) { + resize_display(); + gotwinch = 0; + } + if (nb <= 0) { + if (errno == EINTR) { + read_set = read_template; + continue; + } + /* panic, we don't know what happened */ + p_error("Unexpected error from select"); + quit(); + } + if (FD_ISSET(sockt, &read_set)) { + /* There is data on sockt */ + nb = read(sockt, buf, sizeof buf); + if (nb <= 0) { + message("Connection closed. Exiting"); + quit(); + } + display(&his_win, buf, nb); + } + if (FD_ISSET(fileno(stdin), &read_set)) { + /* + * We can't make the tty non_blocking, because + * curses's output routines would screw up + */ + int i; + ioctl(0, FIONREAD, (void *) &nb); + if (nb > sizeof buf) + nb = sizeof buf; + nb = read(STDIN_FILENO, buf, nb); + display(&my_win, buf, nb); + /* might lose data here because sockt is non-blocking */ + for (i = 0; i < nb; ++i) + if (buf[i] == '\r') + buf[i] = '\n'; + write(sockt, buf, nb); + } + } +} + +/* + * p_error prints the system error message on the standard location + * on the screen and then exits. (i.e. a curses version of perror) + */ +void +p_error(string) + const char *string; +{ + wmove(my_win.x_win, current_line, 0); + wprintw(my_win.x_win, "[%s : %s (%d)]\n", + string, strerror(errno), errno); + wrefresh(my_win.x_win); + move(LINES-1, 0); + refresh(); + quit(); +} + +/* + * Display string in the standard location + */ +void +message(string) + const char *string; +{ + wmove(my_win.x_win, current_line, 0); + wprintw(my_win.x_win, "[%s]\n", string); + if (current_line < my_win.x_nlines - 1) + current_line++; + wrefresh(my_win.x_win); +} diff --git a/remote_cmds/talk.tproj/look_up.c b/remote_cmds/talk.tproj/look_up.c new file mode 100644 index 0000000..3fd003f --- /dev/null +++ b/remote_cmds/talk.tproj/look_up.c @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/look_up.c,v 1.8 2005/02/09 09:13:36 stefanf Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)look_up.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <protocols/talkd.h> + +#include <errno.h> +#include <string.h> + +#include "talk_ctl.h" +#include "talk.h" + +/* + * See if the local daemon has an invitation for us. + */ +int +check_local() +{ + CTL_RESPONSE response; + CTL_RESPONSE *rp = &response; + struct sockaddr addr; + + /* the rest of msg was set up in get_names */ +#ifdef MSG_EOR + /* copy new style sockaddr to old, swap family (short in old) */ + msg.ctl_addr = *(struct osockaddr *)&ctl_addr; + msg.ctl_addr.sa_family = htons(ctl_addr.sin_family); +#else + msg.ctl_addr = *(struct sockaddr *)&ctl_addr; +#endif + /* must be initiating a talk */ + if (!look_for_invite(rp)) + return (0); + /* + * There was an invitation waiting for us, + * so connect with the other (hopefully waiting) party + */ + current_state = "Waiting to connect with caller"; + do { + if (rp->addr.sa_family != AF_INET) + p_error("Response uses invalid network address"); + (void)memcpy(&addr, &rp->addr.sa_family, sizeof(addr)); + addr.sa_family = rp->addr.sa_family; + addr.sa_len = sizeof(addr); + errno = 0; + if (connect(sockt, &addr, sizeof(addr)) != -1) + return (1); + } while (errno == EINTR); + if (errno == ECONNREFUSED) { + /* + * The caller gave up, but his invitation somehow + * was not cleared. Clear it and initiate an + * invitation. (We know there are no newer invitations, + * the talkd works LIFO.) + */ + ctl_transact(his_machine_addr, msg, DELETE, rp); + close(sockt); + open_sockt(); + return (0); + } + p_error("Unable to connect with initiator"); + /*NOTREACHED*/ + return (0); +} + +/* + * Look for an invitation on 'machine' + */ +int +look_for_invite(rp) + CTL_RESPONSE *rp; +{ + current_state = "Checking for invitation on caller's machine"; + ctl_transact(his_machine_addr, msg, LOOK_UP, rp); + /* the switch is for later options, such as multiple invitations */ + switch (rp->answer) { + + case SUCCESS: + msg.id_num = htonl(rp->id_num); + return (1); + + default: + /* there wasn't an invitation waiting for us */ + return (0); + } +} diff --git a/remote_cmds/talk.tproj/msgs.c b/remote_cmds/talk.tproj/msgs.c new file mode 100644 index 0000000..f8cb59a --- /dev/null +++ b/remote_cmds/talk.tproj/msgs.c @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/msgs.c,v 1.6 2001/12/11 23:51:14 markm Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +/* + * A package to display what is happening every MSG_INTERVAL seconds + * if we are slow connecting. + */ + +#include <signal.h> + +#include "talk.h" + +#define MSG_INTERVAL 4 + +const char *current_state; +int current_line = 0; + +/* ARGSUSED */ +void +disp_msg(signo) + int signo __unused; +{ + message(current_state); +} + +void +start_msgs() +{ + struct itimerval itimer; + + message(current_state); + signal(SIGALRM, disp_msg); + itimer.it_value.tv_sec = itimer.it_interval.tv_sec = MSG_INTERVAL; + itimer.it_value.tv_usec = itimer.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0); +} + +void +end_msgs() +{ + struct itimerval itimer; + + timerclear(&itimer.it_value); + timerclear(&itimer.it_interval); + setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0); + signal(SIGALRM, SIG_DFL); +} diff --git a/remote_cmds/talk.tproj/talk.1 b/remote_cmds/talk.tproj/talk.1 new file mode 100644 index 0000000..bf105bb --- /dev/null +++ b/remote_cmds/talk.tproj/talk.1 @@ -0,0 +1,172 @@ +.\" 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. +.\" +.\" @(#)talk.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/talk/talk.1,v 1.20 2005/02/13 22:25:24 ru Exp $ +.\" +.Dd August 21, 2008 +.Dt TALK 1 +.Os +.Sh NAME +.Nm talk +.Nd talk to another user +.Sh SYNOPSIS +.Nm +.Ar person +.Op Ar ttyname +.Sh DESCRIPTION +The +.Nm +utility is a visual communication program which copies lines from your +terminal to that of another user. +.Pp +Options available: +.Bl -tag -width ttyname +.It Ar person +If you wish to talk to someone on your own machine, then +.Ar person +is just the person's login name. +If you wish to talk to a user on +another host, then +.Ar person +is of the form +.Ql user@host +or +.Ql host!user +or +.Ql host:user . +.It Ar ttyname +If you wish to talk to a user who is logged in more than once, the +.Ar ttyname +argument may be used to indicate the appropriate terminal +name, where +.Ar ttyname +is of the form +.Ql ttyXX . +.El +.Pp +When first called, +.Nm +sends the message +.Bd -literal -offset indent -compact +Message from TalkDaemon@his_machine... +talk: connection requested by your_name@your_machine. +talk: respond with: talk your_name@your_machine +.Ed +.Pp +to the user you wish to talk to. +At this point, the recipient +of the message should reply by typing +.Pp +.Dl talk \ your_name@your_machine +.Pp +It does not matter from which machine the recipient replies, as +long as his login-name is the same. +Once communication is established, +the two parties may type simultaneously, with their output appearing +in separate windows. +Typing control-L +.Ql ^L +will cause the screen to +be reprinted. +Typing control-D +.Ql ^D +will clear both parts of your screen to be cleared, while +the control-D character will be sent to the remote side +(and just displayed by this +.Nm +client). +Your erase, kill, and word kill characters will +behave normally. +To exit, just type your interrupt character; +.Nm +then moves the cursor to the bottom of the screen and restores the +terminal to its previous state. +.Pp +Permission to talk may be denied or granted by use of the +.Xr mesg 1 +command. +At the outset talking is allowed. +.Sh CONFIGURATION +The +.Nm +utility relies on the +.Nm talkd +system daemon. See +.Xr talkd 8 +for information about enabling +.Nm talkd . +.Sh FILES +.Bl -tag -width /var/run/utmpx -compact +.It Pa /etc/hosts +to find the recipient's machine +.It Pa /var/run/utmpx +to find the recipient's tty +.El +.Sh SEE ALSO +.Xr mail 1 , +.Xr mesg 1 , +.Xr wall 1 , +.Xr who 1 , +.Xr write 1 , +.Xr talkd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +In +.Fx 5.3 , +the default behaviour of +.Nm +was changed to treat local-to-local talk requests as originating +and terminating at +.Em localhost . +Before this change, it was required that the hostname (as per +.Xr gethostname 3 ) +resolved to a valid IPv4 address (via +.Xr gethostbyname 3 ) , +making +.Nm +unsuitable for use in configurations where +.Xr talkd 8 +was bound to the loopback interface (normally for security reasons). +.Sh BUGS +The version of +.Nm +released with +.Bx 4.3 +uses a protocol that +is incompatible with the protocol used in the version released with +.Bx 4.2 . +.Pp +Multibyte characters are not recognized. diff --git a/remote_cmds/talk.tproj/talk.c b/remote_cmds/talk.tproj/talk.c new file mode 100644 index 0000000..d86e770 --- /dev/null +++ b/remote_cmds/talk.tproj/talk.c @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/talk/talk.c,v 1.11 2002/08/19 03:07:56 jmallett Exp $"); + +#ifndef lint +static const char sccsid[] = "@(#)talk.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* __APPLE__ */ + +#ifndef lint +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#include <locale.h> + +#include "talk.h" + +/* + * talk: A visual form of write. Using sockets, a two way + * connection is set up between the two people talking. + * With the aid of curses, the screen is split into two + * windows, and each users text is added to the window, + * one character at a time... + * + * Written by Kipp Hickman + * + * Modified to run under 4.1a by Clem Cole and Peter Moore + * Modified to run between hosts by Peter Moore, 8/19/82 + * Modified to run under 4.1c by Peter Moore 3/17/83 + * Fixed to not run with unwriteable terminals MRVM 28/12/94 + */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + (void) setlocale(LC_CTYPE, ""); + + get_names(argc, argv); +#ifndef __APPLE__ + setproctitle(""); +#endif + check_writeable(); + init_display(); + open_ctl(); + open_sockt(); + start_msgs(); + if (!check_local()) + invite_remote(); + end_msgs(); + set_edit_chars(); + talk(); + return 0; +} diff --git a/remote_cmds/talk.tproj/talk.h b/remote_cmds/talk.tproj/talk.h new file mode 100644 index 0000000..7f3bcf4 --- /dev/null +++ b/remote_cmds/talk.tproj/talk.h @@ -0,0 +1,97 @@ +/* + * 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. + * + * @(#)talk.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/talk/talk.h,v 1.5 2004/04/19 21:37:29 cognet Exp $ + */ + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <protocols/talkd.h> +#include <curses.h> +#include <unistd.h> + +extern int sockt; +extern int curses_initialized; +extern int invitation_waiting; + +extern const char *current_state; +extern int current_line; + +typedef struct xwin { + WINDOW *x_win; + int x_nlines; + int x_ncols; + int x_line; + int x_col; + char kill; + char cerase; + char werase; +} xwin_t; + +extern xwin_t my_win; +extern xwin_t his_win; +extern WINDOW *line_win; + +extern void announce_invite(void); +extern int check_local(void); +extern void check_writeable(void); +extern void ctl_transact(struct in_addr,CTL_MSG,int,CTL_RESPONSE *); +extern void disp_msg(int); +extern void display(xwin_t *, char *, int); +extern void end_msgs(void); +extern void get_addrs(char *, char *); +extern int get_iface(struct in_addr *, struct in_addr *); +extern void get_names(int, char **); +extern void init_display(void); +extern void invite_remote(void); +extern int look_for_invite(CTL_RESPONSE *); +extern int max(int, int); +extern void message(const char *); +extern void open_ctl(void); +extern void open_sockt(void); +extern void p_error(const char *); +extern void print_addr(struct sockaddr_in); +extern void quit(void); +extern int readwin(WINDOW *, int, int); +extern void re_invite(int); +extern void send_delete(void); +extern void set_edit_chars(void); +extern void sig_sent(int); +extern void sig_winch(int); +extern void start_msgs(void); +extern void talk(void); +extern void resize_display(void); diff --git a/remote_cmds/talk.tproj/talk_ctl.h b/remote_cmds/talk.tproj/talk_ctl.h new file mode 100644 index 0000000..91c75d0 --- /dev/null +++ b/remote_cmds/talk.tproj/talk_ctl.h @@ -0,0 +1,43 @@ +/* + * 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. + * + * @(#)talk_ctl.h 8.1 (Berkeley) 6/6/93 + */ + +extern struct sockaddr_in daemon_addr; +extern struct sockaddr_in ctl_addr; +extern struct sockaddr_in my_addr; +extern struct in_addr my_machine_addr; +extern struct in_addr his_machine_addr; +extern u_short daemon_port; +extern int ctl_sockt; +extern CTL_MSG msg; diff --git a/remote_cmds/talkd.tproj/Makefile b/remote_cmds/talkd.tproj/Makefile new file mode 100644 index 0000000..f67b40b --- /dev/null +++ b/remote_cmds/talkd.tproj/Makefile @@ -0,0 +1,14 @@ +Project = ntalkd +Install_Dir = /usr/libexec + +HFILES = talkd.h +CFILES = announce.c print.c process.c table.c talkd.c ../wall.tproj/ttymsg.c +MANPAGES = ntalkd.8 +LAUNCHD_PLISTS = ntalk.plist + +Extra_CC_Flags = -Wall -Werror -fPIE +Extra_LD_Flags = -dead_strip -pie + +Extra_CC_Flags += -D__FBSDID=__RCSID + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/talkd.tproj/announce.c b/remote_cmds/talkd.tproj/announce.c new file mode 100644 index 0000000..5091513 --- /dev/null +++ b/remote_cmds/talkd.tproj/announce.c @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)announce.c 8.3 (Berkeley) 4/28/95"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/talkd/announce.c,v 1.16 2003/04/03 05:13:27 jmallett Exp $"; +#endif /* not lint */ + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/socket.h> + +#include <protocols/talkd.h> + +#include <errno.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <vis.h> + +#ifdef __APPLE__ +#include <util.h> +#else +#include "ttymsg.h" +#endif +#include "extern.h" + +extern char hostname[]; + +/* + * Announce an invitation to talk. + */ + +/* + * See if the user is accepting messages. If so, announce that + * a talk is requested. + */ +int +announce(CTL_MSG *request, const char *remote_machine) +{ + char full_tty[32]; + struct stat stbuf; + + (void)snprintf(full_tty, sizeof(full_tty), + "%s%s", _PATH_DEV, request->r_tty); + if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0) + return (PERMISSION_DENIED); + return (print_mesg(request->r_tty, request, remote_machine)); +} + +#define max(a,b) ( (a) > (b) ? (a) : (b) ) +#define N_LINES 5 +#define N_CHARS 256 + +/* + * Build a block of characters containing the message. + * It is sent blank filled and in a single block to + * try to keep the message in one piece if the recipient + * in in vi at the time + */ +int +print_mesg(const char *tty, CTL_MSG *request, + const char *remote_machine) +{ + struct timeval now; + time_t clock_sec; + struct timezone zone; + struct tm *localclock; + struct iovec iovec; + char line_buf[N_LINES][N_CHARS]; + int sizes[N_LINES]; + char big_buf[N_LINES*N_CHARS]; + char *bptr, *lptr, *vis_user; + int i, j, max_size; + + i = 0; + max_size = 0; + gettimeofday(&now, &zone); + clock_sec = now.tv_sec; + localclock = localtime(&clock_sec); + (void)snprintf(line_buf[i], N_CHARS, " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)snprintf(line_buf[i], N_CHARS, + "Message from Talk_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...", + hostname, localclock->tm_hour , localclock->tm_min, + localclock->tm_year + 1900, localclock->tm_mon + 1, + localclock->tm_mday); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + + vis_user = malloc(strlen(request->l_name) * 4 + 1); + strvis(vis_user, request->l_name, VIS_CSTYLE); + (void)snprintf(line_buf[i], N_CHARS, + "talk: connection requested by %s@%s", vis_user, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s", + vis_user, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)snprintf(line_buf[i], N_CHARS, " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + bptr = big_buf; + *bptr++ = '\007'; /* send something to wake them up */ + *bptr++ = '\r'; /* add a \r in case of raw mode */ + *bptr++ = '\n'; + for (i = 0; i < N_LINES; i++) { + /* copy the line into the big buffer */ + lptr = line_buf[i]; + while (*lptr != '\0') + *(bptr++) = *(lptr++); + /* pad out the rest of the lines with blanks */ + for (j = sizes[i]; j < max_size + 2; j++) + *(bptr++) = ' '; + *(bptr++) = '\r'; /* add a \r in case of raw mode */ + *(bptr++) = '\n'; + } + *bptr = '\0'; + iovec.iov_base = big_buf; + iovec.iov_len = bptr - big_buf; + /* + * we choose a timeout of RING_WAIT-5 seconds so that we don't + * stack up processes trying to write messages to a tty + * that is permanently blocked. + */ + if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) + return (FAILED); + + return (SUCCESS); +} diff --git a/remote_cmds/talkd.tproj/extern.h b/remote_cmds/talkd.tproj/extern.h new file mode 100644 index 0000000..3b73236 --- /dev/null +++ b/remote_cmds/talkd.tproj/extern.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002 M. Warner Losh. 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. + * + * 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: src/libexec/talkd/extern.h,v 1.3 2003/04/03 05:13:27 jmallett Exp $ + */ + +int announce(CTL_MSG *, const char *); +int delete_invite(u_int32_t); +void do_announce(CTL_MSG *, CTL_RESPONSE *); +CTL_MSG *find_match(CTL_MSG *request); +CTL_MSG *find_request(CTL_MSG *request); +int find_user(const char *name, char *tty); +void insert_table(CTL_MSG *, CTL_RESPONSE *); +int new_id(void); +int print_mesg(const char *, CTL_MSG *, const char *); +void print_request(const char *, CTL_MSG *); +void print_response(const char *, CTL_RESPONSE *); +void process_request(CTL_MSG *mp, CTL_RESPONSE *rp); +void timeout(int sig); diff --git a/remote_cmds/talkd.tproj/ntalk.plist b/remote_cmds/talkd.tproj/ntalk.plist new file mode 100644 index 0000000..2e63da2 --- /dev/null +++ b/remote_cmds/talkd.tproj/ntalk.plist @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <true/> + <key>Label</key> + <string>com.apple.ntalkd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/ntalkd</string> + </array> + <key>inetdCompatibility</key> + <dict> + <key>Wait</key> + <true/> + </dict> + <key>Sockets</key> + <dict> + <key>Listeners</key> + <dict> + <key>SockServiceName</key> + <string>ntalk</string> + <key>SockType</key> + <string>dgram</string> + </dict> + </dict> +</dict> +</plist> diff --git a/remote_cmds/talkd.tproj/ntalkd.8 b/remote_cmds/talkd.tproj/ntalkd.8 new file mode 100644 index 0000000..7fe81a5 --- /dev/null +++ b/remote_cmds/talkd.tproj/ntalkd.8 @@ -0,0 +1,98 @@ +.\" Copyright (c) 1983, 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. +.\" +.\" @(#)talkd.8 8.2 (Berkeley) 12/11/93 +.\" $FreeBSD: src/libexec/talkd/talkd.8,v 1.7 2003/09/08 19:57:18 ru Exp $ +.\" +.Dd August 21, 2008 +.Dt TALKD 8 +.Os +.Sh NAME +.Nm talkd +.Nd remote user communication server +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility +is the server that notifies a user that someone else wants to +initiate a conversation. +It acts as a repository of invitations, responding to requests +by clients wishing to rendezvous to hold a conversation. +In normal operation, a client, the caller, +initiates a rendezvous by sending a +.Tn CTL_MSG +to the server of +type +.Tn LOOK_UP +(see +.In protocols/talkd.h ) . +This causes the server to search its invitation +tables to check if an invitation currently exists for the caller +(to speak to the callee specified in the message). +.Pp +If the lookup fails, +the caller then sends an +.Tn ANNOUNCE +message causing the server to +broadcast an announcement on the callee's login ports requesting contact. +.Pp +When the callee responds, the local server uses the +recorded invitation to respond with the appropriate rendezvous +address and the caller and callee client programs establish a +stream connection through which the conversation takes place. +.Sh CONFIGURATION +The +.Nm +utility is managed by +.Xr launchd 8 +as specified in the +.Pa ntalk.plist +property list. +By default the property list contains the +.Em Disabled +key set to true, so +.Nm +is never invoked. +.Pp +Execute the following command as root to enable +.Nm talkd : +.Dl "launchctl load -w /System/Library/LaunchDaemons/ntalk.plist" +.Pp +.Sh SEE ALSO +.Xr talk 1 , +.Xr write 1 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 . diff --git a/remote_cmds/talkd.tproj/print.c b/remote_cmds/talkd.tproj/print.c new file mode 100644 index 0000000..add853c --- /dev/null +++ b/remote_cmds/talkd.tproj/print.c @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/talkd/print.c,v 1.12 2003/04/03 05:13:27 jmallett Exp $"; +#endif /* not lint */ + +#include <sys/cdefs.h> + +/* debug print routines */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <protocols/talkd.h> +#include <stdio.h> +#include <syslog.h> + +#include "extern.h" + +static const char *types[] = + { "leave_invite", "look_up", "delete", "announce" }; +#define NTYPES (sizeof (types) / sizeof (types[0])) +static const char *answers[] = + { "success", "not_here", "failed", "machine_unknown", "permission_denied", + "unknown_request", "badversion", "badaddr", "badctladdr" }; +#define NANSWERS (sizeof (answers) / sizeof (answers[0])) + +void +print_request(const char *cp, CTL_MSG *mp) +{ + const char *tp; + char tbuf[80]; + + if (mp->type > NTYPES) { + (void)snprintf(tbuf, sizeof(tbuf), "type %d", mp->type); + tp = tbuf; + } else + tp = types[mp->type]; + syslog(LOG_DEBUG, "%s: %s: id %lu, l_user %s, r_user %s, r_tty %s", + cp, tp, (long)mp->id_num, mp->l_name, mp->r_name, mp->r_tty); +} + +void +print_response(const char *cp, CTL_RESPONSE *rp) +{ + const char *tp, *ap; + char tbuf[80], abuf[80]; + + if (rp->type > NTYPES) { + (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type); + tp = tbuf; + } else + tp = types[rp->type]; + if (rp->answer > NANSWERS) { + (void)snprintf(abuf, sizeof(abuf), "answer %d", rp->answer); + ap = abuf; + } else + ap = answers[rp->answer]; + syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num)); +} diff --git a/remote_cmds/talkd.tproj/process.c b/remote_cmds/talkd.tproj/process.c new file mode 100644 index 0000000..e17d69e --- /dev/null +++ b/remote_cmds/talkd.tproj/process.c @@ -0,0 +1,279 @@ +/* + * 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. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/talkd/process.c,v 1.11 2005/05/06 15:28:54 delphij Exp $"; +#endif /* not lint */ + +#include <sys/cdefs.h> + +/* + * process.c handles the requests, which can be of three types: + * ANNOUNCE - announce to a user that a talk is wanted + * LEAVE_INVITE - insert the request into the table + * LOOK_UP - look up to see if a request is waiting in + * in the table for the local user + * DELETE - delete invitation + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <protocols/talkd.h> +#include <ctype.h> +#include <err.h> +#include <netdb.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include "extern.h" + +extern int debug; + +void +process_request(CTL_MSG *mp, CTL_RESPONSE *rp) +{ + CTL_MSG *ptr; + char *s; + + rp->vers = TALK_VERSION; + rp->type = mp->type; + rp->id_num = htonl(0); + if (mp->vers != TALK_VERSION) { + syslog(LOG_WARNING, "bad protocol version %d", mp->vers); + rp->answer = BADVERSION; + return; + } + mp->id_num = ntohl(mp->id_num); + mp->addr.sa_family = ntohs(mp->addr.sa_family); + if (mp->addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "bad address, family %d", + mp->addr.sa_family); + rp->answer = BADADDR; + return; + } + mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); + if (mp->ctl_addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "bad control address, family %d", + mp->ctl_addr.sa_family); + rp->answer = BADCTLADDR; + return; + } + for (s = mp->l_name; *s; s++) + if (!isprint(*s)) { + syslog(LOG_NOTICE, "illegal user name. Aborting"); + rp->answer = FAILED; + return; + } + mp->pid = ntohl(mp->pid); + if (debug) + print_request("process_request", mp); + switch (mp->type) { + + case ANNOUNCE: + do_announce(mp, rp); + break; + + case LEAVE_INVITE: + ptr = find_request(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } else + insert_table(mp, rp); + break; + + case LOOK_UP: + ptr = find_match(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->addr = ptr->addr; + rp->addr.sa_family = htons(ptr->addr.sa_family); + rp->answer = SUCCESS; + } else + rp->answer = NOT_HERE; + break; + + case DELETE: + rp->answer = delete_invite(mp->id_num); + break; + + default: + rp->answer = UNKNOWN_REQUEST; + break; + } + if (debug) + print_response("process_request", rp); +} + +void +do_announce(CTL_MSG *mp, CTL_RESPONSE *rp) +{ + struct hostent *hp; + CTL_MSG *ptr; + int result; + + /* see if the user is logged */ + result = find_user(mp->r_name, mp->r_tty); + if (result != SUCCESS) { + rp->answer = result; + return; + } +#define satosin(sa) ((struct sockaddr_in *)(sa)) + hp = gethostbyaddr((char *)&satosin(&mp->ctl_addr)->sin_addr, + sizeof (struct in_addr), AF_INET); + if (hp == (struct hostent *)0) { + rp->answer = MACHINE_UNKNOWN; + return; + } + ptr = find_request(mp); + if (ptr == (CTL_MSG *) 0) { + insert_table(mp, rp); + rp->answer = announce(mp, hp->h_name); + return; + } + if (mp->id_num > ptr->id_num) { + /* + * This is an explicit re-announce, so update the id_num + * field to avoid duplicates and re-announce the talk. + */ + ptr->id_num = new_id(); + rp->id_num = htonl(ptr->id_num); + rp->answer = announce(mp, hp->h_name); + } else { + /* a duplicated request, so ignore it */ + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } +} + +#ifdef __APPLE__ +#include <utmpx.h> +#else +#include <utmp.h> +#endif + +/* + * Search utmp for the local user + */ +int +find_user(const char *name, char *tty) +{ +#ifdef __APPLE__ + struct utmpx *u; + int status; + struct stat statb; + + char line[sizeof(u->ut_line) + 1]; + char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; + + setutxent(); +#define SCMPN(a, b) strncmp(a, b, sizeof (a)) + status = NOT_HERE; + (void) strcpy(ftty, _PATH_DEV); + while ((u = getutxent()) != NULL) + if (u->ut_type == USER_PROCESS && SCMPN(u->ut_user, name) == 0) { + strncpy(line, u->ut_line, sizeof(u->ut_line)); + line[sizeof(u->ut_line)] = '\0'; + if (*tty == '\0') { + status = PERMISSION_DENIED; + /* no particular tty was requested */ + (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, + line); + if (stat(ftty, &statb) == 0) { + if (!(statb.st_mode & 020)) + continue; + (void) strcpy(tty, line); + status = SUCCESS; + break; + } + } + if (strcmp(line, tty) == 0) { + status = SUCCESS; + break; + } + } + endutxent(); +#else + struct utmp ubuf; + int status; + FILE *fd; + struct stat statb; + time_t best = 0; + char line[sizeof(ubuf.ut_line) + 1]; + char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; + + if ((fd = fopen(_PATH_UTMP, "r")) == NULL) { + warnx("can't read %s", _PATH_UTMP); + return (FAILED); + } +#define SCMPN(a, b) strncmp(a, b, sizeof (a)) + status = NOT_HERE; + (void) strcpy(ftty, _PATH_DEV); + while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) + if (SCMPN(ubuf.ut_name, name) == 0) { + strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line)); + line[sizeof(ubuf.ut_line)] = '\0'; + if (*tty == '\0' || best != 0) { + if (best == 0) + status = PERMISSION_DENIED; + /* no particular tty was requested */ + (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, + line); + if (stat(ftty, &statb) == 0) { + if (!(statb.st_mode & 020)) + continue; + if (statb.st_atime > best) { + best = statb.st_atime; + (void) strcpy(tty, line); + status = SUCCESS; + continue; + } + } + } + if (strcmp(line, tty) == 0) { + status = SUCCESS; + break; + } + } + fclose(fd); +#endif + return (status); +} diff --git a/remote_cmds/talkd.tproj/table.c b/remote_cmds/talkd.tproj/table.c new file mode 100644 index 0000000..77c7d33 --- /dev/null +++ b/remote_cmds/talkd.tproj/table.c @@ -0,0 +1,241 @@ +/* + * 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. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/talkd/table.c,v 1.9 2003/04/03 05:13:27 jmallett Exp $"; +#endif /* not lint */ + +#include <sys/cdefs.h> + +/* + * Routines to handle insertion, deletion, etc on the table + * of requests kept by the daemon. Nothing fancy here, linear + * search on a double-linked list. A time is kept with each + * entry so that overly old invitations can be eliminated. + * + * Consider this a mis-guided attempt at modularity + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <protocols/talkd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "extern.h" + +#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */ + +#define NIL ((TABLE_ENTRY *)0) + +extern int debug; +struct timeval tp; +struct timezone txp; + +typedef struct table_entry TABLE_ENTRY; + +struct table_entry { + CTL_MSG request; + long time; + TABLE_ENTRY *next; + TABLE_ENTRY *last; +}; + +static void delete(TABLE_ENTRY *); + +TABLE_ENTRY *table = NIL; + +/* + * Look in the table for an invitation that matches the current + * request looking for an invitation + */ +CTL_MSG * +find_match(CTL_MSG *request) +{ + TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + if (debug) + print_request("find_match", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->l_name, ptr->request.r_name) == 0 && + strcmp(request->r_name, ptr->request.l_name) == 0 && + ptr->request.type == LEAVE_INVITE) + return (&ptr->request); + } + return ((CTL_MSG *)0); +} + +/* + * Look for an identical request, as opposed to a complimentary + * one as find_match does + */ +CTL_MSG * +find_request(CTL_MSG *request) +{ + TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + /* + * See if this is a repeated message, and check for + * out of date entries in the table while we are it. + */ + if (debug) + print_request("find_request", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->r_name, ptr->request.r_name) == 0 && + strcmp(request->l_name, ptr->request.l_name) == 0 && + request->type == ptr->request.type && + request->pid == ptr->request.pid) { + /* update the time if we 'touch' it */ + ptr->time = current_time; + return (&ptr->request); + } + } + return ((CTL_MSG *)0); +} + +void +insert_table(CTL_MSG *request, CTL_RESPONSE *response) +{ + TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + request->id_num = new_id(); + response->id_num = htonl(request->id_num); + /* insert a new entry into the top of the list */ + ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY)); + if (ptr == NIL) { + syslog(LOG_ERR, "insert_table: Out of memory"); + _exit(1); + } + ptr->time = current_time; + ptr->request = *request; + ptr->next = table; + if (ptr->next != NIL) + ptr->next->last = ptr; + ptr->last = NIL; + table = ptr; +} + +/* + * Generate a unique non-zero sequence number + */ +int +new_id(void) +{ + static int current_id = 0; + + current_id = (current_id + 1) % MAX_ID; + /* 0 is reserved, helps to pick up bugs */ + if (current_id == 0) + current_id = 1; + return (current_id); +} + +/* + * Delete the invitation with id 'id_num' + */ +int +delete_invite(u_int32_t id_num) +{ + TABLE_ENTRY *ptr; + + ptr = table; + if (debug) + syslog(LOG_DEBUG, "delete_invite(%d)", id_num); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if (ptr->request.id_num == id_num) + break; + if (debug) + print_request("", &ptr->request); + } + if (ptr != NIL) { + delete(ptr); + return (SUCCESS); + } + return (NOT_HERE); +} + +/* + * Classic delete from a double-linked list + */ +static void +delete(TABLE_ENTRY *ptr) +{ + + if (debug) + print_request("delete", &ptr->request); + if (table == ptr) + table = ptr->next; + else if (ptr->last != NIL) + ptr->last->next = ptr->next; + if (ptr->next != NIL) + ptr->next->last = ptr->last; + free((char *)ptr); +} diff --git a/remote_cmds/talkd.tproj/talkd.c b/remote_cmds/talkd.tproj/talkd.c new file mode 100644 index 0000000..1f28cb8 --- /dev/null +++ b/remote_cmds/talkd.tproj/talkd.c @@ -0,0 +1,145 @@ +/* + * 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. + */ + +#ifndef lint +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/talkd/talkd.c,v 1.17 2004/06/14 22:44:13 bms Exp $"; +#endif /* not lint */ + +#include <sys/cdefs.h> + +/* + * The top level of the daemon, the format is heavily borrowed + * from rwhod.c. Basically: find out who and where you are; + * disconnect all descriptors and ttys, and then endless + * loop on waiting for and processing requests + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <protocols/talkd.h> +#include <err.h> +#include <errno.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "extern.h" + +CTL_MSG request; +CTL_RESPONSE response; + +int debug = 0; +long lastmsgtime; + +char hostname[MAXHOSTNAMELEN + 1]; + +#define TIMEOUT 30 +#define MAXIDLE 120 + +int +main(int argc, char *argv[]) +{ + register CTL_MSG *mp = &request; + int cc; + struct sockaddr ctl_addr; + + /* + * removed so ntalkd can run in tty sandbox + */ + if (getuid()) + errx(1, "getuid: not super-user"); + + openlog("talkd", LOG_PID, LOG_DAEMON); + if (gethostname(hostname, sizeof(hostname) - 1) < 0) { + syslog(LOG_ERR, "gethostname: %m"); + _exit(1); + } + hostname[sizeof(hostname) - 1] = '\0'; + if (chdir(_PATH_DEV) < 0) { + syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV); + _exit(1); + } + if (argc > 1 && strcmp(argv[1], "-d") == 0) + debug = 1; + signal(SIGALRM, timeout); + alarm(TIMEOUT); + for (;;) { + cc = recv(0, (char *)mp, sizeof(*mp), 0); + if (cc != sizeof (*mp)) { + if (cc < 0 && errno != EINTR) + syslog(LOG_WARNING, "recv: %m"); + continue; + } + lastmsgtime = time(0); + (void)memcpy(&ctl_addr, &mp->ctl_addr, sizeof(ctl_addr)); + ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); + ctl_addr.sa_len = sizeof(ctl_addr); + process_request(mp, &response); + /* can block here, is this what I want? */ + cc = sendto(STDIN_FILENO, (char *)&response, + sizeof(response), 0, &ctl_addr, sizeof(ctl_addr)); + if (cc != sizeof (response)) + syslog(LOG_WARNING, "sendto: %m"); + } + /* NOTREACHED */ + return 0; +} + +void +timeout(int sig __unused) +{ + int save_errno = errno; + + if (time(0) - lastmsgtime >= MAXIDLE) + _exit(0); + alarm(TIMEOUT); + errno = save_errno; +} diff --git a/remote_cmds/telnet.tproj/Makefile b/remote_cmds/telnet.tproj/Makefile new file mode 100644 index 0000000..dc8569b --- /dev/null +++ b/remote_cmds/telnet.tproj/Makefile @@ -0,0 +1,18 @@ +Project = telnet +Install_Dir = /usr/local/bin + +HFILES = externs.h fdset.h general.h krb4-proto.h ring.h types.h\ + defines.h misc.h misc-proto.h +CFILES = authenc.c commands.c main.c network.c ring.c sys_bsd.c\ + telnet.c terminal.c tn3270.c utilities.c + +Extra_CC_Flags = -Wall -Werror -Wno-string-plus-int -fPIE +Extra_CC_Flags += -D__FBSDID=__RCSID +Extra_LD_Flags = -dead_strip -pie + +Extra_CC_Flags += -DTERMCAP -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK \ + -DAUTHENTICATION -DKRB5 -DSKEY -DIPSEC -DINET6 -DFORWARD + # -DENCRYPTION -DKRB4 +Extra_LD_Libraries = -lcurses -lkrb4 -lkrb5 -lipsec -ltelnet + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/telnet.tproj/authenc.c b/remote_cmds/telnet.tproj/authenc.c new file mode 100644 index 0000000..258a013 --- /dev/null +++ b/remote_cmds/telnet.tproj/authenc.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/authenc.c,v 1.6 2003/05/04 02:54:48 obrien Exp $"); + +#ifdef AUTHENTICATION +#include <sys/types.h> +#include <arpa/telnet.h> +#include <pwd.h> +#include <unistd.h> +#include <libtelnet/encrypt.h> +#include <libtelnet/misc.h> + +#include "general.h" +#include "ring.h" +#include "externs.h" +#include "defines.h" +#include "types.h" + +int +net_write(unsigned char *str, int len) +{ + if (NETROOM() > len) { + ring_supply_data(&netoring, str, len); + if (str[0] == IAC && str[1] == SE) + printsub('>', &str[2], len-2); + return(len); + } + return(0); +} + +void +net_encrypt(void) +{ +#ifdef ENCRYPTION + if (encrypt_output) + ring_encrypt(&netoring, encrypt_output); + else + ring_clearto(&netoring); +#endif /* ENCRYPTION */ +} + +int +telnet_spin(void) +{ + return(-1); +} + +char * +telnet_getenv(char *val) +{ + return((char *)env_getvalue((unsigned char *)val)); +} + +char * +telnet_gets(const char *prom, char *result, int length, int echo) +{ + extern int globalmode; + int om = globalmode; + char *res; + + TerminalNewMode(-1); + if (echo) { + printf("%s", prom); + res = fgets(result, length, stdin); + } else if ((res = getpass(prom))) { + strncpy(result, res, length); + res = result; + } + TerminalNewMode(om); + return(res); +} +#endif /* AUTHENTICATION */ diff --git a/remote_cmds/telnet.tproj/commands.c b/remote_cmds/telnet.tproj/commands.c new file mode 100644 index 0000000..56a0794 --- /dev/null +++ b/remote_cmds/telnet.tproj/commands.c @@ -0,0 +1,3063 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/commands.c,v 1.35 2005/02/28 12:46:52 tobez Exp $"); + +/* Use RFC 2292 constants in <netinet6/in6.h> */ +#define __APPLE_USE_RFC_2292 + +#include <sys/param.h> +#include <sys/un.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <pwd.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <arpa/telnet.h> +#include <arpa/inet.h> + +#include "general.h" + +#include "ring.h" + +#include "externs.h" +#include "defines.h" +#include "types.h" +#include "misc.h" + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +typedef int (*intrtn_t)(int, char **); + +#ifdef AUTHENTICATION +extern int auth_togdebug(int); +#endif +#ifdef ENCRYPTION +extern int EncryptAutoEnc(int); +extern int EncryptAutoDec(int); +extern int EncryptDebug(int); +extern int EncryptVerbose(int); +#endif /* ENCRYPTION */ +#if defined(IPPROTO_IP) && defined(IP_TOS) +int tos = -1; +#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ + +char *hostname; +static char _hostname[MAXHOSTNAMELEN]; + +static int help(int, char **); +static int call(intrtn_t, ...); +static void cmdrc(char *, char *); +#ifdef INET6 +static int switch_af(struct addrinfo **); +#endif +static int togglehelp(void); +static int send_tncmd(void (*)(int, int), const char *, char *); +static int setmod(int); +static int clearmode(int); +static int modehelp(void); +#ifndef __APPLE__ +static int sourceroute(struct addrinfo *, char *, char **, int *, int *, int *); +#endif + +typedef struct { + const char *name; /* command name */ + const char *help; /* help string (NULL for no help) */ + int (*handler)(int, char **); /* 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]; + +#ifdef OPIE +#include <sys/wait.h> +#define PATH_OPIEKEY "/usr/bin/opiekey" +static int +opie_calc(int argc, char *argv[]) +{ + int status; + + if(argc != 3) { + printf("%s sequence challenge\n", argv[0]); + return (0); + } + + switch(fork()) { + case 0: + execv(PATH_OPIEKEY, argv); + exit (1); + case -1: + perror("fork"); + break; + default: + (void) wait(&status); + if (WIFEXITED(status)) + return (WEXITSTATUS(status)); + } + return (0); +} +#endif + +static void +makeargv(void) +{ + char *cp, *cp2, c; + char **argp = margv; + + margc = 0; + cp = line; + if (*cp == '!') { /* Special case shell escape */ + strcpy(saveline, line); /* save for shell command */ + *argp++ = strdup("!"); /* No room in string to get this */ + margc++; + cp++; + } + while ((c = *cp)) { + 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(char *s) +{ + 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 const char * +control(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.... + */ + 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 { + const char *name; /* How user refers to it (case independent) */ + const char *help; /* Help information (0 ==> no help) */ + int needconnect; /* Need to be connected */ + int narg; /* Number of arguments */ + int (*handler)(char *, ...); /* 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(void), + send_help(void), + send_docmd(char *), + send_dontcmd(char *), + send_willcmd(char *), + send_wontcmd(char *); + +static struct sendlist Sendlist[] = { + { "ao", "Send Telnet Abort output", 1, 0, NULL, 2, AO }, + { "ayt", "Send Telnet 'Are You There'", 1, 0, NULL, 2, AYT }, + { "brk", "Send Telnet Break", 1, 0, NULL, 2, BREAK }, + { "break", NULL, 1, 0, NULL, 2, BREAK }, + { "ec", "Send Telnet Erase Character", 1, 0, NULL, 2, EC }, + { "el", "Send Telnet Erase Line", 1, 0, NULL, 2, EL }, + { "escape", "Send current escape character",1, 0, (int (*)(char *, ...))send_esc, 1, 0 }, + { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, NULL, 2, GA }, + { "ip", "Send Telnet Interrupt Process",1, 0, NULL, 2, IP }, + { "intp", NULL, 1, 0, NULL, 2, IP }, + { "interrupt", NULL, 1, 0, NULL, 2, IP }, + { "intr", NULL, 1, 0, NULL, 2, IP }, + { "nop", "Send Telnet 'No operation'", 1, 0, NULL, 2, NOP }, + { "eor", "Send Telnet 'End of Record'", 1, 0, NULL, 2, EOR }, + { "abort", "Send Telnet 'Abort Process'", 1, 0, NULL, 2, ABORT }, + { "susp", "Send Telnet 'Suspend Process'",1, 0, NULL, 2, SUSP }, + { "eof", "Send Telnet End of File Character", 1, 0, NULL, 2, xEOF }, + { "synch", "Perform Telnet 'Synch operation'", 1, 0, (int (*)(char *, ...))dosynch, 2, 0 }, + { "getstatus", "Send request for STATUS", 1, 0, (int (*)(char *, ...))get_status, 6, 0 }, + { "?", "Display send options", 0, 0, (int (*)(char *, ...))send_help, 0, 0 }, + { "help", NULL, 0, 0, (int (*)(char *, ...))send_help, 0, 0 }, + { "do", NULL, 0, 1, (int (*)(char *, ...))send_docmd, 3, 0 }, + { "dont", NULL, 0, 1, (int (*)(char *, ...))send_dontcmd, 3, 0 }, + { "will", NULL, 0, 1, (int (*)(char *, ...))send_willcmd, 3, 0 }, + { "wont", NULL, 0, 1, (int (*)(char *, ...))send_wontcmd, 3, 0 }, + { NULL, NULL, 0, 0, NULL, 0, 0 } +}; + +#define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ + sizeof(struct sendlist))) + +static int +sendcmd(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((void *)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 ((void *)s->handler == (void *)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"); + 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(void) +{ + NETADD(escape); + return 1; +} + +static int +send_docmd(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(char *name) +{ + return(send_tncmd(send_will, "will", name)); +} + +static int +send_wontcmd(char *name) +{ + return(send_tncmd(send_wont, "wont", name)); +} + +static int +send_tncmd(void (*func)(int, int), const char *cmd, char *name) +{ + char **cpp; + extern char *telopts[]; + int val = 0; + + if (isprefix(name, "help") || isprefix(name, "?")) { + int col, len; + + printf("usage: send %s <value|option>\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 { + 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(void) +{ + 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(void) +{ + donelclchars = 1; + return 1; +} + +static int +togdebug(void) +{ +#ifndef NOT43 + if (net > 0 && + (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, telnet_debug)) < 0) { + perror("setsockopt (SO_DEBUG)"); + } +#else /* NOT43 */ + if (telnet_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(void) +{ + if (crlf) { + printf("Will send carriage returns as telnet <CR><LF>.\n"); + } else { + printf("Will send carriage returns as telnet <CR><NUL>.\n"); + } + return 1; +} + +int binmode; + +static int +togbinary(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(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(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; +} + +struct togglelist { + const char *name; /* name of toggle */ + const char *help; /* help message */ + int (*handler)(int); /* routine to do actual setting */ + int *variable; + const 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" }, +#ifdef 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 <CR><LF>", + (int (*)(int))togcrlf, + &crlf, + 0 }, + { "crmod", + "mapping of received carriage returns", + 0, + &crmod, + "map carriage return on output" }, + { "localchars", + "local recognition of certain control characters", + (int (*)(int))lclchars, + &localchars, + "recognize certain control characters" }, + { " ", "", NULL, NULL, NULL }, /* empty line */ + { "debug", + "debugging", + (int (*)(int))togdebug, + &telnet_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" }, + { "termdata", + "(debugging) toggle printing of hexadecimal terminal data", + 0, + &termdata, + "print hexadecimal representation of terminal traffic" }, + { "?", + NULL, + (int (*)(int))togglehelp, + NULL, + NULL }, + { NULL, NULL, NULL, NULL, NULL }, + { "help", + NULL, + (int (*)(int))togglehelp, + NULL, + NULL }, + { NULL, NULL, NULL, NULL, NULL } +}; + +static int +togglehelp(void) +{ + 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(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(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((void *)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, 0, 0, 0, {}, 0, 0 }; +#endif + +struct setlist { + const char *name; /* name */ + const char *help; /* help information */ + void (*handler)(char *); + cc_t *charp; /* where it is located at */ +}; + +static struct setlist Setlist[] = { +#ifdef KLUDGELINEMODE + { "echo", "character to toggle local echoing on/off", NULL, &echoc }, +#endif + { "escape", "character to escape back to telnet command mode", NULL, &escape }, + { "rlogin", "rlogin escape character", 0, &rlogin }, + { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, + { " ", "", NULL, NULL }, + { " ", "The following need 'localchars' to be toggled true", NULL, NULL }, + { "flushoutput", "character to cause an Abort Output", NULL, termFlushCharp }, + { "interrupt", "character to cause an Interrupt Process", NULL, termIntCharp }, + { "quit", "character to cause an Abort process", NULL, termQuitCharp }, + { "eof", "character to cause an EOF ", NULL, termEofCharp }, + { " ", "", NULL, NULL }, + { " ", "The following are for local editing in linemode", NULL, NULL }, + { "erase", "character to use to erase a character", NULL, termEraseCharp }, + { "kill", "character to use to erase a line", NULL, termKillCharp }, + { "lnext", "character to use for literal next", NULL, termLiteralNextCharp }, + { "susp", "character to cause a Suspend Process", NULL, termSuspCharp }, + { "reprint", "character to use for line reprint", NULL, termRprntCharp }, + { "worderase", "character to use to erase a word", NULL, termWerasCharp }, + { "start", "character to use for XON", NULL, termStartCharp }, + { "stop", "character to use for XOFF", NULL, termStopCharp }, + { "forw1", "alternate end of line character", NULL, termForw1Charp }, + { "forw2", "alternate end of line character", NULL, termForw2Charp }, + { "ayt", "alternate AYT character", NULL, termAytCharp }, + { NULL, NULL, NULL, NULL } +}; + +static struct setlist * +getset(char *name) +{ + return (struct setlist *) + genget(name, (char **) Setlist, sizeof(struct setlist)); +} + +void +set_escape_char(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(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((void *)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((void *)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(int argc, char *argv[]) +{ + struct setlist *ct; + struct togglelist *c; + 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((void *)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((void *)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(void) +{ + kludgelinemode = 1; + send_wont(TELOPT_LINEMODE, 1); + send_dont(TELOPT_SGA, 1); + send_dont(TELOPT_ECHO, 1); + return 1; +} +#endif + +static int +dolinemode(void) +{ +#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(void) +{ +#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(int bit, int 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; +} + +static int +setmod(int bit) +{ + return dolmmode(bit, 1); +} + +static int +clearmode(int bit) +{ + return dolmmode(bit, 0); +} + +struct modelist { + const char *name; /* command name */ + const char *help; /* help string */ + int (*handler)(int);/* routine which executes command */ + int needconnect; /* Do we need to be connected to execute? */ + int arg1; +}; + +static struct modelist ModeList[] = { + { "character", "Disable LINEMODE option", (int (*)(int))docharmode, 1, 0 }, +#ifdef KLUDGELINEMODE + { "", "(or disable obsolete line-by-line mode)", NULL, 0, 0 }, +#endif + { "line", "Enable LINEMODE option", (int (*)(int))dolinemode, 1, 0 }, +#ifdef KLUDGELINEMODE + { "", "(or enable obsolete line-by-line mode)", NULL, 0, 0 }, +#endif + { "", "", NULL, 0, 0 }, + { "", "These require the LINEMODE option to be enabled", NULL, 0, 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, (int (*)(int))modehelp, 0, 0 }, +#ifdef KLUDGELINEMODE + { "kludgeline", 0, (int (*)(int))dokludgemode, 1, 0 }, +#endif + { "", "", NULL, 0, 0 }, + { "?", "Print help information", (int (*)(int))modehelp, 0, 0 }, + { NULL, NULL, NULL, 0, 0 }, +}; + + +static int +modehelp(void) +{ + 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(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((void *)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(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((void *)sl) || Ambiguous((void *)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(int argc, char *argv[]) +{ + 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]; + (void) fflush(stdout); + return 1; +} + +static int +togcrmod(void) +{ + 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; +} + +static int +suspend(void) +{ +#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; +} + +static int +shell(int argc, char *argv[] __unused) +{ + 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. + */ + const char *shellp, *shellname; + + 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], (char *)0); + else + execl(shellp, shellname, (char *)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; +} + +static int +bye(int argc, char *argv[]) +{ + extern int resettermname; + + if (connected) { + (void) shutdown(net, 2); + printf("Connection closed.\n"); + (void) NetClose(net); + connected = 0; + resettermname = 1; +#ifdef AUTHENTICATION +#ifdef ENCRYPTION + auth_encrypt_connect(connected); +#endif +#endif + /* reset options */ + tninit(); + } + if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { + longjmp(toplevel, 1); + /* NOTREACHED */ + } + return 1; /* Keep lint, etc., happy */ +} + +void +quit(void) +{ + (void) call(bye, "bye", "fromquit", 0); + Exit(0); +} + +static int +logout(void) +{ + send_do(TELOPT_LOGOUT, 1); + (void) netflush(); + return 1; +} + + +/* + * The SLC command. + */ + +struct slclist { + const char *name; + const char *help; + void (*handler)(int); + int arg; +}; + +static void slc_help(void); + +struct slclist SlcList[] = { + { "export", "Use local special character definitions", + (void (*)(int))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", NULL, (void (*)(int))slc_help, 0 }, + { "?", "Print help information", (void (*)(int))slc_help, 0 }, + { NULL, NULL, NULL, 0 }, +}; + +static void +slc_help(void) +{ + 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(char *name) +{ + return (struct slclist *) + genget(name, (char **) SlcList, sizeof(struct slclist)); +} + +static int +slccmd(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((void *)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 { + const char *name; + const char *help; + void (*handler)(unsigned char *, unsigned char *); + int narg; +}; + +extern struct env_lst * + env_define(const unsigned char *, unsigned char *); +extern void + env_undefine(unsigned char *), + env_export(const unsigned char *), + env_unexport(const unsigned char *), + env_send(unsigned char *), +#if defined(OLD_ENVIRON) && defined(ENV_HACK) + env_varval(unsigned char *), +#endif + env_list(void); +static void + env_help(void); + +struct envlist EnvList[] = { + { "define", "Define an environment variable", + (void (*)(unsigned char *, unsigned char *))env_define, 2 }, + { "undefine", "Undefine an environment variable", + (void (*)(unsigned char *, unsigned char *))env_undefine, 1 }, + { "export", "Mark an environment variable for automatic export", + (void (*)(unsigned char *, unsigned char *))env_export, 1 }, + { "unexport", "Don't mark an environment variable for automatic export", + (void (*)(unsigned char *, unsigned char *))env_unexport, 1 }, + { "send", "Send an environment variable", (void (*)(unsigned char *, unsigned char *))env_send, 1 }, + { "list", "List the current environment variables", + (void (*)(unsigned char *, unsigned char *))env_list, 0 }, +#if defined(OLD_ENVIRON) && defined(ENV_HACK) + { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", + (void (*)(unsigned char *, unsigned char *))env_varval, 1 }, +#endif + { "help", NULL, (void (*)(unsigned char *, unsigned char *))env_help, 0 }, + { "?", "Print help information", (void (*)(unsigned char *, unsigned char *))env_help, 0 }, + { NULL, NULL, NULL, 0 }, +}; + +static void +env_help(void) +{ + 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(char *name) +{ + return (struct envlist *) + genget(name, (char **) EnvList, sizeof(struct envlist)); +} + +static int +env_cmd(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((void *)c)) { + fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", + argv[1]); + return 0; + } + if (c->narg + 2 != argc && strcasecmp(argv[1],"define")==0 && c->narg + 1 != 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)((unsigned char *)argv[2], (unsigned char *)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; + +static struct env_lst * +env_find(const unsigned char *var) +{ + struct env_lst *ep; + + for (ep = envlisthead.next; ep; ep = ep->next) { + if (strcmp((const char *)ep->var, (const char *)var) == 0) + return(ep); + } + return(NULL); +} + +void +env_init(void) +{ + char *ev; + struct env_lst *ep; + int i; + + const char *safe_vars[]= + {"USER", "PRINTER", "DISPLAY", "TERM", "COLUMNS", "LINES"}; + + for(i=0;i<sizeof(safe_vars)/sizeof(const char *);i++) { + if((ev=getenv(safe_vars[i]))) { + ep=env_define((unsigned char *)safe_vars[i],(unsigned char *)ev); + ep->export=0; + } + } + + /* + * 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((const unsigned char *)"DISPLAY")) + && ((*ep->value == ':') + || (strncmp((char *)ep->value, "unix:", 5) == 0))) { + char hbuf[256+1]; + char *cp, *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((const unsigned char *)"USER") == NULL) && (ep = env_find((const unsigned char *)"LOGNAME"))) { + env_define((const unsigned char *)"USER", ep->value); + env_unexport((const unsigned char *)"USER"); + } + env_export((const unsigned char *)"DISPLAY"); + env_export((const unsigned char *)"PRINTER"); +} + +struct env_lst * +env_define(const unsigned char *var, unsigned char *value) +{ + char *ev; + 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((const char *)var); + ep->export = 1; + ep->var = (unsigned char *)strdup((const char *)var); + + if(value) + ep->value = (unsigned char *)strdup((const char *)value); + else if((ev=getenv((const char *)var))) + ep->value = (unsigned char *)strdup(ev); + else ep->value = (unsigned char *)strdup(""); + return(ep); +} + +void +env_undefine(unsigned char *var) +{ + 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(const unsigned char *var) +{ + struct env_lst *ep; + + if ((ep = env_find(var))) + ep->export = 1; +} + +void +env_unexport(const unsigned char *var) +{ + struct env_lst *ep; + + if ((ep = env_find(var))) + ep->export = 0; +} + +void +env_send(unsigned char *var) +{ + 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(void) +{ + 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(int init, int welldefined) +{ + 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(const unsigned char *var) +{ + struct env_lst *ep; + + if ((ep = env_find(var))) + return(ep->value); + return(NULL); +} + +#if defined(OLD_ENVIRON) && defined(ENV_HACK) +void +env_varval(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 + +#ifdef AUTHENTICATION +/* + * The AUTHENTICATE command. + */ + +struct authlist { + const char *name; + const char *help; + int (*handler)(char *); + int narg; +}; + +extern int + auth_enable(char *), + auth_disable(char *), + auth_status(void); +static int + auth_help(void); + +struct authlist AuthList[] = { + { "status", "Display current status of authentication information", + (int (*)(char *))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", NULL, (int (*)(char *))auth_help, 0 }, + { "?", "Print help information", (int (*)(char *))auth_help, 0 }, + { NULL, NULL, NULL, 0 }, +}; + +static int +auth_help(void) +{ + 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(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((void *)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])); +} +#endif + +#ifdef ENCRYPTION +/* + * The ENCRYPT command. + */ + +struct encryptlist { + const char *name; + const char *help; + int (*handler)(char *, char *); + int needconnect; + int minarg; + int maxarg; +}; + +extern int + EncryptEnable(char *, char *), + EncryptDisable(char *, char *), + EncryptType(char *, char *), + EncryptStart(char *), + EncryptStartInput(void), + EncryptStartOutput(void), + EncryptStop(char *), + EncryptStopInput(void), + EncryptStopOutput(void), + EncryptStatus(void); +static int + EncryptHelp(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)", + (int (*)(char *, char *))EncryptStart, 1, 0, 1 }, + { "stop", "Stop encryption. ('encrypt stop ?' for more)", + (int (*)(char *, char *))EncryptStop, 1, 0, 1 }, + { "input", "Start encrypting the input stream", + (int (*)(char *, char *))EncryptStartInput, 1, 0, 0 }, + { "-input", "Stop encrypting the input stream", + (int (*)(char *, char *))EncryptStopInput, 1, 0, 0 }, + { "output", "Start encrypting the output stream", + (int (*)(char *, char *))EncryptStartOutput, 1, 0, 0 }, + { "-output", "Stop encrypting the output stream", + (int (*)(char *, char *))EncryptStopOutput, 1, 0, 0 }, + + { "status", "Display current status of authentication information", + (int (*)(char *, char *))EncryptStatus, 0, 0, 0 }, + { "help", NULL, (int (*)(char *, char *))EncryptHelp, 0, 0, 0 }, + { "?", "Print help information", (int (*)(char *, char *))EncryptHelp, 0, 0, 0 }, + { NULL, NULL, NULL, 0, 0, 0 }, +}; + +static int +EncryptHelp(void) +{ + 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; +} + +static int +encrypt_cmd(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((void *)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)); +} +#endif /* ENCRYPTION */ + +/* + * Print status about the connection. + */ +/*ARGSUSED*/ +static int +status(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"); + } + printf("Escape character is '%s'.\n", control(escape)); + (void) fflush(stdout); + return 1; +} + +#ifdef SIGINFO +/* + * Function that gets called when SIGINFO is received. + */ +void +ayt_status(void) +{ + (void) call(status, "status", "notmuch", 0); +} +#endif + +static const char * +sockaddr_ntop(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; + case AF_UNIX: + addr = &((struct sockaddr_un *)sa)->sun_path; + 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(int lnet, 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(lnet, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); + return 0; +} +#endif + +#ifdef INET6 +/* + * When an Address Family related error happend, check if retry with + * another AF is possible or not. + * Return 1, if retry with another af is OK. Else, return 0. + */ +static int +switch_af(struct addrinfo **aip) +{ + int nextaf; + struct addrinfo *ai; + + ai = *aip; + nextaf = (ai->ai_family == AF_INET) ? AF_INET6 : AF_INET; + do + ai=ai->ai_next; + while (ai != NULL && ai->ai_family != nextaf); + *aip = ai; + if (*aip != NULL) { + return 1; + } + return 0; +} +#endif + +int +tn(int argc, char *argv[]) +{ + char *srp = 0; + int proto = 0, opt = 0; + int srlen = 0; +#ifndef __APPLE__ + int srcroute = 0, result; +#else + int srcroute = 0; +#endif + char *cmd, *hostp = 0, *portp = 0, *user = 0; + char *src_addr = NULL; + struct addrinfo hints, *res, *res0 = NULL, *src_res, *src_res0 = NULL; + int error = 0, af_error = 0; + + 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) { + 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, &src_res); +#ifdef EAI_NODATA + if ((error == EAI_NODATA) || (error == EAI_NONAME)) +#else + if (error == EAI_NONAME) +#endif + { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &src_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)); + setuid(getuid()); + return 0; + } + src_res0 = src_res; + } + if (hostp[0] == '/') { + struct sockaddr_un su; + + if (strlen(hostp) >= sizeof(su.sun_path)) { + fprintf(stderr, "hostname too long for unix domain socket: %s", + hostp); + goto fail; + } + memset(&su, 0, sizeof su); + su.sun_family = AF_UNIX; + strncpy(su.sun_path, hostp, sizeof su.sun_path); + printf("Trying %s...\n", hostp); + net = socket(PF_UNIX, SOCK_STREAM, 0); + if ( net < 0) { + perror("socket"); + goto fail; + } + if (connect(net, (struct sockaddr *)&su, sizeof su) == -1) { + perror(su.sun_path); + (void) NetClose(net); + goto fail; + } + goto af_unix; + } else if (hostp[0] == '@' || hostp[0] == '!') { + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) + hostname = strrchr(hostp, '@'); + if (hostname == NULL) { + hostname = hostp; + } else { + hostname++; + srcroute = 1; + } + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = strdup("telnet"); + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else if (*portp == '+') { + portp++; + telnetport = -1; + } else + telnetport = 0; + +#ifdef __APPLE__ + { + /* + * 5760578: Attempt to convert service name to a + * numeric port before calling getaddrinfo(). + */ + struct servent *srv = getservbyname(portp, NULL); + if (srv != NULL) { + asprintf(&portp, "%d", ntohs(srv->s_port)); + } + } +#endif /* __APPLE__ */ + + 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) { + 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()); + goto fail; + } + if (hints.ai_flags == AI_NUMERICHOST) { + /* hostname has numeric */ + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + NI_NAMEREQD); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else { + /* hostname has FQDN */ + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strncpy(_hostname, res->ai_canonname, sizeof(_hostname) - 1); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + res0 = res; + #ifdef INET6 + af_again: + #endif +#ifndef __APPLE__ + if (srcroute != 0) { + static char hostbuf[BUFSIZ]; + + if (af_error == 0) { /* save intermediate hostnames for retry */ + strncpy(hostbuf, hostp, BUFSIZ - 1); + hostbuf[BUFSIZ - 1] = '\0'; + } else + hostp = hostbuf; + srp = 0; + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + goto af_again; + } +#endif + setuid(getuid()); + goto fail; + } else if (result == -1) { + printf("Bad source route option: %s\n", hostp); + setuid(getuid()); + goto fail; + } + } +#endif + do { + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + setuid(getuid()); + if (net < 0) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + goto af_again; + } +#endif + perror("telnet: socket"); + goto fail; + } + 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 (telnet_debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { + perror("setsockopt (SO_DEBUG)"); + } + + if (src_addr != NULL) { + for (src_res = src_res0; src_res != 0; src_res = src_res->ai_next) + if (src_res->ai_family == res->ai_family) + break; + if (src_res == NULL) + src_res = src_res0; + if (bind(net, src_res->ai_addr, src_res->ai_addrlen) == -1) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + (void) NetClose(net); + goto af_again; + } +#endif + perror("bind"); + (void) NetClose(net); + goto fail; + } + } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) { + (void) NetClose(net); + goto fail; + } + if (setpolicy(net, res, ipsec_policy_out) < 0) { + (void) NetClose(net); + goto fail; + } +#endif + + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + struct addrinfo *next; + + next = res->ai_next; + /* If already an af failed, only try same af. */ + if (af_error != 0) + while (next != NULL && next->ai_family != res->ai_family) + next = next->ai_next; + warn("connect to address %s", sockaddr_ntop(res->ai_addr)); + if (next != NULL) { + res = next; + (void) NetClose(net); + continue; + } + warnx("Unable to connect to remote host"); + (void) NetClose(net); + goto fail; + } + connected++; +#ifdef AUTHENTICATION +#ifdef ENCRYPTION + auth_encrypt_connect(connected); +#endif +#endif + } while (connected == 0); + freeaddrinfo(res0); + if (src_res0 != NULL) + freeaddrinfo(src_res0); + cmdrc(hostp, hostname); + af_unix: + connected = 1; + 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*/ + fail: + if (res0 != NULL) + freeaddrinfo(res0); + if (src_res0 != NULL) + freeaddrinfo(src_res0); + return 0; +} + +#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", +#ifdef AUTHENTICATION + authhelp[] = "turn on (off) authentication ('auth ?' for more)", +#endif +#ifdef ENCRYPTION + encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)", +#endif /* ENCRYPTION */ + zhelp[] = "suspend telnet", +#ifdef OPIE + opiehelp[] = "compute response to OPIE 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, (int (*)(int, char **))logout, 1 }, + { "display", displayhelp, display, 0 }, + { "mode", modestring, modecmd, 0 }, + { "telnet", openhelp, tn, 0 }, + { "open", openhelp, tn, 0 }, + { "quit", quithelp, (int (*)(int, char **))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 }, +#ifdef AUTHENTICATION + { "auth", authhelp, auth_cmd, 0 }, +#endif +#ifdef ENCRYPTION + { "encrypt", encrypthelp, encrypt_cmd, 0 }, +#endif /* ENCRYPTION */ + { "z", zhelp, (int (*)(int, char **))suspend, 0 }, + { "!", shellhelp, shell, 1 }, + { "environ", envhelp, env_cmd, 0 }, + { "?", helphelp, help, 0 }, +#ifdef OPIE + { "opie", opiehelp, opie_calc, 0 }, +#endif + { NULL, NULL, NULL, 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, (int (*)(int, char **))togcrmod, 0 }, + { NULL, NULL, NULL, 0 } +}; + + +/* + * Call routine with argc, argv set from args (terminated by 0). + */ + +static int +call(intrtn_t routine, ...) +{ + va_list ap; + char *args[100]; + int argno = 0; + + va_start(ap, routine); + while ((args[argno++] = va_arg(ap, char *)) != 0); + va_end(ap); + return (*routine)(argno-1, args); +} + + +static Command * +getcmd(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(int top, const char *tbuf, int cnt) +{ + Command *c; + + setcommandmode(); + if (!top) { + putchar('\n'); + } else { + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + } + for (;;) { + if (rlogin == _POSIX_VDISABLE) + printf("%s> ", prompt); + if (tbuf) { + 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((void *)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*/ + } + setconnmode(0); + } +} + +/* + * Help command. + */ +static int +help(int argc, char *argv[]) +{ + 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", (int)HELPINDENT, c->name, + c->help); + } + return 0; + } + else while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (Ambiguous((void *)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(char *m1, char *m2) +{ + Command *c; + FILE *rcfile; + int gotmachine = 0; + int l1 = strlen(m1); + int l2 = strlen(m2); + char m1save[MAXHOSTNAMELEN]; + + if (skiprc) + return; + + strlcpy(m1save, m1, sizeof(m1save)); + 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((void *)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); +} + +#ifndef __APPLE__ +/* + * 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: + * + * 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. + */ +static int +sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, int *optp) +{ + static char buf[1024 + ALIGNBYTES]; /*XXX*/ + char *cp, *cp2, *lsrp, *ep; + struct sockaddr_in *_sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; + struct cmsghdr *cmsg = NULL; +#endif + struct addrinfo hints, *res; + int error; + char c; + + /* + * Verify the arguments, and make sure we have + * at least 7 bytes for the option. + */ + if (cpp == NULL || lenp == NULL) + return -1; + if (*cpp != NULL) { + switch (ai->ai_family) { + case AF_INET: + if (*lenp < 7) + return -1; + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (int)CMSG_SPACE(sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return -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; + ep = lsrp + *lenp; + } else { + *cpp = lsrp = (char *)ALIGN(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. + */ + if (*cp == '!') { + cp++; + *lsrp++ = IPOPT_SSRR; + } else + *lsrp++ = IPOPT_LSRR; + + if (*cp != '@') + return -1; + + lsrp++; /* skip over length, we'll fill it in later */ + *lsrp++ = 4; + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } + + cp++; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; + for (c = 0;;) { + 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 ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { + *cp2++ = '\0'; + } else + continue; + break; + } + if (!c) + cp2 = 0; + + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); +#ifdef EAI_NODATA + if ((error == EAI_NODATA) || (error == EAI_NONAME)) +#else + if (error == EAI_NONAME) +#endif + { + 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); + } +#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 + */ +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)CMSG_DATA(cmsg) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) + return -1; + } else +#endif + if (lsrp + 4 > ep) + return -1; + freeaddrinfo(res); + } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { + if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { + *cpp = 0; + *lenp = 0; + return -1; + } + *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ + *lenp = lsrp - *cpp; + } + freeaddrinfo(res); + return 1; +} +#endif diff --git a/remote_cmds/telnet.tproj/defines.h b/remote_cmds/telnet.tproj/defines.h new file mode 100644 index 0000000..d49cb2d --- /dev/null +++ b/remote_cmds/telnet.tproj/defines.h @@ -0,0 +1,56 @@ +/* + * 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. + * + * @(#)defines.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/contrib/telnet/telnet/defines.h,v 1.2 2001/11/30 21:06:35 markm Exp $ + */ + +#define settimer(x) clocks.x = clocks.system++ + +#define NETADD(c) { *netoring.supply = c; ring_supplied(&netoring, 1); } +#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); } +#define NETBYTES() (ring_full_count(&netoring)) +#define NETROOM() (ring_empty_count(&netoring)) + +#define TTYADD(c) if (!(SYNCHing||flushout)) { \ + *ttyoring.supply = c; \ + ring_supplied(&ttyoring, 1); \ + } +#define TTYBYTES() (ring_full_count(&ttyoring)) +#define TTYROOM() (ring_empty_count(&ttyoring)) + +/* Various modes */ +#define MODE_LOCAL_CHARS(m) ((m)&(MODE_EDIT|MODE_TRAPSIG)) +#define MODE_LOCAL_ECHO(m) ((m)&MODE_ECHO) +#define MODE_COMMAND_LINE(m) ((m)==-1) + +#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ diff --git a/remote_cmds/telnet.tproj/externs.h b/remote_cmds/telnet.tproj/externs.h new file mode 100644 index 0000000..bc450ec --- /dev/null +++ b/remote_cmds/telnet.tproj/externs.h @@ -0,0 +1,495 @@ +/* + * 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: src/contrib/telnet/telnet/externs.h,v 1.11 2007/07/01 12:08:04 gnn Exp $ + */ + +#ifndef BSD +# define BSD 43 +#endif + +/* + * ucb stdio.h defines BSD as something weird + */ +#if defined(sun) && defined(__svr4__) +#define BSD 43 +#endif + +#ifndef USE_TERMIO +# if BSD > 43 || defined(SYSV_TERMIO) +# define USE_TERMIO +# endif +#endif + +#include <stdio.h> +#include <setjmp.h> +#include <sys/ioctl.h> +#include <errno.h> +#ifdef USE_TERMIO +# ifndef VINTR +# include <sys/termios.h> +# endif +# define termio termios +#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 + +#include <string.h> + +#if defined(IPSEC) +#ifdef __APPLE__ +#include <netinet6/ipsec.h> +#else +#include <netipsec/ipsec.h> +#endif +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + +#ifndef _POSIX_VDISABLE +# ifdef sun +# include <sys/param.h> /* pick up VDISABLE definition, mayby */ +# endif +# ifdef VDISABLE +# define _POSIX_VDISABLE VDISABLE +# else +# define _POSIX_VDISABLE ((cc_t)'\377') +# endif +#endif + +#define SUBBUFSIZE 256 + +#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 */ + 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 <CR><LF> (or <CR><NUL>)? */ + 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 */ + termdata, /* Print out terminal data flow */ + telnet_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)(unsigned char *, int); +extern int (*decrypt_input)(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 + +#if defined(USE_TERMIO) +#define SIG_FUNC_RET void +#else +#define SIG_FUNC_RET int +#endif + +#ifdef SIGINFO +extern SIG_FUNC_RET + ayt_status(void); +#endif + +extern FILE + *NetTrace; /* Where debugging output goes */ +extern unsigned char + NetTraceFile[]; /* Name of file where debugging output goes */ +extern void + SetNetTrace(char *); /* Function to change where debugging goes */ + +extern jmp_buf + peerdied, + toplevel; /* For error conditions. */ + +extern void + command(int, const char *, int), + Dump(char, unsigned char *, int), + env_init(void), + Exit(int), + ExitString(const char *, int), + init_network(void), + init_sys(void), + init_telnet(void), + init_terminal(void), + intp(void), + optionstatus(void), + printoption(const char *, int, int), + printsub(char, unsigned char *, int), + quit(void), + sendabort(void), + sendbrk(void), + sendeof(void), + sendsusp(void), + sendnaws(void), + sendayt(void), + setconnmode(int), + setcommandmode(void), + set_escape_char(char *s), + setneturg(void), + sys_telnet_init(void), + telnet(char *), + tel_enter_binary(int), + tel_leave_binary(int), + TerminalFlushOutput(void), + TerminalNewMode(int), + TerminalRestoreState(void), + TerminalSaveState(void), + TerminalDefaultChars(void), + TerminalSpeeds(long *, long *), + tninit(void), + upcase(char *), + willoption(int), + wontoption(int); + +extern void + send_do(int, int), + send_dont(int, int), + send_will(int, int), + send_wont(int, int); + +extern void + lm_will(unsigned char *, int), + lm_wont(unsigned char *, int), + lm_do(unsigned char *, int), + lm_dont(unsigned char *, int), + lm_mode(unsigned char *, int, int); + +extern void + slc_init(void), + slcstate(void), + slc_mode_export(void), + slc_mode_import(int), + slc_import(int), + slc_export(void), + slc(unsigned char *, int), + slc_check(void), + slc_start_reply(void), + slc_add_reply(unsigned char, unsigned char, cc_t), + slc_end_reply(void); +extern int + getconnmode(void), + opt_welldefined(const char *), + NetClose(int), + netflush(void), + process_rings(int, int, int, int, int, int), + rlogin_susp(void), + SetSockOpt(int, int, int, int), + slc_update(void), + stilloob(void), + telrcv(void), + TerminalRead(char *, int), + TerminalWrite(char *, int), + TerminalAutoFlush(void), + TerminalWindowSize(long *, long *), + TerminalSpecialChars(int), + tn(int, char **), + ttyflush(int); + +extern void + env_opt(unsigned char *, int), + env_opt_start(void), + env_opt_start_info(void), + env_opt_add(unsigned char *), + env_opt_end(int); + +extern unsigned char + *env_default(int, int), + *env_getvalue(const unsigned char *); + +extern int + get_status(char *), + dosynch(char *); + +extern cc_t + *tcval(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(__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; + +extern void + xmitAO(void), + xmitEC(void), + xmitEL(void); diff --git a/remote_cmds/telnet.tproj/fdset.h b/remote_cmds/telnet.tproj/fdset.h new file mode 100644 index 0000000..045bb72 --- /dev/null +++ b/remote_cmds/telnet.tproj/fdset.h @@ -0,0 +1,49 @@ +/* + * 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. + * + * @(#)fdset.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * The following is defined just in case someone should want to run + * this telnet on a 4.2 system. + * + */ + +#ifndef FD_SETSIZE + +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) + +#endif diff --git a/remote_cmds/telnet.tproj/general.h b/remote_cmds/telnet.tproj/general.h new file mode 100644 index 0000000..4efa951 --- /dev/null +++ b/remote_cmds/telnet.tproj/general.h @@ -0,0 +1,45 @@ +/* + * 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. + * + * @(#)general.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Some general definitions. + */ + + +#define numberof(x) (sizeof x/sizeof x[0]) +#define highestof(x) (numberof(x)-1) + +#define ClearElement(x) memset((char *)&x, 0, sizeof x) +#define ClearArray(x) memset((char *)x, 0, sizeof x) diff --git a/remote_cmds/telnet.tproj/krb4-proto.h b/remote_cmds/telnet.tproj/krb4-proto.h new file mode 100644 index 0000000..9f3f0b0 --- /dev/null +++ b/remote_cmds/telnet.tproj/krb4-proto.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifdef __STDC__ +# define P(s) s +#else +# define P(s) () +#endif + +/* add_ticket.c */ +int add_ticket P((KTEXT , int , char *, int , char *, char *, char *, int , KTEXT )); + +/* cr_err_reply.c */ +void cr_err_reply P((KTEXT , char *, char *, char *, u_long , u_long , char *)); + +/* create_auth_reply.c */ +KTEXT create_auth_reply P((char *, char *, char *, long , int , unsigned long , int , KTEXT )); + +/* create_ciph.c */ +int create_ciph P((KTEXT , C_Block , char *, char *, char *, unsigned long , int , KTEXT , unsigned long , C_Block )); + +/* create_death_packet.c */ +KTEXT krb_create_death_packet P((char *)); + +/* create_ticket.c */ +int krb_create_ticket P((KTEXT , unsigned int , char *, char *, char *, long , char *, int , long , char *, char *, C_Block )); + +/* debug_decl.c */ + +/* decomp_ticket.c */ +int decomp_ticket P((KTEXT , unsigned char *, char *, char *, char *, unsigned long *, C_Block , int *, unsigned long *, char *, char *, C_Block , Key_schedule )); + +/* dest_tkt.c */ +int dest_tkt P((void )); + +/* extract_ticket.c */ +int extract_ticket P((KTEXT , int , char *, int *, int *, char *, KTEXT )); + +/* fgetst.c */ +int fgetst P((FILE *, char *, int )); + +/* get_ad_tkt.c */ +int get_ad_tkt P((char *, char *, char *, int )); + +/* get_admhst.c */ +int krb_get_admhst P((char *, char *, int )); + +/* get_cred.c */ +int krb_get_cred P((char *, char *, char *, CREDENTIALS *)); + +/* get_in_tkt.c */ +int krb_get_pw_in_tkt P((char *, char *, char *, char *, char *, int , char *)); +int placebo_read_password P((des_cblock *, char *, int )); +int placebo_read_pw_string P((char *, int , char *, int )); + +/* get_krbhst.c */ +int krb_get_krbhst P((char *, char *, int )); + +/* get_krbrlm.c */ +int krb_get_lrealm P((char *, int )); + +/* get_phost.c */ +char *krb_get_phost P((char *)); + +/* get_pw_tkt.c */ +int get_pw_tkt P((char *, char *, char *, char *)); + +/* get_request.c */ +int get_request P((KTEXT , int , char **, char **)); + +/* get_svc_in_tkt.c */ +int krb_get_svc_in_tkt P((char *, char *, char *, char *, char *, int , char *)); + +/* get_tf_fullname.c */ +int krb_get_tf_fullname P((char *, char *, char *, char *)); + +/* get_tf_realm.c */ +int krb_get_tf_realm P((char *, char *)); + +/* getopt.c */ +int getopt P((int , char **, char *)); + +/* getrealm.c */ +char *krb_realmofhost P((char *)); + +/* getst.c */ +int getst P((int , char *, int )); + +/* in_tkt.c */ +int in_tkt P((char *, char *)); + +/* k_gethostname.c */ +int k_gethostname P((char *, int )); + +/* klog.c */ +char *klog P((int , char *, int , int , int , int , int , int , int , int , int , int )); +int kset_logfile P((char *)); + +/* kname_parse.c */ +int kname_parse P((char *, char *, char *, char *)); +int k_isname P((char *)); +int k_isinst P((char *)); +int k_isrealm P((char *)); + +/* kntoln.c */ +int krb_kntoln P((AUTH_DAT *, char *)); + +/* krb_err_txt.c */ + +/* krb_get_in_tkt.c */ +int krb_get_in_tkt P((char *, char *, char *, char *, char *, int , int (*key_proc )(), int (*decrypt_proc )(), char *)); + +/* kuserok.c */ +int kuserok P((AUTH_DAT *, char *)); + +/* log.c */ +void log P((char *, int , int , int , int , int , int , int , int , int , int )); +int set_logfile P((char *)); +int new_log P((long , char *)); + +/* mk_err.c */ +long krb_mk_err P((u_char *, long , char *)); + +/* mk_priv.c */ +long krb_mk_priv P((u_char *, u_char *, u_long , Key_schedule , C_Block , struct sockaddr_in *, struct sockaddr_in *)); + +/* mk_req.c */ +int krb_mk_req P((KTEXT , char *, char *, char *, long )); +int krb_set_lifetime P((int )); + +/* mk_safe.c */ +long krb_mk_safe P((u_char *, u_char *, u_long , C_Block *, struct sockaddr_in *, struct sockaddr_in *)); + +/* month_sname.c */ +char *month_sname P((int )); + +/* netread.c */ +int krb_net_read P((int , char *, int )); + +/* netwrite.c */ +int krb_net_write P((int , char *, int )); + +/* one.c */ + +/* pkt_cipher.c */ +KTEXT pkt_cipher P((KTEXT )); + +/* pkt_clen.c */ +int pkt_clen P((KTEXT )); + +/* rd_err.c */ +int krb_rd_err P((u_char *, u_long , long *, MSG_DAT *)); + +/* rd_priv.c */ +long krb_rd_priv P((u_char *, u_long , Key_schedule , C_Block , struct sockaddr_in *, struct sockaddr_in *, MSG_DAT *)); + +/* rd_req.c */ +int krb_set_key P((char *, int )); +int krb_rd_req P((KTEXT , char *, char *, long , AUTH_DAT *, char *)); + +/* rd_safe.c */ +long krb_rd_safe P((u_char *, u_long , C_Block *, struct sockaddr_in *, struct sockaddr_in *, MSG_DAT *)); + +/* read_service_key.c */ +int read_service_key P((char *, char *, char *, int , char *, char *)); + +/* recvauth.c */ +int krb_recvauth P((long , int , KTEXT , char *, char *, struct sockaddr_in *, struct sockaddr_in *, AUTH_DAT *, char *, Key_schedule , char *)); + +/* save_credentials.c */ +int save_credentials P((char *, char *, char *, C_Block , int , int , KTEXT , long )); + +/* send_to_kdc.c */ +int send_to_kdc P((KTEXT , KTEXT , char *)); + +/* sendauth.c */ +int krb_sendauth P((long , int , KTEXT , char *, char *, char *, u_long , MSG_DAT *, CREDENTIALS *, Key_schedule , struct sockaddr_in *, struct sockaddr_in *, char *)); +int krb_sendsvc P((int , char *)); + +/* setenv.c */ +int setenv P((char *, char *, int )); +void unsetenv P((char *)); +char *getenv P((char *)); +char *_findenv P((char *, int *)); + +/* stime.c */ +char *stime P((long *)); + +/* tf_shm.c */ +int krb_shm_create P((char *)); +int krb_is_diskless P((void )); +int krb_shm_dest P((char *)); + +/* tf_util.c */ +int tf_init P((char *, int )); +int tf_get_pname P((char *)); +int tf_get_pinst P((char *)); +int tf_get_cred P((CREDENTIALS *)); +int tf_close P((void )); +int tf_save_cred P((char *, char *, char *, C_Block , int , int , KTEXT , long )); + +/* tkt_string.c */ +char *tkt_string P((void )); +void krb_set_tkt_string P((char *)); + +/* util.c */ +int ad_print P((AUTH_DAT *)); +int placebo_cblock_print P((des_cblock )); + +#undef P diff --git a/remote_cmds/telnet.tproj/main.c b/remote_cmds/telnet.tproj/main.c new file mode 100644 index 0000000..fd01a30 --- /dev/null +++ b/remote_cmds/telnet.tproj/main.c @@ -0,0 +1,379 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/main.c,v 1.20 2005/01/09 10:24:45 maxim Exp $"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ring.h" +#include "externs.h" +#include "defines.h" + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#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 defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +extern int tos; + +int family = AF_UNSPEC; + +/* + * Initialize variables. + */ +void +tninit(void) +{ + init_terminal(); + + init_network(); + + init_telnet(); + + init_sys(); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s %s%s%s%s\n", + prompt, +#ifdef AUTHENTICATION + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-c] [-d]", + "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", +#else + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-c] [-d]", + "\n\t[-e char] [-l user] [-n tracefile] ", +#endif + "[-r] [-s src_addr] [-u] ", +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy] " +#endif +#ifdef ENCRYPTION + "[-y] [host-name [port]]" +#else /* ENCRYPTION */ + "[host-name [port]]" +#endif /* ENCRYPTION */ + ); + exit(1); +} + +/* + * main. Parse arguments, invoke the protocol or command parser. + */ + +int +main(int argc, char *argv[]) +{ + u_long ultmp; + int ch; + char *ep, *user; + char *src_addr = NULL; +#ifdef FORWARD + extern int forward_flags; +#endif /* FORWARD */ + + tninit(); /* Clear out things */ + + TerminalSaveState(); + + if ((prompt = strrchr(argv[0], '/'))) + ++prompt; + else + prompt = argv[0]; + + user = NULL; + + rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; +#ifdef AUTHENTICATION + autologin = 1; +#else + autologin = -1; +#endif + +#ifdef ENCRYPTION + encrypt_auto(1); + decrypt_auto(1); +#endif + +#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:uxy" 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 + + 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 +#define MAXTOS 255 + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > MAXTOS) + fprintf(stderr, "%s%s%s%s\n", + prompt, ": Bad TOS argument '", + optarg, + "; will try to use default TOS"); + else + tos = ultmp; +#endif + break; + case 'X': +#ifdef AUTHENTICATION + auth_disable_name(optarg); +#endif + break; + case 'a': +#ifdef AUTHENTICATION + /* It's the default now, so ignore */ +#else + autologin = 1; +#endif + break; + case 'c': + skiprc = 1; + break; + case 'd': + telnet_debug = 1; + break; + case 'e': + set_escape_char(optarg); + break; + case 'f': +#ifdef AUTHENTICATION +#if 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 +#else + fprintf(stderr, + "%s: Warning: -f ignored, no Kerberos V5 support.\n", + prompt); +#endif + break; + case 'F': +#ifdef AUTHENTICATION +#if 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 +#else + fprintf(stderr, + "%s: Warning: -F ignored, no Kerberos V5 support.\n", + prompt); +#endif + break; + case 'k': +#ifdef AUTHENTICATION +#if 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 +#else + fprintf(stderr, + "%s: Warning: -k ignored, no Kerberos V4 support.\n", + prompt); +#endif + break; + case 'l': +#ifdef AUTHENTICATION + /* This is the default now, so ignore it */ +#else + autologin = 1; +#endif + user = optarg; + break; + case 'n': + SetNetTrace(optarg); + break; + case 'r': + rlogin = '~'; + break; + case 's': + src_addr = optarg; + break; + case 'u': + family = AF_UNIX; + break; + case 'x': +#ifndef ENCRYPTION + fprintf(stderr, + "%s: Warning: -x ignored, no ENCRYPT support.\n", + prompt); +#endif /* ENCRYPTION */ + break; + case 'y': +#ifdef ENCRYPTION + encrypt_auto(0); + decrypt_auto(0); +#else + fprintf(stderr, + "%s: Warning: -y 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++ = strdup("-l"); + *argp++ = user; + } + if (src_addr) { + *argp++ = strdup("-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 (;;) { + command(1, 0, 0); + } + return 0; +} diff --git a/remote_cmds/telnet.tproj/misc-proto.h b/remote_cmds/telnet.tproj/misc-proto.h new file mode 100644 index 0000000..511a1bf --- /dev/null +++ b/remote_cmds/telnet.tproj/misc-proto.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 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. + * + * @(#)misc-proto.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: src/crypto/telnet/libtelnet/misc-proto.h,v 1.1.1.1.8.1 2002/04/13 10:59:07 markm Exp $ + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef __MISC_PROTO__ +#define __MISC_PROTO__ + +void auth_encrypt_init(char *, char *, const char *, int); +void auth_encrypt_connect(int); +void printd(const unsigned char *, int); + +int isprefix(char *, const char *); +char **genget(char *, char **, int); +int Ambiguous(char **); + +int getent(char *, const char *); +char *Getstr(const char *, char **); + +/* + * These functions are imported from the application + */ +int net_write(unsigned char *, int); +void net_encrypt(void); +int telnet_spin(void); +char *telnet_getenv(char *); +char *telnet_gets(const char *, char *, int, int); +void printsub(char, unsigned char *, int); +#endif diff --git a/remote_cmds/telnet.tproj/misc.h b/remote_cmds/telnet.tproj/misc.h new file mode 100644 index 0000000..41ffa7f --- /dev/null +++ b/remote_cmds/telnet.tproj/misc.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 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. + * + * @(#)misc.h 8.1 (Berkeley) 6/4/93 + */ + +extern char *UserNameRequested; +extern char *LocalHostName; +extern char *RemoteHostName; +extern int ConnectedCount; +extern int ReservedPort; + +#include "misc-proto.h" diff --git a/remote_cmds/telnet.tproj/network.c b/remote_cmds/telnet.tproj/network.c new file mode 100644 index 0000000..e93345f --- /dev/null +++ b/remote_cmds/telnet.tproj/network.c @@ -0,0 +1,182 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)network.c 8.2 (Berkeley) 12/15/93"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/network.c,v 1.7 2003/05/04 02:54:48 obrien Exp $"); + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <errno.h> +#include <stdlib.h> + +#include <arpa/telnet.h> +#include <unistd.h> + +#include "ring.h" + +#include "defines.h" +#include "externs.h" +#include "fdset.h" + +Ring netoring, netiring; +unsigned char netobuf[2*BUFSIZ], netibuf[BUFSIZ]; + +/* + * Initialize internal network data structures. + */ + +void +init_network(void) +{ + if (ring_init(&netoring, netobuf, sizeof netobuf) != 1) { + exit(1); + } + if (ring_init(&netiring, netibuf, sizeof netibuf) != 1) { + exit(1); + } + NetTrace = stdout; +} + + +/* + * Check to see if any out-of-band data exists on a socket (for + * Telnet "synch" processing). + */ + +int +stilloob(void) +{ + static struct timeval timeout = { 0, 0 }; + fd_set excepts; + int value; + + do { + FD_ZERO(&excepts); + FD_SET(net, &excepts); + value = select(net+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); + } while ((value == -1) && (errno == EINTR)); + + if (value < 0) { + perror("select"); + (void) quit(); + /* NOTREACHED */ + } + if (FD_ISSET(net, &excepts)) { + return 1; + } else { + return 0; + } +} + + +/* + * setneturg() + * + * Sets "neturg" to the current location. + */ + +void +setneturg(void) +{ + ring_mark(&netoring); +} + + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + * + * The return value indicates whether we did any + * useful work. + */ + +int +netflush(void) +{ + int n, n1; + +#ifdef ENCRYPTION + if (encrypt_output) + ring_encrypt(&netoring, encrypt_output); +#endif /* ENCRYPTION */ + if ((n1 = n = ring_full_consecutive(&netoring)) > 0) { + if (!ring_at_mark(&netoring)) { + n = send(net, (char *)netoring.consume, n, 0); /* normal write */ + } else { + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + n = send(net, (char *)netoring.consume, 1, MSG_OOB);/* URGENT data */ + } + } + if (n < 0) { + if (errno != ENOBUFS && errno != EWOULDBLOCK) { + setcommandmode(); + perror(hostname); + (void)NetClose(net); + ring_clear_mark(&netoring); + longjmp(peerdied, -1); + /*NOTREACHED*/ + } + n = 0; + } + if (netdata && n) { + Dump('>', netoring.consume, n); + } + if (n) { + ring_consumed(&netoring, n); + /* + * If we sent all, and more to send, then recurse to pick + * up the other half. + */ + if ((n1 == n) && ring_full_consecutive(&netoring)) { + (void) netflush(); + } + return 1; + } else { + return 0; + } +} diff --git a/remote_cmds/telnet.tproj/ring.c b/remote_cmds/telnet.tproj/ring.c new file mode 100644 index 0000000..0706133 --- /dev/null +++ b/remote_cmds/telnet.tproj/ring.c @@ -0,0 +1,322 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)ring.c 8.2 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/ring.c,v 1.7 2003/05/04 02:54:48 obrien Exp $"); + +/* + * This defines a structure for a ring buffer. + * + * The circular buffer has two parts: + *((( + * full: [consume, supply) + * empty: [supply, consume) + *]]] + * + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#ifdef size_t +#undef size_t +#endif + +#include <sys/types.h> +#ifndef FILIO_H +#include <sys/ioctl.h> +#endif +#include <sys/socket.h> + +#include "ring.h" +#include "general.h" + +/* Internal macros */ + +#if !defined(MIN) +#define MIN(a,b) (((a)<(b))? (a):(b)) +#endif /* !defined(MIN) */ + +#define ring_subtract(d,a,b) (((a)-(b) >= 0)? \ + (a)-(b): (((a)-(b))+(d)->size)) + +#define ring_increment(d,a,c) (((a)+(c) < (d)->top)? \ + (a)+(c) : (((a)+(c))-(d)->size)) + +#define ring_decrement(d,a,c) (((a)-(c) >= (d)->bottom)? \ + (a)-(c) : (((a)-(c))-(d)->size)) + + +/* + * The following is a clock, used to determine full, empty, etc. + * + * There is some trickiness here. Since the ring buffers are initialized + * to ZERO on allocation, we need to make sure, when interpreting the + * clock, that when the times are EQUAL, then the buffer is FULL. + */ +static u_long ring_clock = 0; + + +#define ring_empty(d) (((d)->consume == (d)->supply) && \ + ((d)->consumetime >= (d)->supplytime)) +#define ring_full(d) (((d)->supply == (d)->consume) && \ + ((d)->supplytime > (d)->consumetime)) + +/* Buffer state transition routines */ + +int +ring_init(Ring *ring, unsigned char *buffer, int count) +{ + memset((char *)ring, 0, sizeof *ring); + + ring->size = count; + + ring->supply = ring->consume = ring->bottom = buffer; + + ring->top = ring->bottom+ring->size; + +#ifdef ENCRYPTION + ring->clearto = 0; +#endif /* ENCRYPTION */ + + return 1; +} + +/* Mark routines */ + +/* + * Mark the most recently supplied byte. + */ + +void +ring_mark(Ring *ring) +{ + ring->mark = ring_decrement(ring, ring->supply, 1); +} + +/* + * Is the ring pointing to the mark? + */ + +int +ring_at_mark(Ring *ring) +{ + if (ring->mark == ring->consume) { + return 1; + } else { + return 0; + } +} + +/* + * Clear any mark set on the ring. + */ + +void +ring_clear_mark(Ring *ring) +{ + ring->mark = 0; +} + +/* + * Add characters from current segment to ring buffer. + */ +void +ring_supplied(Ring *ring, int count) +{ + ring->supply = ring_increment(ring, ring->supply, count); + ring->supplytime = ++ring_clock; +} + +/* + * We have just consumed "c" bytes. + */ +void +ring_consumed(Ring *ring, int count) +{ + if (count == 0) /* don't update anything */ + return; + + if (ring->mark && + (ring_subtract(ring, ring->mark, ring->consume) < count)) { + ring->mark = 0; + } +#ifdef ENCRYPTION + if (ring->consume < ring->clearto && + ring->clearto <= ring->consume + count) + ring->clearto = 0; + else if (ring->consume + count > ring->top && + ring->bottom <= ring->clearto && + ring->bottom + ((ring->consume + count) - ring->top)) + ring->clearto = 0; +#endif /* ENCRYPTION */ + ring->consume = ring_increment(ring, ring->consume, count); + ring->consumetime = ++ring_clock; + /* + * Try to encourage "ring_empty_consecutive()" to be large. + */ + if (ring_empty(ring)) { + ring->consume = ring->supply = ring->bottom; + } +} + + + +/* Buffer state query routines */ + + +/* Number of bytes that may be supplied */ +int +ring_empty_count(Ring *ring) +{ + if (ring_empty(ring)) { /* if empty */ + return ring->size; + } else { + return ring_subtract(ring, ring->consume, ring->supply); + } +} + +/* number of CONSECUTIVE bytes that may be supplied */ +int +ring_empty_consecutive(Ring *ring) +{ + if ((ring->consume < ring->supply) || ring_empty(ring)) { + /* + * if consume is "below" supply, or empty, then + * return distance to the top + */ + return ring_subtract(ring, ring->top, ring->supply); + } else { + /* + * else, return what we may. + */ + return ring_subtract(ring, ring->consume, ring->supply); + } +} + +/* Return the number of bytes that are available for consuming + * (but don't give more than enough to get to cross over set mark) + */ + +int +ring_full_count(Ring *ring) +{ + if ((ring->mark == 0) || (ring->mark == ring->consume)) { + if (ring_full(ring)) { + return ring->size; /* nothing consumed, but full */ + } else { + return ring_subtract(ring, ring->supply, ring->consume); + } + } else { + return ring_subtract(ring, ring->mark, ring->consume); + } +} + +/* + * Return the number of CONSECUTIVE bytes available for consuming. + * However, don't return more than enough to cross over set mark. + */ +int +ring_full_consecutive(Ring *ring) +{ + if ((ring->mark == 0) || (ring->mark == ring->consume)) { + if ((ring->supply < ring->consume) || ring_full(ring)) { + return ring_subtract(ring, ring->top, ring->consume); + } else { + return ring_subtract(ring, ring->supply, ring->consume); + } + } else { + if (ring->mark < ring->consume) { + return ring_subtract(ring, ring->top, ring->consume); + } else { /* Else, distance to mark */ + return ring_subtract(ring, ring->mark, ring->consume); + } + } +} + +/* + * Move data into the "supply" portion of of the ring buffer. + */ +void +ring_supply_data(Ring *ring, unsigned char *buffer, int count) +{ + int i; + + while (count) { + i = MIN(count, ring_empty_consecutive(ring)); + memcpy(ring->supply, buffer, i); + ring_supplied(ring, i); + count -= i; + buffer += i; + } +} + +#ifdef ENCRYPTION +void +ring_encrypt(Ring *ring, void (*encryptor)(unsigned char *, int)) +{ + unsigned char *s, *c; + + if (ring_empty(ring) || ring->clearto == ring->supply) + return; + + if (!(c = ring->clearto)) + c = ring->consume; + + s = ring->supply; + + if (s <= c) { + (*encryptor)(c, ring->top - c); + (*encryptor)(ring->bottom, s - ring->bottom); + } else + (*encryptor)(c, s - c); + + ring->clearto = ring->supply; +} + + void +ring_clearto(ring) + Ring *ring; +{ + if (!ring_empty(ring)) + ring->clearto = ring->supply; + else + ring->clearto = 0; +} +#endif /* ENCRYPTION */ diff --git a/remote_cmds/telnet.tproj/ring.h b/remote_cmds/telnet.tproj/ring.h new file mode 100644 index 0000000..6b8d11d --- /dev/null +++ b/remote_cmds/telnet.tproj/ring.h @@ -0,0 +1,107 @@ +/* + * 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. + * + * @(#)ring.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/contrib/telnet/telnet/ring.h,v 1.4 2001/11/30 22:28:07 markm Exp $ + */ + +#if defined(P) +# undef P +#endif + +#if defined(__STDC__) || defined(LINT_ARGS) +# define P(x) x +#else +# define P(x) () +#endif + +/* + * This defines a structure for a ring buffer. + * + * The circular buffer has two parts: + *((( + * full: [consume, supply) + * empty: [supply, consume) + *]]] + * + */ +typedef struct { + unsigned char *consume, /* where data comes out of */ + *supply, /* where data comes in to */ + *bottom, /* lowest address in buffer */ + *top, /* highest address+1 in buffer */ + *mark; /* marker (user defined) */ +#ifdef ENCRYPTION + unsigned char *clearto; /* Data to this point is clear text */ + unsigned char *encryyptedto; /* Data is encrypted to here */ +#endif /* ENCRYPTION */ + int size; /* size in bytes of buffer */ + u_long consumetime, /* help us keep straight full, empty, etc. */ + supplytime; +} Ring; + +/* Here are some functions and macros to deal with the ring buffer */ + +/* Initialization routine */ +extern int + ring_init(Ring *ring, unsigned char *buffer, int count); + +/* Data movement routines */ +extern void + ring_supply_data(Ring *ring, unsigned char *buffer, int count); +#ifdef notdef +extern void + ring_consume_data(Ring *ring, unsigned char *buffer, int count); +#endif + +/* Buffer state transition routines */ +extern void + ring_supplied(Ring *ring, int count), + ring_consumed(Ring *ring, int count); + +/* Buffer state query routines */ +extern int + ring_at_mark(Ring *), + ring_empty_count(Ring *ring), + ring_empty_consecutive(Ring *ring), + ring_full_count(Ring *ring), + ring_full_consecutive(Ring *ring); + +#ifdef ENCRYPTION +extern void + ring_encrypt(Ring *ring, void (*func)(unsigned char *, int)), + ring_clearto(Ring *ring); +#endif /* ENCRYPTION */ + +extern void + ring_clear_mark(Ring *), + ring_mark(Ring *); diff --git a/remote_cmds/telnet.tproj/sys_bsd.c b/remote_cmds/telnet.tproj/sys_bsd.c new file mode 100644 index 0000000..0c08223 --- /dev/null +++ b/remote_cmds/telnet.tproj/sys_bsd.c @@ -0,0 +1,1145 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)sys_bsd.c 8.4 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/sys_bsd.c,v 1.12 2003/05/04 02:54:48 obrien Exp $"); + +/* + * The following routines try to encapsulate what is system dependent + * (at least between 4.x and dos) which is used in telnet.c. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <arpa/telnet.h> + +#include "ring.h" +#include "fdset.h" +#include "defines.h" +#include "externs.h" +#include "types.h" + +int + tout, /* Output file descriptor */ + tin, /* Input file descriptor */ + net; + +#ifndef USE_TERMIO +struct tchars otc = { 0 }, ntc = { 0 }; +struct ltchars oltc = { 0 }, nltc = { 0 }; +struct sgttyb ottyb = { 0 }, nttyb = { 0 }; +int olmode = 0; +# define cfgetispeed(ptr) (ptr)->sg_ispeed +# define cfgetospeed(ptr) (ptr)->sg_ospeed +# define old_tc ottyb + +#else /* USE_TERMIO */ +struct termio old_tc = { 0, 0, 0, 0, {}, 0, 0 }; + +# ifndef TCSANOW +# ifdef TCSETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, (char *)t) +# define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD) +# ifdef CIBAUD +# define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT) +# else +# define cfgetispeed(ptr) cfgetospeed(ptr) +# endif +# endif /* TCSANOW */ +# ifdef sysV88 +# define TIOCFLUSH TC_PX_DRAIN +# endif +#endif /* USE_TERMIO */ + +static fd_set *ibitsp, *obitsp, *xbitsp; +int fdsn; + +#ifdef SIGINT +static SIG_FUNC_RET intr(int); +#endif /* SIGINT */ +#ifdef SIGQUIT +static SIG_FUNC_RET intr2(int); +#endif /* SIGQUIT */ +#ifdef SIGTSTP +static SIG_FUNC_RET susp(int); +#endif /* SIGTSTP */ +#ifdef SIGINFO +static SIG_FUNC_RET ayt(int); +#endif + +void +init_sys(void) +{ + tout = fileno(stdout); + tin = fileno(stdin); + errno = 0; +} + +int +TerminalWrite(char *buf, int n) +{ + return write(tout, buf, n); +} + +int +TerminalRead(char *buf, int n) +{ + return read(tin, buf, n); +} + +/* + * + */ + +int +TerminalAutoFlush(void) +{ +#if defined(LNOFLSH) + int flush; + + ioctl(0, TIOCLGET, (char *)&flush); + return !(flush&LNOFLSH); /* if LNOFLSH, no autoflush */ +#else /* LNOFLSH */ + return 1; +#endif /* LNOFLSH */ +} + +#ifdef KLUDGELINEMODE +extern int kludgelinemode; +#endif +/* + * TerminalSpecialChars() + * + * Look at an input character to see if it is a special character + * and decide what to do. + * + * Output: + * + * 0 Don't add this character. + * 1 Do add this character + */ + +int +TerminalSpecialChars(int c) +{ + if (c == termIntChar) { + intp(); + return 0; + } else if (c == termQuitChar) { +#ifdef KLUDGELINEMODE + if (kludgelinemode) + sendbrk(); + else +#endif + sendabort(); + return 0; + } else if (c == termEofChar) { + if (my_want_state_is_will(TELOPT_LINEMODE)) { + sendeof(); + return 0; + } + return 1; + } else if (c == termSuspChar) { + sendsusp(); + return(0); + } else if (c == termFlushChar) { + xmitAO(); /* Transmit Abort Output */ + return 0; + } else if (!MODE_LOCAL_CHARS(globalmode)) { + if (c == termKillChar) { + xmitEL(); + return 0; + } else if (c == termEraseChar) { + xmitEC(); /* Transmit Erase Character */ + return 0; + } + } + return 1; +} + + +/* + * Flush output to the terminal + */ + +void +TerminalFlushOutput(void) +{ +#ifdef TIOCFLUSH + (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0); +#else + (void) ioctl(fileno(stdout), TCFLSH, (char *) 0); +#endif +} + +void +TerminalSaveState(void) +{ +#ifndef USE_TERMIO + ioctl(0, TIOCGETP, (char *)&ottyb); + ioctl(0, TIOCGETC, (char *)&otc); + ioctl(0, TIOCGLTC, (char *)&oltc); + ioctl(0, TIOCLGET, (char *)&olmode); + + ntc = otc; + nltc = oltc; + nttyb = ottyb; + +#else /* USE_TERMIO */ + tcgetattr(0, &old_tc); + + new_tc = old_tc; + +#ifndef VDISCARD + termFlushChar = CONTROL('O'); +#endif +#ifndef VWERASE + termWerasChar = CONTROL('W'); +#endif +#ifndef VREPRINT + termRprntChar = CONTROL('R'); +#endif +#ifndef VLNEXT + termLiteralNextChar = CONTROL('V'); +#endif +#ifndef VSTART + termStartChar = CONTROL('Q'); +#endif +#ifndef VSTOP + termStopChar = CONTROL('S'); +#endif +#ifndef VSTATUS + termAytChar = CONTROL('T'); +#endif +#endif /* USE_TERMIO */ +} + +cc_t * +tcval(int func) +{ + switch(func) { + case SLC_IP: return(&termIntChar); + case SLC_ABORT: return(&termQuitChar); + case SLC_EOF: return(&termEofChar); + case SLC_EC: return(&termEraseChar); + case SLC_EL: return(&termKillChar); + case SLC_XON: return(&termStartChar); + case SLC_XOFF: return(&termStopChar); + case SLC_FORW1: return(&termForw1Char); +#ifdef USE_TERMIO + case SLC_FORW2: return(&termForw2Char); +# ifdef VDISCARD + case SLC_AO: return(&termFlushChar); +# endif +# ifdef VSUSP + case SLC_SUSP: return(&termSuspChar); +# endif +# ifdef VWERASE + case SLC_EW: return(&termWerasChar); +# endif +# ifdef VREPRINT + case SLC_RP: return(&termRprntChar); +# endif +# ifdef VLNEXT + case SLC_LNEXT: return(&termLiteralNextChar); +# endif +# ifdef VSTATUS + case SLC_AYT: return(&termAytChar); +# endif +#endif + + case SLC_SYNCH: + case SLC_BRK: + case SLC_EOR: + default: + return((cc_t *)0); + } +} + +void +TerminalDefaultChars(void) +{ +#ifndef USE_TERMIO + ntc = otc; + nltc = oltc; + nttyb.sg_kill = ottyb.sg_kill; + nttyb.sg_erase = ottyb.sg_erase; +#else /* USE_TERMIO */ + memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc)); +# ifndef VDISCARD + termFlushChar = CONTROL('O'); +# endif +# ifndef VWERASE + termWerasChar = CONTROL('W'); +# endif +# ifndef VREPRINT + termRprntChar = CONTROL('R'); +# endif +# ifndef VLNEXT + termLiteralNextChar = CONTROL('V'); +# endif +# ifndef VSTART + termStartChar = CONTROL('Q'); +# endif +# ifndef VSTOP + termStopChar = CONTROL('S'); +# endif +# ifndef VSTATUS + termAytChar = CONTROL('T'); +# endif +#endif /* USE_TERMIO */ +} + +/* + * TerminalNewMode - set up terminal to a specific mode. + * MODE_ECHO: do local terminal echo + * MODE_FLOW: do local flow control + * MODE_TRAPSIG: do local mapping to TELNET IAC sequences + * MODE_EDIT: do local line editing + * + * Command mode: + * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG + * local echo + * local editing + * local xon/xoff + * local signal mapping + * + * Linemode: + * local/no editing + * Both Linemode and Single Character mode: + * local/remote echo + * local/no xon/xoff + * local/no signal mapping + */ + +void +TerminalNewMode(int f) +{ + static int prevmode = 0; +#ifndef USE_TERMIO + struct tchars tc; + struct ltchars ltc; + struct sgttyb sb; + int lmode; +#else /* USE_TERMIO */ + struct termio tmp_tc; +#endif /* USE_TERMIO */ + int onoff; + int old; + cc_t esc; + + globalmode = f&~MODE_FORCE; + if (prevmode == f) + return; + + /* + * Write any outstanding data before switching modes + * ttyflush() returns 0 only when there is no more data + * left to write out, it returns -1 if it couldn't do + * anything at all, otherwise it returns 1 + the number + * of characters left to write. +#ifndef USE_TERMIO + * We would really like ask the kernel to wait for the output + * to drain, like we can do with the TCSADRAIN, but we don't have + * that option. The only ioctl that waits for the output to + * drain, TIOCSETP, also flushes the input queue, which is NOT + * what we want (TIOCSETP is like TCSADFLUSH). +#endif + */ + old = ttyflush(SYNCHing|flushout); + if (old < 0 || old > 1) { +#ifdef USE_TERMIO + tcgetattr(tin, &tmp_tc); +#endif /* USE_TERMIO */ + do { + /* + * Wait for data to drain, then flush again. + */ +#ifdef USE_TERMIO + tcsetattr(tin, TCSADRAIN, &tmp_tc); +#endif /* USE_TERMIO */ + old = ttyflush(SYNCHing|flushout); + } while (old < 0 || old > 1); + } + + old = prevmode; + prevmode = f&~MODE_FORCE; +#ifndef USE_TERMIO + sb = nttyb; + tc = ntc; + ltc = nltc; + lmode = olmode; +#else + tmp_tc = new_tc; +#endif + + if (f&MODE_ECHO) { +#ifndef USE_TERMIO + sb.sg_flags |= ECHO; +#else + tmp_tc.c_lflag |= ECHO; + tmp_tc.c_oflag |= ONLCR; + if (crlf) + tmp_tc.c_iflag |= ICRNL; +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags &= ~ECHO; +#else + tmp_tc.c_lflag &= ~ECHO; + tmp_tc.c_oflag &= ~ONLCR; +#endif + } + + if ((f&MODE_FLOW) == 0) { +#ifndef USE_TERMIO + tc.t_startc = _POSIX_VDISABLE; + tc.t_stopc = _POSIX_VDISABLE; +#else + tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ + } else { + if (restartany < 0) { + tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ + } else if (restartany > 0) { + tmp_tc.c_iflag |= IXOFF|IXON|IXANY; + } else { + tmp_tc.c_iflag |= IXOFF|IXON; + tmp_tc.c_iflag &= ~IXANY; + } +#endif + } + + if ((f&MODE_TRAPSIG) == 0) { +#ifndef USE_TERMIO + tc.t_intrc = _POSIX_VDISABLE; + tc.t_quitc = _POSIX_VDISABLE; + tc.t_eofc = _POSIX_VDISABLE; + ltc.t_suspc = _POSIX_VDISABLE; + ltc.t_dsuspc = _POSIX_VDISABLE; +#else + tmp_tc.c_lflag &= ~ISIG; +#endif + localchars = 0; + } else { +#ifdef USE_TERMIO + tmp_tc.c_lflag |= ISIG; +#endif + localchars = 1; + } + + if (f&MODE_EDIT) { +#ifndef USE_TERMIO + sb.sg_flags &= ~CBREAK; + sb.sg_flags |= CRMOD; +#else + tmp_tc.c_lflag |= ICANON; +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags |= CBREAK; + if (f&MODE_ECHO) + sb.sg_flags |= CRMOD; + else + sb.sg_flags &= ~CRMOD; +#else + tmp_tc.c_lflag &= ~ICANON; + tmp_tc.c_iflag &= ~ICRNL; + tmp_tc.c_cc[VMIN] = 1; + tmp_tc.c_cc[VTIME] = 0; +#endif + } + + if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { +#ifndef USE_TERMIO + ltc.t_lnextc = _POSIX_VDISABLE; +#else +# ifdef VLNEXT + tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE); +# endif +#endif + } + + if (f&MODE_SOFT_TAB) { +#ifndef USE_TERMIO + sb.sg_flags |= XTABS; +#else +# ifdef OXTABS + tmp_tc.c_oflag |= OXTABS; +# endif +# ifdef TABDLY + tmp_tc.c_oflag &= ~TABDLY; + tmp_tc.c_oflag |= TAB3; +# endif +#endif + } else { +#ifndef USE_TERMIO + sb.sg_flags &= ~XTABS; +#else +# ifdef OXTABS + tmp_tc.c_oflag &= ~OXTABS; +# endif +# ifdef TABDLY + tmp_tc.c_oflag &= ~TABDLY; +# endif +#endif + } + + if (f&MODE_LIT_ECHO) { +#ifndef USE_TERMIO + lmode &= ~LCTLECH; +#else +# ifdef ECHOCTL + tmp_tc.c_lflag &= ~ECHOCTL; +# endif +#endif + } else { +#ifndef USE_TERMIO + lmode |= LCTLECH; +#else +# ifdef ECHOCTL + tmp_tc.c_lflag |= ECHOCTL; +# endif +#endif + } + + if (f == -1) { + onoff = 0; + } else { +#ifndef USE_TERMIO + if (f & MODE_OUTBIN) + lmode |= LLITOUT; + else + lmode &= ~LLITOUT; + + if (f & MODE_INBIN) + lmode |= LPASS8; + else + lmode &= ~LPASS8; +#else + if (f & MODE_INBIN) + tmp_tc.c_iflag &= ~ISTRIP; + else + tmp_tc.c_iflag |= ISTRIP; + if (f & MODE_OUTBIN) { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= CS8; + tmp_tc.c_oflag &= ~OPOST; + } else { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); + tmp_tc.c_oflag |= OPOST; + } +#endif + onoff = 1; + } + + if (f != -1) { +#ifdef SIGINT + (void) signal(SIGINT, intr); +#endif +#ifdef SIGQUIT + (void) signal(SIGQUIT, intr2); +#endif +#ifdef SIGTSTP + (void) signal(SIGTSTP, susp); +#endif /* SIGTSTP */ +#ifdef SIGINFO + (void) signal(SIGINFO, ayt); +#endif +#if defined(USE_TERMIO) && defined(NOKERNINFO) + tmp_tc.c_lflag |= NOKERNINFO; +#endif + /* + * We don't want to process ^Y here. It's just another + * character that we'll pass on to the back end. It has + * to process it because it will be processed when the + * user attempts to read it, not when we send it. + */ +#ifndef USE_TERMIO + ltc.t_dsuspc = _POSIX_VDISABLE; +#else +# ifdef VDSUSP + tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); +# endif +#endif +#ifdef USE_TERMIO + /* + * If the VEOL character is already set, then use VEOL2, + * otherwise use VEOL. + */ + esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; + if ((tmp_tc.c_cc[VEOL] != esc) +# ifdef VEOL2 + && (tmp_tc.c_cc[VEOL2] != esc) +# endif + ) { + if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL] = esc; +# ifdef VEOL2 + else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL2] = esc; +# endif + } +#else + if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE)) + tc.t_brkc = esc; +#endif + } else { +#ifdef SIGINFO + (void) signal(SIGINFO, (void (*)(int))ayt_status); +#endif +#ifdef SIGINT + (void) signal(SIGINT, SIG_DFL); +#endif +#ifdef SIGQUIT + (void) signal(SIGQUIT, SIG_DFL); +#endif +#ifdef SIGTSTP + (void) signal(SIGTSTP, SIG_DFL); +# ifndef SOLARIS + (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); +# else /* SOLARIS */ + (void) sigrelse(SIGTSTP); +# endif /* SOLARIS */ +#endif /* SIGTSTP */ +#ifndef USE_TERMIO + ltc = oltc; + tc = otc; + sb = ottyb; + lmode = olmode; +#else + tmp_tc = old_tc; +#endif + } +#ifndef USE_TERMIO + ioctl(tin, TIOCLSET, (char *)&lmode); + ioctl(tin, TIOCSLTC, (char *)<c); + ioctl(tin, TIOCSETC, (char *)&tc); + ioctl(tin, TIOCSETN, (char *)&sb); +#else + if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) + tcsetattr(tin, TCSANOW, &tmp_tc); +#endif + + ioctl(tin, FIONBIO, (char *)&onoff); + ioctl(tout, FIONBIO, (char *)&onoff); + +} + +/* + * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD). + */ +#if B4800 != 4800 +#define DECODE_BAUD +#endif + +#ifdef DECODE_BAUD +#ifndef B7200 +#define B7200 B4800 +#endif + +#ifndef B14400 +#define B14400 B9600 +#endif + +#ifndef B19200 +# define B19200 B14400 +#endif + +#ifndef B28800 +#define B28800 B19200 +#endif + +#ifndef B38400 +# define B38400 B28800 +#endif + +#ifndef B57600 +#define B57600 B38400 +#endif + +#ifndef B76800 +#define B76800 B57600 +#endif + +#ifndef B115200 +#define B115200 B76800 +#endif + +#ifndef B230400 +#define B230400 B115200 +#endif + + +/* + * This code assumes that the values B0, B50, B75... + * are in ascending order. They do not have to be + * contiguous. + */ +struct termspeeds { + long speed; + long value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 7200, B7200 }, { 9600, B9600 }, + { 14400, B14400 }, { 19200, B19200 }, { 28800, B28800 }, + { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 }, + { 230400, B230400 }, { -1, B230400 } +}; +#endif /* DECODE_BAUD */ + +void +TerminalSpeeds(long *ispeed, long *ospeed) +{ +#ifdef DECODE_BAUD + struct termspeeds *tp; +#endif /* DECODE_BAUD */ + long in, out; + + out = cfgetospeed(&old_tc); + in = cfgetispeed(&old_tc); + if (in == 0) + in = out; + +#ifdef DECODE_BAUD + tp = termspeeds; + while ((tp->speed != -1) && (tp->value < in)) + tp++; + *ispeed = tp->speed; + + tp = termspeeds; + while ((tp->speed != -1) && (tp->value < out)) + tp++; + *ospeed = tp->speed; +#else /* DECODE_BAUD */ + *ispeed = in; + *ospeed = out; +#endif /* DECODE_BAUD */ +} + +int +TerminalWindowSize(long *rows, long *cols) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + + if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) { + *rows = ws.ws_row; + *cols = ws.ws_col; + return 1; + } +#endif /* TIOCGWINSZ */ + return 0; +} + +int +NetClose(int fd) +{ + return close(fd); +} + +static void +NetNonblockingIO(int fd, int onoff) +{ + ioctl(fd, FIONBIO, (char *)&onoff); +} + + +/* + * Various signal handling routines. + */ + +/* ARGSUSED */ +static SIG_FUNC_RET +deadpeer(int sig __unused) +{ + setcommandmode(); + longjmp(peerdied, -1); +} + +/* ARGSUSED */ +SIG_FUNC_RET +intr(int sig __unused) +{ + if (localchars) { + intp(); + return; + } + setcommandmode(); + longjmp(toplevel, -1); +} + +/* ARGSUSED */ +SIG_FUNC_RET +intr2(int sig __unused) +{ + if (localchars) { +#ifdef KLUDGELINEMODE + if (kludgelinemode) + sendbrk(); + else +#endif + sendabort(); + return; + } +} + +#ifdef SIGTSTP +/* ARGSUSED */ +SIG_FUNC_RET +susp(int sig __unused) +{ + if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) + return; + if (localchars) + sendsusp(); +} +#endif + +#ifdef SIGWINCH +/* ARGSUSED */ +static SIG_FUNC_RET +sendwin(int sig __unused) +{ + if (connected) { + sendnaws(); + } +} +#endif + +#ifdef SIGINFO +/* ARGSUSED */ +SIG_FUNC_RET +ayt(int sig __unused) +{ + if (connected) + sendayt(); + else + ayt_status(); +} +#endif + + +void +sys_telnet_init(void) +{ + (void) signal(SIGINT, intr); + (void) signal(SIGQUIT, intr2); + (void) signal(SIGPIPE, deadpeer); +#ifdef SIGWINCH + (void) signal(SIGWINCH, sendwin); +#endif +#ifdef SIGTSTP + (void) signal(SIGTSTP, susp); +#endif +#ifdef SIGINFO + (void) signal(SIGINFO, ayt); +#endif + + setconnmode(0); + + NetNonblockingIO(net, 1); + +#if defined(SO_OOBINLINE) + if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) { + perror("SetSockOpt"); + } +#endif /* defined(SO_OOBINLINE) */ +} + +/* + * Process rings - + * + * This routine tries to fill up/empty our various rings. + * + * The parameter specifies whether this is a poll operation, + * or a block-until-something-happens operation. + * + * The return value is 1 if something happened, 0 if not. + */ + +int +process_rings(int netin, int netout, int netex, int ttyin, int ttyout, int poll) +{ + int c; + int returnValue = 0; + static struct timeval TimeValue = { 0, 0 }; + int maxfd = -1; + int tmp; + + if ((netout || netin || netex) && net > maxfd) + maxfd = net; + + if (ttyout && tout > maxfd) + maxfd = tout; + if (ttyin && tin > maxfd) + maxfd = tin; + tmp = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); + if (tmp > fdsn) { + if (ibitsp) + free(ibitsp); + if (obitsp) + free(obitsp); + if (xbitsp) + free(xbitsp); + + fdsn = tmp; + if ((ibitsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + if ((obitsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + if ((xbitsp = (fd_set *)malloc(fdsn)) == NULL) + err(1, "malloc"); + memset(ibitsp, 0, fdsn); + memset(obitsp, 0, fdsn); + memset(xbitsp, 0, fdsn); + } + + if (netout) + FD_SET(net, obitsp); + if (ttyout) + FD_SET(tout, obitsp); + if (ttyin) + FD_SET(tin, ibitsp); + if (netin) + FD_SET(net, ibitsp); + if (netex) + FD_SET(net, xbitsp); + if ((c = select(maxfd + 1, ibitsp, obitsp, xbitsp, + (poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) { + if (c == -1) { + /* + * we can get EINTR if we are in line mode, + * and the user does an escape (TSTP), or + * some other signal generator. + */ + if (errno == EINTR) { + return 0; + } + /* I don't like this, does it ever happen? */ + printf("sleep(5) from telnet, after select: %s\r\n", strerror(errno)); + sleep(5); + } + return 0; + } + + /* + * Any urgent data? + */ + if (FD_ISSET(net, xbitsp)) { + FD_CLR(net, xbitsp); + SYNCHing = 1; + (void) ttyflush(1); /* flush already enqueued data */ + } + + /* + * Something to read from the network... + */ + if (FD_ISSET(net, ibitsp)) { + int canread; + + FD_CLR(net, ibitsp); + canread = ring_empty_consecutive(&netiring); +#if !defined(SO_OOBINLINE) + /* + * In 4.2 (and some early 4.3) 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; + static int bogus_oob = 0, first = 1; + + ioctl(net, SIOCATMARK, (char *)&atmark); + if (atmark) { + c = recv(net, netiring.supply, canread, MSG_OOB); + if ((c == -1) && (errno == EINVAL)) { + c = recv(net, netiring.supply, canread, 0); + if (clocks.didnetreceive < clocks.gotDM) { + SYNCHing = stilloob(net); + } + } else if (first && c > 0) { + /* + * Bogosity check. Systems based on 4.2BSD + * do not return an error if you do a second + * recv(MSG_OOB). So, we do one. If it + * succeeds and returns exactly the same + * data, then assume that we are running + * on a broken system and set the bogus_oob + * flag. (If the data was different, then + * we probably got some valid new data, so + * increment the count...) + */ + int i; + i = recv(net, netiring.supply + c, canread - c, MSG_OOB); + if (i == c && + memcmp(netiring.supply, netiring.supply + c, i) == 0) { + bogus_oob = 1; + first = 0; + } else if (i < 0) { + bogus_oob = 0; + first = 0; + } else + c += i; + } + if (bogus_oob && c > 0) { + int i; + /* + * Bogosity. We have to do the read + * to clear the atmark to get out of + * an infinate loop. + */ + i = read(net, netiring.supply + c, canread - c); + if (i > 0) + c += i; + } + } else { + c = recv(net, netiring.supply, canread, 0); + } + } else { + c = recv(net, netiring.supply, canread, 0); + } + settimer(didnetreceive); +#else /* !defined(SO_OOBINLINE) */ + c = recv(net, (char *)netiring.supply, canread, 0); +#endif /* !defined(SO_OOBINLINE) */ + if (c < 0 && errno == EWOULDBLOCK) { + c = 0; + } else if (c <= 0) { + return -1; + } + if (netdata) { + Dump('<', netiring.supply, c); + } + if (c) + ring_supplied(&netiring, c); + returnValue = 1; + } + + /* + * Something to read from the tty... + */ + if (FD_ISSET(tin, ibitsp)) { + FD_CLR(tin, ibitsp); + c = TerminalRead((char*)ttyiring.supply, ring_empty_consecutive(&ttyiring)); + if (c < 0 && errno == EIO) + c = 0; + if (c < 0 && errno == EWOULDBLOCK) { + c = 0; + } else { + /* EOF detection for line mode!!!! */ + if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { + /* must be an EOF... */ + *ttyiring.supply = termEofChar; + c = 1; + } + if (c <= 0) { + return -1; + } + if (termdata) { + Dump('<', ttyiring.supply, c); + } + ring_supplied(&ttyiring, c); + } + returnValue = 1; /* did something useful */ + } + + if (FD_ISSET(net, obitsp)) { + FD_CLR(net, obitsp); + returnValue |= netflush(); + } + if (FD_ISSET(tout, obitsp)) { + FD_CLR(tout, obitsp); + returnValue |= (ttyflush(SYNCHing|flushout) > 0); + } + + return returnValue; +} diff --git a/remote_cmds/telnet.tproj/telnet.1 b/remote_cmds/telnet.tproj/telnet.1 new file mode 100644 index 0000000..46d129d --- /dev/null +++ b/remote_cmds/telnet.tproj/telnet.1 @@ -0,0 +1,1417 @@ +.\" 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: src/crypto/telnet/telnet/telnet.1,v 1.4.2.9 2002/04/13 10:59:08 markm Exp $ +.\" +.Dd January 27, 2000 +.Dt TELNET 1 +.Os +.Sh NAME +.Nm telnet +.Nd user interface to the +.Tn TELNET +protocol +.Sh SYNOPSIS +.Nm +.Op Fl 468EFKLNacdfruxy +.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 Dq Li 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 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.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. +This is now the default, so this option is ignored. +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 u +Forces +.Nm +to use +.Dv AF_UNIX +addresses only (e.g., +.Ux +domain sockets, accessed with a file path). +.It Fl x +Turns on encryption of the data stream if possible. +This is now the default, so this option is ignored. +.It Fl y +Suppresses encryption of the data stream. +.It Ar host +Indicates the official name, an alias, or the Internet address +of a remote host. +If +.Ar host +starts with a +.Ql / , +.Nm +establishes a connection to the corresponding named socket. +.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 +Valid arguments for the +.Ic encrypt +command are: +.Bl -tag -width Ar +.It Ic disable Ar type Xo +.Op Cm input | output +.Xc +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 Xo +.Op Cm input | output +.Xc +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 Op Cm 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 Op Cm 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 may be sent through the +.Dv TELNET ENVIRON +option. +The initial set of variables is populated with the +contents of the following environment variables, if +present: +.Ev USER , PRINTER , DISPLAY , TERM , COLUMNS , LINES. +Only the first three are exported, by default. +.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 . +If value is empty, the value is taken from the environment variable. +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 +.Op Oo Fl Oc Ns Ar port +.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 ) , +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 . +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 opie Ar sequence challenge +The +.Ic opie +command computes a response to the OPIE 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 . +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. +.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 <CR><LF> . +If this is +.Dv FALSE , +then carriage returns will be send as +.Li <CR><NUL> . +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 . +.It Ic ?\& +Displays the legal +.Ic toggle +commands. +.El +.It Ic z +Suspend +.Nm . +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 +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/remote_cmds/telnet.tproj/telnet.c b/remote_cmds/telnet.tproj/telnet.c new file mode 100644 index 0000000..541cf9b --- /dev/null +++ b/remote_cmds/telnet.tproj/telnet.c @@ -0,0 +1,2418 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)telnet.c 8.4 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/telnet.c,v 1.16 2005/03/28 14:45:12 nectar Exp $"); + +#include <sys/types.h> + +/* By the way, we need to include curses.h before telnet.h since, + * among other things, telnet.h #defines 'DO', which is a variable + * declared in curses.h. + */ + +#include <ctype.h> +#include <curses.h> +#include <signal.h> +#include <stdlib.h> +#include <term.h> +#include <unistd.h> +#include <arpa/telnet.h> + +#include "ring.h" + +#include "defines.h" +#include "externs.h" +#include "types.h" +#include "general.h" + +#ifdef TERMCAP +#include <curses.h> +#include <term.h> +#endif /* TERMCAP */ + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif +#include <libtelnet/misc.h> + +#define strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x)) + +static unsigned char subbuffer[SUBBUFSIZE], + *subpointer, *subend; /* buffer for sub-options */ +#define SB_CLEAR() subpointer = subbuffer; +#define SB_TERM() { subend = subpointer; SB_CLEAR(); } +#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ + *subpointer++ = (c); \ + } + +#define SB_GET() ((*subpointer++)&0xff) +#define SB_PEEK() ((*subpointer)&0xff) +#define SB_EOF() (subpointer >= subend) +#define SB_LEN() (subend - subpointer) + +char options[256]; /* The combined options */ +char do_dont_resp[256]; +char will_wont_resp[256]; + +int + eight = 0, + autologin = 0, /* Autologin anyone? */ + skiprc = 0, + connected, + showoptions, + ISend, /* trying to send network data in */ + telnet_debug = 0, + crmod, + netdata, /* Print out network data flow */ + crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ + telnetport, + SYNCHing, /* we are in TELNET SYNCH mode */ + flushout, /* flush output */ + autoflush = 0, /* flush output when interrupting? */ + autosynch, /* send interrupt characters with SYNCH? */ + localflow, /* we handle flow control locally */ + restartany, /* if flow control enabled, restart on any character */ + localchars, /* we recognize interrupt/quit */ + donelclchars, /* the user has set "localchars" */ + donebinarytoggle, /* the user has put us in binary */ + dontlecho, /* do we suppress local echoing right now? */ + globalmode, + doaddrlookup = 1, /* do a reverse address lookup? */ + clienteof = 0; + +char *prompt = 0; +#ifdef ENCRYPTION +char *line; /* hack around breakage in sra.c :-( !! */ +#endif + +cc_t escape; +cc_t rlogin; +#ifdef KLUDGELINEMODE +cc_t echoc; +#endif + +/* + * Telnet receiver states for fsm + */ +#define TS_DATA 0 +#define TS_IAC 1 +#define TS_WILL 2 +#define TS_WONT 3 +#define TS_DO 4 +#define TS_DONT 5 +#define TS_CR 6 +#define TS_SB 7 /* sub-option collection */ +#define TS_SE 8 /* looking for sub-option end */ + +static int telrcv_state; +#ifdef OLD_ENVIRON +unsigned char telopt_environ = TELOPT_NEW_ENVIRON; +#else +# define telopt_environ TELOPT_NEW_ENVIRON +#endif + +jmp_buf toplevel; +jmp_buf peerdied; + +int flushline; +int linemode; + +#ifdef KLUDGELINEMODE +int kludgelinemode = 1; +#endif + +static int is_unique(char *, char **, char **); + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +Clocks clocks; + +/* + * Initialize telnet environment. + */ + +void +init_telnet(void) +{ + env_init(); + + SB_CLEAR(); + ClearArray(options); + + connected = ISend = localflow = donebinarytoggle = 0; +#ifdef AUTHENTICATION +#ifdef ENCRYPTION + auth_encrypt_connect(connected); +#endif +#endif + restartany = -1; + + SYNCHing = 0; + + /* Don't change NetTrace */ + + escape = CONTROL(']'); + rlogin = _POSIX_VDISABLE; +#ifdef KLUDGELINEMODE + echoc = CONTROL('E'); +#endif + + flushline = 1; + telrcv_state = TS_DATA; +} + + +/* + * These routines are in charge of sending option negotiations + * to the other side. + * + * The basic idea is that we send the negotiation if either side + * is in disagreement as to what the current state should be. + */ + +void +send_do(int c, int init) +{ + if (init) { + if (((do_dont_resp[c] == 0) && my_state_is_do(c)) || + my_want_state_is_do(c)) + return; + set_my_want_state_do(c); + do_dont_resp[c]++; + } + if (telnetport < 0) + return; + NET2ADD(IAC, DO); + NETADD(c); + printoption("SENT", DO, c); +} + +void +send_dont(int c, int init) +{ + if (init) { + if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) || + my_want_state_is_dont(c)) + return; + set_my_want_state_dont(c); + do_dont_resp[c]++; + } + if (telnetport < 0) + return; + NET2ADD(IAC, DONT); + NETADD(c); + printoption("SENT", DONT, c); +} + +void +send_will(int c, int init) +{ + if (init) { + if (((will_wont_resp[c] == 0) && my_state_is_will(c)) || + my_want_state_is_will(c)) + return; + set_my_want_state_will(c); + will_wont_resp[c]++; + } + if (telnetport < 0) + return; + NET2ADD(IAC, WILL); + NETADD(c); + printoption("SENT", WILL, c); +} + +void +send_wont(int c, int init) +{ + if (init) { + if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) || + my_want_state_is_wont(c)) + return; + set_my_want_state_wont(c); + will_wont_resp[c]++; + } + if (telnetport < 0) + return; + NET2ADD(IAC, WONT); + NETADD(c); + printoption("SENT", WONT, c); +} + +void +willoption(int option) +{ + int new_state_ok = 0; + + if (do_dont_resp[option]) { + --do_dont_resp[option]; + if (do_dont_resp[option] && my_state_is_do(option)) + --do_dont_resp[option]; + } + + if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) { + + switch (option) { + + case TELOPT_ECHO: + case TELOPT_BINARY: + case TELOPT_SGA: + settimer(modenegotiated); + /* FALLTHROUGH */ + case TELOPT_STATUS: +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: +#endif +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: +#endif /* ENCRYPTION */ + new_state_ok = 1; + break; + + case TELOPT_TM: + if (flushout) + flushout = 0; + /* + * Special case for TM. If we get back a WILL, + * pretend we got back a WONT. + */ + set_my_want_state_dont(option); + set_my_state_dont(option); + return; /* Never reply to TM will's/wont's */ + + case TELOPT_LINEMODE: + default: + break; + } + + if (new_state_ok) { + set_my_want_state_do(option); + send_do(option, 0); + setconnmode(0); /* possibly set new tty mode */ + } else { + do_dont_resp[option]++; + send_dont(option, 0); + } + } + set_my_state_do(option); +#ifdef ENCRYPTION + if (option == TELOPT_ENCRYPT) + encrypt_send_support(); +#endif /* ENCRYPTION */ +} + +void +wontoption(int option) +{ + if (do_dont_resp[option]) { + --do_dont_resp[option]; + if (do_dont_resp[option] && my_state_is_dont(option)) + --do_dont_resp[option]; + } + + if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) { + + switch (option) { + +#ifdef KLUDGELINEMODE + case TELOPT_SGA: + if (!kludgelinemode) + break; + /* FALLTHROUGH */ +#endif + case TELOPT_ECHO: + settimer(modenegotiated); + break; + + case TELOPT_TM: + if (flushout) + flushout = 0; + set_my_want_state_dont(option); + set_my_state_dont(option); + return; /* Never reply to TM will's/wont's */ + + default: + break; + } + set_my_want_state_dont(option); + if (my_state_is_do(option)) + send_dont(option, 0); + setconnmode(0); /* Set new tty mode */ + } else if (option == TELOPT_TM) { + /* + * Special case for TM. + */ + if (flushout) + flushout = 0; + set_my_want_state_dont(option); + } + set_my_state_dont(option); +} + +static void +dooption(int option) +{ + int new_state_ok = 0; + + if (will_wont_resp[option]) { + --will_wont_resp[option]; + if (will_wont_resp[option] && my_state_is_will(option)) + --will_wont_resp[option]; + } + + if (will_wont_resp[option] == 0) { + if (my_want_state_is_wont(option)) { + + switch (option) { + + case TELOPT_TM: + /* + * Special case for TM. We send a WILL, but pretend + * we sent WONT. + */ + send_will(option, 0); + set_my_want_state_wont(TELOPT_TM); + set_my_state_wont(TELOPT_TM); + return; + + case TELOPT_BINARY: /* binary mode */ + case TELOPT_NAWS: /* window size */ + case TELOPT_TSPEED: /* terminal speed */ + case TELOPT_LFLOW: /* local flow control */ + case TELOPT_TTYPE: /* terminal type option */ + case TELOPT_SGA: /* no big deal */ +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: /* encryption variable option */ +#endif /* ENCRYPTION */ + new_state_ok = 1; + break; + + case TELOPT_NEW_ENVIRON: /* New environment variable option */ +#ifdef OLD_ENVIRON + if (my_state_is_will(TELOPT_OLD_ENVIRON)) + send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */ + goto env_common; + case TELOPT_OLD_ENVIRON: /* Old environment variable option */ + if (my_state_is_will(TELOPT_NEW_ENVIRON)) + break; /* Don't enable if new one is in use! */ + env_common: + telopt_environ = option; +#endif + new_state_ok = 1; + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + if (autologin) + new_state_ok = 1; + break; +#endif + + case TELOPT_XDISPLOC: /* X Display location */ + if (env_getvalue((const unsigned char *)"DISPLAY")) + new_state_ok = 1; + break; + + case TELOPT_LINEMODE: +#ifdef KLUDGELINEMODE + kludgelinemode = 0; + send_do(TELOPT_SGA, 1); +#endif + set_my_want_state_will(TELOPT_LINEMODE); + send_will(option, 0); + set_my_state_will(TELOPT_LINEMODE); + slc_init(); + return; + + case TELOPT_ECHO: /* We're never going to echo... */ + default: + break; + } + + if (new_state_ok) { + set_my_want_state_will(option); + send_will(option, 0); + setconnmode(0); /* Set new tty mode */ + } else { + will_wont_resp[option]++; + send_wont(option, 0); + } + } else { + /* + * Handle options that need more things done after the + * other side has acknowledged the option. + */ + switch (option) { + case TELOPT_LINEMODE: +#ifdef KLUDGELINEMODE + kludgelinemode = 0; + send_do(TELOPT_SGA, 1); +#endif + set_my_state_will(option); + slc_init(); + send_do(TELOPT_SGA, 0); + return; + } + } + } + set_my_state_will(option); +} + +static void +dontoption(int option) +{ + + if (will_wont_resp[option]) { + --will_wont_resp[option]; + if (will_wont_resp[option] && my_state_is_wont(option)) + --will_wont_resp[option]; + } + + if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) { + switch (option) { + case TELOPT_LINEMODE: + linemode = 0; /* put us back to the default state */ + break; +#ifdef OLD_ENVIRON + case TELOPT_NEW_ENVIRON: + /* + * The new environ option wasn't recognized, try + * the old one. + */ + send_will(TELOPT_OLD_ENVIRON, 1); + telopt_environ = TELOPT_OLD_ENVIRON; + break; +#endif + } + /* we always accept a DONT */ + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + setconnmode(0); /* Set new tty mode */ + } + set_my_state_wont(option); +} + +/* + * Given a buffer returned by tgetent(), this routine will turn + * the pipe separated list of names in the buffer into an array + * of pointers to null terminated names. We toss out any bad, + * duplicate, or verbose names (names with spaces). + */ + +static const char *name_unknown = "UNKNOWN"; +static const char *unknown[] = { NULL, NULL }; + +static const char ** +mklist(char *buf, char *name) +{ + int n; + char c, *cp, **argvp, *cp2, **argv, **avt; + + if (name) { + if (strlen(name) > 40) { + name = 0; + unknown[0] = name_unknown; + } else { + unknown[0] = name; + upcase(name); + } + } else + unknown[0] = name_unknown; + /* + * Count up the number of names. + */ + for (n = 1, cp = buf; *cp && *cp != ':'; cp++) { + if (*cp == '|') + n++; + } + /* + * Allocate an array to put the name pointers into + */ + argv = (char **)malloc((n+3)*sizeof(char *)); + if (argv == 0) + return(unknown); + + /* + * Fill up the array of pointers to names. + */ + *argv = 0; + argvp = argv+1; + n = 0; + for (cp = cp2 = buf; (c = *cp); cp++) { + if (c == '|' || c == ':') { + *cp++ = '\0'; + /* + * Skip entries that have spaces or are over 40 + * characters long. If this is our environment + * name, then put it up front. Otherwise, as + * long as this is not a duplicate name (case + * insensitive) add it to the list. + */ + if (n || (cp - cp2 > 41)) + ; + else if (name && (strncasecmp(name, cp2, cp-cp2) == 0)) + *argv = cp2; + else if (is_unique(cp2, argv+1, argvp)) + *argvp++ = cp2; + if (c == ':') + break; + /* + * Skip multiple delimiters. Reset cp2 to + * the beginning of the next name. Reset n, + * the flag for names with spaces. + */ + while ((c = *cp) == '|') + cp++; + cp2 = cp; + n = 0; + } + /* + * Skip entries with spaces or non-ascii values. + * Convert lower case letters to upper case. + */ + if ((c == ' ') || !isascii(c)) + n = 1; + else if (islower(c)) + *cp = toupper(c); + } + + /* + * Check for an old V6 2 character name. If the second + * name points to the beginning of the buffer, and is + * only 2 characters long, move it to the end of the array. + */ + if ((argv[1] == buf) && (strlen(argv[1]) == 2)) { + --argvp; + for (avt = &argv[1]; avt < argvp; avt++) + *avt = *(avt+1); + *argvp++ = buf; + } + + /* + * Duplicate last name, for TTYPE option, and null + * terminate the array. If we didn't find a match on + * our terminal name, put that name at the beginning. + */ + cp = *(argvp-1); + *argvp++ = cp; + *argvp = 0; + + if (*argv == 0) { + if (name) + *argv = name; + else { + --argvp; + for (avt = argv; avt < argvp; avt++) + *avt = *(avt+1); + } + } + if (*argv) + return((const char **)argv); + else + return(unknown); +} + +static int +is_unique(char *name, char **as, char **ae) +{ + char **ap; + int n; + + n = strlen(name) + 1; + for (ap = as; ap < ae; ap++) + if (strncasecmp(*ap, name, n) == 0) + return(0); + return (1); +} + +#ifdef TERMCAP +char termbuf[1024]; + +/*ARGSUSED*/ +#ifdef __APPLE__ +__private_extern__ int +#else +static int +#endif +setupterm(char *tname, int fd, int *errp) +{ + if (tgetent(termbuf, tname) == 1) { + termbuf[1023] = '\0'; + if (errp) + *errp = 1; + return(0); + } + if (errp) + *errp = 0; + return(-1); +} +#else +#define termbuf ttytype +extern char ttytype[]; +#endif + +int resettermname = 1; + +static const char * +gettermname(void) +{ + char *tname; + static const char **tnamep = 0; + static const char **next; + int err; + + if (resettermname) { + resettermname = 0; + if (tnamep && tnamep != unknown) + free(tnamep); + if ((tname = (char *)env_getvalue((const unsigned char *)"TERM")) && + (setupterm(tname, 1, &err) == 0)) { + tnamep = mklist(termbuf, tname); + } else { + if (tname && (strlen(tname) <= 40)) { + unknown[0] = tname; + upcase(tname); + } else + unknown[0] = name_unknown; + tnamep = unknown; + } + next = tnamep; + } + if (*next == 0) + next = tnamep; + return(*next++); +} +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type, send request. + * Terminal speed (send request). + * Local flow control (is request). + * Linemode + */ + +static void +suboption(void) +{ + unsigned char subchar; + + printsub('<', subbuffer, SB_LEN()+2); + switch (subchar = SB_GET()) { + case TELOPT_TTYPE: + if (my_want_state_is_wont(TELOPT_TTYPE)) + return; + if (SB_EOF() || SB_GET() != TELQUAL_SEND) { + return; + } else { + const char *name; + unsigned char temp[50]; + int len; + + name = gettermname(); + len = strlen(name) + 4 + 2; + if (len < NETROOM()) { + sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, + TELQUAL_IS, name, IAC, SE); + ring_supply_data(&netoring, temp, len); + printsub('>', &temp[2], len-2); + } else { + ExitString("No room in buffer for terminal type.\n", 1); + /*NOTREACHED*/ + } + } + break; + case TELOPT_TSPEED: + if (my_want_state_is_wont(TELOPT_TSPEED)) + return; + if (SB_EOF()) + return; + if (SB_GET() == TELQUAL_SEND) { + long ospeed, ispeed; + unsigned char temp[50]; + int len; + + TerminalSpeeds(&ispeed, &ospeed); + + sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED, + TELQUAL_IS, ospeed, ispeed, IAC, SE); + len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ + + if (len < NETROOM()) { + ring_supply_data(&netoring, temp, len); + printsub('>', temp+2, len - 2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + } + break; + case TELOPT_LFLOW: + if (my_want_state_is_wont(TELOPT_LFLOW)) + return; + if (SB_EOF()) + return; + switch(SB_GET()) { + case LFLOW_RESTART_ANY: + restartany = 1; + break; + case LFLOW_RESTART_XON: + restartany = 0; + break; + case LFLOW_ON: + localflow = 1; + break; + case LFLOW_OFF: + localflow = 0; + break; + default: + return; + } + setcommandmode(); + setconnmode(0); + break; + + case TELOPT_LINEMODE: + if (my_want_state_is_wont(TELOPT_LINEMODE)) + return; + if (SB_EOF()) + return; + switch (SB_GET()) { + case WILL: + lm_will(subpointer, SB_LEN()); + break; + case WONT: + lm_wont(subpointer, SB_LEN()); + break; + case DO: + lm_do(subpointer, SB_LEN()); + break; + case DONT: + lm_dont(subpointer, SB_LEN()); + break; + case LM_SLC: + slc(subpointer, SB_LEN()); + break; + case LM_MODE: + lm_mode(subpointer, SB_LEN(), 0); + break; + default: + break; + } + break; + +#ifdef OLD_ENVIRON + case TELOPT_OLD_ENVIRON: +#endif + case TELOPT_NEW_ENVIRON: + if (SB_EOF()) + return; + switch(SB_PEEK()) { + case TELQUAL_IS: + case TELQUAL_INFO: + if (my_want_state_is_dont(subchar)) + return; + break; + case TELQUAL_SEND: + if (my_want_state_is_wont(subchar)) { + return; + } + break; + default: + return; + } + env_opt(subpointer, SB_LEN()); + break; + + case TELOPT_XDISPLOC: + if (my_want_state_is_wont(TELOPT_XDISPLOC)) + return; + if (SB_EOF()) + return; + if (SB_GET() == TELQUAL_SEND) { + unsigned char temp[50], *dp; + int len; + + if ((dp = env_getvalue((const unsigned char *)"DISPLAY")) == NULL || + strlen((const char *)dp) > sizeof(temp) - 7) { + /* + * Something happened, we no longer have a DISPLAY + * variable. Or it is too long. So, turn off the option. + */ + send_wont(TELOPT_XDISPLOC, 1); + break; + } + snprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", IAC, SB, + TELOPT_XDISPLOC, TELQUAL_IS, dp, IAC, SE); + len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ + + if (len < NETROOM()) { + ring_supply_data(&netoring, temp, len); + printsub('>', temp+2, len - 2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + } + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: { + if (!autologin) + break; + if (SB_EOF()) + return; + switch(SB_GET()) { + case TELQUAL_IS: + if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) + return; + auth_is(subpointer, SB_LEN()); + break; + case TELQUAL_SEND: + if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) + return; + auth_send(subpointer, SB_LEN()); + break; + case TELQUAL_REPLY: + if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) + return; + auth_reply(subpointer, SB_LEN()); + break; + case TELQUAL_NAME: + if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) + return; + auth_name(subpointer, SB_LEN()); + break; + } + } + break; +#endif +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + if (SB_EOF()) + return; + switch(SB_GET()) { + case ENCRYPT_START: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_start(subpointer, SB_LEN()); + break; + case ENCRYPT_END: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_end(); + break; + case ENCRYPT_SUPPORT: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_support(subpointer, SB_LEN()); + break; + case ENCRYPT_REQSTART: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_request_start(subpointer, SB_LEN()); + break; + case ENCRYPT_REQEND: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + /* + * We can always send an REQEND so that we cannot + * get stuck encrypting. We should only get this + * if we have been able to get in the correct mode + * anyhow. + */ + encrypt_request_end(); + break; + case ENCRYPT_IS: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_is(subpointer, SB_LEN()); + break; + case ENCRYPT_REPLY: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_reply(subpointer, SB_LEN()); + break; + case ENCRYPT_ENC_KEYID: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_enc_keyid(subpointer, SB_LEN()); + break; + case ENCRYPT_DEC_KEYID: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_dec_keyid(subpointer, SB_LEN()); + break; + default: + break; + } + break; +#endif /* ENCRYPTION */ + default: + break; + } +} + +static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE }; + +void +lm_will(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_will: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: /* We shouldn't ever get this... */ + default: + str_lm[3] = DONT; + str_lm[4] = cmd[0]; + if (NETROOM() > (int)sizeof(str_lm)) { + ring_supply_data(&netoring, str_lm, sizeof(str_lm)); + printsub('>', &str_lm[2], sizeof(str_lm)-2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + break; + } +} + +void +lm_wont(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_wont: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: /* We shouldn't ever get this... */ + default: + /* We are always DONT, so don't respond */ + return; + } +} + +void +lm_do(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_do: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: + default: + str_lm[3] = WONT; + str_lm[4] = cmd[0]; + if (NETROOM() > (int)sizeof(str_lm)) { + ring_supply_data(&netoring, str_lm, sizeof(str_lm)); + printsub('>', &str_lm[2], sizeof(str_lm)-2); + } +/*@*/ else printf("lm_do: not enough room in buffer\n"); + break; + } +} + +void +lm_dont(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_dont: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: + default: + /* we are always WONT, so don't respond */ + break; + } +} + +static unsigned char str_lm_mode[] = { + IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE +}; + +void +lm_mode(unsigned char *cmd, int len, int init) +{ + if (len != 1) + return; + if ((linemode&MODE_MASK&~MODE_ACK) == *cmd) + return; + if (*cmd&MODE_ACK) + return; + linemode = *cmd&(MODE_MASK&~MODE_ACK); + str_lm_mode[4] = linemode; + if (!init) + str_lm_mode[4] |= MODE_ACK; + if (NETROOM() > (int)sizeof(str_lm_mode)) { + ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode)); + printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2); + } +/*@*/ else printf("lm_mode: not enough room in buffer\n"); + setconnmode(0); /* set changed mode */ +} + + + +/* + * slc() + * Handle special character suboption of LINEMODE. + */ + +struct spc { + cc_t val; + cc_t *valp; + char flags; /* Current flags & level */ + char mylevel; /* Maximum level & flags */ +} spc_data[NSLC+1]; + +#define SLC_IMPORT 0 +#define SLC_EXPORT 1 +#define SLC_RVALUE 2 +static int slc_mode = SLC_EXPORT; + +void +slc_init(void) +{ + struct spc *spcp; + + localchars = 1; + for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) { + spcp->val = 0; + spcp->valp = 0; + spcp->flags = spcp->mylevel = SLC_NOSUPPORT; + } + +#define initfunc(func, flags) { \ + spcp = &spc_data[func]; \ + if ((spcp->valp = tcval(func))) { \ + spcp->val = *spcp->valp; \ + spcp->mylevel = SLC_VARIABLE|flags; \ + } else { \ + spcp->val = 0; \ + spcp->mylevel = SLC_DEFAULT; \ + } \ + } + + initfunc(SLC_SYNCH, 0); + /* No BRK */ + initfunc(SLC_AO, 0); + initfunc(SLC_AYT, 0); + /* No EOR */ + initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT); + initfunc(SLC_EOF, 0); +#ifndef SYSV_TERMIO + initfunc(SLC_SUSP, SLC_FLUSHIN); +#endif + initfunc(SLC_EC, 0); + initfunc(SLC_EL, 0); +#ifndef SYSV_TERMIO + initfunc(SLC_EW, 0); + initfunc(SLC_RP, 0); + initfunc(SLC_LNEXT, 0); +#endif + initfunc(SLC_XON, 0); + initfunc(SLC_XOFF, 0); +#ifdef SYSV_TERMIO + spc_data[SLC_XON].mylevel = SLC_CANTCHANGE; + spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE; +#endif + initfunc(SLC_FORW1, 0); +#ifdef USE_TERMIO + initfunc(SLC_FORW2, 0); + /* No FORW2 */ +#endif + + initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT); +#undef initfunc + + if (slc_mode == SLC_EXPORT) + slc_export(); + else + slc_import(1); + +} + +void +slcstate(void) +{ + printf("Special characters are %s values\n", + slc_mode == SLC_IMPORT ? "remote default" : + slc_mode == SLC_EXPORT ? "local" : + "remote"); +} + +void +slc_mode_export(void) +{ + slc_mode = SLC_EXPORT; + if (my_state_is_will(TELOPT_LINEMODE)) + slc_export(); +} + +void +slc_mode_import(int def) +{ + slc_mode = def ? SLC_IMPORT : SLC_RVALUE; + if (my_state_is_will(TELOPT_LINEMODE)) + slc_import(def); +} + +unsigned char slc_import_val[] = { + IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE +}; +unsigned char slc_import_def[] = { + IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE +}; + +void +slc_import(int def) +{ + if (NETROOM() > (int)sizeof(slc_import_val)) { + if (def) { + ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def)); + printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2); + } else { + ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val)); + printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2); + } + } +/*@*/ else printf("slc_import: not enough room\n"); +} + +void +slc_export(void) +{ + struct spc *spcp; + + TerminalDefaultChars(); + + slc_start_reply(); + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (spcp->mylevel != SLC_NOSUPPORT) { + if (spcp->val == (cc_t)(_POSIX_VDISABLE)) + spcp->flags = SLC_NOSUPPORT; + else + spcp->flags = spcp->mylevel; + if (spcp->valp) + spcp->val = *spcp->valp; + slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); + } + } + slc_end_reply(); + (void)slc_update(); + setconnmode(1); /* Make sure the character values are set */ +} + +void +slc(unsigned char *cp, int len) +{ + struct spc *spcp; + int func,level; + + slc_start_reply(); + + for (; len >= 3; len -=3, cp +=3) { + + func = cp[SLC_FUNC]; + + if (func == 0) { + /* + * Client side: always ignore 0 function. + */ + continue; + } + if (func > NSLC) { + if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT) + slc_add_reply(func, SLC_NOSUPPORT, 0); + continue; + } + + spcp = &spc_data[func]; + + level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK); + + if ((cp[SLC_VALUE] == (unsigned char)spcp->val) && + ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) { + continue; + } + + if (level == (SLC_DEFAULT|SLC_ACK)) { + /* + * This is an error condition, the SLC_ACK + * bit should never be set for the SLC_DEFAULT + * level. Our best guess to recover is to + * ignore the SLC_ACK bit. + */ + cp[SLC_FLAGS] &= ~SLC_ACK; + } + + if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) { + spcp->val = (cc_t)cp[SLC_VALUE]; + spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */ + continue; + } + + level &= ~SLC_ACK; + + if (level <= (spcp->mylevel&SLC_LEVELBITS)) { + spcp->flags = cp[SLC_FLAGS]|SLC_ACK; + spcp->val = (cc_t)cp[SLC_VALUE]; + } + if (level == SLC_DEFAULT) { + if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT) + spcp->flags = spcp->mylevel; + else + spcp->flags = SLC_NOSUPPORT; + } + slc_add_reply(func, spcp->flags, spcp->val); + } + slc_end_reply(); + if (slc_update()) + setconnmode(1); /* set the new character values */ +} + +void +slc_check(void) +{ + struct spc *spcp; + + slc_start_reply(); + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (spcp->valp && spcp->val != *spcp->valp) { + spcp->val = *spcp->valp; + if (spcp->val == (cc_t)(_POSIX_VDISABLE)) + spcp->flags = SLC_NOSUPPORT; + else + spcp->flags = spcp->mylevel; + slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); + } + } + slc_end_reply(); + setconnmode(1); +} + +unsigned char slc_reply[128]; +unsigned char const * const slc_reply_eom = &slc_reply[sizeof(slc_reply)]; +unsigned char *slc_replyp; + +void +slc_start_reply(void) +{ + slc_replyp = slc_reply; + *slc_replyp++ = IAC; + *slc_replyp++ = SB; + *slc_replyp++ = TELOPT_LINEMODE; + *slc_replyp++ = LM_SLC; +} + +void +slc_add_reply(unsigned char func, unsigned char flags, cc_t value) +{ + /* A sequence of up to 6 bytes my be written for this member of the SLC + * suboption list by this function. The end of negotiation command, + * which is written by slc_end_reply(), will require 2 additional + * bytes. Do not proceed unless there is sufficient space for these + * items. + */ + if (&slc_replyp[6+2] > slc_reply_eom) + return; + if ((*slc_replyp++ = func) == IAC) + *slc_replyp++ = IAC; + if ((*slc_replyp++ = flags) == IAC) + *slc_replyp++ = IAC; + if ((*slc_replyp++ = (unsigned char)value) == IAC) + *slc_replyp++ = IAC; +} + +void +slc_end_reply(void) +{ + int len; + + /* The end of negotiation command requires 2 bytes. */ + if (&slc_replyp[2] > slc_reply_eom) + return; + *slc_replyp++ = IAC; + *slc_replyp++ = SE; + len = slc_replyp - slc_reply; + if (len <= 6) + return; + if (NETROOM() > len) { + ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply); + printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2); + } +/*@*/else printf("slc_end_reply: not enough room\n"); +} + +int +slc_update(void) +{ + struct spc *spcp; + int need_update = 0; + + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (!(spcp->flags&SLC_ACK)) + continue; + spcp->flags &= ~SLC_ACK; + if (spcp->valp && (*spcp->valp != spcp->val)) { + *spcp->valp = spcp->val; + need_update = 1; + } + } + return(need_update); +} + +#ifdef OLD_ENVIRON +# ifdef ENV_HACK +/* + * Earlier version of telnet/telnetd from the BSD code had + * the definitions of VALUE and VAR reversed. To ensure + * maximum interoperability, we assume that the server is + * an older BSD server, until proven otherwise. The newer + * BSD servers should be able to handle either definition, + * so it is better to use the wrong values if we don't + * know what type of server it is. + */ +int env_auto = 1; +int old_env_var = OLD_ENV_VAR; +int old_env_value = OLD_ENV_VALUE; +# else +# define old_env_var OLD_ENV_VAR +# define old_env_value OLD_ENV_VALUE +# endif +#endif + +void +env_opt(unsigned char *buf, int len) +{ + unsigned char *ep = 0, *epc = 0; + int i; + + switch(buf[0]&0xff) { + case TELQUAL_SEND: + env_opt_start(); + if (len == 1) { + env_opt_add(NULL); + } else for (i = 1; i < len; i++) { + switch (buf[i]&0xff) { +#ifdef OLD_ENVIRON + case OLD_ENV_VAR: +# ifdef ENV_HACK + if (telopt_environ == TELOPT_OLD_ENVIRON + && env_auto) { + /* Server has the same definitions */ + old_env_var = OLD_ENV_VAR; + old_env_value = OLD_ENV_VALUE; + } + /* FALLTHROUGH */ +# endif + case OLD_ENV_VALUE: + /* + * Although OLD_ENV_VALUE is not legal, we will + * still recognize it, just in case it is an + * old server that has VAR & VALUE mixed up... + */ + /* FALLTHROUGH */ +#else + case NEW_ENV_VAR: +#endif + case ENV_USERVAR: + if (ep) { + *epc = 0; + env_opt_add(ep); + } + ep = epc = &buf[i+1]; + break; + case ENV_ESC: + i++; + /*FALLTHROUGH*/ + default: + if (epc) + *epc++ = buf[i]; + break; + } + } + if (ep) { + *epc = 0; + env_opt_add(ep); + } + env_opt_end(1); + break; + + case TELQUAL_IS: + case TELQUAL_INFO: + /* Ignore for now. We shouldn't get it anyway. */ + break; + + default: + break; + } +} + +#define OPT_REPLY_SIZE (2 * SUBBUFSIZE) +unsigned char *opt_reply = NULL; +unsigned char *opt_replyp; +unsigned char *opt_replyend; + +void +env_opt_start(void) +{ + if (opt_reply) + opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE); + else + opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE); + if (opt_reply == NULL) { +/*@*/ printf("env_opt_start: malloc()/realloc() failed!!!\n"); + opt_reply = opt_replyp = opt_replyend = NULL; + return; + } + opt_replyp = opt_reply; + opt_replyend = opt_reply + OPT_REPLY_SIZE; + *opt_replyp++ = IAC; + *opt_replyp++ = SB; + *opt_replyp++ = telopt_environ; + *opt_replyp++ = TELQUAL_IS; +} + +void +env_opt_start_info(void) +{ + env_opt_start(); + if (opt_replyp) + opt_replyp[-1] = TELQUAL_INFO; +} + +void +env_opt_add(unsigned char *ep) +{ + unsigned char *vp, c; + + if (opt_reply == NULL) /*XXX*/ + return; /*XXX*/ + + if (ep == NULL || *ep == '\0') { + /* Send user defined variables first. */ + env_default(1, 0); + while ((ep = env_default(0, 0))) + env_opt_add(ep); + + /* Now add the list of well know variables. */ + env_default(1, 1); + while ((ep = env_default(0, 1))) + env_opt_add(ep); + return; + } + vp = env_getvalue(ep); + if (opt_replyp + (vp ? 2 * strlen((char *)vp) : 0) + + 2 * strlen((char *)ep) + 6 > opt_replyend) + { + int len; +#ifdef __APPLE__ + // rdar://problem/4022837 + opt_replyend += OPT_REPLY_SIZE + 2*strlen((char *)ep) + 2*(vp ? strlen((char *)vp) : 0); +#else + opt_replyend += OPT_REPLY_SIZE; +#endif + len = opt_replyend - opt_reply; + opt_reply = (unsigned char *)realloc(opt_reply, len); + if (opt_reply == NULL) { +/*@*/ printf("env_opt_add: realloc() failed!!!\n"); + opt_reply = opt_replyp = opt_replyend = NULL; + return; + } + opt_replyp = opt_reply + len - (opt_replyend - opt_replyp); + opt_replyend = opt_reply + len; + } + if (opt_welldefined((const char *)ep)) +#ifdef OLD_ENVIRON + if (telopt_environ == TELOPT_OLD_ENVIRON) + *opt_replyp++ = old_env_var; + else +#endif + *opt_replyp++ = NEW_ENV_VAR; + else + *opt_replyp++ = ENV_USERVAR; + for (;;) { + while ((c = *ep++)) { + if (opt_replyp + (2 + 2) > opt_replyend) + return; + switch(c&0xff) { + case IAC: + *opt_replyp++ = IAC; + break; + case NEW_ENV_VAR: + case NEW_ENV_VALUE: + case ENV_ESC: + case ENV_USERVAR: + *opt_replyp++ = ENV_ESC; + break; + } + *opt_replyp++ = c; + } + if ((ep = vp)) { + if (opt_replyp + (1 + 2 + 2) > opt_replyend) + return; +#ifdef OLD_ENVIRON + if (telopt_environ == TELOPT_OLD_ENVIRON) + *opt_replyp++ = old_env_value; + else +#endif + *opt_replyp++ = NEW_ENV_VALUE; + vp = NULL; + } else + break; + } +} + +int +opt_welldefined(const char *ep) +{ + if ((strcmp(ep, "USER") == 0) || + (strcmp(ep, "DISPLAY") == 0) || + (strcmp(ep, "PRINTER") == 0) || + (strcmp(ep, "SYSTEMTYPE") == 0) || + (strcmp(ep, "JOB") == 0) || + (strcmp(ep, "ACCT") == 0)) + return(1); + return(0); +} + +void +env_opt_end(int emptyok) +{ + int len; + + if (opt_replyp + 2 > opt_replyend) + return; + len = opt_replyp + 2 - opt_reply; + if (emptyok || len > 6) { + *opt_replyp++ = IAC; + *opt_replyp++ = SE; + if (NETROOM() > len) { + ring_supply_data(&netoring, opt_reply, len); + printsub('>', &opt_reply[2], len - 2); + } +/*@*/ else printf("slc_end_reply: not enough room\n"); + } + if (opt_reply) { + free(opt_reply); + opt_reply = opt_replyp = opt_replyend = NULL; + } +} + + + +int +telrcv(void) +{ + int c; + int scc; + unsigned char *sbp = NULL; + int count; + int returnValue = 0; + + scc = 0; + count = 0; + while (TTYROOM() > 2) { + if (scc == 0) { + if (count) { + ring_consumed(&netiring, count); + returnValue = 1; + count = 0; + } + sbp = netiring.consume; + scc = ring_full_consecutive(&netiring); + if (scc == 0) { + /* No more data coming in */ + break; + } + } + + c = *sbp++ & 0xff, scc--; count++; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + + switch (telrcv_state) { + + case TS_CR: + telrcv_state = TS_DATA; + if (c == '\0') { + break; /* Ignore \0 after CR */ + } + else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) { + TTYADD(c); + break; + } + /* FALLTHROUGH */ + + case TS_DATA: + if (c == IAC && telnetport >= 0) { + telrcv_state = TS_IAC; + break; + } + /* + * The 'crmod' hack (see following) is needed + * since we can't * set CRMOD on output only. + * Machines like MULTICS like to send \r without + * \n; since we must turn off CRMOD to get proper + * input, the mapping is done here (sigh). + */ + if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) { + if (scc > 0) { + c = *sbp&0xff; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + if (c == 0) { + sbp++, scc--; count++; + /* a "true" CR */ + TTYADD('\r'); + } else if (my_want_state_is_dont(TELOPT_ECHO) && + (c == '\n')) { + sbp++, scc--; count++; + TTYADD('\n'); + } else { +#ifdef ENCRYPTION + if (decrypt_input) + (*decrypt_input)(-1); +#endif /* ENCRYPTION */ + + TTYADD('\r'); + if (crmod) { + TTYADD('\n'); + } + } + } else { + telrcv_state = TS_CR; + TTYADD('\r'); + if (crmod) { + TTYADD('\n'); + } + } + } else { + TTYADD(c); + } + continue; + + case TS_IAC: +process_iac: + switch (c) { + + case WILL: + telrcv_state = TS_WILL; + continue; + + case WONT: + telrcv_state = TS_WONT; + continue; + + case DO: + telrcv_state = TS_DO; + continue; + + case DONT: + telrcv_state = TS_DONT; + continue; + + case DM: + /* + * We may have missed an urgent notification, + * so make sure we flush whatever is in the + * buffer currently. + */ + printoption("RCVD", IAC, DM); + SYNCHing = 1; + (void) ttyflush(1); + SYNCHing = stilloob(); + settimer(gotDM); + break; + + case SB: + SB_CLEAR(); + telrcv_state = TS_SB; + continue; + + case IAC: + TTYADD(IAC); + break; + + case NOP: + case GA: + default: + printoption("RCVD", IAC, c); + break; + } + telrcv_state = TS_DATA; + continue; + + case TS_WILL: + printoption("RCVD", WILL, c); + willoption(c); + telrcv_state = TS_DATA; + continue; + + case TS_WONT: + printoption("RCVD", WONT, c); + wontoption(c); + telrcv_state = TS_DATA; + continue; + + case TS_DO: + printoption("RCVD", DO, c); + dooption(c); + if (c == TELOPT_NAWS) { + sendnaws(); + } else if (c == TELOPT_LFLOW) { + localflow = 1; + setcommandmode(); + setconnmode(0); + } + telrcv_state = TS_DATA; + continue; + + case TS_DONT: + printoption("RCVD", DONT, c); + dontoption(c); + flushline = 1; + setconnmode(0); /* set new tty mode (maybe) */ + telrcv_state = TS_DATA; + continue; + + case TS_SB: + if (c == IAC) { + telrcv_state = TS_SE; + } else { + SB_ACCUM(c); + } + continue; + + case TS_SE: + if (c != SE) { + if (c != IAC) { + /* + * This is an error. We only expect to get + * "IAC IAC" or "IAC SE". Several things may + * have happend. An IAC was not doubled, the + * IAC SE was left off, or another option got + * inserted into the suboption are all possibilities. + * If we assume that the IAC was not doubled, + * and really the IAC SE was left off, we could + * get into an infinate loop here. So, instead, + * we terminate the suboption, and process the + * partial suboption if we can. + */ + SB_ACCUM(IAC); + SB_ACCUM(c); + subpointer -= 2; + SB_TERM(); + + printoption("In SUBOPTION processing, RCVD", IAC, c); + suboption(); /* handle sub-option */ + telrcv_state = TS_IAC; + goto process_iac; + } + SB_ACCUM(c); + telrcv_state = TS_SB; + } else { + SB_ACCUM(IAC); + SB_ACCUM(SE); + subpointer -= 2; + SB_TERM(); + suboption(); /* handle sub-option */ + telrcv_state = TS_DATA; + } + } + } + if (count) + ring_consumed(&netiring, count); + return returnValue||count; +} + +static int bol = 1, local = 0; + +int +rlogin_susp(void) +{ + if (local) { + local = 0; + bol = 1; + command(0, "z\n", 2); + return(1); + } + return(0); +} + +static int +telsnd(void) +{ + int tcc; + int count; + int returnValue = 0; + unsigned char *tbp = NULL; + + tcc = 0; + count = 0; + while (NETROOM() > 2) { + int sc; + int c; + + if (tcc == 0) { + if (count) { + ring_consumed(&ttyiring, count); + returnValue = 1; + count = 0; + } + tbp = ttyiring.consume; + tcc = ring_full_consecutive(&ttyiring); + if (tcc == 0) { + break; + } + } + c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; + if (rlogin != _POSIX_VDISABLE) { + if (bol) { + bol = 0; + if (sc == rlogin) { + local = 1; + continue; + } + } else if (local) { + local = 0; + if (sc == '.' || c == termEofChar) { + bol = 1; + command(0, "close\n", 6); + continue; + } + if (sc == termSuspChar) { + bol = 1; + command(0, "z\n", 2); + continue; + } + if (sc == escape) { + command(0, (const char *)tbp, tcc); + bol = 1; + count += tcc; + tcc = 0; + flushline = 1; + break; + } + if (sc != rlogin) { + ++tcc; + --tbp; + --count; + c = sc = rlogin; + } + } + if ((sc == '\n') || (sc == '\r')) + bol = 1; + } else if (escape != _POSIX_VDISABLE && sc == escape) { + /* + * Double escape is a pass through of a single escape character. + */ + if (tcc && strip(*tbp) == escape) { + tbp++; + tcc--; + count++; + bol = 0; + } else { + command(0, (char *)tbp, tcc); + bol = 1; + count += tcc; + tcc = 0; + flushline = 1; + break; + } + } else + bol = 0; +#ifdef KLUDGELINEMODE + if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) { + if (tcc > 0 && strip(*tbp) == echoc) { + tcc--; tbp++; count++; + } else { + dontlecho = !dontlecho; + settimer(echotoggle); + setconnmode(0); + flushline = 1; + break; + } + } +#endif + if (MODE_LOCAL_CHARS(globalmode)) { + if (TerminalSpecialChars(sc) == 0) { + bol = 1; + break; + } + } + if (my_want_state_is_wont(TELOPT_BINARY)) { + switch (c) { + case '\n': + /* + * If we are in CRMOD mode (\r ==> \n) + * on our local machine, then probably + * a newline (unix) is CRLF (TELNET). + */ + if (MODE_LOCAL_CHARS(globalmode)) { + NETADD('\r'); + } + NETADD('\n'); + bol = flushline = 1; + break; + case '\r': + if (!crlf) { + NET2ADD('\r', '\0'); + } else { + NET2ADD('\r', '\n'); + } + bol = flushline = 1; + break; + case IAC: + NET2ADD(IAC, IAC); + break; + default: + NETADD(c); + break; + } + } else if (c == IAC) { + NET2ADD(IAC, IAC); + } else { + NETADD(c); + } + } + if (count) + ring_consumed(&ttyiring, count); + return returnValue||count; /* Non-zero if we did anything */ +} + +/* + * Scheduler() + * + * Try to do something. + * + * If we do something useful, return 1; else return 0. + * + */ + +static int +Scheduler(int block) +{ + /* One wants to be a bit careful about setting returnValue + * to one, since a one implies we did some useful work, + * and therefore probably won't be called to block next + */ + int returnValue; + int netin, netout, netex, ttyin, ttyout; + + /* Decide which rings should be processed */ + + netout = ring_full_count(&netoring) && + (flushline || + (my_want_state_is_wont(TELOPT_LINEMODE) +#ifdef KLUDGELINEMODE + && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA)) +#endif + ) || + my_want_state_is_will(TELOPT_BINARY)); + ttyout = ring_full_count(&ttyoring); + + ttyin = ring_empty_count(&ttyiring) && (clienteof == 0); + + netin = !ISend && ring_empty_count(&netiring); + + netex = !SYNCHing; + + /* Call to system code to process rings */ + + returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); + + /* Now, look at the input rings, looking for work to do. */ + + if (ring_full_count(&ttyiring)) { + returnValue |= telsnd(); + } + + if (ring_full_count(&netiring)) { + returnValue |= telrcv(); + } + return returnValue; +} + +#ifdef AUTHENTICATION +#define __unusedhere +#else +#define __unusedhere __unused +#endif +/* + * Select from tty and network... + */ +void +telnet(char *user __unusedhere) +{ + sys_telnet_init(); + +#ifdef AUTHENTICATION +#ifdef ENCRYPTION + { + static char local_host[256] = { 0 }; + + if (!local_host[0]) { + gethostname(local_host, sizeof(local_host)); + local_host[sizeof(local_host)-1] = 0; + } + auth_encrypt_init(local_host, hostname, "TELNET", 0); + auth_encrypt_user(user); + } +#endif +#endif + if (telnetport > 0) { +#ifdef AUTHENTICATION + if (autologin) + send_will(TELOPT_AUTHENTICATION, 1); +#endif +#ifdef ENCRYPTION + send_do(TELOPT_ENCRYPT, 1); + send_will(TELOPT_ENCRYPT, 1); +#endif /* ENCRYPTION */ + send_do(TELOPT_SGA, 1); + send_will(TELOPT_TTYPE, 1); + send_will(TELOPT_NAWS, 1); + send_will(TELOPT_TSPEED, 1); + send_will(TELOPT_LFLOW, 1); + send_will(TELOPT_LINEMODE, 1); + send_will(TELOPT_NEW_ENVIRON, 1); + send_do(TELOPT_STATUS, 1); + if (env_getvalue((const unsigned char *)"DISPLAY")) + send_will(TELOPT_XDISPLOC, 1); + if (eight) + tel_enter_binary(eight); + } + + for (;;) { + int schedValue; + + while ((schedValue = Scheduler(0)) != 0) { + if (schedValue == -1) { + setcommandmode(); + return; + } + } + + if (Scheduler(1) == -1) { + setcommandmode(); + return; + } + } +} + +#if 0 /* XXX - this not being in is a bug */ +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ + +static char * +nextitem(char *current) +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} +#endif /* 0 */ + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ + +static void +netclear(void) +{ + /* Deleted */ +} + +/* + * These routines add various telnet commands to the data stream. + */ + +static void +doflush(void) +{ + NET2ADD(IAC, DO); + NETADD(TELOPT_TM); + flushline = 1; + flushout = 1; + (void) ttyflush(1); /* Flush/drop output */ + /* do printoption AFTER flush, otherwise the output gets tossed... */ + printoption("SENT", DO, TELOPT_TM); +} + +void +xmitAO(void) +{ + NET2ADD(IAC, AO); + printoption("SENT", IAC, AO); + if (autoflush) { + doflush(); + } +} + +void +xmitEL(void) +{ + NET2ADD(IAC, EL); + printoption("SENT", IAC, EL); +} + +void +xmitEC(void) +{ + NET2ADD(IAC, EC); + printoption("SENT", IAC, EC); +} + +int +dosynch(char *ch __unused) +{ + netclear(); /* clear the path to the network */ + NETADD(IAC); + setneturg(); + NETADD(DM); + printoption("SENT", IAC, DM); + return 1; +} + +int want_status_response = 0; + +int +get_status(char *ch __unused) +{ + unsigned char tmp[16]; + unsigned char *cp; + + if (my_want_state_is_dont(TELOPT_STATUS)) { + printf("Remote side does not support STATUS option\n"); + return 0; + } + cp = tmp; + + *cp++ = IAC; + *cp++ = SB; + *cp++ = TELOPT_STATUS; + *cp++ = TELQUAL_SEND; + *cp++ = IAC; + *cp++ = SE; + if (NETROOM() >= cp - tmp) { + ring_supply_data(&netoring, tmp, cp-tmp); + printsub('>', tmp+2, cp - tmp - 2); + } + ++want_status_response; + return 1; +} + +void +intp(void) +{ + NET2ADD(IAC, IP); + printoption("SENT", IAC, IP); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendbrk(void) +{ + NET2ADD(IAC, BREAK); + printoption("SENT", IAC, BREAK); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendabort(void) +{ + NET2ADD(IAC, ABORT); + printoption("SENT", IAC, ABORT); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendsusp(void) +{ + NET2ADD(IAC, SUSP); + printoption("SENT", IAC, SUSP); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendeof(void) +{ + NET2ADD(IAC, xEOF); + printoption("SENT", IAC, xEOF); +} + +void +sendayt(void) +{ + NET2ADD(IAC, AYT); + printoption("SENT", IAC, AYT); +} + +/* + * Send a window size update to the remote system. + */ + +void +sendnaws(void) +{ + long rows, cols; + unsigned char tmp[16]; + unsigned char *cp; + + if (my_state_is_wont(TELOPT_NAWS)) + return; + +#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \ + if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; } + + if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */ + return; + } + + cp = tmp; + + *cp++ = IAC; + *cp++ = SB; + *cp++ = TELOPT_NAWS; + PUTSHORT(cp, cols); + PUTSHORT(cp, rows); + *cp++ = IAC; + *cp++ = SE; + if (NETROOM() >= cp - tmp) { + ring_supply_data(&netoring, tmp, cp-tmp); + printsub('>', tmp+2, cp - tmp - 2); + } +} + +void +tel_enter_binary(int rw) +{ + if (rw&1) + send_do(TELOPT_BINARY, 1); + if (rw&2) + send_will(TELOPT_BINARY, 1); +} + +void +tel_leave_binary(int rw) +{ + if (rw&1) + send_dont(TELOPT_BINARY, 1); + if (rw&2) + send_wont(TELOPT_BINARY, 1); +} diff --git a/remote_cmds/telnet.tproj/terminal.c b/remote_cmds/telnet.tproj/terminal.c new file mode 100644 index 0000000..96edc35 --- /dev/null +++ b/remote_cmds/telnet.tproj/terminal.c @@ -0,0 +1,242 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)terminal.c 8.2 (Berkeley) 2/16/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/terminal.c,v 1.7 2003/05/04 02:54:48 obrien Exp $"); + +#include <arpa/telnet.h> +#include <sys/types.h> + +#include <stdlib.h> + +#include "ring.h" + +#include "externs.h" +#include "types.h" + +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +Ring ttyoring, ttyiring; +unsigned char ttyobuf[2*BUFSIZ], ttyibuf[BUFSIZ]; + +int termdata; /* Debugging flag */ + +#ifdef USE_TERMIO +# ifndef VDISCARD +cc_t termFlushChar; +# endif +# ifndef VLNEXT +cc_t termLiteralNextChar; +# endif +# ifndef VSUSP +cc_t termSuspChar; +# endif +# ifndef VWERASE +cc_t termWerasChar; +# endif +# ifndef VREPRINT +cc_t termRprntChar; +# endif +# ifndef VSTART +cc_t termStartChar; +# endif +# ifndef VSTOP +cc_t termStopChar; +# endif +# ifndef VEOL +cc_t termForw1Char; +# endif +# ifndef VEOL2 +cc_t termForw2Char; +# endif +# ifndef VSTATUS +cc_t termAytChar; +# endif +#else +cc_t termForw2Char; +cc_t termAytChar; +#endif + +/* + * initialize the terminal data structures. + */ + +void +init_terminal(void) +{ + if (ring_init(&ttyoring, ttyobuf, sizeof ttyobuf) != 1) { + exit(1); + } + if (ring_init(&ttyiring, ttyibuf, sizeof ttyibuf) != 1) { + exit(1); + } + autoflush = TerminalAutoFlush(); +} + +/* + * Send as much data as possible to the terminal. + * + * Return value: + * -1: No useful work done, data waiting to go out. + * 0: No data was waiting, so nothing was done. + * 1: All waiting data was written out. + * n: All data - n was written out. + */ + +int +ttyflush(int drop) +{ + int n, n0, n1; + + n0 = ring_full_count(&ttyoring); + if ((n1 = n = ring_full_consecutive(&ttyoring)) > 0) { + if (drop) { + TerminalFlushOutput(); + /* we leave 'n' alone! */ + } else { + n = TerminalWrite((char *)ttyoring.consume, n); + } + } + if (n > 0) { + if (termdata && n) { + Dump('>', ttyoring.consume, n); + } + /* + * If we wrote everything, and the full count is + * larger than what we wrote, then write the + * rest of the buffer. + */ + if (n1 == n && n0 > n) { + n1 = n0 - n; + if (!drop) + n1 = TerminalWrite((char *)ttyoring.bottom, n1); + if (n1 > 0) + n += n1; + } + ring_consumed(&ttyoring, n); + } + if (n < 0) + return -1; + if (n == n0) { + if (n0) + return -1; + return 0; + } + return n0 - n + 1; +} + + +/* + * These routines decides on what the mode should be (based on the values + * of various global variables). + */ + + +int +getconnmode(void) +{ + extern int linemode; + int mode = 0; +#ifdef KLUDGELINEMODE + extern int kludgelinemode; +#endif + + if (my_want_state_is_dont(TELOPT_ECHO)) + mode |= MODE_ECHO; + + if (localflow) + mode |= MODE_FLOW; + + if (my_want_state_is_will(TELOPT_BINARY)) + mode |= MODE_INBIN; + + if (his_want_state_is_will(TELOPT_BINARY)) + mode |= MODE_OUTBIN; + +#ifdef KLUDGELINEMODE + if (kludgelinemode) { + if (my_want_state_is_dont(TELOPT_SGA)) { + mode |= (MODE_TRAPSIG|MODE_EDIT); + if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { + mode &= ~MODE_ECHO; + } + } + return(mode); + } +#endif + if (my_want_state_is_will(TELOPT_LINEMODE)) + mode |= linemode; + return(mode); +} + +void +setconnmode(int force) +{ +#ifdef ENCRYPTION + static int enc_passwd = 0; +#endif /* ENCRYPTION */ + int newmode; + + newmode = getconnmode()|(force?MODE_FORCE:0); + + TerminalNewMode(newmode); + +#ifdef ENCRYPTION + if ((newmode & (MODE_ECHO|MODE_EDIT)) == MODE_EDIT) { + if (my_want_state_is_will(TELOPT_ENCRYPT) + && (enc_passwd == 0) && !encrypt_output) { + encrypt_request_start(0, 0); + enc_passwd = 1; + } + } else { + if (enc_passwd) { + encrypt_request_end(); + enc_passwd = 0; + } + } +#endif /* ENCRYPTION */ + +} + +void +setcommandmode(void) +{ + TerminalNewMode(-1); +} diff --git a/remote_cmds/telnet.tproj/tn3270.c b/remote_cmds/telnet.tproj/tn3270.c new file mode 100644 index 0000000..c2bbd8f --- /dev/null +++ b/remote_cmds/telnet.tproj/tn3270.c @@ -0,0 +1,435 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * 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. + */ + +#include <sys/cdefs.h> +#ifndef lint +__unused static char sccsid[] = "@(#)tn3270.c 8.2 (Berkeley) 5/30/95"; +#endif /* not lint */ + +#include <sys/types.h> +#include <arpa/telnet.h> + +#include "general.h" + +#include "defines.h" +#include "ring.h" +#include "externs.h" +#include "fdset.h" + +#if defined(TN3270) + +#include "../ctlr/screen.h" +#include "../general/globals.h" + +#include "../sys_curses/telextrn.h" +#include "../ctlr/externs.h" + +#if defined(unix) || defined(__APPLE__) +int + HaveInput, /* There is input available to scan */ + cursesdata, /* Do we dump curses data? */ + sigiocount; /* Number of times we got a SIGIO */ + +char tline[200]; +char *transcom = 0; /* transparent mode command (default: none) */ +#endif /* defined(unix) || defined(__APPLE__) */ + +char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp; + +static char sb_terminal[] = { IAC, SB, + TELOPT_TTYPE, TELQUAL_IS, + 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2', + IAC, SE }; +#define SBTERMMODEL 13 + +static int + Sent3270TerminalType; /* Have we said we are a 3270? */ + +#endif /* defined(TN3270) */ + + + void +init_3270() +{ +#if defined(TN3270) +#if defined(unix) || defined(__APPLE__) + HaveInput = 0; + sigiocount = 0; +#endif /* defined(unix) || defined(__APPLE__) */ + Sent3270TerminalType = 0; + Ifrontp = Ibackp = Ibuf; + init_ctlr(); /* Initialize some things */ + init_keyboard(); + init_screen(); + init_system(); +#endif /* defined(TN3270) */ +} + + +#if defined(TN3270) + +/* + * DataToNetwork - queue up some data to go to network. If "done" is set, + * then when last byte is queued, we add on an IAC EOR sequence (so, + * don't call us with "done" until you want that done...) + * + * We actually do send all the data to the network buffer, since our + * only client needs for us to do that. + */ + + int +DataToNetwork(buffer, count, done) + register char *buffer; /* where the data is */ + register int count; /* how much to send */ + int done; /* is this the last of a logical block */ +{ + register int loop, c; + int origCount; + + origCount = count; + + while (count) { + /* If not enough room for EORs, IACs, etc., wait */ + if (NETROOM() < 6) { + fd_set o; + + FD_ZERO(&o); + netflush(); + while (NETROOM() < 6) { + FD_SET(net, &o); + (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0, + (struct timeval *) 0); + netflush(); + } + } + c = ring_empty_count(&netoring); + if (c > count) { + c = count; + } + loop = c; + while (loop) { + if (((unsigned char)*buffer) == IAC) { + break; + } + buffer++; + loop--; + } + if ((c = c-loop)) { + ring_supply_data(&netoring, buffer-c, c); + count -= c; + } + if (loop) { + NET2ADD(IAC, IAC); + count--; + buffer++; + } + } + + if (done) { + NET2ADD(IAC, EOR); + netflush(); /* try to move along as quickly as ... */ + } + return(origCount - count); +} + + +#if defined(unix) || defined(__APPLE__) + void +inputAvailable(signo) + int signo; +{ + HaveInput = 1; + sigiocount++; +} +#endif /* defined(unix) || defined(__APPLE__) */ + + void +outputPurge() +{ + (void) ttyflush(1); +} + + +/* + * The following routines are places where the various tn3270 + * routines make calls into telnet.c. + */ + +/* + * DataToTerminal - queue up some data to go to terminal. + * + * Note: there are people who call us and depend on our processing + * *all* the data at one time (thus the select). + */ + + int +DataToTerminal(buffer, count) + register char *buffer; /* where the data is */ + register int count; /* how much to send */ +{ + register int c; + int origCount; + + origCount = count; + + while (count) { + if (TTYROOM() == 0) { +#if defined(unix) || defined(__APPLE__) + fd_set o; + + FD_ZERO(&o); +#endif /* defined(unix) || defined(__APPLE__) */ + (void) ttyflush(0); + while (TTYROOM() == 0) { +#if defined(unix) || defined(__APPLE__) + FD_SET(tout, &o); + (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, + (struct timeval *) 0); +#endif /* defined(unix) || defined(__APPLE__) */ + (void) ttyflush(0); + } + } + c = TTYROOM(); + if (c > count) { + c = count; + } + ring_supply_data(&ttyoring, buffer, c); + count -= c; + buffer += c; + } + return(origCount); +} + + +/* + * Push3270 - Try to send data along the 3270 output (to screen) direction. + */ + + int +Push3270() +{ + int save = ring_full_count(&netiring); + + if (save) { + if (Ifrontp+save > Ibuf+sizeof Ibuf) { + if (Ibackp != Ibuf) { + memmove(Ibuf, Ibackp, Ifrontp-Ibackp); + Ifrontp -= (Ibackp-Ibuf); + Ibackp = Ibuf; + } + } + if (Ifrontp+save < Ibuf+sizeof Ibuf) { + (void)telrcv(); + } + } + return save != ring_full_count(&netiring); +} + + +/* + * Finish3270 - get the last dregs of 3270 data out to the terminal + * before quitting. + */ + + void +Finish3270() +{ + while (Push3270() || !DoTerminalOutput()) { +#if defined(unix) || defined(__APPLE__) + HaveInput = 0; +#endif /* defined(unix) || defined(__APPLE__) */ + ; + } +} + + +/* StringToTerminal - output a null terminated string to the terminal */ + + void +StringToTerminal(s) + char *s; +{ + int count; + + count = strlen(s); + if (count) { + (void) DataToTerminal(s, count); /* we know it always goes... */ + } +} + + +#if ((!defined(NOT43)) || defined(PUTCHAR)) +/* _putchar - output a single character to the terminal. This name is so that + * curses(3x) can call us to send out data. + */ + + void +_putchar(c) + char c; +{ +#if defined(sun) /* SunOS 4.0 bug */ + c &= 0x7f; +#endif /* defined(sun) */ + if (cursesdata) { + Dump('>', &c, 1); + } + if (!TTYROOM()) { + (void) DataToTerminal(&c, 1); + } else { + TTYADD(c); + } +} +#endif /* ((!defined(NOT43)) || defined(PUTCHAR)) */ + + void +SetIn3270() +{ + if (Sent3270TerminalType && my_want_state_is_will(TELOPT_BINARY) + && my_want_state_is_do(TELOPT_BINARY) && !donebinarytoggle) { + if (!In3270) { + In3270 = 1; + Init3270(); /* Initialize 3270 functions */ + /* initialize terminal key mapping */ + InitTerminal(); /* Start terminal going */ + setconnmode(0); + } + } else { + if (In3270) { + StopScreen(1); + In3270 = 0; + Stop3270(); /* Tell 3270 we aren't here anymore */ + setconnmode(0); + } + } +} + +/* + * tn3270_ttype() + * + * Send a response to a terminal type negotiation. + * + * Return '0' if no more responses to send; '1' if a response sent. + */ + + int +tn3270_ttype() +{ + /* + * Try to send a 3270 type terminal name. Decide which one based + * on the format of our screen, and (in the future) color + * capaiblities. + */ + InitTerminal(); /* Sets MaxNumberColumns, MaxNumberLines */ + if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) { + Sent3270TerminalType = 1; + if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) { + MaxNumberLines = 27; + MaxNumberColumns = 132; + sb_terminal[SBTERMMODEL] = '5'; + } else if (MaxNumberLines >= 43) { + MaxNumberLines = 43; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '4'; + } else if (MaxNumberLines >= 32) { + MaxNumberLines = 32; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '3'; + } else { + MaxNumberLines = 24; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '2'; + } + NumberLines = 24; /* before we start out... */ + NumberColumns = 80; + ScreenSize = NumberLines*NumberColumns; + if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) { + ExitString("Programming error: MAXSCREENSIZE too small.\n", + 1); + /*NOTREACHED*/ + } + printsub('>', sb_terminal+2, sizeof sb_terminal-2); + ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal); + return 1; + } else { + return 0; + } +} + +#if defined(unix) || defined(__APPLE__) + int +settranscom(argc, argv) + int argc; + char *argv[]; +{ + int i; + + if (argc == 1 && transcom) { + transcom = 0; + } + if (argc == 1) { + return 1; + } + transcom = tline; + (void) strcpy(transcom, argv[1]); + for (i = 2; i < argc; ++i) { + (void) strcat(transcom, " "); + (void) strcat(transcom, argv[i]); + } + return 1; +} +#endif /* defined(unix) || defined(__APPLE__) */ + +#endif /* defined(TN3270) */ diff --git a/remote_cmds/telnet.tproj/types.h b/remote_cmds/telnet.tproj/types.h new file mode 100644 index 0000000..191d311 --- /dev/null +++ b/remote_cmds/telnet.tproj/types.h @@ -0,0 +1,52 @@ +/* + * 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. + * + * @(#)types.h 8.1 (Berkeley) 6/6/93 + */ + +typedef struct { + char *modedescriptions; + char modetype; +} Modelist; + +extern Modelist modelist[]; + +typedef struct { + int + system, /* what the current time is */ + echotoggle, /* last time user entered echo character */ + modenegotiated, /* last time operating mode negotiated */ + didnetreceive, /* last time we read data from network */ + gotDM; /* when did we last see a data mark */ +} Clocks; + +extern Clocks clocks; diff --git a/remote_cmds/telnet.tproj/utilities.c b/remote_cmds/telnet.tproj/utilities.c new file mode 100644 index 0000000..418c982 --- /dev/null +++ b/remote_cmds/telnet.tproj/utilities.c @@ -0,0 +1,912 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)utilities.c 8.3 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnet/utilities.c,v 1.8 2003/05/04 02:54:48 obrien Exp $"); + +#define TELOPTS +#define TELCMDS +#define SLC_NAMES +#include <arpa/telnet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> + +#include "general.h" + +#include "fdset.h" + +#include "ring.h" + +#include "defines.h" + +#include "externs.h" + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +FILE *NetTrace = 0; /* Not in bss, since needs to stay */ +int prettydump; + +/* + * upcase() + * + * Upcase (in place) the argument. + */ + +void +upcase(char *argument) +{ + int c; + + while ((c = *argument) != 0) { + if (islower(c)) { + *argument = toupper(c); + } + argument++; + } +} + +/* + * SetSockOpt() + * + * Compensate for differences in 4.2 and 4.3 systems. + */ + +int +SetSockOpt(int fd, int level, int option, int yesno) +{ + return setsockopt(fd, level, option, + (char *)&yesno, sizeof yesno); +} + +/* + * The following are routines used to print out debugging information. + */ + +unsigned char NetTraceFile[256] = "(standard output)"; + +void +SetNetTrace(char *file) +{ + if (NetTrace && NetTrace != stdout) + fclose(NetTrace); + if (file && (strcmp(file, "-") != 0)) { + NetTrace = fopen(file, "w"); + if (NetTrace) { + strcpy((char *)NetTraceFile, file); + return; + } + fprintf(stderr, "Cannot open %s.\n", file); + } + NetTrace = stdout; + strcpy((char *)NetTraceFile, "(standard output)"); +} + +void +Dump(char direction, unsigned char *buffer, int length) +{ +# define BYTES_PER_LINE 32 +# define min(x,y) ((x<y)? x:y) + unsigned char *pThis; + int offset; + + offset = 0; + + while (length) { + /* print one line */ + fprintf(NetTrace, "%c 0x%x\t", direction, offset); + pThis = buffer; + if (prettydump) { + buffer = buffer + min(length, BYTES_PER_LINE/2); + while (pThis < buffer) { + fprintf(NetTrace, "%c%.2x", + (((*pThis)&0xff) == 0xff) ? '*' : ' ', + (*pThis)&0xff); + pThis++; + } + length -= BYTES_PER_LINE/2; + offset += BYTES_PER_LINE/2; + } else { + buffer = buffer + min(length, BYTES_PER_LINE); + while (pThis < buffer) { + fprintf(NetTrace, "%.2x", (*pThis)&0xff); + pThis++; + } + length -= BYTES_PER_LINE; + offset += BYTES_PER_LINE; + } + if (NetTrace == stdout) { + fprintf(NetTrace, "\r\n"); + } else { + fprintf(NetTrace, "\n"); + } + if (length < 0) { + fflush(NetTrace); + return; + } + /* find next unique line */ + } + fflush(NetTrace); +} + + +void +printoption(const char *direction, int cmd, int option) +{ + if (!showoptions) + return; + if (cmd == IAC) { + if (TELCMD_OK(option)) + fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option)); + else + fprintf(NetTrace, "%s IAC %d", direction, option); + } else { + const char *fmt; + fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : + (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; + if (fmt) { + fprintf(NetTrace, "%s %s ", direction, fmt); + if (TELOPT_OK(option)) + fprintf(NetTrace, "%s", TELOPT(option)); + else if (option == TELOPT_EXOPL) + fprintf(NetTrace, "EXOPL"); + else + fprintf(NetTrace, "%d", option); + } else + fprintf(NetTrace, "%s %d %d", direction, cmd, option); + } + if (NetTrace == stdout) { + fprintf(NetTrace, "\r\n"); + fflush(NetTrace); + } else { + fprintf(NetTrace, "\n"); + } + return; +} + +void +optionstatus(void) +{ + int i; + extern char will_wont_resp[], do_dont_resp[]; + + for (i = 0; i < 256; i++) { + if (do_dont_resp[i]) { + if (TELOPT_OK(i)) + printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]); + else if (TELCMD_OK(i)) + printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]); + else + printf("resp DO_DONT %d: %d\n", i, + do_dont_resp[i]); + if (my_want_state_is_do(i)) { + if (TELOPT_OK(i)) + printf("want DO %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf("want DO %s\n", TELCMD(i)); + else + printf("want DO %d\n", i); + } else { + if (TELOPT_OK(i)) + printf("want DONT %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf("want DONT %s\n", TELCMD(i)); + else + printf("want DONT %d\n", i); + } + } else { + if (my_state_is_do(i)) { + if (TELOPT_OK(i)) + printf(" DO %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf(" DO %s\n", TELCMD(i)); + else + printf(" DO %d\n", i); + } + } + if (will_wont_resp[i]) { + if (TELOPT_OK(i)) + printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]); + else if (TELCMD_OK(i)) + printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]); + else + printf("resp WILL_WONT %d: %d\n", + i, will_wont_resp[i]); + if (my_want_state_is_will(i)) { + if (TELOPT_OK(i)) + printf("want WILL %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf("want WILL %s\n", TELCMD(i)); + else + printf("want WILL %d\n", i); + } else { + if (TELOPT_OK(i)) + printf("want WONT %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf("want WONT %s\n", TELCMD(i)); + else + printf("want WONT %d\n", i); + } + } else { + if (my_state_is_will(i)) { + if (TELOPT_OK(i)) + printf(" WILL %s\n", TELOPT(i)); + else if (TELCMD_OK(i)) + printf(" WILL %s\n", TELCMD(i)); + else + printf(" WILL %d\n", i); + } + } + } + +} + +void +printsub(char direction, unsigned char *pointer, int length) +{ + int i; +#ifdef AUTHENTICATION + char buf[512]; +#endif + extern int want_status_response; + + if (showoptions || direction == 0 || + (want_status_response && (pointer[0] == TELOPT_STATUS))) { + if (direction) { + fprintf(NetTrace, "%s IAC SB ", + (direction == '<')? "RCVD":"SENT"); + if (length >= 3) { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) { + fprintf(NetTrace, "(terminated by "); + if (TELOPT_OK(i)) + fprintf(NetTrace, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + fprintf(NetTrace, "%s ", TELCMD(i)); + else + fprintf(NetTrace, "%d ", i); + if (TELOPT_OK(j)) + fprintf(NetTrace, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + fprintf(NetTrace, "%s", TELCMD(j)); + else + fprintf(NetTrace, "%d", j); + fprintf(NetTrace, ", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) { + fprintf(NetTrace, "(Empty suboption??\?)"); + if (NetTrace == stdout) + fflush(NetTrace); + return; + } + switch (pointer[0]) { + case TELOPT_TTYPE: + fprintf(NetTrace, "TERMINAL-TYPE "); + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND"); + break; + default: + fprintf(NetTrace, + "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + case TELOPT_TSPEED: + fprintf(NetTrace, "TERMINAL-SPEED"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, " IS "); + fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2); + break; + default: + if (pointer[1] == 1) + fprintf(NetTrace, " SEND"); + else + fprintf(NetTrace, " %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + } + break; + + case TELOPT_LFLOW: + fprintf(NetTrace, "TOGGLE-FLOW-CONTROL"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case LFLOW_OFF: + fprintf(NetTrace, " OFF"); break; + case LFLOW_ON: + fprintf(NetTrace, " ON"); break; + case LFLOW_RESTART_ANY: + fprintf(NetTrace, " RESTART-ANY"); break; + case LFLOW_RESTART_XON: + fprintf(NetTrace, " RESTART-XON"); break; + default: + fprintf(NetTrace, " %d (unknown)", pointer[1]); + } + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + + case TELOPT_NAWS: + fprintf(NetTrace, "NAWS"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + if (length == 2) { + fprintf(NetTrace, " ?%d?", pointer[1]); + break; + } + fprintf(NetTrace, " %d %d (%d)", + pointer[1], pointer[2], + (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); + if (length == 4) { + fprintf(NetTrace, " ?%d?", pointer[3]); + break; + } + fprintf(NetTrace, " %d %d (%d)", + pointer[3], pointer[4], + (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); + for (i = 5; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + fprintf(NetTrace, "AUTHENTICATION"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_REPLY: + case TELQUAL_IS: + fprintf(NetTrace, " %s ", (pointer[1] == TELQUAL_IS) ? + "IS" : "REPLY"); + if (AUTHTYPE_NAME_OK(pointer[2])) + fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[2])); + else + fprintf(NetTrace, "%d ", pointer[2]); + if (length < 3) { + fprintf(NetTrace, "(partial suboption??\?)"); + break; + } + fprintf(NetTrace, "%s|%s", + ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + + auth_printsub(&pointer[1], length - 1, (unsigned char *)buf, sizeof(buf)); + fprintf(NetTrace, "%s", buf); + break; + + case TELQUAL_SEND: + i = 2; + fprintf(NetTrace, " SEND "); + while (i < length) { + if (AUTHTYPE_NAME_OK(pointer[i])) + fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[i])); + else + fprintf(NetTrace, "%d ", pointer[i]); + if (++i >= length) { + fprintf(NetTrace, "(partial suboption??\?)"); + break; + } + fprintf(NetTrace, "%s|%s ", + ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + ++i; + } + break; + + case TELQUAL_NAME: + i = 2; + fprintf(NetTrace, " NAME \""); + while (i < length) + putc(pointer[i++], NetTrace); + putc('"', NetTrace); + break; + + default: + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + } + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + fprintf(NetTrace, "ENCRYPT"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case ENCRYPT_START: + fprintf(NetTrace, " START"); + break; + + case ENCRYPT_END: + fprintf(NetTrace, " END"); + break; + + case ENCRYPT_REQSTART: + fprintf(NetTrace, " REQUEST-START"); + break; + + case ENCRYPT_REQEND: + fprintf(NetTrace, " REQUEST-END"); + break; + + case ENCRYPT_IS: + case ENCRYPT_REPLY: + fprintf(NetTrace, " %s ", (pointer[1] == ENCRYPT_IS) ? + "IS" : "REPLY"); + if (length < 3) { + fprintf(NetTrace, " (partial suboption??\?)"); + break; + } + if (ENCTYPE_NAME_OK(pointer[2])) + fprintf(NetTrace, "%s ", ENCTYPE_NAME(pointer[2])); + else + fprintf(NetTrace, " %d (unknown)", pointer[2]); + + encrypt_printsub(&pointer[1], length - 1, (unsigned char *)buf, sizeof(buf)); + fprintf(NetTrace, "%s", buf); + break; + + case ENCRYPT_SUPPORT: + i = 2; + fprintf(NetTrace, " SUPPORT "); + while (i < length) { + if (ENCTYPE_NAME_OK(pointer[i])) + fprintf(NetTrace, "%s ", ENCTYPE_NAME(pointer[i])); + else + fprintf(NetTrace, "%d ", pointer[i]); + i++; + } + break; + + case ENCRYPT_ENC_KEYID: + fprintf(NetTrace, " ENC_KEYID "); + goto encommon; + + case ENCRYPT_DEC_KEYID: + fprintf(NetTrace, " DEC_KEYID "); + goto encommon; + + default: + fprintf(NetTrace, " %d (unknown)", pointer[1]); + encommon: + for (i = 2; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + break; +#endif /* ENCRYPTION */ + + case TELOPT_LINEMODE: + fprintf(NetTrace, "LINEMODE "); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case WILL: + fprintf(NetTrace, "WILL "); + goto common; + case WONT: + fprintf(NetTrace, "WONT "); + goto common; + case DO: + fprintf(NetTrace, "DO "); + goto common; + case DONT: + fprintf(NetTrace, "DONT "); + common: + if (length < 3) { + fprintf(NetTrace, "(no option??\?)"); + break; + } + switch (pointer[2]) { + case LM_FORWARDMASK: + fprintf(NetTrace, "Forward Mask"); + for (i = 3; i < length; i++) + fprintf(NetTrace, " %x", pointer[i]); + break; + default: + fprintf(NetTrace, "%d (unknown)", pointer[2]); + for (i = 3; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + break; + + case LM_SLC: + fprintf(NetTrace, "SLC"); + for (i = 2; i < length - 2; i += 3) { + if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC])); + else + fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]); + switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { + case SLC_NOSUPPORT: + fprintf(NetTrace, " NOSUPPORT"); break; + case SLC_CANTCHANGE: + fprintf(NetTrace, " CANTCHANGE"); break; + case SLC_VARIABLE: + fprintf(NetTrace, " VARIABLE"); break; + case SLC_DEFAULT: + fprintf(NetTrace, " DEFAULT"); break; + } + fprintf(NetTrace, "%s%s%s", + pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); + if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| + SLC_FLUSHOUT| SLC_LEVELBITS)) + fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]); + fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]); + if ((pointer[i+SLC_VALUE] == IAC) && + (pointer[i+SLC_VALUE+1] == IAC)) + i++; + } + for (; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + + case LM_MODE: + fprintf(NetTrace, "MODE "); + if (length < 3) { + fprintf(NetTrace, "(no mode??\?)"); + break; + } + { + char tbuf[64]; + sprintf(tbuf, "%s%s%s%s%s", + pointer[2]&MODE_EDIT ? "|EDIT" : "", + pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", + pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", + pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", + pointer[2]&MODE_ACK ? "|ACK" : ""); + fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0"); + } + if (pointer[2]&~(MODE_MASK)) + fprintf(NetTrace, " (0x%x)", pointer[2]); + for (i = 3; i < length; i++) + fprintf(NetTrace, " ?0x%x?", pointer[i]); + break; + default: + fprintf(NetTrace, "%d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + } + break; + + case TELOPT_STATUS: { + const char *cp; + int j, k; + + fprintf(NetTrace, "STATUS"); + + switch (pointer[1]) { + default: + if (pointer[1] == TELQUAL_SEND) + fprintf(NetTrace, " SEND"); + else + fprintf(NetTrace, " %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + case TELQUAL_IS: + if (--want_status_response < 0) + want_status_response = 0; + if (NetTrace == stdout) + fprintf(NetTrace, " IS\r\n"); + else + fprintf(NetTrace, " IS\n"); + + for (i = 2; i < length; i++) { + switch(pointer[i]) { + case DO: cp = "DO"; goto common2; + case DONT: cp = "DONT"; goto common2; + case WILL: cp = "WILL"; goto common2; + case WONT: cp = "WONT"; goto common2; + common2: + i++; + if (TELOPT_OK((int)pointer[i])) + fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i])); + else + fprintf(NetTrace, " %s %d", cp, pointer[i]); + + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + break; + + case SB: + fprintf(NetTrace, " SB "); + i++; + j = k = i; + while (j < length) { + if (pointer[j] == SE) { + if (j+1 == length) + break; + if (pointer[j+1] == SE) + j++; + else + break; + } + pointer[k++] = pointer[j++]; + } + printsub(0, &pointer[i], k - i); + if (i < length) { + fprintf(NetTrace, " SE"); + i = j; + } else + i = j - 1; + + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + + break; + + default: + fprintf(NetTrace, " %d", pointer[i]); + break; + } + } + break; + } + break; + } + + case TELOPT_XDISPLOC: + fprintf(NetTrace, "X-DISPLAY-LOCATION "); + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND"); + break; + default: + fprintf(NetTrace, "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + + case TELOPT_NEW_ENVIRON: + fprintf(NetTrace, "NEW-ENVIRON "); +#ifdef OLD_ENVIRON + goto env_common1; + case TELOPT_OLD_ENVIRON: + fprintf(NetTrace, "OLD-ENVIRON"); + env_common1: +#endif + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS "); + goto env_common; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND "); + goto env_common; + case TELQUAL_INFO: + fprintf(NetTrace, "INFO "); + env_common: + { + int noquote = 2; +#if defined(ENV_HACK) && defined(OLD_ENVIRON) + extern int old_env_var, old_env_value; +#endif + for (i = 2; i < length; i++ ) { + switch (pointer[i]) { + case NEW_ENV_VALUE: +#ifdef OLD_ENVIRON + /* case NEW_ENV_OVAR: */ + if (pointer[0] == TELOPT_OLD_ENVIRON) { +# ifdef ENV_HACK + if (old_env_var == OLD_ENV_VALUE) + fprintf(NetTrace, "\" (VALUE) " + noquote); + else +# endif + fprintf(NetTrace, "\" VAR " + noquote); + } else +#endif /* OLD_ENVIRON */ + fprintf(NetTrace, "%s", "\" VALUE " + noquote); + noquote = 2; + break; + + case NEW_ENV_VAR: +#ifdef OLD_ENVIRON + /* case OLD_ENV_VALUE: */ + if (pointer[0] == TELOPT_OLD_ENVIRON) { +# ifdef ENV_HACK + if (old_env_value == OLD_ENV_VAR) + fprintf(NetTrace, "\" (VAR) " + noquote); + else +# endif + fprintf(NetTrace, "\" VALUE " + noquote); + } else +#endif /* OLD_ENVIRON */ + fprintf(NetTrace, "%s", "\" VAR " + noquote); + noquote = 2; + break; + + case ENV_ESC: + fprintf(NetTrace, "%s", "\" ESC " + noquote); + noquote = 2; + break; + + case ENV_USERVAR: + fprintf(NetTrace, "%s", "\" USERVAR " + noquote); + noquote = 2; + break; + + default: + if (isprint(pointer[i]) && pointer[i] != '"') { + if (noquote) { + putc('"', NetTrace); + noquote = 0; + } + putc(pointer[i], NetTrace); + } else { + fprintf(NetTrace, "\" %03o " + noquote, + pointer[i]); + noquote = 2; + } + break; + } + } + if (!noquote) + putc('"', NetTrace); + break; + } + } + break; + + default: + if (TELOPT_OK(pointer[0])) + fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0])); + else + fprintf(NetTrace, "%d (unknown)", pointer[0]); + for (i = 1; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + if (direction) { + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + } + if (NetTrace == stdout) + fflush(NetTrace); + } +} + +/* EmptyTerminal - called to make sure that the terminal buffer is empty. + * Note that we consider the buffer to run all the + * way to the kernel (thus the select). + */ + +static void +EmptyTerminal(void) +{ + fd_set o; + + FD_ZERO(&o); + + if (TTYBYTES() == 0) { + FD_SET(tout, &o); + (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, + (struct timeval *) 0); /* wait for TTLOWAT */ + } else { + while (TTYBYTES()) { + (void) ttyflush(0); + FD_SET(tout, &o); + (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, + (struct timeval *) 0); /* wait for TTLOWAT */ + } + } +} + +static void +SetForExit(void) +{ + setconnmode(0); + do { + (void)telrcv(); /* Process any incoming data */ + EmptyTerminal(); + } while (ring_full_count(&netiring)); /* While there is any */ + setcommandmode(); + fflush(stdout); + fflush(stderr); + setconnmode(0); + EmptyTerminal(); /* Flush the path to the tty */ + setcommandmode(); +} + +void +Exit(int returnCode) +{ + SetForExit(); + exit(returnCode); +} + +void +ExitString(const char *string, int returnCode) +{ + SetForExit(); + fwrite(string, 1, strlen(string), stderr); + exit(returnCode); +} diff --git a/remote_cmds/telnetd.tproj/Makefile b/remote_cmds/telnetd.tproj/Makefile new file mode 100644 index 0000000..c3f7feb --- /dev/null +++ b/remote_cmds/telnetd.tproj/Makefile @@ -0,0 +1,50 @@ +Project = telnetd +ifeq "$(RC_TARGET_CONFIG)" "iPhone" +Install_Dir = /usr/libexec +else +Install_Dir = /usr/local/libexec +endif + +HFILES = defs.h ext.h pathnames.h telnetd.h +CFILES = global.c slc.c state.c sys_term.c telnetd.c\ + termstat.c utility.c +ifeq "$(RC_TARGET_CONFIG)" "iPhone" +LAUNCHD_PLISTS = telnet.plist +endif + +Extra_CC_Flags = -Wall -Werror -Wno-string-plus-int -fPIE +Extra_CC_Flags += -D__FBSDID=__RCSID +Extra_LD_Flags = -dead_strip -pie + +Extra_CC_Flags += -DNO_UTMP -DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO \ + -DDIAGNOSTICS -DOLD_ENVIRON -DENV_HACK -DINET6 \ + # -DAUTHENTICATION -DENCRYPTION +Extra_LD_Libraries = -lcurses -ltelnet + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make + +after_install: +ifeq "$(RC_TARGET_CONFIG)" "iPhone" + /usr/libexec/PlistBuddy -x \ + -c "Delete :Disabled" \ + -c "Add :PosixSpawnType string Interactive" \ + -c "Delete :SessionCreate" \ + -c "Set :Sockets:Listeners:Bonjour false" \ + -c "Add :Sockets:Listeners:SockFamily string IPv4" \ + -c "Add :Sockets:Listeners:SockNodeName string localhost" \ + "$(DSTROOT)/System/Library/LaunchDaemons/telnet.plist" +ifeq "$(RC_PLATFORM_NAME)" "BridgeOS" + /usr/libexec/PlistBuddy -x \ + -c "Delete :Sockets:Listeners:SockNodeName" \ + -c "Delete :Sockets:Listeners:SockFamily" \ + "$(DSTROOT)/System/Library/LaunchDaemons/telnet.plist" +endif + plutil -convert binary1 "$(DSTROOT)/System/Library/LaunchDaemons/$(LAUNCHD_PLISTS)" +endif + /usr/bin/codesign --force --sign - --entitlements entitlements.plist $(DSTROOT)$(Install_Dir)/$(Project) + +# Install a special launchd plist for the DebugDiskImage (38885624) +ifeq "$(RC_TARGET_CONFIG)" "iPhone" + plutil -replace ProgramArguments -json '["/var/personalized_debug/usr/libexec/telnetd","-p","/var/personalized_debug/usr/bin/login"]' -o "$(DSTROOT)/System/Library/LaunchDaemons/telnet.debug.plist" "$(DSTROOT)/System/Library/LaunchDaemons/telnet.plist" + plutil -replace Label -string com.apple.telnetd.debug "$(DSTROOT)/System/Library/LaunchDaemons/telnet.debug.plist" +endif diff --git a/remote_cmds/telnetd.tproj/authenc.c b/remote_cmds/telnetd.tproj/authenc.c new file mode 100644 index 0000000..10934ff --- /dev/null +++ b/remote_cmds/telnetd.tproj/authenc.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)authenc.c 8.2 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/authenc.c,v 1.8 2003/05/04 02:54:49 obrien Exp $"); + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) +#include "telnetd.h" +#include <libtelnet/misc.h> + +int +net_write(unsigned char *str, int len) +{ + if (nfrontp + len < netobuf + BUFSIZ) { + output_datalen(str, len); + return(len); + } + return(0); +} + +void +net_encrypt(void) +{ +#ifdef ENCRYPTION + char *s = (nclearto > nbackp) ? nclearto : nbackp; + if (s < nfrontp && encrypt_output) { + (*encrypt_output)((unsigned char *)s, nfrontp - s); + } + nclearto = nfrontp; +#endif /* ENCRYPTION */ +} + +int +telnet_spin(void) +{ + ttloop(); + return(0); +} + +char * +telnet_getenv(char *val) +{ + return(getenv(val)); +} + +char * +telnet_gets(const char *prompt __unused, char *result __unused, int length __unused, int echo __unused) +{ + return(NULL); +} +#endif /* ENCRYPTION */ +#endif /* AUTHENTICATION */ diff --git a/remote_cmds/telnetd.tproj/defs.h b/remote_cmds/telnetd.tproj/defs.h new file mode 100644 index 0000000..31ef2fb --- /dev/null +++ b/remote_cmds/telnetd.tproj/defs.h @@ -0,0 +1,258 @@ +/* + * 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. + * + * @(#)defs.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: src/contrib/telnet/telnetd/defs.h,v 1.2 2001/08/29 14:16:15 markm Exp $ + */ + +/* + * Telnet server defines + */ +#include <sys/types.h> +#include <sys/param.h> + +#ifndef BSD +# define BSD 43 +#endif + +#if defined(PRINTOPTIONS) && defined(DIAGNOSTICS) +#define TELOPTS +#define TELCMDS +#define SLC_NAMES +#endif + +#if defined(SYSV_TERMIO) && !defined(USE_TERMIO) +# define USE_TERMIO +#endif + +#include <sys/socket.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef FILIO_H +#include <sys/ioctl.h> +#else +#include <sys/filio.h> +#endif + +#include <netinet/in.h> + +#include <arpa/telnet.h> + +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#endif +#include <signal.h> +#include <errno.h> +#include <netdb.h> +#include <syslog.h> +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_ODELAY +#define LOG_ODELAY 0 +#endif +#include <ctype.h> +#ifndef NO_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#ifndef USE_TERMIO +#include <sgtty.h> +#else +# ifdef SYSV_TERMIO +# include <termio.h> +# else +# include <termios.h> +# endif +#endif +#if !defined(USE_TERMIO) || defined(NO_CC_T) +typedef unsigned char cc_t; +#endif + +#ifdef __STDC__ +#include <unistd.h> +#endif + +#ifndef _POSIX_VDISABLE +# ifdef VDISABLE +# define _POSIX_VDISABLE VDISABLE +# else +# define _POSIX_VDISABLE ((unsigned char)'\377') +# endif +#endif + +#if !defined(TIOCSCTTY) && defined(TCSETCTTY) +# define TIOCSCTTY TCSETCTTY +#endif + +#ifndef FD_SET +#ifndef HAVE_fd_set +typedef struct fd_set { int fds_bits[1]; } fd_set; +#endif + +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) +#endif /* FD_SET */ + +/* + * I/O data buffers defines + */ +#define NETSLOP 64 + +#define NIACCUM(c) { *netip++ = c; \ + ncc++; \ + } + +/* clock manipulations */ +#define settimer(x) (clocks.x = ++clocks.system) +#define sequenceIs(x,y) (clocks.x < clocks.y) + +/* + * Linemode support states, in decreasing order of importance + */ +#define REAL_LINEMODE 0x04 +#define KLUDGE_OK 0x03 +#define NO_AUTOKLUDGE 0x02 +#define KLUDGE_LINEMODE 0x01 +#define NO_LINEMODE 0x00 + +/* + * Structures of information for each special character function. + */ +typedef struct { + unsigned char flag; /* the flags for this function */ + cc_t val; /* the value of the special character */ +} slcent, *Slcent; + +typedef struct { + slcent defset; /* the default settings */ + slcent current; /* the current settings */ + cc_t *sptr; /* a pointer to the char in */ + /* system data structures */ +} slcfun, *Slcfun; + +#ifdef DIAGNOSTICS +/* + * Diagnostics capabilities + */ +#define TD_REPORT 0x01 /* Report operations to client */ +#define TD_EXERCISE 0x02 /* Exercise client's implementation */ +#define TD_NETDATA 0x04 /* Display received data stream */ +#define TD_PTYDATA 0x08 /* Display data passed to pty */ +#define TD_OPTIONS 0x10 /* Report just telnet options */ +#endif /* DIAGNOSTICS */ + +/* + * 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) + +/* + * Tricky code here. What we want to know is if the MY_STATE_WILL + * and MY_WANT_STATE_WILL bits have the same value. Since the two + * bits are adjacent, a little arithmatic will show that by adding + * in the lower bit, the upper bit will be set if the two bits were + * different, and clear if they were the same. + */ +#define my_will_wont_is_changing(opt) \ + ((options[opt]+MY_STATE_WILL) & MY_WANT_STATE_WILL) + +#define my_do_dont_is_changing(opt) \ + ((options[opt]+MY_STATE_DO) & MY_WANT_STATE_DO) + +/* + * 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 + +#define his_will_wont_is_changing my_do_dont_is_changing +#define his_do_dont_is_changing my_will_wont_is_changing diff --git a/remote_cmds/telnetd.tproj/entitlements.plist b/remote_cmds/telnetd.tproj/entitlements.plist new file mode 100644 index 0000000..4d3ae68 --- /dev/null +++ b/remote_cmds/telnetd.tproj/entitlements.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.network.server</key> + <true/> +</dict> +</plist> diff --git a/remote_cmds/telnetd.tproj/ext.h b/remote_cmds/telnetd.tproj/ext.h new file mode 100644 index 0000000..0905b61 --- /dev/null +++ b/remote_cmds/telnetd.tproj/ext.h @@ -0,0 +1,222 @@ +/* + * 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. + * + * @(#)ext.h 8.2 (Berkeley) 12/15/93 + * $FreeBSD: src/contrib/telnet/telnetd/ext.h,v 1.11 2001/11/30 22:28:07 markm Exp $ + */ + +/* + * Telnet server variable declarations + */ +extern char options[256]; +extern char do_dont_resp[256]; +extern char will_wont_resp[256]; +extern int linemode; /* linemode on/off */ +#ifdef LINEMODE +extern int uselinemode; /* what linemode to use (on/off) */ +extern int editmode; /* edit modes in use */ +extern int useeditmode; /* edit modes to use */ +extern int alwayslinemode; /* command line option */ +extern int lmodetype; /* Client support for linemode */ +#endif /* LINEMODE */ +extern int flowmode; /* current flow control state */ +extern int restartany; /* restart output on any character state */ +#ifdef DIAGNOSTICS +extern int diagnostic; /* telnet diagnostic capabilities */ +#endif /* DIAGNOSTICS */ +#ifdef BFTPDAEMON +extern int bftpd; /* behave as bftp daemon */ +#endif /* BFTPDAEMON */ +#ifdef AUTHENTICATION +extern int auth_level; +#endif + +extern slcfun slctab[NSLC + 1]; /* slc mapping table */ + +extern char *terminaltype; + +/* + * I/O data buffers, pointers, and counters. + */ +extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; + +extern char netibuf[BUFSIZ], *netip; + +extern char netobuf[BUFSIZ], *nfrontp, *nbackp; +extern char *neturg; /* one past last bye of urgent data */ + +extern int pcc, ncc; + +extern int mpty, spty, net; +extern char line[16]; +extern int SYNCHing; /* we are in TELNET SYNCH mode */ + +extern void + _termstat(void), + add_slc(char, char, cc_t), + check_slc(void), + change_slc(char, char, cc_t), + cleanup(int), + clientstat(int, int, int), + copy_termbuf(char *, size_t), + deferslc(void), + defer_terminit(void), + do_opt_slc(unsigned char *, int), + doeof(void), + dooption(int), + dontoption(int), + edithost(char *, char *), + fatal(int, const char *), + fatalperror(int, const char *), + get_slc_defaults(void), + init_env(void), + init_termbuf(void), + interrupt(void), + localstat(void), + flowstat(void), + netclear(void), + netflush(void), +#ifdef DIAGNOSTICS + printoption(const char *, int), + printdata(const char *, char *, int), + printsub(char, unsigned char *, int), +#endif + process_slc(unsigned char, unsigned char, cc_t), + ptyflush(void), + putchr(int), + putf(char *, char *), + recv_ayt(void), + send_do(int, int), + send_dont(int, int), + send_slc(void), + send_status(void), + send_will(int, int), + send_wont(int, int), + sendbrk(void), + sendsusp(void), + set_termbuf(void), + start_login(char *, int, char *), + start_slc(int), +#ifdef AUTHENTICATION + start_slave(char *), +#else + start_slave(char *, int, char *), +#endif + suboption(void), + telrcv(void), + ttloop(void), + tty_binaryin(int), + tty_binaryout(int); + +extern int + end_slc(unsigned char **), + getnpty(void), +#ifndef convex + getpty(int *, int *), +#endif + login_tty(int), + spcset(int, cc_t *, cc_t **), + stilloob(int), + terminit(void), + termstat(void), + tty_flowmode(void), + tty_restartany(void), + tty_isbinaryin(void), + tty_isbinaryout(void), + tty_iscrnl(void), + tty_isecho(void), + tty_isediting(void), + tty_islitecho(void), + tty_isnewmap(void), + tty_israw(void), + tty_issofttab(void), + tty_istrapsig(void), + tty_linemode(void); + +extern void + tty_rspeed(int), + tty_setecho(int), + tty_setedit(int), + tty_setlinemode(int), + tty_setlitecho(int), + tty_setsig(int), + tty_setsofttab(int), + tty_tspeed(int), + willoption(int), + wontoption(int); + +int output_data(const char *, ...) __printflike(1, 2); +void output_datalen(const char *, int); +void startslave(char *, int, char *); + +#ifdef ENCRYPTION +extern void (*encrypt_output)(unsigned char *, int); +extern int (*decrypt_input)(int); +extern char *nclearto; +#endif /* ENCRYPTION */ + + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +extern struct { + int + system, /* what the current time is */ + echotoggle, /* last time user entered echo character */ + modenegotiated, /* last time operating mode negotiated */ + didnetreceive, /* last time we read data from network */ + ttypesubopt, /* ttype subopt is received */ + tspeedsubopt, /* tspeed subopt is received */ + environsubopt, /* environ subopt is received */ + oenvironsubopt, /* old environ subopt is received */ + xdisplocsubopt, /* xdisploc subopt is received */ + baseline, /* time started to do timed action */ + gotDM; /* when did we last see a data mark */ +} clocks; + +#ifndef DEFAULT_IM +#ifdef __APPLE__ +# define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r" +#else +# ifdef ultrix +# define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r" +# else +# ifdef __FreeBSD__ +# define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r" +# else +# define DEFAULT_IM "\r\n\r\n4.4 BSD UNIX (%h) (%t)\r\n\r\r\n\r" +# endif +# endif +#endif /* __APPLE__ */ +#endif diff --git a/remote_cmds/telnetd.tproj/global.c b/remote_cmds/telnetd.tproj/global.c new file mode 100644 index 0000000..eefd129 --- /dev/null +++ b/remote_cmds/telnetd.tproj/global.c @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)global.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/global.c,v 1.6 2003/05/04 02:54:49 obrien Exp $"); + +/* + * Allocate global variables. We do this + * by including the header file that defines + * them all as externs, but first we define + * the keyword "extern" to be nothing, so that + * we will actually allocate the space. + */ + +#include "defs.h" +#define extern +#include "ext.h" diff --git a/remote_cmds/telnetd.tproj/pathnames.h b/remote_cmds/telnetd.tproj/pathnames.h new file mode 100644 index 0000000..43fce7e --- /dev/null +++ b/remote_cmds/telnetd.tproj/pathnames.h @@ -0,0 +1,56 @@ +/* + * 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: src/contrib/telnet/telnetd/pathnames.h,v 1.3 2001/08/20 12:28:40 markm Exp $ + */ + +#if BSD > 43 + +# include <paths.h> + +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/usr/bin/login" +# endif + +#else + +# define _PATH_TTY "/dev/tty" +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/bin/login" +# endif + +#endif + +#ifdef BFTPDAEMON +#define BFTPPATH "/usr/ucb/bftp" +#endif /* BFTPDAEMON */ diff --git a/remote_cmds/telnetd.tproj/slc.c b/remote_cmds/telnetd.tproj/slc.c new file mode 100644 index 0000000..eb31ef8 --- /dev/null +++ b/remote_cmds/telnetd.tproj/slc.c @@ -0,0 +1,484 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)slc.c 8.2 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/slc.c,v 1.9 2003/05/04 02:54:49 obrien Exp $"); + +#include "telnetd.h" + +#ifdef LINEMODE +/* + * local variables + */ +static unsigned char *def_slcbuf = (unsigned char *)0; +static int def_slclen = 0; +static int slcchange; /* change to slc is requested */ +static unsigned char *slcptr; /* pointer into slc buffer */ +static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */ + +/* + * send_slc + * + * Write out the current special characters to the client. + */ +void +send_slc(void) +{ + int i; + + /* + * Send out list of triplets of special characters + * to client. We only send info on the characters + * that are currently supported. + */ + for (i = 1; i <= NSLC; i++) { + if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT) + continue; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + +} /* end of send_slc */ + +/* + * default_slc + * + * Set pty special characters to all the defaults. + */ +static void +default_slc(void) +{ + int i; + + for (i = 1; i <= NSLC; i++) { + slctab[i].current.val = slctab[i].defset.val; + if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE)) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + if (slctab[i].sptr) { + *(slctab[i].sptr) = slctab[i].defset.val; + } + } + slcchange = 1; + +} /* end of default_slc */ +#endif /* LINEMODE */ + +/* + * get_slc_defaults + * + * Initialize the slc mapping table. + */ +void +get_slc_defaults(void) +{ + int i; + + init_termbuf(); + + for (i = 1; i <= NSLC; i++) { + slctab[i].defset.flag = + spcset(i, &slctab[i].defset.val, &slctab[i].sptr); + slctab[i].current.flag = SLC_NOSUPPORT; + slctab[i].current.val = 0; + } + +} /* end of get_slc_defaults */ + +#ifdef LINEMODE +/* + * add_slc + * + * Add an slc triplet to the slc buffer. + */ +void +add_slc(char func, char flag, cc_t val) +{ + + if ((*slcptr++ = (unsigned char)func) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)flag) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)val) == 0xff) + *slcptr++ = 0xff; + +} /* end of add_slc */ + +/* + * start_slc + * + * Get ready to process incoming slc's and respond to them. + * + * The parameter getit is non-zero if it is necessary to grab a copy + * of the terminal control structures. + */ +void +start_slc(int getit) +{ + + slcchange = 0; + if (getit) + init_termbuf(); + (void) sprintf((char *)slcbuf, "%c%c%c%c", + IAC, SB, TELOPT_LINEMODE, LM_SLC); + slcptr = slcbuf + 4; + +} /* end of start_slc */ + +/* + * end_slc + * + * Finish up the slc negotiation. If something to send, then send it. + */ +int +end_slc(unsigned char **bufp) +{ + int len; + + /* + * If a change has occured, store the new terminal control + * structures back to the terminal driver. + */ + if (slcchange) { + set_termbuf(); + } + + /* + * If the pty state has not yet been fully processed and there is a + * deferred slc request from the client, then do not send any + * sort of slc negotiation now. We will respond to the client's + * request very soon. + */ + if (def_slcbuf && (terminit() == 0)) { + return(0); + } + + if (slcptr > (slcbuf + 4)) { + if (bufp) { + *bufp = &slcbuf[4]; + return(slcptr - slcbuf - 4); + } else { + (void) sprintf((char *)slcptr, "%c%c", IAC, SE); + slcptr += 2; + len = slcptr - slcbuf; + output_datalen((const char*)slcbuf, len); + netflush(); /* force it out immediately */ + DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2);); + } + } + return (0); + +} /* end of end_slc */ + +/* + * process_slc + * + * Figure out what to do about the client's slc + */ +void +process_slc(unsigned char func, unsigned char flag, cc_t val) +{ + int hislevel, mylevel, ack; + + /* + * Ensure that we know something about this function + */ + if (func > NSLC) { + add_slc(func, SLC_NOSUPPORT, 0); + return; + } + + /* + * Process the special case requests of 0 SLC_DEFAULT 0 + * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't + * worry about whether the value is actually 0 or not. + */ + if (func == 0) { + if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) { + default_slc(); + send_slc(); + } else if (flag == SLC_VARIABLE) { + send_slc(); + } + return; + } + + /* + * Appears to be a function that we know something about. So + * get on with it and see what we know. + */ + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].current.flag & SLC_LEVELBITS; + ack = flag & SLC_ACK; + /* + * ignore the command if: + * the function value and level are the same as what we already have; + * or the level is the same and the ack bit is set + */ + if (hislevel == mylevel && (val == slctab[func].current.val || ack)) { + return; + } else if (ack) { + /* + * If we get here, we got an ack, but the levels don't match. + * This shouldn't happen. If it does, it is probably because + * we have sent two requests to set a variable without getting + * a response between them, and this is the first response. + * So, ignore it, and wait for the next response. + */ + return; + } else { + change_slc(func, flag, val); + } + +} /* end of process_slc */ + +/* + * change_slc + * + * Process a request to change one of our special characters. + * Compare client's request with what we are capable of supporting. + */ +void +change_slc(char func, char flag, cc_t val) +{ + int hislevel, mylevel; + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[(int)func].defset.flag & SLC_LEVELBITS; + /* + * If client is setting a function to NOSUPPORT + * or DEFAULT, then we can easily and directly + * accomodate the request. + */ + if (hislevel == SLC_NOSUPPORT) { + slctab[(int)func].current.flag = flag; + slctab[(int)func].current.val = (cc_t)_POSIX_VDISABLE; + flag |= SLC_ACK; + add_slc(func, flag, val); + return; + } + if (hislevel == SLC_DEFAULT) { + /* + * Special case here. If client tells us to use + * the default on a function we don't support, then + * return NOSUPPORT instead of what we may have as a + * default level of DEFAULT. + */ + if (mylevel == SLC_DEFAULT) { + slctab[(int)func].current.flag = SLC_NOSUPPORT; + } else { + slctab[(int)func].current.flag = slctab[(int)func].defset.flag; + } + slctab[(int)func].current.val = slctab[(int)func].defset.val; + add_slc(func, slctab[(int)func].current.flag, + slctab[(int)func].current.val); + return; + } + + /* + * Client wants us to change to a new value or he + * is telling us that he can't change to our value. + * Some of the slc's we support and can change, + * some we do support but can't change, + * and others we don't support at all. + * If we can change it then we have a pointer to + * the place to put the new value, so change it, + * otherwise, continue the negotiation. + */ + if (slctab[(int)func].sptr) { + /* + * We can change this one. + */ + slctab[(int)func].current.val = val; + *(slctab[(int)func].sptr) = val; + slctab[(int)func].current.flag = flag; + flag |= SLC_ACK; + slcchange = 1; + add_slc(func, flag, val); + } else { + /* + * It is not possible for us to support this + * request as he asks. + * + * If our level is DEFAULT, then just ack whatever was + * sent. + * + * If he can't change and we can't change, + * then degenerate to NOSUPPORT. + * + * Otherwise we send our level back to him, (CANTCHANGE + * or NOSUPPORT) and if CANTCHANGE, send + * our value as well. + */ + if (mylevel == SLC_DEFAULT) { + slctab[(int)func].current.flag = flag; + slctab[(int)func].current.val = val; + flag |= SLC_ACK; + } else if (hislevel == SLC_CANTCHANGE && + mylevel == SLC_CANTCHANGE) { + flag &= ~SLC_LEVELBITS; + flag |= SLC_NOSUPPORT; + slctab[(int)func].current.flag = flag; + } else { + flag &= ~SLC_LEVELBITS; + flag |= mylevel; + slctab[(int)func].current.flag = flag; + if (mylevel == SLC_CANTCHANGE) { + slctab[(int)func].current.val = + slctab[(int)func].defset.val; + val = slctab[(int)func].current.val; + } + } + add_slc(func, flag, val); + } + +} /* end of change_slc */ + +#if defined(USE_TERMIO) && (VEOF == VMIN) +cc_t oldeofc = '\004'; +#endif + +/* + * check_slc + * + * Check the special characters in use and notify the client if any have + * changed. Only those characters that are capable of being changed are + * likely to have changed. If a local change occurs, kick the support level + * and flags up to the defaults. + */ +void +check_slc(void) +{ + int i; + + for (i = 1; i <= NSLC; i++) { +#if defined(USE_TERMIO) && (VEOF == VMIN) + /* + * In a perfect world this would be a neat little + * function. But in this world, we should not notify + * client of changes to the VEOF char when + * ICANON is off, because it is not representing + * a special character. + */ + if (i == SLC_EOF) { + if (!tty_isediting()) + continue; + else if (slctab[i].sptr) + oldeofc = *(slctab[i].sptr); + } +#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */ + if (slctab[i].sptr && + (*(slctab[i].sptr) != slctab[i].current.val)) { + slctab[i].current.val = *(slctab[i].sptr); + if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + } +} /* check_slc */ + +/* + * do_opt_slc + * + * Process an slc option buffer. Defer processing of incoming slc's + * until after the terminal state has been processed. Save the first slc + * request that comes along, but discard all others. + * + * ptr points to the beginning of the buffer, len is the length. + */ +void +do_opt_slc(unsigned char *ptr, int len) +{ + unsigned char func, flag; + cc_t val; + unsigned char *end = ptr + len; + + if (terminit()) { /* go ahead */ + while (ptr < end) { + func = *ptr++; + if (ptr >= end) break; + flag = *ptr++; + if (ptr >= end) break; + val = (cc_t)*ptr++; + + process_slc(func, flag, val); + + } + } else { + /* + * save this slc buffer if it is the first, otherwise dump + * it. + */ + if (def_slcbuf == (unsigned char *)0) { + def_slclen = len; + def_slcbuf = (unsigned char *)malloc((unsigned)len); + if (def_slcbuf == (unsigned char *)0) + return; /* too bad */ + memmove(def_slcbuf, ptr, len); + } + } + +} /* end of do_opt_slc */ + +/* + * deferslc + * + * Do slc stuff that was deferred. + */ +void +deferslc(void) +{ + if (def_slcbuf) { + start_slc(1); + do_opt_slc(def_slcbuf, def_slclen); + (void) end_slc(0); + free(def_slcbuf); + def_slcbuf = (unsigned char *)0; + def_slclen = 0; + } + +} /* end of deferslc */ + +#endif /* LINEMODE */ diff --git a/remote_cmds/telnetd.tproj/state.c b/remote_cmds/telnetd.tproj/state.c new file mode 100644 index 0000000..a4dc138 --- /dev/null +++ b/remote_cmds/telnetd.tproj/state.c @@ -0,0 +1,1631 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)state.c 8.5 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/state.c,v 1.14 2003/05/04 02:54:49 obrien Exp $"); + +#include <stdarg.h> +#include "telnetd.h" +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +unsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; +unsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; +unsigned char will[] = { IAC, WILL, '%', 'c', 0 }; +unsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; +int not42 = 1; + +/* + * Buffer for sub-options, and macros + * for suboptions buffer manipulations + */ +unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer; + +#define SB_CLEAR() subpointer = subbuffer +#define SB_TERM() { subend = subpointer; SB_CLEAR(); } +#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ + *subpointer++ = (c); \ + } +#define SB_GET() ((*subpointer++)&0xff) +#define SB_EOF() (subpointer >= subend) +#define SB_LEN() (subend - subpointer) + +#ifdef ENV_HACK +unsigned char *subsave; +#define SB_SAVE() subsave = subpointer; +#define SB_RESTORE() subpointer = subsave; +#endif + + +/* + * State for recv fsm + */ +#define TS_DATA 0 /* base state */ +#define TS_IAC 1 /* look for double IAC's */ +#define TS_CR 2 /* CR-LF ->'s CR */ +#define TS_SB 3 /* throw away begin's... */ +#define TS_SE 4 /* ...end's (suboption negotiation) */ +#define TS_WILL 5 /* will option negotiation */ +#define TS_WONT 6 /* wont " */ +#define TS_DO 7 /* do " */ +#define TS_DONT 8 /* dont " */ + +static void doclientstat(void); + +void +telrcv(void) +{ + int c; + static int state = TS_DATA; + + while (ncc > 0) { + if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) + break; + c = *netip++ & 0377, ncc--; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + switch (state) { + + case TS_CR: + state = TS_DATA; + /* Strip off \n or \0 after a \r */ + if ((c == 0) || (c == '\n')) { + break; + } + /* FALLTHROUGH */ + + case TS_DATA: + if (c == IAC) { + state = TS_IAC; + break; + } + /* + * We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + * + * We USED to map \r\n ==> \n, since \r\n says + * that we want to be in column 1 of the next + * printable line, and \n is the standard + * unix way of saying that (\r is only good + * if CRMOD is set, which it normally is). + */ + if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { + int nc = *netip; +#ifdef ENCRYPTION + if (decrypt_input) + nc = (*decrypt_input)(nc & 0xff); +#endif /* ENCRYPTION */ +#ifdef LINEMODE + /* + * If we are operating in linemode, + * convert to local end-of-line. + */ + if (linemode && (ncc > 0) && (('\n' == nc) || + ((0 == nc) && tty_iscrnl())) ) { + netip++; ncc--; + c = '\n'; + } else +#endif + { +#ifdef ENCRYPTION + if (decrypt_input) + (void)(*decrypt_input)(-1); +#endif /* ENCRYPTION */ + state = TS_CR; + } + } + *pfrontp++ = c; + break; + + case TS_IAC: +gotiac: switch (c) { + + /* + * Send the process on the pty side an + * interrupt. Do this with a NULL or + * interrupt char; depending on the tty mode. + */ + case IP: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + interrupt(); + break; + + case BREAK: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + sendbrk(); + break; + + /* + * Are You There? + */ + case AYT: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + recv_ayt(); + break; + + /* + * Abort Output + */ + case AO: + { + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + + if (slctab[SLC_AO].sptr && + *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { + *pfrontp++ = + (unsigned char)*slctab[SLC_AO].sptr; + } + + netclear(); /* clear buffer back */ + output_data("%c%c", IAC, DM); + neturg = nfrontp-1; /* off by one XXX */ + DIAG(TD_OPTIONS, + printoption("td: send IAC", DM)); + break; + } + + /* + * Erase Character and + * Erase Line + */ + case EC: + case EL: + { + cc_t ch; + + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + if (c == EC) + ch = *slctab[SLC_EC].sptr; + else + ch = *slctab[SLC_EL].sptr; + if (ch != (cc_t)(_POSIX_VDISABLE)) + *pfrontp++ = (unsigned char)ch; + break; + } + + /* + * Check for urgent data... + */ + case DM: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + SYNCHing = stilloob(net); + settimer(gotDM); + break; + + + /* + * Begin option subnegotiation... + */ + case SB: + state = TS_SB; + SB_CLEAR(); + continue; + + case WILL: + state = TS_WILL; + continue; + + case WONT: + state = TS_WONT; + continue; + + case DO: + state = TS_DO; + continue; + + case DONT: + state = TS_DONT; + continue; + case EOR: + if (his_state_is_will(TELOPT_EOR)) + doeof(); + break; + + /* + * Handle RFC 10xx Telnet linemode option additions + * to command stream (EOF, SUSP, ABORT). + */ + case xEOF: + doeof(); + break; + + case SUSP: + sendsusp(); + break; + + case ABORT: + sendbrk(); + break; + + case IAC: + *pfrontp++ = c; + break; + } + state = TS_DATA; + break; + + case TS_SB: + if (c == IAC) { + state = TS_SE; + } else { + SB_ACCUM(c); + } + break; + + case TS_SE: + if (c != SE) { + if (c != IAC) { + /* + * bad form of suboption negotiation. + * handle it in such a way as to avoid + * damage to local state. Parse + * suboption buffer found so far, + * then treat remaining stream as + * another command sequence. + */ + + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(c); + subpointer -= 2; + + SB_TERM(); + suboption(); + state = TS_IAC; + goto gotiac; + } + SB_ACCUM(c); + state = TS_SB; + } else { + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(SE); + subpointer -= 2; + + SB_TERM(); + suboption(); /* handle sub-option */ + state = TS_DATA; + } + break; + + case TS_WILL: + willoption(c); + state = TS_DATA; + continue; + + case TS_WONT: + wontoption(c); + state = TS_DATA; + continue; + + case TS_DO: + dooption(c); + state = TS_DATA; + continue; + + case TS_DONT: + dontoption(c); + state = TS_DATA; + continue; + + default: + syslog(LOG_ERR, "panic state=%d", state); + printf("telnetd: panic state=%d\n", state); + exit(1); + } + } +} /* end of telrcv */ + +/* + * The will/wont/do/dont state machines are based on Dave Borman's + * Telnet option processing state machine. + * + * These correspond to the following states: + * my_state = the last negotiated state + * want_state = what I want the state to go to + * want_resp = how many requests I have sent + * All state defaults are negative, and resp defaults to 0. + * + * When initiating a request to change state to new_state: + * + * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { + * do nothing; + * } else { + * want_state = new_state; + * send new_state; + * want_resp++; + * } + * + * When receiving new_state: + * + * if (want_resp) { + * want_resp--; + * if (want_resp && (new_state == my_state)) + * want_resp--; + * } + * if ((want_resp == 0) && (new_state != want_state)) { + * if (ok_to_switch_to new_state) + * want_state = new_state; + * else + * want_resp++; + * send want_state; + * } + * my_state = new_state; + * + * Note that new_state is implied in these functions by the function itself. + * will and do imply positive new_state, wont and dont imply negative. + * + * Finally, there is one catch. If we send a negative response to a + * positive request, my_state will be the positive while want_state will + * remain negative. my_state will revert to negative when the negative + * acknowlegment arrives from the peer. Thus, my_state generally tells + * us not only the last negotiated state, but also tells us what the peer + * wants to be doing as well. It is important to understand this difference + * as we may wish to be processing data streams based on our desired state + * (want_state) or based on what the peer thinks the state is (my_state). + * + * This all works fine because if the peer sends a positive request, the data + * that we receive prior to negative acknowlegment will probably be affected + * by the positive state, and we can process it as such (if we can; if we + * can't then it really doesn't matter). If it is that important, then the + * peer probably should be buffering until this option state negotiation + * is complete. + * + */ +void +send_do(int option, int init) +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || + his_want_state_is_will(option)) + return; + /* + * Special case for TELOPT_TM: We send a DO, but pretend + * that we sent a DONT, so that we can send more DOs if + * we want to. + */ + if (option == TELOPT_TM) + set_his_want_state_wont(option); + else + set_his_want_state_will(option); + do_dont_resp[option]++; + } + output_data((const char *)doopt, option); + + DIAG(TD_OPTIONS, printoption("td: send do", option)); +} + +void +willoption(int option) +{ + int changeok = 0; + void (*func)(void) = 0; + + /* + * process input from peer. + */ + + DIAG(TD_OPTIONS, printoption("td: recv will", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_will(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_wont(option)) { + switch (option) { + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_ECHO: + /* + * See comments below for more info. + */ + not42 = 0; /* looks like a 4.2 system */ + break; + + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * This telnetd implementation does not really + * support timing marks, it just uses them to + * support the kludge linemode stuff. If we + * receive a will or wont TM in response to our + * do TM request that may have been sent to + * determine kludge linemode support, process + * it, otherwise TM should get a negative + * response back. + */ + /* + * Handle the linemode kludge stuff. + * If we are not currently supporting any + * linemode at all, then we assume that this + * is the client telling us to use kludge + * linemode in response to our query. Set the + * linemode type that is to be supported, note + * that the client wishes to use linemode, and + * eat the will TM as though it never arrived. + */ + if (lmodetype < KLUDGE_LINEMODE) { + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == NO_AUTOKLUDGE) { + lmodetype = KLUDGE_OK; + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + /* + * We never respond to a WILL TM, and + * we leave the state WONT. + */ + return; + + case TELOPT_LFLOW: + /* + * If we are going to support flow control + * option, then don't worry peer that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_DEFAULT; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; + case TELOPT_TTYPE: + case TELOPT_SGA: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + case TELOPT_OLD_ENVIRON: + changeok++; + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + changeok++; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + changeok++; + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + func = encrypt_send_support; + changeok++; + break; +#endif /* ENCRYPTION */ + + default: + break; + } + if (changeok) { + set_his_want_state_will(option); + send_do(option, 0); + } else { + do_dont_resp[option]++; + send_dont(option, 0); + } + } else { + /* + * Option processing that should happen when + * we receive conformation of a change in + * state that we had requested. + */ + switch (option) { + case TELOPT_ECHO: + not42 = 0; /* looks like a 4.2 system */ + /* + * Egads, he responded "WILL ECHO". Turn + * it off right now! + */ + send_dont(option, 1); + /* + * "WILL ECHO". Kludge upon kludge! + * A 4.2 client is now echoing user input at + * the tty. This is probably undesireable and + * it should be stopped. The client will + * respond WONT TM to the DO TM that we send to + * check for kludge linemode. When the WONT TM + * arrives, linemode will be turned off and a + * change propogated to the pty. This change + * will cause us to process the new pty state + * in localstat(), which will notice that + * linemode is off and send a WILL ECHO + * so that we are properly in character mode and + * all is well. + */ + break; +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + func = encrypt_send_support; + break; +#endif /* ENCRYPTION */ + case TELOPT_LFLOW: + func = flowstat; + break; + } + } + } + set_his_state_will(option); + if (func) + (*func)(); +} /* end of willoption */ + +void +send_dont(int option, int init) +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || + his_want_state_is_wont(option)) + return; + set_his_want_state_wont(option); + do_dont_resp[option]++; + } + output_data((const char *)dont, option); + + DIAG(TD_OPTIONS, printoption("td: send dont", option)); +} + +void +wontoption(int option) +{ + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv wont", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_wont(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_will(option)) { + /* it is always ok to change to negative state */ + switch (option) { + case TELOPT_ECHO: + not42 = 1; /* doesn't seem to be a 4.2 system */ + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(0); + set_termbuf(); + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * If real linemode is supported, then client is + * asking to turn linemode off. + */ + if (lmodetype != REAL_LINEMODE) + break; + lmodetype = KLUDGE_LINEMODE; +# endif /* KLUDGELINEMODE */ + clientstat(TELOPT_LINEMODE, WONT, 0); + break; +#endif /* LINEMODE */ + + case TELOPT_TM: + /* + * If we get a WONT TM, and had sent a DO TM, + * don't respond with a DONT TM, just leave it + * as is. Short circut the state machine to + * achive this. + */ + set_his_want_state_wont(TELOPT_TM); + return; + + case TELOPT_LFLOW: + /* + * If we are not going to support flow control + * option, then let peer know that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + + /* + * For options that we might spin waiting for + * sub-negotiation, if the client turns off the + * option rather than responding to the request, + * we have to treat it here as if we got a response + * to the sub-negotiation, (by updating the timers) + * so that we'll break out of the loop. + */ + case TELOPT_TTYPE: + settimer(ttypesubopt); + break; + + case TELOPT_TSPEED: + settimer(tspeedsubopt); + break; + + case TELOPT_XDISPLOC: + settimer(xdisplocsubopt); + break; + + case TELOPT_OLD_ENVIRON: + settimer(oenvironsubopt); + break; + + case TELOPT_NEW_ENVIRON: + settimer(environsubopt); + break; + + default: + break; + } + set_his_want_state_wont(option); + if (his_state_is_will(option)) + send_dont(option, 0); + } else { + switch (option) { + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + if (lmodetype < NO_AUTOKLUDGE) { + lmodetype = NO_LINEMODE; + clientstat(TELOPT_LINEMODE, WONT, 0); + send_will(TELOPT_SGA, 1); + send_will(TELOPT_ECHO, 1); + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + default: + break; + } + } + } + set_his_state_wont(option); + +} /* end of wontoption */ + +void +send_will(int option, int init) +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| + my_want_state_is_will(option)) + return; + set_my_want_state_will(option); + will_wont_resp[option]++; + } + output_data((const char *)will, option); + + DIAG(TD_OPTIONS, printoption("td: send will", option)); +} + +#if !defined(LINEMODE) || !defined(KLUDGELINEMODE) +/* + * When we get a DONT SGA, we will try once to turn it + * back on. If the other side responds DONT SGA, we + * leave it at that. This is so that when we talk to + * clients that understand KLUDGELINEMODE but not LINEMODE, + * we'll keep them in char-at-a-time mode. + */ +int turn_on_sga = 0; +#endif + +void +dooption(int option) +{ + int changeok = 0; + + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv do", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_will(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { + switch (option) { + case TELOPT_ECHO: +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if (lmodetype == NO_LINEMODE) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(1); + set_termbuf(); + } + changeok++; + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we must + * process an incoming do SGA for linemode + * purposes. + */ + if (lmodetype == KLUDGE_LINEMODE) { + /* + * Receipt of "do SGA" in kludge + * linemode is the peer asking us to + * turn off linemode. Make note of + * the request. + */ + clientstat(TELOPT_LINEMODE, WONT, 0); + /* + * If linemode did not get turned off + * then don't tell peer that we did. + * Breaking here forces a wont SGA to + * be returned. + */ + if (linemode) + break; + } +#else + turn_on_sga = 0; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + changeok++; + break; + + case TELOPT_STATUS: + changeok++; + break; + + case TELOPT_TM: + /* + * Special case for TM. We send a WILL, but + * pretend we sent a WONT. + */ + send_will(option, 0); + set_my_want_state_wont(option); + set_my_state_wont(option); + return; + + case TELOPT_LOGOUT: + /* + * When we get a LOGOUT option, respond + * with a WILL LOGOUT, make sure that + * it gets written out to the network, + * and then just go away... + */ + set_my_want_state_will(TELOPT_LOGOUT); + send_will(TELOPT_LOGOUT, 0); + set_my_state_will(TELOPT_LOGOUT); + (void)netflush(); + cleanup(0); + /* NOT REACHED */ + break; + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + changeok++; + break; +#endif /* ENCRYPTION */ + case TELOPT_LINEMODE: + case TELOPT_TTYPE: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_LFLOW: + case TELOPT_XDISPLOC: +#ifdef TELOPT_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: + default: + break; + } + if (changeok) { + set_my_want_state_will(option); + send_will(option, 0); + } else { + will_wont_resp[option]++; + send_wont(option, 0); + } + } + set_my_state_will(option); + +} /* end of dooption */ + +void +send_wont(int option, int init) +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || + my_want_state_is_wont(option)) + return; + set_my_want_state_wont(option); + will_wont_resp[option]++; + } + output_data((const char *)wont, option); + + DIAG(TD_OPTIONS, printoption("td: send wont", option)); +} + +void +dontoption(int option) +{ + /* + * Process client input. + */ + + + DIAG(TD_OPTIONS, printoption("td: recv dont", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_wont(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { + switch (option) { + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(0); + set_termbuf(); + break; + + case TELOPT_ECHO: /* we should stop echoing */ +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if ((lmodetype != REAL_LINEMODE) && + (lmodetype != KLUDGE_LINEMODE)) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(0); + set_termbuf(); + } + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we + * must process an incoming do SGA for + * linemode purposes. + */ + if ((lmodetype == KLUDGE_LINEMODE) || + (lmodetype == KLUDGE_OK)) { + /* + * The client is asking us to turn + * linemode on. + */ + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + /* + * If we did not turn line mode on, + * then what do we say? Will SGA? + * This violates design of telnet. + * Gross. Very Gross. + */ + } + break; +#else + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + set_my_state_wont(option); + if (turn_on_sga ^= 1) + send_will(option, 1); + return; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + default: + break; + } + + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + } + set_my_state_wont(option); + +} /* end of dontoption */ + +#ifdef ENV_HACK +int env_ovar = -1; +int env_ovalue = -1; +#else /* ENV_HACK */ +# define env_ovar OLD_ENV_VAR +# define env_ovalue OLD_ENV_VALUE +#endif /* ENV_HACK */ + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type is + * Linemode + * Window size + * Terminal speed + */ +void +suboption(void) +{ + int subchar; + + DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); + + subchar = SB_GET(); + switch (subchar) { + case TELOPT_TSPEED: { + int xspeed, rspeed; + + if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ + break; + + settimer(tspeedsubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + + xspeed = atoi((char *)subpointer); + + while (SB_GET() != ',' && !SB_EOF()); + if (SB_EOF()) + return; + + rspeed = atoi((char *)subpointer); + clientstat(TELOPT_TSPEED, xspeed, rspeed); + + break; + + } /* end of case TELOPT_TSPEED */ + + case TELOPT_TTYPE: { /* Yaaaay! */ + static char terminalname[41]; + + if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ + break; + settimer(ttypesubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) { + return; /* ??? XXX but, this is the most robust */ + } + + terminaltype = terminalname; + + while ((terminaltype < (terminalname + sizeof terminalname-1)) && + !SB_EOF()) { + int c; + + c = SB_GET(); + if (isupper(c)) { + c = tolower(c); + } + *terminaltype++ = c; /* accumulate name */ + } + *terminaltype = 0; + terminaltype = terminalname; + break; + } /* end of case TELOPT_TTYPE */ + + case TELOPT_NAWS: { + int xwinsize, ywinsize; + + if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ + break; + + if (SB_EOF()) + return; + xwinsize = SB_GET() << 8; + if (SB_EOF()) + return; + xwinsize |= SB_GET(); + if (SB_EOF()) + return; + ywinsize = SB_GET() << 8; + if (SB_EOF()) + return; + ywinsize |= SB_GET(); + clientstat(TELOPT_NAWS, xwinsize, ywinsize); + + break; + + } /* end of case TELOPT_NAWS */ + +#ifdef LINEMODE + case TELOPT_LINEMODE: { + int request; + + if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ + break; + /* + * Process linemode suboptions. + */ + if (SB_EOF()) + break; /* garbage was sent */ + request = SB_GET(); /* get will/wont */ + + if (SB_EOF()) + break; /* another garbage check */ + + if (request == LM_SLC) { /* SLC is not preceded by WILL or WONT */ + /* + * Process suboption buffer of slc's + */ + start_slc(1); + do_opt_slc(subpointer, subend - subpointer); + (void) end_slc(0); + break; + } else if (request == LM_MODE) { + if (SB_EOF()) + return; + useeditmode = SB_GET(); /* get mode flag */ + clientstat(LM_MODE, 0, 0); + break; + } + + if (SB_EOF()) + break; + switch (SB_GET()) { /* what suboption? */ + case LM_FORWARDMASK: + /* + * According to spec, only server can send request for + * forwardmask, and client can only return a positive response. + * So don't worry about it. + */ + + default: + break; + } + break; + } /* end of case TELOPT_LINEMODE */ +#endif + case TELOPT_STATUS: { + int mode; + + if (SB_EOF()) + break; + mode = SB_GET(); + switch (mode) { + case TELQUAL_SEND: + if (my_state_is_will(TELOPT_STATUS)) + send_status(); + break; + + case TELQUAL_IS: + break; + + default: + break; + } + break; + } /* end of case TELOPT_STATUS */ + + case TELOPT_XDISPLOC: { + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + settimer(xdisplocsubopt); + subpointer[SB_LEN()] = '\0'; + (void)setenv("DISPLAY", (char *)subpointer, 1); + break; + } /* end of case TELOPT_XDISPLOC */ + +#ifdef TELOPT_NEW_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: { + int c; + char *cp, *varp, *valp; + + if (SB_EOF()) + return; + c = SB_GET(); + if (c == TELQUAL_IS) { + if (subchar == TELOPT_OLD_ENVIRON) + settimer(oenvironsubopt); + else + settimer(environsubopt); + } else if (c != TELQUAL_INFO) { + return; + } + +#ifdef TELOPT_NEW_ENVIRON + if (subchar == TELOPT_NEW_ENVIRON) { + while (!SB_EOF()) { + c = SB_GET(); + if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) + break; + } + } else +#endif + { +#ifdef ENV_HACK + /* + * We only want to do this if we haven't already decided + * whether or not the other side has its VALUE and VAR + * reversed. + */ + if (env_ovar < 0) { + int last = -1; /* invalid value */ + int empty = 0; + int got_var = 0, got_value = 0, got_uservar = 0; + + /* + * The other side might have its VALUE and VAR values + * reversed. To be interoperable, we need to determine + * which way it is. If the first recognized character + * is a VAR or VALUE, then that will tell us what + * type of client it is. If the fist recognized + * character is a USERVAR, then we continue scanning + * the suboption looking for two consecutive + * VAR or VALUE fields. We should not get two + * consecutive VALUE fields, so finding two + * consecutive VALUE or VAR fields will tell us + * what the client is. + */ + SB_SAVE(); + while (!SB_EOF()) { + c = SB_GET(); + switch(c) { + case OLD_ENV_VAR: + if (last < 0 || last == OLD_ENV_VAR + || (empty && (last == OLD_ENV_VALUE))) + goto env_ovar_ok; + got_var++; + last = OLD_ENV_VAR; + break; + case OLD_ENV_VALUE: + if (last < 0 || last == OLD_ENV_VALUE + || (empty && (last == OLD_ENV_VAR))) + goto env_ovar_wrong; + got_value++; + last = OLD_ENV_VALUE; + break; + case ENV_USERVAR: + /* count strings of USERVAR as one */ + if (last != ENV_USERVAR) + got_uservar++; + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + last = ENV_USERVAR; + break; + case ENV_ESC: + if (!SB_EOF()) + c = SB_GET(); + /* FALLTHROUGH */ + default: + empty = 0; + continue; + } + empty = 1; + } + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + /* + * Ok, the first thing was a USERVAR, and there + * are not two consecutive VAR or VALUE commands, + * and none of the VAR or VALUE commands are empty. + * If the client has sent us a well-formed option, + * then the number of VALUEs received should always + * be less than or equal to the number of VARs and + * USERVARs received. + * + * If we got exactly as many VALUEs as VARs and + * USERVARs, the client has the same definitions. + * + * If we got exactly as many VARs as VALUEs and + * USERVARS, the client has reversed definitions. + */ + if (got_uservar + got_var == got_value) { + env_ovar_ok: + env_ovar = OLD_ENV_VAR; + env_ovalue = OLD_ENV_VALUE; + } else if (got_uservar + got_value == got_var) { + env_ovar_wrong: + env_ovar = OLD_ENV_VALUE; + env_ovalue = OLD_ENV_VAR; + DIAG(TD_OPTIONS, + output_data("ENVIRON VALUE and VAR are reversed!\r\n")); + + } + } + SB_RESTORE(); +#endif + + while (!SB_EOF()) { + c = SB_GET(); + if ((c == env_ovar) || (c == ENV_USERVAR)) + break; + } + } + + if (SB_EOF()) + return; + + cp = varp = (char *)subpointer; + valp = 0; + + while (!SB_EOF()) { + c = SB_GET(); + if (subchar == TELOPT_OLD_ENVIRON) { + if (c == env_ovar) + c = NEW_ENV_VAR; + else if (c == env_ovalue) + c = NEW_ENV_VALUE; + } + switch (c) { + + case NEW_ENV_VALUE: + *cp = '\0'; + cp = valp = (char *)subpointer; + break; + + case NEW_ENV_VAR: + case ENV_USERVAR: + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + cp = varp = (char *)subpointer; + valp = 0; + break; + + case ENV_ESC: + if (SB_EOF()) + break; + c = SB_GET(); + /* FALLTHROUGH */ + default: + *cp++ = c; + break; + } + } + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + break; + } /* end of case TELOPT_NEW_ENVIRON */ +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + if (SB_EOF()) + break; + switch(SB_GET()) { + case TELQUAL_SEND: + case TELQUAL_REPLY: + /* + * These are sent by us and cannot be sent by + * the client. + */ + break; + case TELQUAL_IS: + auth_is(subpointer, SB_LEN()); + break; + case TELQUAL_NAME: + auth_name(subpointer, SB_LEN()); + break; + } + break; +#endif +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + if (SB_EOF()) + break; + switch(SB_GET()) { + case ENCRYPT_SUPPORT: + encrypt_support(subpointer, SB_LEN()); + break; + case ENCRYPT_IS: + encrypt_is(subpointer, SB_LEN()); + break; + case ENCRYPT_REPLY: + encrypt_reply(subpointer, SB_LEN()); + break; + case ENCRYPT_START: + encrypt_start(subpointer, SB_LEN()); + break; + case ENCRYPT_END: + encrypt_end(); + break; + case ENCRYPT_REQSTART: + encrypt_request_start(subpointer, SB_LEN()); + break; + case ENCRYPT_REQEND: + /* + * We can always send an REQEND so that we cannot + * get stuck encrypting. We should only get this + * if we have been able to get in the correct mode + * anyhow. + */ + encrypt_request_end(); + break; + case ENCRYPT_ENC_KEYID: + encrypt_enc_keyid(subpointer, SB_LEN()); + break; + case ENCRYPT_DEC_KEYID: + encrypt_dec_keyid(subpointer, SB_LEN()); + break; + default: + break; + } + break; +#endif /* ENCRYPTION */ + + default: + break; + } /* end of switch */ + +} /* end of suboption */ + +static void +doclientstat(void) +{ + clientstat(TELOPT_LINEMODE, WILL, 0); +} + +#define ADD(c) *ncp++ = c +#define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; } +void +send_status(void) +{ + unsigned char statusbuf[256]; + unsigned char *ncp; + unsigned char i; + + ncp = statusbuf; + + netflush(); /* get rid of anything waiting to go out */ + + ADD(IAC); + ADD(SB); + ADD(TELOPT_STATUS); + ADD(TELQUAL_IS); + + /* + * We check the want_state rather than the current state, + * because if we received a DO/WILL for an option that we + * don't support, and the other side didn't send a DONT/WONT + * in response to our WONT/DONT, then the "state" will be + * WILL/DO, and the "want_state" will be WONT/DONT. We + * need to go by the latter. + */ + for (i = 0; i < (unsigned char)NTELOPTS; i++) { + if (my_want_state_is_will(i)) { + ADD(WILL); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + if (his_want_state_is_will(i)) { + ADD(DO); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + } + + if (his_want_state_is_will(TELOPT_LFLOW)) { + ADD(SB); + ADD(TELOPT_LFLOW); + if (flowmode) { + ADD(LFLOW_ON); + } else { + ADD(LFLOW_OFF); + } + ADD(SE); + + if (restartany >= 0) { + ADD(SB); + ADD(TELOPT_LFLOW); + if (restartany) { + ADD(LFLOW_RESTART_ANY); + } else { + ADD(LFLOW_RESTART_XON); + } + ADD(SE); + } + } + +#ifdef LINEMODE + if (his_want_state_is_will(TELOPT_LINEMODE)) { + unsigned char *cp, *cpe; + int len; + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_MODE); + ADD_DATA(editmode); + ADD(SE); + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_SLC); + start_slc(0); + send_slc(); + len = end_slc(&cp); + for (cpe = cp + len; cp < cpe; cp++) + ADD_DATA(*cp); + ADD(SE); + } +#endif /* LINEMODE */ + + ADD(IAC); + ADD(SE); + + output_datalen((const char *)statusbuf, ncp - statusbuf); + netflush(); /* Send it on its way */ + + DIAG(TD_OPTIONS, + {printsub('>', statusbuf, ncp - statusbuf); netflush();}); +} + +/* + * This function appends data to nfrontp and advances nfrontp. + * Returns the number of characters written altogether (the + * buffer may have been flushed in the process). + */ + +int +output_data(const char *format, ...) +{ + va_list args; + int len; + char *buf; + + va_start(args, format); + if ((len = vasprintf(&buf, format, args)) == -1) + return -1; + output_datalen(buf, len); + va_end(args); + free(buf); + return (len); +} + +void +output_datalen(const char *buf, int len) +{ + int remaining, copied; + + remaining = BUFSIZ - (nfrontp - netobuf); + while (len > 0) { + /* Free up enough space if the room is too low*/ + if ((len > BUFSIZ ? BUFSIZ : len) > remaining) { + netflush(); + remaining = BUFSIZ - (nfrontp - netobuf); + } + + /* Copy out as much as will fit */ + copied = remaining > len ? len : remaining; + memmove(nfrontp, buf, copied); + nfrontp += copied; + len -= copied; + remaining -= copied; + buf += copied; + } + return; +} diff --git a/remote_cmds/telnetd.tproj/strlcpy.c b/remote_cmds/telnetd.tproj/strlcpy.c new file mode 100644 index 0000000..9b39b41 --- /dev/null +++ b/remote_cmds/telnetd.tproj/strlcpy.c @@ -0,0 +1,74 @@ +/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * 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. The name of the author 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 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 AUTHOR 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 defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ +#ifndef lint +static const char rcsid[] = + "$FreeBSD: src/lib/libc/string/strlcpy.c,v 1.3 2001/05/24 08:47:41 obrien Exp $"; +#endif + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/remote_cmds/telnetd.tproj/sys_term.c b/remote_cmds/telnetd.tproj/sys_term.c new file mode 100644 index 0000000..23da55b --- /dev/null +++ b/remote_cmds/telnetd.tproj/sys_term.c @@ -0,0 +1,1410 @@ + /* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/sys_term.c,v 1.18 2003/05/04 02:54:49 obrien Exp $"); + +#include <sys/types.h> +#include <sys/tty.h> +#ifdef __APPLE__ +#include <util.h> +#else +#include <libutil.h> +#endif +#include <stdlib.h> +#ifndef __APPLE__ +#include <utmp.h> +#endif + +#include "telnetd.h" +#include "pathnames.h" + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif + +int cleanopen(char *); +void scrub_env(void); + +#ifdef UTMPX +#include <utmpx.h> +struct utmpx wtmp; +#endif /* UTMPX */ + +#ifndef NO_UTMP +# include <utmp.h> +struct utmp wtmp; + +#ifdef _PATH_WTMP +char wtmpf[] = _PATH_WTMP; +#else +char wtmpf[] = "/var/log/wtmp"; +#endif +#ifdef _PATH_UTMP +char utmpf[] = _PATH_UTMP; +#else +char utmpf[] = "/var/run/utmp"; +#endif +#endif /* NO_UTMP */ + +char *envinit[3]; +extern char **environ; + +#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) +#define SCMPN(a, b) strncmp(a, b, sizeof(a)) + +#ifdef t_erase +#undef t_erase +#undef t_kill +#undef t_intrc +#undef t_quitc +#undef t_startc +#undef t_stopc +#undef t_eofc +#undef t_brkc +#undef t_suspc +#undef t_dsuspc +#undef t_rprntc +#undef t_flushc +#undef t_werasc +#undef t_lnextc +#endif + +#ifndef USE_TERMIO +struct termbuf { + struct sgttyb sg; + struct tchars tc; + struct ltchars ltc; + int state; + int lflags; +} termbuf, termbuf2; +# define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val) +# define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val) +# define cfgetospeed(tp) (tp)->sg.sg_ospeed +# define cfgetispeed(tp) (tp)->sg.sg_ispeed +#else /* USE_TERMIO */ +# ifndef TCSANOW +# ifdef TCSETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, t) +# define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# ifdef CIBAUD +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \ + (tp)->c_cflag |= ((val)<<IBSHIFT) +# define cfgetispeed(tp) (((tp)->c_cflag & CIBAUD)>>IBSHIFT) +# else +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) +# endif +# endif /* TCSANOW */ +struct termios termbuf, termbuf2; /* pty control structure */ +#endif /* USE_TERMIO */ + +#include <sys/types.h> +#ifndef __APPLE__ +#include <libutil.h> +#endif + +int cleanopen(char *); +void scrub_env(void); +static char **addarg(char **, const char *); + +/* + * init_termbuf() + * copy_termbuf(cp) + * set_termbuf() + * + * These three routines are used to get and set the "termbuf" structure + * to and from the kernel. init_termbuf() gets the current settings. + * copy_termbuf() hands in a new "termbuf" to write to the kernel, and + * set_termbuf() writes the structure into the kernel. + */ + +void +init_termbuf(void) +{ +#ifndef USE_TERMIO + if (ioctl(spty, TIOCGETP, (char *)&termbuf.sg) == -1) + perror("ioctl TIOCGETP"); + if (ioctl(spty, TIOCGETC, (char *)&termbuf.tc) == -1) + perror("ioctl TIOCGETC"); + if (ioctl(spty, TIOCGLTC, (char *)&termbuf.ltc) == -1) + perror("ioctl TIOCGLTC"); +# ifdef TIOCGSTATE + if (-1 == ioctl(spty, TIOCGSTATE, (char *)&termbuf.state)) + perror("ioctl TIOCGSTATE"); +# endif +#else + if (-1 == tcgetattr(spty, &termbuf)) + perror("tcgetattr"); +#endif + termbuf2 = termbuf; +} + +#if defined(LINEMODE) && defined(TIOCPKT_IOCTL) +void +copy_termbuf(char *cp, size_t len) +{ + if (len > sizeof(termbuf)) + len = sizeof(termbuf); + memmove((char *)&termbuf, cp, len); + termbuf2 = termbuf; +} +#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ + +void +set_termbuf(void) +{ + /* + * Only make the necessary changes. + */ +#ifndef USE_TERMIO + if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, + sizeof(termbuf.sg))) { + if (-1 == ioctl(spty, TIOCSETN, (char *)&termbuf.sg)) + perror("ioctl TIOSETN"); + } + if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, + sizeof(termbuf.tc))) { + if (-1 == ioctl(spty, TIOCSETC, (char *)&termbuf.tc)) + perror("ioctl TIOSETC"); + } + if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, + sizeof(termbuf.ltc))) { + if (-1 == ioctl(spty, TIOCSLTC, (char *)&termbuf.ltc)) + perror("ioctl TIOCSLTC"); + } + if (termbuf.lflags != termbuf2.lflags) { + (void) ioctl(spty, TIOCLSET, (char *)&termbuf.lflags); + } +#else /* USE_TERMIO */ + if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) { + if (-1 == tcsetattr(spty, TCSANOW, &termbuf)) + perror("tcsetattr"); + } +#endif /* USE_TERMIO */ +} + + +/* + * spcset(func, valp, valpp) + * + * This function takes various special characters (func), and + * sets *valp to the current value of that character, and + * *valpp to point to where in the "termbuf" structure that + * value is kept. + * + * It returns the SLC_ level of support for this function. + */ + +#ifndef USE_TERMIO +int +spcset(int func, cc_t *valp, cc_t **valpp) +{ + switch(func) { + case SLC_EOF: + *valp = termbuf.tc.t_eofc; + *valpp = (cc_t *)&termbuf.tc.t_eofc; + return(SLC_VARIABLE); + case SLC_EC: + *valp = termbuf.sg.sg_erase; + *valpp = (cc_t *)&termbuf.sg.sg_erase; + return(SLC_VARIABLE); + case SLC_EL: + *valp = termbuf.sg.sg_kill; + *valpp = (cc_t *)&termbuf.sg.sg_kill; + return(SLC_VARIABLE); + case SLC_IP: + *valp = termbuf.tc.t_intrc; + *valpp = (cc_t *)&termbuf.tc.t_intrc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + *valp = termbuf.tc.t_quitc; + *valpp = (cc_t *)&termbuf.tc.t_quitc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: + *valp = termbuf.tc.t_startc; + *valpp = (cc_t *)&termbuf.tc.t_startc; + return(SLC_VARIABLE); + case SLC_XOFF: + *valp = termbuf.tc.t_stopc; + *valpp = (cc_t *)&termbuf.tc.t_stopc; + return(SLC_VARIABLE); + case SLC_AO: + *valp = termbuf.ltc.t_flushc; + *valpp = (cc_t *)&termbuf.ltc.t_flushc; + return(SLC_VARIABLE); + case SLC_SUSP: + *valp = termbuf.ltc.t_suspc; + *valpp = (cc_t *)&termbuf.ltc.t_suspc; + return(SLC_VARIABLE); + case SLC_EW: + *valp = termbuf.ltc.t_werasc; + *valpp = (cc_t *)&termbuf.ltc.t_werasc; + return(SLC_VARIABLE); + case SLC_RP: + *valp = termbuf.ltc.t_rprntc; + *valpp = (cc_t *)&termbuf.ltc.t_rprntc; + return(SLC_VARIABLE); + case SLC_LNEXT: + *valp = termbuf.ltc.t_lnextc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_FORW1: + *valp = termbuf.tc.t_brkc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_BRK: + case SLC_SYNCH: + case SLC_AYT: + case SLC_EOR: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_DEFAULT); + default: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_NOSUPPORT); + } +} + +#else /* USE_TERMIO */ + + +#define setval(a, b) *valp = termbuf.c_cc[a]; \ + *valpp = &termbuf.c_cc[a]; \ + return(b); +#define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT); + +int +spcset(int func, cc_t *valp, cc_t **valpp) +{ + switch(func) { + case SLC_EOF: + setval(VEOF, SLC_VARIABLE); + case SLC_EC: + setval(VERASE, SLC_VARIABLE); + case SLC_EL: + setval(VKILL, SLC_VARIABLE); + case SLC_IP: + setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: +#ifdef VSTART + setval(VSTART, SLC_VARIABLE); +#else + defval(0x13); +#endif + case SLC_XOFF: +#ifdef VSTOP + setval(VSTOP, SLC_VARIABLE); +#else + defval(0x11); +#endif + case SLC_EW: +#ifdef VWERASE + setval(VWERASE, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_RP: +#ifdef VREPRINT + setval(VREPRINT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_LNEXT: +#ifdef VLNEXT + setval(VLNEXT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_AO: +#if !defined(VDISCARD) && defined(VFLUSHO) +# define VDISCARD VFLUSHO +#endif +#ifdef VDISCARD + setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); +#else + defval(0); +#endif + case SLC_SUSP: +#ifdef VSUSP + setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN); +#else + defval(0); +#endif +#ifdef VEOL + case SLC_FORW1: + setval(VEOL, SLC_VARIABLE); +#endif +#ifdef VEOL2 + case SLC_FORW2: + setval(VEOL2, SLC_VARIABLE); +#endif + case SLC_AYT: +#ifdef VSTATUS + setval(VSTATUS, SLC_VARIABLE); +#else + defval(0); +#endif + + case SLC_BRK: + case SLC_SYNCH: + case SLC_EOR: + defval(0); + + default: + *valp = 0; + *valpp = 0; + return(SLC_NOSUPPORT); + } +} +#endif /* USE_TERMIO */ + +/* + * getpty() + * + * Allocate a pty. As a side effect, the external character + * array "line" contains the name of the slave side. + * + * Returns the file descriptor of the opened pty. + */ +char alpha[] = "0123456789abcdefghijklmnopqrstuv"; +char line[16]; + +int +getpty(int *ptynum __unused, int *slavepty) +{ + int p; +#ifdef __APPLE__ + // rdar://problem/5169227 + p = open("/dev/ptmx", 2); + if (p >= 0) { + grantpt(p); + unlockpt(p); + strcpy(line, ptsname(p)); + *slavepty = cleanopen(line); + return(p); + } + +#else /* !__APPLE__ */ + const char *cp; + char *p1, *p2; + int i; + + (void) strcpy(line, _PATH_DEV); + (void) strcat(line, "ptyXX"); + p1 = &line[8]; + p2 = &line[9]; + + for (cp = "pqrsPQRS"; *cp; cp++) { + struct stat stb; + + *p1 = *cp; + *p2 = '0'; + /* + * This stat() check is just to keep us from + * looping through all 256 combinations if there + * aren't that many ptys available. + */ + if (stat(line, &stb) < 0) + break; + for (i = 0; i < 32; i++) { + *p2 = alpha[i]; + p = open(line, 2); + if (p > 0) { + line[5] = 't'; + chown(line, 0, 0); + chmod(line, 0600); + return(p); + } + } + } +#endif /* __APPLE__ */ + return(-1); +} + +#ifdef LINEMODE +/* + * tty_flowmode() Find out if flow control is enabled or disabled. + * tty_linemode() Find out if linemode (external processing) is enabled. + * tty_setlinemod(on) Turn on/off linemode. + * tty_isecho() Find out if echoing is turned on. + * tty_setecho(on) Enable/disable character echoing. + * tty_israw() Find out if terminal is in RAW mode. + * tty_binaryin(on) Turn on/off BINARY on input. + * tty_binaryout(on) Turn on/off BINARY on output. + * tty_isediting() Find out if line editing is enabled. + * tty_istrapsig() Find out if signal trapping is enabled. + * tty_setedit(on) Turn on/off line editing. + * tty_setsig(on) Turn on/off signal trapping. + * tty_issofttab() Find out if tab expansion is enabled. + * tty_setsofttab(on) Turn on/off soft tab expansion. + * tty_islitecho() Find out if typed control chars are echoed literally + * tty_setlitecho() Turn on/off literal echo of control chars + * tty_tspeed(val) Set transmit speed to val. + * tty_rspeed(val) Set receive speed to val. + */ + + +int +tty_linemode(void) +{ +#ifndef USE_TERMIO + return(termbuf.state & TS_EXTPROC); +#else + return(termbuf.c_lflag & EXTPROC); +#endif +} + +void +tty_setlinemode(int on) +{ +#ifdef TIOCEXT + set_termbuf(); + if (-1 == ioctl(spty, TIOCEXT, (char *)&on)) + perror("ioctl TIOCEXT"); + init_termbuf(); +#else /* !TIOCEXT */ +# ifdef EXTPROC + if (on) + termbuf.c_lflag |= EXTPROC; + else + termbuf.c_lflag &= ~EXTPROC; +# endif +#endif /* TIOCEXT */ +} +#endif /* LINEMODE */ + +int +tty_isecho(void) +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & ECHO); +#else + return (termbuf.c_lflag & ECHO); +#endif +} + +int +tty_flowmode(void) +{ +#ifndef USE_TERMIO + return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0); +#else + return((termbuf.c_iflag & IXON) ? 1 : 0); +#endif +} + +int +tty_restartany(void) +{ +#ifndef USE_TERMIO +# ifdef DECCTQ + return((termbuf.lflags & DECCTQ) ? 0 : 1); +# else + return(-1); +# endif +#else + return((termbuf.c_iflag & IXANY) ? 1 : 0); +#endif +} + +void +tty_setecho(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= ECHO|CRMOD; + else + termbuf.sg.sg_flags &= ~(ECHO|CRMOD); +#else + if (on) + termbuf.c_lflag |= ECHO; + else + termbuf.c_lflag &= ~ECHO; +#endif +} + +int +tty_israw(void) +{ +#ifndef USE_TERMIO + return(termbuf.sg.sg_flags & RAW); +#else + return(!(termbuf.c_lflag & ICANON)); +#endif +} + +#ifdef AUTHENTICATION +#if defined(NO_LOGIN_F) && defined(LOGIN_R) +int +tty_setraw(int on) +{ +# ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= RAW; + else + termbuf.sg.sg_flags &= ~RAW; +# else + if (on) + termbuf.c_lflag &= ~ICANON; + else + termbuf.c_lflag |= ICANON; +# endif +} +#endif +#endif /* AUTHENTICATION */ + +void +tty_binaryin(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LPASS8; + else + termbuf.lflags &= ~LPASS8; +#else + if (on) { + termbuf.c_iflag &= ~ISTRIP; + } else { + termbuf.c_iflag |= ISTRIP; + } +#endif +} + +void +tty_binaryout(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LLITOUT; + else + termbuf.lflags &= ~LLITOUT; +#else + if (on) { + termbuf.c_cflag &= ~(CSIZE|PARENB); + termbuf.c_cflag |= CS8; + termbuf.c_oflag &= ~OPOST; + } else { + termbuf.c_cflag &= ~CSIZE; + termbuf.c_cflag |= CS7|PARENB; + termbuf.c_oflag |= OPOST; + } +#endif +} + +int +tty_isbinaryin(void) +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LPASS8); +#else + return(!(termbuf.c_iflag & ISTRIP)); +#endif +} + +int +tty_isbinaryout(void) +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LLITOUT); +#else + return(!(termbuf.c_oflag&OPOST)); +#endif +} + +#ifdef LINEMODE +int +tty_isediting(void) +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags & (CBREAK|RAW))); +#else + return(termbuf.c_lflag & ICANON); +#endif +} + +int +tty_istrapsig(void) +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags&RAW)); +#else + return(termbuf.c_lflag & ISIG); +#endif +} + +void +tty_setedit(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags &= ~CBREAK; + else + termbuf.sg.sg_flags |= CBREAK; +#else + if (on) + termbuf.c_lflag |= ICANON; + else + termbuf.c_lflag &= ~ICANON; +#endif +} + +void +tty_setsig(int on) +{ +#ifndef USE_TERMIO + if (on) + ; +#else + if (on) + termbuf.c_lflag |= ISIG; + else + termbuf.c_lflag &= ~ISIG; +#endif +} +#endif /* LINEMODE */ + +int +tty_issofttab(void) +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & XTABS); +#else +# ifdef OXTABS + return (termbuf.c_oflag & OXTABS); +# endif +# ifdef TABDLY + return ((termbuf.c_oflag & TABDLY) == TAB3); +# endif +#endif +} + +void +tty_setsofttab(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= XTABS; + else + termbuf.sg.sg_flags &= ~XTABS; +#else + if (on) { +# ifdef OXTABS + termbuf.c_oflag |= OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB3; +# endif + } else { +# ifdef OXTABS + termbuf.c_oflag &= ~OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB0; +# endif + } +#endif +} + +int +tty_islitecho(void) +{ +#ifndef USE_TERMIO + return (!(termbuf.lflags & LCTLECH)); +#else +# ifdef ECHOCTL + return (!(termbuf.c_lflag & ECHOCTL)); +# endif +# ifdef TCTLECH + return (!(termbuf.c_lflag & TCTLECH)); +# endif +# if !defined(ECHOCTL) && !defined(TCTLECH) + return (0); /* assumes ctl chars are echoed '^x' */ +# endif +#endif +} + +void +tty_setlitecho(int on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags &= ~LCTLECH; + else + termbuf.lflags |= LCTLECH; +#else +# ifdef ECHOCTL + if (on) + termbuf.c_lflag &= ~ECHOCTL; + else + termbuf.c_lflag |= ECHOCTL; +# endif +# ifdef TCTLECH + if (on) + termbuf.c_lflag &= ~TCTLECH; + else + termbuf.c_lflag |= TCTLECH; +# endif +#endif +} + +int +tty_iscrnl(void) +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & CRMOD); +#else + return (termbuf.c_iflag & ICRNL); +#endif +} + +/* + * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD). + */ +#if B4800 != 4800 +#define DECODE_BAUD +#endif + +#ifdef DECODE_BAUD + +/* + * A table of available terminal speeds + */ +struct termspeeds { + int speed; + int value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, +#ifdef B7200 + { 7200, B7200 }, +#endif + { 9600, B9600 }, +#ifdef B14400 + { 14400, B14400 }, +#endif +#ifdef B19200 + { 19200, B19200 }, +#endif +#ifdef B28800 + { 28800, B28800 }, +#endif +#ifdef B38400 + { 38400, B38400 }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif +#ifdef B230400 + { 230400, B230400 }, +#endif + { -1, 0 } +}; +#endif /* DECODE_BAUD */ + +void +tty_tspeed(int val) +{ +#ifdef DECODE_BAUD + struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + if (tp->speed == -1) /* back up to last valid value */ + --tp; + cfsetospeed(&termbuf, tp->value); +#else /* DECODE_BAUD */ + cfsetospeed(&termbuf, val); +#endif /* DECODE_BAUD */ +} + +void +tty_rspeed(int val) +{ +#ifdef DECODE_BAUD + struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + if (tp->speed == -1) /* back up to last valid value */ + --tp; + cfsetispeed(&termbuf, tp->value); +#else /* DECODE_BAUD */ + cfsetispeed(&termbuf, val); +#endif /* DECODE_BAUD */ +} + +/* + * getptyslave() + * + * Open the slave side of the pty, and do any initialization + * that is necessary. + */ +static void +getptyslave(void) +{ + int t = -1; + char erase; + +# ifdef LINEMODE + int waslm; +# endif +# ifdef TIOCGWINSZ + struct winsize ws; + extern int def_row, def_col; +# endif + extern int def_tspeed, def_rspeed; + /* + * Opening the slave side may cause initilization of the + * kernel tty structure. We need remember the state of + * if linemode was turned on + * terminal window size + * terminal speed + * erase character + * so that we can re-set them if we need to. + */ +# ifdef LINEMODE + waslm = tty_linemode(); +# endif + erase = termbuf.c_cc[VERASE]; + + /* + * Make sure that we don't have a controlling tty, and + * that we are the session (process group) leader. + */ +# ifdef TIOCNOTTY + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } +# endif + + t = spty; + if (t < 0) + fatalperror(net, line); + + + /* + * set up the tty modes as we like them to be. + */ + init_termbuf(); +# ifdef TIOCGWINSZ + if (def_row || def_col) { + memset((char *)&ws, 0, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void)ioctl(t, TIOCSWINSZ, (char *)&ws); + } +# endif + + /* + * Settings for sgtty based systems + */ +# ifndef USE_TERMIO + termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; +# endif /* USE_TERMIO */ + + /* + * Settings for all other termios/termio based + * systems, other than 4.4BSD. In 4.4BSD the + * kernel does the initial terminal setup. + */ + tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); + tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); + if (erase) + termbuf.c_cc[VERASE] = erase; +# ifdef LINEMODE + if (waslm) + tty_setlinemode(1); +# endif /* LINEMODE */ + + /* + * Set the tty modes, and make this our controlling tty. + */ + set_termbuf(); + if (login_tty(t) == -1) + fatalperror(net, "login_tty"); + if (net > 2) + (void) close(net); +#ifdef AUTHENTICATION +#if defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Leave the pty open so that we can write out the rlogin + * protocol for /bin/login, if the authentication works. + */ +#else + if (pty > 2) { + (void) close(pty); + pty = -1; + } +#endif +#endif /* AUTHENTICATION */ +} + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif +/* + * Open the specified slave side of the pty, + * making sure that we have a clean tty. + */ +int +cleanopen(char *li) +{ + int t; + + /* + * Make sure that other people can't open the + * slave side of the connection. + */ + (void) chown(li, 0, 0); + (void) chmod(li, 0600); + + (void) revoke(li); + + t = open(line, O_RDWR|O_NOCTTY); + + if (t < 0) + return(-1); + + return(t); +} + +/* + * startslave(host) + * + * Given a hostname, do whatever + * is necessary to startup the login process on the slave side of the pty. + */ + +/* ARGSUSED */ +void +startslave(char *host, int autologin, char *autoname) +{ + int i; + +#ifdef AUTHENTICATION + if (!autoname || !autoname[0]) + autologin = 0; + + if (autologin < auth_level) { + fatal(net, "Authorization failed"); + exit(1); + } +#endif + + + if ((i = fork()) < 0) + fatalperror(net, "fork"); + if (i) { + } else { + getptyslave(); + start_login(host, autologin, autoname); + /*NOTREACHED*/ + } +} + +void +init_env(void) +{ + char **envp; + + envp = envinit; + if ((*envp = getenv("TZ"))) + *envp++ -= 3; + *envp = 0; + environ = envinit; +} + + +/* + * start_login(host) + * + * Assuming that we are now running as a child processes, this + * function will turn us into the login process. + */ + +#ifndef AUTHENTICATION +#define undef1 __unused +#else +#define undef1 +#endif + +void +start_login(char *host undef1, int autologin undef1, char *name undef1) +{ + char **argv; +#ifdef UTMPX + // rdar://problem/4433603 + int pid = getpid(); + struct utmpx utmpx; + + /* + * Create utmp entry for child + */ + + bzero(&utmpx, sizeof(utmpx)); + SCPYN(utmpx.ut_user, ".telnet"); + SCPYN(utmpx.ut_line, line + sizeof(_PATH_DEV) - 1); + utmpx.ut_pid = pid; + utmpx.ut_id[0] = 't'; + utmpx.ut_id[1] = 'n'; + utmpx.ut_id[2] = SC_WILDC; + utmpx.ut_id[3] = SC_WILDC; + utmpx.ut_type = LOGIN_PROCESS; + (void) time(&utmpx.ut_tv.tv_sec); + if (makeutx(&utmpx) == NULL) + fatal(net, "makeutx failed"); +#endif /* UTMPX */ + + scrub_env(); + + /* + * -h : pass on name of host. + * WARNING: -h is accepted by login if and only if + * getuid() == 0. + * -p : don't clobber the environment (so terminal type stays set). + * + * -f : force this login, he has already been authenticated + */ + argv = addarg(0, "login"); + +#if !defined(NO_LOGIN_H) +#ifdef AUTHENTICATION +# if defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Don't add the "-h host" option if we are going + * to be adding the "-r host" option down below... + */ + if ((auth_level < 0) || (autologin != AUTH_VALID)) +# endif + { + argv = addarg(argv, "-h"); + argv = addarg(argv, host); + } +#endif /* AUTHENTICATION */ +#endif +#if !defined(NO_LOGIN_P) + argv = addarg(argv, "-p"); +#endif +#ifdef LINEMODE + /* + * Set the environment variable "LINEMODE" to either + * "real" or "kludge" if we are operating in either + * real or kludge linemode. + */ + if (lmodetype == REAL_LINEMODE) + setenv("LINEMODE", "real", 1); +# ifdef KLUDGELINEMODE + else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) + setenv("LINEMODE", "kludge", 1); +# endif +#endif +#ifdef BFTPDAEMON + /* + * Are we working as the bftp daemon? If so, then ask login + * to start bftp instead of shell. + */ + if (bftpd) { + argv = addarg(argv, "-e"); + argv = addarg(argv, BFTPPATH); + } else +#endif +#ifdef AUTHENTICATION + if (auth_level >= 0 && autologin == AUTH_VALID) { +# if !defined(NO_LOGIN_F) + argv = addarg(argv, "-f"); + argv = addarg(argv, "--"); + argv = addarg(argv, name); +# else +# if defined(LOGIN_R) + /* + * We don't have support for "login -f", but we + * can fool /bin/login into thinking that we are + * rlogind, and allow us to log in without a + * password. The rlogin protocol expects + * local-user\0remote-user\0term/speed\0 + */ + + if (pty > 2) { + char *cp; + char speed[128]; + int isecho, israw, xpty, len; + extern int def_rspeed; +# ifndef LOGIN_HOST + /* + * Tell login that we are coming from "localhost". + * If we passed in the real host name, then the + * user would have to allow .rhost access from + * every machine that they want authenticated + * access to work from, which sort of defeats + * the purpose of an authenticated login... + * So, we tell login that the session is coming + * from "localhost", and the user will only have + * to have "localhost" in their .rhost file. + */ +# define LOGIN_HOST "localhost" +# endif + argv = addarg(argv, "-r"); + argv = addarg(argv, LOGIN_HOST); + + xpty = pty; + pty = 0; + init_termbuf(); + isecho = tty_isecho(); + israw = tty_israw(); + if (isecho || !israw) { + tty_setecho(0); /* Turn off echo */ + tty_setraw(1); /* Turn on raw */ + set_termbuf(); + } + len = strlen(name)+1; + write(xpty, name, len); + write(xpty, name, len); + snprintf(speed, sizeof(speed), + "%s/%d", (cp = getenv("TERM")) ? cp : "", + (def_rspeed > 0) ? def_rspeed : 9600); + len = strlen(speed)+1; + write(xpty, speed, len); + + if (isecho || !israw) { + init_termbuf(); + tty_setecho(isecho); + tty_setraw(israw); + set_termbuf(); + if (!israw) { + /* + * Write a newline to ensure + * that login will be able to + * read the line... + */ + write(xpty, "\n", 1); + } + } + pty = xpty; + } +# else + argv = addarg(argv, "--"); + argv = addarg(argv, name); +# endif +# endif + } else +#endif + if (getenv("USER")) { + argv = addarg(argv, "--"); + argv = addarg(argv, getenv("USER")); +#if defined(LOGIN_ARGS) && defined(NO_LOGIN_P) + { + char **cpp; + for (cpp = environ; *cpp; cpp++) + argv = addarg(argv, *cpp); + } +#endif + /* + * Assume that login will set the USER variable + * correctly. For SysV systems, this means that + * USER will no longer be set, just LOGNAME by + * login. (The problem is that if the auto-login + * fails, and the user then specifies a different + * account name, he can get logged in with both + * LOGNAME and USER in his environment, but the + * USER value will be wrong. + */ + unsetenv("USER"); + } +#ifdef AUTHENTICATION +#if defined(NO_LOGIN_F) && defined(LOGIN_R) + if (pty > 2) + close(pty); +#endif +#endif /* AUTHENTICATION */ + closelog(); + + if (altlogin == NULL) { + altlogin = _PATH_LOGIN; + } + execv(altlogin, argv); + + syslog(LOG_ERR, "%s: %m", altlogin); + fatalperror(net, altlogin); + /*NOTREACHED*/ +} + +static char ** +addarg(char **argv, const char *val) +{ + char **cpp; + + if (argv == NULL) { + /* + * 10 entries, a leading length, and a null + */ + argv = (char **)malloc(sizeof(*argv) * 12); + if (argv == NULL) + return(NULL); + *argv++ = (char *)10; + *argv = (char *)0; + } + for (cpp = argv; *cpp; cpp++) + ; + if (cpp == &argv[(long)argv[-1]]) { + --argv; + *argv = (char *)((long)(*argv) + 10); + argv = (char **)realloc(argv, sizeof(*argv)*((long)(*argv) + 2)); + if (argv == NULL) + return(NULL); + argv++; + cpp = &argv[(long)argv[-1] - 10]; + } + *cpp++ = strdup(val); + *cpp = 0; + return(argv); +} + +/* + * scrub_env() + * + * We only accept the environment variables listed below. + */ +void +scrub_env(void) +{ + static const char *rej[] = { + "TERMCAP=/", + NULL + }; + + static const char *acc[] = { + "XAUTH=", "XAUTHORITY=", "DISPLAY=", + "TERM=", + "EDITOR=", + "PAGER=", + "LOGNAME=", + "POSIXLY_CORRECT=", + "PRINTER=", + NULL + }; + + char **cpp, **cpp2; + const char **p; + + for (cpp2 = cpp = environ; *cpp; cpp++) { + int reject_it = 0; + + for(p = rej; *p; p++) + if(strncmp(*cpp, *p, strlen(*p)) == 0) { + reject_it = 1; + break; + } + if (reject_it) + continue; + + for(p = acc; *p; p++) + if(strncmp(*cpp, *p, strlen(*p)) == 0) + break; + if(*p != NULL) + *cpp2++ = *cpp; + } + *cpp2 = NULL; +} + +/* + * cleanup() + * + * This is the routine to call when we are all through, to + * clean up anything that needs to be cleaned up. + */ +/* ARGSUSED */ +void +cleanup(int sig __unused) +{ +#ifdef __APPLE__ + // rdar://problem/5169227 + (void) shutdown(net, SHUT_RDWR); +#else /* !__APPLE__ */ + char *p; + sigset_t mask; + + p = line + sizeof(_PATH_DEV) - 1; + /* + * Block all signals before clearing the utmp entry. We don't want to + * be called again after calling logout() and then not add the wtmp + * entry because of not finding the corresponding entry in utmp. + */ + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + (void) shutdown(net, 2); +#endif + _exit(1); +} diff --git a/remote_cmds/telnetd.tproj/telnet.plist b/remote_cmds/telnetd.tproj/telnet.plist new file mode 100644 index 0000000..6dff1eb --- /dev/null +++ b/remote_cmds/telnetd.tproj/telnet.plist @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <true/> + <key>Label</key> + <string>com.apple.telnetd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/telnetd</string> + </array> + <key>inetdCompatibility</key> + <dict> + <key>Wait</key> + <false/> + </dict> + <key>Sockets</key> + <dict> + <key>Listeners</key> + <dict> + <key>SockServiceName</key> + <string>telnet</string> + <key>Bonjour</key> + <true/> + </dict> + </dict> + <key>SessionCreate</key> + <true/> + <key>EnablePressuredExit</key> + <false/> + <key>EnableTransactions</key> + <false/> +</dict> +</plist> diff --git a/remote_cmds/telnetd.tproj/telnetd.8 b/remote_cmds/telnetd.tproj/telnetd.8 new file mode 100644 index 0000000..3bf2f51 --- /dev/null +++ b/remote_cmds/telnetd.tproj/telnetd.8 @@ -0,0 +1,607 @@ +.\" 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: src/contrib/telnet/telnetd/telnetd.8,v 1.21 2005/01/21 21:57:05 ru Exp $ +.\" +.Dd January 9, 2005 +.Dt TELNETD 8 +.Os +.Sh NAME +.Nm telnetd +.Nd DARPA +.Tn TELNET +protocol server +.Sh SYNOPSIS +.Nm /usr/libexec/telnetd +.Op Fl 46BUhlkns +.Op Fl D Ar debugmode +.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 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 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.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 . +.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 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 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 u Ar len +This option is used to specify the size of the field +in the +.Dv utmpx +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 utmpx +file. +.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 . +.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 +.Dq cooked +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 . +.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 +.Bx 4.2 +.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. +.El +.Sh NOTES +By default +.Nm +will read the +.Em \&he , +.Em \&hn , +and +.Em \&im +capabilities from +.Pa /etc/gettytab +and use that information (if present) to determine +what to display before the login: prompt. +You can +also use a System V style +.Pa /etc/issue +file by using the +.Em \&if +capability, which will override +.Em \&im . +The information specified in either +.Em \&im +or +.Em \&if +will be displayed to both console and remote logins. +.\" .Sh ENVIRONMENT +.Sh FILES +.Bl -tag -width /usr/ucb/bftp -compact +.It Pa /etc/services +.It Pa /etc/gettytab +.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), +.Xr gettytab 5 +.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 +.El +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. +.Sh BUGS +Some +.Tn TELNET +commands are only partially implemented. +.Pp +Because of bugs in the original +.Bx 4.2 +.Xr telnet 1 , +.Nm +performs some dubious protocol exchanges to try to discover if the remote +client is, in fact, a +.Bx 4.2 +.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. diff --git a/remote_cmds/telnetd.tproj/telnetd.c b/remote_cmds/telnetd.tproj/telnetd.c new file mode 100644 index 0000000..a952a93 --- /dev/null +++ b/remote_cmds/telnetd.tproj/telnetd.c @@ -0,0 +1,1330 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/telnetd.c,v 1.29 2006/09/26 21:46:11 ru Exp $"); + +#include "telnetd.h" +#include "pathnames.h" + +#include <sys/mman.h> +#include <err.h> +#ifndef __APPLE__ +#include <libutil.h> +#endif +#include <paths.h> +#include <termcap.h> +#ifndef __APPLE__ +#include <utmp.h> +#endif + +#include <arpa/inet.h> + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +int auth_level = 0; +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif +#include <libtelnet/misc.h> + +char remote_hostname[MAXHOSTNAMELEN]; +size_t utmp_len = sizeof(remote_hostname) - 1; +int registerd_host_only = 0; + + +/* + * I/O data buffers, + * pointers, and counters. + */ +char ptyibuf[BUFSIZ], *ptyip = ptyibuf; +char ptyibuf2[BUFSIZ]; + +int readstream(int, char *, int); +void doit(struct sockaddr *); +int terminaltypeok(char *); + +int hostinfo = 1; /* do we print login banner? */ + +static int debug = 0; +int keepalive = 1; +const char *altlogin; + +void doit(struct sockaddr *); +int terminaltypeok(char *); +void startslave(char *, int, char *); +extern void usage(void); +static void _gettermname(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 +#ifdef LINEMODE + 'l', +#endif + '\0' +}; + +int family = AF_INET; + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif /* MAXHOSTNAMELEN */ + +char *hostname; +char host_name[MAXHOSTNAMELEN]; + +extern void telnet(int, int, char *); + +int level; +char user_name[256]; + +int +main(int argc, char *argv[]) +{ + u_long ultmp; + struct sockaddr_storage from; +#ifdef __APPLE__ + int on = 1; + socklen_t fromlen; +#else + int on = 1, fromlen; +#endif + int ch; +#if defined(IPPROTO_IP) && defined(IP_TOS) + int tos = -1; +#endif + char *ep; + + // radar:17828581 + (void)signal(SIGTERM, SIG_DFL); + + 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 <ESC> (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*/ + + 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; + +#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; + + 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 +#define MAXTOS 255 + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > MAXTOS) + warnx("%s%s%s", + "bad TOS argument '", optarg, + "'; will try to use default TOS"); + else + tos = ultmp; +#endif + break; + + case 'u': + utmp_len = (size_t)atoi(optarg); + 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) { +#ifdef __APPLE__ + int s, ns, error; + socklen_t foo; +#else + int s, ns, foo, error; +#endif + const char *service = "telnet"; + struct addrinfo hints, *res; + + if (argc > 1) { + usage(); + /* NOT REACHED */ + } 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(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 (debug > 1) + (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) + err(1, "bind"); + if (listen(s, 1) < 0) + err(1, "listen"); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); + if (ns < 0) + err(1, "accept"); + (void) setsockopt(ns, SOL_SOCKET, SO_DEBUG, + (char *)&on, sizeof(on)); + (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 */ + } + + 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((struct sockaddr *)&from); + /* NOTREACHED */ + return(0); +} /* end of main */ + + void +usage() +{ + fprintf(stderr, "usage: telnetd"); +#ifdef AUTHENTICATION + fprintf(stderr, + " [-4] [-6] [-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(LINEMODE) && defined(KLUDGELINEMODE) + fprintf(stderr, " [-k]"); +#endif +#ifdef LINEMODE + fprintf(stderr, " [-l]"); +#endif + fprintf(stderr, " [-n]"); + fprintf(stderr, "\n\t"); +#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 +}; + + +#ifndef AUTHENTICATION +#define undef2 __unused +#else +#define undef2 +#endif + +static int +getterminaltype(char *name undef2) +{ + int retval = -1; + + settimer(baseline); +#ifdef 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 }; + + output_datalen((char*)sb, 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 }; + + output_datalen((char*)sb, 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 }; + + output_datalen((char*)sb, 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 }; + + output_datalen((char*)sb, sizeof sb); + DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); + } + if (his_state_is_will(TELOPT_TTYPE)) { + + output_datalen((char*)ttytype_sbbuf, 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 */ + +static void +_gettermname(void) +{ + /* + * 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); + output_datalen((char*)ttytype_sbbuf, sizeof ttytype_sbbuf); + DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, + sizeof ttytype_sbbuf - 2);); + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); +} + +int +terminaltypeok(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); +} + +#ifdef __APPLE__ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif /* MAXHOSTNAMELEN */ + +char *hostname; +char host_name[MAXHOSTNAMELEN]; + +int level; +char user_name[256]; +#endif /* __APPLE__ */ + +/* + * Get a pty, scan input lines. + */ +void +doit(struct sockaddr *who) +{ +#ifndef __APPLE__ + int err_; /* XXX */ +#else + char *host = NULL; + struct hostent *hp; +#endif + int ptynum; + + /* + * Find an available pty to use. + */ +#ifndef convex + spty = -1; + mpty = getpty(&ptynum, &spty); + if (mpty < 0) + fatal(net, "All network ports in use"); +#else + for (;;) { + char *lp; + + if ((lp = getpty()) == NULL) + fatal(net, "Out of ptys"); + + if ((pty = open(lp, 2)) >= 0) { + strlcpy(line,lp,sizeof(line)); + line[5] = 't'; + break; + } + } +#endif + +#ifdef __APPLE__ + /* get name of connected client */ + hp = gethostbyaddr((char *)&((struct sockaddr_in *)who)->sin_addr, sizeof (struct in_addr), + ((struct sockaddr_in *)who)->sin_family); + + if (hp == NULL && registerd_host_only) { + fatal(net, "Couldn't resolve your address into a host name.\r\n\ + Please contact your net administrator"); + } else if (hp && + (strlen(hp->h_name) <= utmp_len)) { + host = hp->h_name; + } else { + host = inet_ntoa(((struct sockaddr_in *)who)->sin_addr); + } + /* + * We must make a copy because Kerberos is probably going + * to also do a gethost* and overwrite the static data... + */ + strncpy(remote_hostname, host, sizeof(remote_hostname)-1); + remote_hostname[sizeof(remote_hostname)-1] = 0; + host = remote_hostname; + + (void) gethostname(host_name, sizeof(host_name) - 1); + host_name[sizeof(host_name) - 1] = '\0'; + hostname = host_name; +#else /* __APPLE__ */ + /* get name of connected client */ + 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) + err_ = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST); + /* XXX: do 'err_' check */ + + (void) gethostname(host_name, sizeof(host_name) - 1); + host_name[sizeof(host_name) - 1] = '\0'; + hostname = host_name; +#endif /* __APPLE__ */ + +#ifdef AUTHENTICATION +#ifdef ENCRYPTION +/* The above #ifdefs should actually be "or"'ed, not "and"'ed. + * This is a byproduct of needing "#ifdef" and not "#if defined()" + * for unifdef. XXX MarkM + */ + auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); +#endif +#endif + + init_env(); + /* + * get terminal type. + */ + *user_name = 0; + level = getterminaltype(user_name); + setenv("TERM", terminaltype ? terminaltype : "network", 1); + + telnet(net, mpty, remote_hostname); /* begin server process */ + + /*NOTREACHED*/ +} /* end of doit */ + +/* + * Main loop. Select from pty and network, and + * hand data to telnet receiver finite state machine. + */ +void +telnet(int f, int p, char *host) +{ + int on = 1; +#define TABBUFSIZ 512 + char defent[TABBUFSIZ]; + char defstrs[TABBUFSIZ]; +#undef TABBUFSIZ + char *HE; + char *HN; + char *IM; +#ifdef __APPLE__ + char *IF = NULL; + int if_fd = -1; + char *if_buf; + struct stat statbuf; +#endif + 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, output_data("td: simulating recv\r\n")); + 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); + + /* + * Turn on packet mode + */ + (void) ioctl(p, TIOCPKT, (char *)&on); + +#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(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); + +#ifdef TIOCNOTTY + { + int t; + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#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. + */ + +#ifdef __APPLE__ + if (getenv("USER")) + hostinfo = 0; +#endif + if (getent(defent, "default") == 1) { + char *cp=defstrs; + + HE = Getstr("he", &cp); + HN = Getstr("hn", &cp); + IM = Getstr("im", &cp); +#ifdef __APPLE__ + IF = Getstr("if", &cp); +#endif + if (HN && *HN) + (void) strlcpy(host_name, HN, sizeof(host_name)); +#ifdef __APPLE__ + if (IF && (if_fd = open(IF, O_RDONLY, 000)) != -1) + IM = 0; +#endif + if (IM == 0) + IM = strdup(""); + } else { + IM = strdup(DEFAULT_IM); + HE = 0; + } + edithost(HE, host_name); + if (hostinfo && *IM) + putf(IM, ptyibuf2); +#ifdef __APPLE__ + else if (IF && if_fd != -1) { + fstat (if_fd, &statbuf); + if_buf = (char *) mmap (0, statbuf.st_size, PROT_READ, + 0, if_fd, 0); + putf(if_buf, ptyibuf2); + munmap (if_buf, statbuf.st_size); + close (if_fd); + } +#endif + + 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, output_data("td: Entering processing loop\r\n")); + + /* + * 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; + int c; + + if (ncc < 0 && pcc < 0) + break; + + 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), + output_data("td: netread %d chars\r\n", ncc)); + DIAG(TD_NETDATA, printdata("nd", netip, ncc)); + } + + /* + * Something to read from the pty... + */ + if (FD_ISSET(p, &ibits)) { + pcc = read(p, ptyibuf, BUFSIZ); + /* + * 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; +#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. + */ + output_data("%c%c", IAC, 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; + output_data("%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON + : LFLOW_OFF, + IAC, SE); + DIAG(TD_OPTIONS, printsub('>', + (unsigned char *)nfrontp-4, + 4);); + } + } + pcc--; + ptyip = ptyibuf+1; + } + } + + while (pcc > 0) { + if ((&netobuf[BUFSIZ] - nfrontp) < 2) + break; + c = *ptyip++ & 0377, pcc--; + if (c == IAC) + output_data("%c", c); + output_data("%c", c); + if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { + if (pcc > 0 && ((*ptyip & 0377) == '\n')) { + output_data("%c", *ptyip++ & 0377); + pcc--; + } else + output_data("%c", '\0'); + } + } + + 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 + +/* + * Send interrupt to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write intr char. + */ +void +interrupt(void) +{ + ptyflush(); /* half-hearted */ + +#ifdef TCSIG + (void) ioctl(mpty, TCSIG, (char *)SIGINT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_IP].sptr ? + (unsigned char)*slctab[SLC_IP].sptr : '\177'; +#endif /* TCSIG */ +} + +/* + * Send quit to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write quit char. + */ +void +sendbrk(void) +{ + ptyflush(); /* half-hearted */ +#ifdef TCSIG + (void) ioctl(mpty, TCSIG, (char *)SIGQUIT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_ABORT].sptr ? + (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; +#endif /* TCSIG */ +} + +void +sendsusp(void) +{ +#ifdef SIGTSTP + ptyflush(); /* half-hearted */ +# ifdef TCSIG + (void) ioctl(mpty, 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(void) +{ +#if defined(SIGINFO) && defined(TCSIG) + if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { + (void) ioctl(spty, TCSIG, (char *)SIGINFO); + return; + } +#endif + output_data("\r\n[Yes]\r\n"); +} + +void +doeof(void) +{ + 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/remote_cmds/telnetd.tproj/telnetd.h b/remote_cmds/telnetd.tproj/telnetd.h new file mode 100644 index 0000000..e99a7c9 --- /dev/null +++ b/remote_cmds/telnetd.tproj/telnetd.h @@ -0,0 +1,49 @@ +/* + * 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. + * + * @(#)telnetd.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: src/contrib/telnet/telnetd/telnetd.h,v 1.2 2001/11/30 21:06:38 markm Exp $ + */ + + +#include "defs.h" +#include "ext.h" + +#ifdef DIAGNOSTICS +#define DIAG(a,b) if (diagnostic & (a)) b +#else +#define DIAG(a,b) +#endif + +/* other external variables */ +extern char **environ; +extern const char *altlogin; diff --git a/remote_cmds/telnetd.tproj/termstat.c b/remote_cmds/telnetd.tproj/termstat.c new file mode 100644 index 0000000..e29e526 --- /dev/null +++ b/remote_cmds/telnetd.tproj/termstat.c @@ -0,0 +1,632 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)termstat.c 8.2 (Berkeley) 5/30/95"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/termstat.c,v 1.13 2004/07/28 05:37:18 kan Exp $"); + +#include "telnetd.h" + +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +/* + * local variables + */ +int def_tspeed = -1, def_rspeed = -1; +#ifdef TIOCSWINSZ +int def_row = 0, def_col = 0; +#endif +#ifdef LINEMODE +static int _terminit = 0; +#endif /* LINEMODE */ + +#ifdef LINEMODE +/* + * localstat + * + * This function handles all management of linemode. + * + * Linemode allows the client to do the local editing of data + * and send only complete lines to the server. Linemode state is + * based on the state of the pty driver. If the pty is set for + * external processing, then we can use linemode. Further, if we + * can use real linemode, then we can look at the edit control bits + * in the pty to determine what editing the client should do. + * + * Linemode support uses the following state flags to keep track of + * current and desired linemode state. + * alwayslinemode : true if -l was specified on the telnetd + * command line. It means to have linemode on as much as + * possible. + * + * lmodetype: signifies whether the client can + * handle real linemode, or if use of kludgeomatic linemode + * is preferred. It will be set to one of the following: + * REAL_LINEMODE : use linemode option + * NO_KLUDGE : don't initiate kludge linemode. + * KLUDGE_LINEMODE : use kludge linemode + * NO_LINEMODE : client is ignorant of linemode + * + * linemode, uselinemode : linemode is true if linemode + * is currently on, uselinemode is the state that we wish + * to be in. If another function wishes to turn linemode + * on or off, it sets or clears uselinemode. + * + * editmode, useeditmode : like linemode/uselinemode, but + * these contain the edit mode states (edit and trapsig). + * + * The state variables correspond to some of the state information + * in the pty. + * linemode: + * In real linemode, this corresponds to whether the pty + * expects external processing of incoming data. + * In kludge linemode, this more closely corresponds to the + * whether normal processing is on or not. (ICANON in + * system V, or COOKED mode in BSD.) + * If the -l option was specified (alwayslinemode), then + * an attempt is made to force external processing on at + * all times. + * + * The following heuristics are applied to determine linemode + * handling within the server. + * 1) Early on in starting up the server, an attempt is made + * to negotiate the linemode option. If this succeeds + * then lmodetype is set to REAL_LINEMODE and all linemode + * processing occurs in the context of the linemode option. + * 2) If the attempt to negotiate the linemode option failed, + * and the "-k" (don't initiate kludge linemode) isn't set, + * then we try to use kludge linemode. We test for this + * capability by sending "do Timing Mark". If a positive + * response comes back, then we assume that the client + * understands kludge linemode (ech!) and the + * lmodetype flag is set to KLUDGE_LINEMODE. + * 3) Otherwise, linemode is not supported at all and + * lmodetype remains set to NO_LINEMODE (which happens + * to be 0 for convenience). + * 4) At any time a command arrives that implies a higher + * state of linemode support in the client, we move to that + * linemode support. + * + * A short explanation of kludge linemode is in order here. + * 1) The heuristic to determine support for kludge linemode + * is to send a do timing mark. We assume that a client + * that supports timing marks also supports kludge linemode. + * A risky proposition at best. + * 2) Further negotiation of linemode is done by changing the + * the server's state regarding SGA. If server will SGA, + * then linemode is off, if server won't SGA, then linemode + * is on. + */ +void +localstat(void) +{ + int need_will_echo = 0; + + /* + * Check for changes to flow control if client supports it. + */ + flowstat(); + + /* + * Check linemode on/off state + */ + uselinemode = tty_linemode(); + + /* + * If alwayslinemode is on, and pty is changing to turn it off, then + * force linemode back on. + */ + if (alwayslinemode && linemode && !uselinemode) { + uselinemode = 1; + tty_setlinemode(uselinemode); + } + + if (uselinemode) { + /* + * Check for state of BINARY options. + * + * We only need to do the binary dance if we are actually going + * to use linemode. As this confuses some telnet clients + * that don't support linemode, and doesn't gain us + * anything, we don't do it unless we're doing linemode. + * -Crh (henrich@msu.edu) + */ + + if (tty_isbinaryin()) { + if (his_want_state_is_wont(TELOPT_BINARY)) + send_do(TELOPT_BINARY, 1); + } else { + if (his_want_state_is_will(TELOPT_BINARY)) + send_dont(TELOPT_BINARY, 1); + } + + if (tty_isbinaryout()) { + if (my_want_state_is_wont(TELOPT_BINARY)) + send_will(TELOPT_BINARY, 1); + } else { + if (my_want_state_is_will(TELOPT_BINARY)) + send_wont(TELOPT_BINARY, 1); + } + } + +#ifdef ENCRYPTION + /* + * If the terminal is not echoing, but editing is enabled, + * something like password input is going to happen, so + * if we the other side is not currently sending encrypted + * data, ask the other side to start encrypting. + */ + if (his_state_is_will(TELOPT_ENCRYPT)) { + static int enc_passwd = 0; + if (uselinemode && !tty_isecho() && tty_isediting() + && (enc_passwd == 0) && !decrypt_input) { + encrypt_send_request_start(); + enc_passwd = 1; + } else if (enc_passwd) { + encrypt_send_request_end(); + enc_passwd = 0; + } + } +#endif /* ENCRYPTION */ + + /* + * Do echo mode handling as soon as we know what the + * linemode is going to be. + * If the pty has echo turned off, then tell the client that + * the server will echo. If echo is on, then the server + * will echo if in character mode, but in linemode the + * client should do local echoing. The state machine will + * not send anything if it is unnecessary, so don't worry + * about that here. + * + * If we need to send the WILL ECHO (because echo is off), + * then delay that until after we have changed the MODE. + * This way, when the user is turning off both editing + * and echo, the client will get editing turned off first. + * This keeps the client from going into encryption mode + * and then right back out if it is doing auto-encryption + * when passwords are being typed. + */ + if (uselinemode) { + if (tty_isecho()) + send_wont(TELOPT_ECHO, 1); + else + need_will_echo = 1; +#ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_OK) + lmodetype = KLUDGE_LINEMODE; +#endif + } + + /* + * If linemode is being turned off, send appropriate + * command and then we're all done. + */ + if (!uselinemode && linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_dont(TELOPT_LINEMODE, 1); +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) + send_will(TELOPT_SGA, 1); +# endif /* KLUDGELINEMODE */ + send_will(TELOPT_ECHO, 1); + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * If using real linemode check edit modes for possible later use. + * If we are in kludge linemode, do the SGA negotiation. + */ + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig()) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) { + if (tty_isediting() && uselinemode) + send_wont(TELOPT_SGA, 1); + else + send_will(TELOPT_SGA, 1); + } +# endif /* KLUDGELINEMODE */ + + /* + * Negotiate linemode on if pty state has changed to turn it on. + * Send appropriate command and send along edit mode, then all done. + */ + if (uselinemode && !linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_LINEMODE) { + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_do(TELOPT_LINEMODE, 1); + /* send along edit modes */ + output_data("%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + editmode = useeditmode; +# ifdef KLUDGELINEMODE + } +# endif /* KLUDGELINEMODE */ + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * None of what follows is of any value if not using + * real linemode. + */ + if (lmodetype < REAL_LINEMODE) + goto done; +# endif /* KLUDGELINEMODE */ + + if (linemode && his_state_is_will(TELOPT_LINEMODE)) { + /* + * If edit mode changed, send edit mode. + */ + if (useeditmode != editmode) { + /* + * Send along appropriate edit mode mask. + */ + output_data("%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + editmode = useeditmode; + } + + + /* + * Check for changes to special characters in use. + */ + start_slc(0); + check_slc(); + (void) end_slc(0); + } + +done: + if (need_will_echo) + send_will(TELOPT_ECHO, 1); + /* + * Some things should be deferred until after the pty state has + * been set by the local process. Do those things that have been + * deferred now. This only happens once. + */ + if (_terminit == 0) { + _terminit = 1; + defer_terminit(); + } + + netflush(); + set_termbuf(); + return; + +} /* end of localstat */ +#endif /* LINEMODE */ + +/* + * flowstat + * + * Check for changes to flow control + */ +void +flowstat(void) +{ + if (his_state_is_will(TELOPT_LFLOW)) { + if (tty_flowmode() != flowmode) { + flowmode = tty_flowmode(); + output_data("%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON : LFLOW_OFF, + IAC, SE); + } + if (tty_restartany() != restartany) { + restartany = tty_restartany(); + output_data("%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + restartany ? LFLOW_RESTART_ANY + : LFLOW_RESTART_XON, + IAC, SE); + } + } +} + +/* + * clientstat + * + * Process linemode related requests from the client. + * Client can request a change to only one of linemode, editmode or slc's + * at a time, and if using kludge linemode, then only linemode may be + * affected. + */ +void +clientstat(int code, int parm1, int parm2) +{ + + /* + * Get a copy of terminal characteristics. + */ + init_termbuf(); + + /* + * Process request from client. code tells what it is. + */ + switch (code) { +#ifdef LINEMODE + case TELOPT_LINEMODE: + /* + * Don't do anything unless client is asking us to change + * modes. + */ + uselinemode = (parm1 == WILL); + if (uselinemode != linemode) { +# ifdef KLUDGELINEMODE + /* + * If using kludge linemode, make sure that + * we can do what the client asks. + * We can not turn off linemode if alwayslinemode + * and the ICANON bit is set. + */ + if (lmodetype == KLUDGE_LINEMODE) { + if (alwayslinemode && tty_isediting()) { + uselinemode = 1; + } + } + + /* + * Quit now if we can't do it. + */ + if (uselinemode == linemode) + return; + + /* + * If using real linemode and linemode is being + * turned on, send along the edit mode mask. + */ + if (lmodetype == REAL_LINEMODE && uselinemode) +# else /* KLUDGELINEMODE */ + if (uselinemode) +# endif /* KLUDGELINEMODE */ + { + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig()) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; + output_data("%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode, IAC, SE); + editmode = useeditmode; + } + + + tty_setlinemode(uselinemode); + + linemode = uselinemode; + + if (!linemode) + send_will(TELOPT_ECHO, 1); + } + break; + + case LM_MODE: + { + int ack, changed; + + /* + * Client has sent along a mode mask. If it agrees with + * what we are currently doing, ignore it; if not, it could + * be viewed as a request to change. Note that the server + * will change to the modes in an ack if it is different from + * what we currently have, but we will not ack the ack. + */ + useeditmode &= MODE_MASK; + ack = (useeditmode & MODE_ACK); + useeditmode &= ~MODE_ACK; + + if ((changed = (useeditmode ^ editmode))) { + /* + * This check is for a timing problem. If the + * state of the tty has changed (due to the user + * application) we need to process that info + * before we write in the state contained in the + * ack!!! This gets out the new MODE request, + * and when the ack to that command comes back + * we'll set it and be in the right mode. + */ + if (ack) + localstat(); + if (changed & MODE_EDIT) + tty_setedit(useeditmode & MODE_EDIT); + + if (changed & MODE_TRAPSIG) + tty_setsig(useeditmode & MODE_TRAPSIG); + + if (changed & MODE_SOFT_TAB) + tty_setsofttab(useeditmode & MODE_SOFT_TAB); + + if (changed & MODE_LIT_ECHO) + tty_setlitecho(useeditmode & MODE_LIT_ECHO); + + set_termbuf(); + + if (!ack) { + output_data("%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode|MODE_ACK, + IAC, SE); + } + + editmode = useeditmode; + } + + break; + + } /* end of case LM_MODE */ +#endif /* LINEMODE */ + + case TELOPT_NAWS: +#ifdef TIOCSWINSZ + { + struct winsize ws; + + def_col = parm1; + def_row = parm2; +#ifdef LINEMODE + /* + * Defer changing window size until after terminal is + * initialized. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + + /* + * Change window size as requested by client. + */ + + ws.ws_col = parm1; + ws.ws_row = parm2; + (void) ioctl(spty, TIOCSWINSZ, (char *)&ws); + } +#endif /* TIOCSWINSZ */ + + break; + + case TELOPT_TSPEED: + { + def_tspeed = parm1; + def_rspeed = parm2; +#ifdef LINEMODE + /* + * Defer changing the terminal speed. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + /* + * Change terminal speed as requested by client. + * We set the receive speed first, so that if we can't + * store separate receive and transmit speeds, the transmit + * speed will take precedence. + */ + tty_rspeed(parm2); + tty_tspeed(parm1); + set_termbuf(); + + break; + + } /* end of case TELOPT_TSPEED */ + + default: + /* What? */ + break; + } /* end of switch */ + + netflush(); + +} /* end of clientstat */ + +#ifdef LINEMODE +/* + * defer_terminit + * + * Some things should not be done until after the login process has started + * and all the pty modes are set to what they are supposed to be. This + * function is called when the pty state has been processed for the first time. + * It calls other functions that do things that were deferred in each module. + */ +void +defer_terminit(void) +{ + + /* + * local stuff that got deferred. + */ + if (def_tspeed != -1) { + clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); + def_tspeed = def_rspeed = 0; + } + +#ifdef TIOCSWINSZ + if (def_col || def_row) { + struct winsize ws; + + memset((char *)&ws, 0, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void) ioctl(spty, TIOCSWINSZ, (char *)&ws); + } +#endif + + /* + * The only other module that currently defers anything. + */ + deferslc(); + +} /* end of defer_terminit */ + +/* + * terminit + * + * Returns true if the pty state has been processed yet. + */ +int +terminit(void) +{ + return(_terminit); + +} /* end of terminit */ +#endif /* LINEMODE */ diff --git a/remote_cmds/telnetd.tproj/utility.c b/remote_cmds/telnetd.tproj/utility.c new file mode 100644 index 0000000..306c027 --- /dev/null +++ b/remote_cmds/telnetd.tproj/utility.c @@ -0,0 +1,1081 @@ +/* + * 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. + */ + +#if 0 +#ifndef lint +static const char sccsid[] = "@(#)utility.c 8.4 (Berkeley) 5/30/95"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/contrib/telnet/telnetd/utility.c,v 1.13 2003/05/04 02:54:49 obrien Exp $"); + +#ifdef __FreeBSD__ +#include <locale.h> +#include <sys/utsname.h> +#endif +#include <string.h> +#define PRINTOPTIONS +#include "telnetd.h" + +#ifdef AUTHENTICATION +#include <libtelnet/auth.h> +#endif +#ifdef ENCRYPTION +#include <libtelnet/encrypt.h> +#endif + +/* + * utility functions performing io related tasks + */ + +/* + * ttloop + * + * A small subroutine to flush the network output buffer, get some data + * from the network, and pass it through the telnet state machine. We + * also flush the pty input buffer (by dropping its data) if it becomes + * too full. + */ + + void +ttloop() +{ + + DIAG(TD_REPORT, output_data("td: ttloop\r\n")); + if (nfrontp - nbackp > 0) { + netflush(); + } + ncc = read(net, netibuf, sizeof netibuf); + if (ncc < 0) { + syslog(LOG_INFO, "ttloop: read: %m"); + exit(1); + } else if (ncc == 0) { + syslog(LOG_INFO, "ttloop: peer died: %m"); + exit(1); + } + DIAG(TD_REPORT, output_data("td: ttloop read %d chars\r\n", ncc)); + netip = netibuf; + telrcv(); /* state machine */ + if (ncc > 0) { + pfrontp = pbackp = ptyobuf; + telrcv(); + } +} /* end of ttloop */ + +/* + * Check a descriptor to see if out of band data exists on it. + */ +int +stilloob(int s) +{ + static struct timeval timeout = { 0, 0 }; + fd_set excepts; + int value; + + do { + FD_ZERO(&excepts); + FD_SET(s, &excepts); + memset((char *)&timeout, 0, sizeof timeout); + value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); + } while ((value == -1) && (errno == EINTR)); + + if (value < 0) { + fatalperror(mpty, "select"); + } + if (FD_ISSET(s, &excepts)) { + return 1; + } else { + return 0; + } +} + +void +ptyflush(void) +{ + int n; + + if ((n = pfrontp - pbackp) > 0) { + DIAG(TD_REPORT | TD_PTYDATA, + output_data("td: ptyflush %d chars\r\n", n)); + DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); + n = write(mpty, pbackp, n); + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(0); + } + pbackp += n; + if (pbackp == pfrontp) + pbackp = pfrontp = ptyobuf; +} + +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ +static char * +nextitem(char *current) +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} /* end of nextitem */ + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ +void +netclear(void) +{ + char *thisitem, *next; + char *good; +#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ + ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) + +#ifdef ENCRYPTION + thisitem = nclearto > netobuf ? nclearto : netobuf; +#else /* ENCRYPTION */ + thisitem = netobuf; +#endif /* ENCRYPTION */ + + while ((next = nextitem(thisitem)) <= nbackp) { + thisitem = next; + } + + /* Now, thisitem is first before/at boundary. */ + +#ifdef ENCRYPTION + good = nclearto > netobuf ? nclearto : netobuf; +#else /* ENCRYPTION */ + good = netobuf; /* where the good bytes go */ +#endif /* ENCRYPTION */ + + while (nfrontp > thisitem) { + if (wewant(thisitem)) { + int length; + + next = thisitem; + do { + next = nextitem(next); + } while (wewant(next) && (nfrontp > next)); + length = next-thisitem; + memmove(good, thisitem, length); + good += length; + thisitem = next; + } else { + thisitem = nextitem(thisitem); + } + } + + nbackp = netobuf; + nfrontp = good; /* next byte to be sent */ + neturg = 0; +} /* end of netclear */ + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + */ +void +netflush(void) +{ + int n; + extern int not42; + + while ((n = nfrontp - nbackp) > 0) { +#if 0 + /* XXX This causes output_data() to recurse and die */ + DIAG(TD_REPORT, { + n += output_data("td: netflush %d chars\r\n", n); + }); +#endif +#ifdef ENCRYPTION + if (encrypt_output) { + char *s = nclearto ? nclearto : nbackp; + if (nfrontp - s > 0) { + (*encrypt_output)((unsigned char *)s, nfrontp-s); + nclearto = nfrontp; + } + } +#endif /* ENCRYPTION */ + /* + * if no urgent data, or if the other side appears to be an + * old 4.2 client (and thus unable to survive TCP urgent data), + * write the entire buffer in non-OOB mode. + */ + if ((neturg == 0) || (not42 == 0)) { + n = write(net, nbackp, n); /* normal write */ + } else { + n = neturg - nbackp; + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + if (n > 1) { + n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ + } else { + n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ + } + } + if (n == -1) { + if (errno == EWOULDBLOCK || errno == EINTR) + continue; + cleanup(0); + /* NOTREACHED */ + } + nbackp += n; +#ifdef ENCRYPTION + if (nbackp > nclearto) + nclearto = 0; +#endif /* ENCRYPTION */ + if (nbackp >= neturg) { + neturg = 0; + } + if (nbackp == nfrontp) { + nbackp = nfrontp = netobuf; +#ifdef ENCRYPTION + nclearto = 0; +#endif /* ENCRYPTION */ + } + } + return; +} /* end of netflush */ + + +/* + * miscellaneous functions doing a variety of little jobs follow ... + */ + + +void +fatal(int f, const char *msg) +{ + char buf[BUFSIZ]; + + (void) snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg); +#ifdef ENCRYPTION + if (encrypt_output) { + /* + * Better turn off encryption first.... + * Hope it flushes... + */ + encrypt_send_end(); + netflush(); + } +#endif /* ENCRYPTION */ + (void) write(f, buf, (int)strlen(buf)); + sleep(1); /*XXX*/ + exit(1); +} + +void +fatalperror(int f, const char *msg) +{ + char buf[BUFSIZ]; + + (void) snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno)); + fatal(f, buf); +} + +char editedhost[32]; + +void +edithost(char *pat, char *host) +{ + char *res = editedhost; + + if (!pat) + pat = strdup(""); + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + (void) strncpy(res, host, + sizeof editedhost - (res - editedhost) -1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +static char *putlocation; + +static void +putstr(const char *s) +{ + + while (*s) + putchr(*s++); +} + +void +putchr(int cc) +{ + *putlocation++ = cc; +} + +#ifdef __FreeBSD__ +static char fmtstr[] = { "%+" }; +#else +static char fmtstr[] = { "%l:%M%P on %A, %d %B %Y" }; +#endif + +void +putf(char *cp, char *where) +{ + char *slash; + time_t t; + char db[100]; +#ifdef __FreeBSD__ + static struct utsname kerninfo; + + if (!*kerninfo.sysname) + uname(&kerninfo); +#endif + + putlocation = where; + + while (*cp) { + if (*cp =='\n') { + putstr("\r\n"); + cp++; + continue; + } else if (*cp != '%') { + putchr(*cp++); + continue; + } + switch (*++cp) { + + case 't': +#ifdef STREAMSPTY + /* names are like /dev/pts/2 -- we want pts/2 */ + slash = strchr(line+1, '/'); +#else + slash = strrchr(line, '/'); +#endif + if (slash == (char *) 0) + putstr(line); + else + putstr(&slash[1]); + break; + + case 'h': + putstr(editedhost); + break; + + case 'd': +#ifdef __FreeBSD__ + setlocale(LC_TIME, ""); +#endif + (void)time(&t); + (void)strftime(db, sizeof(db), fmtstr, localtime(&t)); + putstr(db); + break; + +#ifdef __FreeBSD__ + case 's': + putstr(kerninfo.sysname); + break; + + case 'm': + putstr(kerninfo.machine); + break; + + case 'r': + putstr(kerninfo.release); + break; + + case 'v': + putstr(kerninfo.version); + break; +#endif + + case '%': + putchr('%'); + break; + } + cp++; + } +} + +#ifdef DIAGNOSTICS +/* + * Print telnet options and commands in plain text, if possible. + */ +void +printoption(const char *fmt, int option) +{ + if (TELOPT_OK(option)) + output_data("%s %s\r\n", fmt, TELOPT(option)); + else if (TELCMD_OK(option)) + output_data("%s %s\r\n", fmt, TELCMD(option)); + else + output_data("%s %d\r\n", fmt, option); + return; +} + +void +printsub(char direction, unsigned char *pointer, int length) +{ + int i = 0; + + if (!(diagnostic & TD_OPTIONS)) + return; + + if (direction) { + output_data("td: %s suboption ", + direction == '<' ? "recv" : "send"); + if (length >= 3) { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) { + output_data("(terminated by "); + if (TELOPT_OK(i)) + output_data("%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + output_data("%s ", TELCMD(i)); + else + output_data("%d ", i); + if (TELOPT_OK(j)) + output_data("%s", TELOPT(j)); + else if (TELCMD_OK(j)) + output_data("%s", TELCMD(j)); + else + output_data("%d", j); + output_data(", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) { + output_data("(Empty suboption??\?)"); + return; + } + switch (pointer[0]) { + case TELOPT_TTYPE: + output_data("TERMINAL-TYPE "); + switch (pointer[1]) { + case TELQUAL_IS: + output_data("IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + output_data("SEND"); + break; + default: + output_data( + "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + case TELOPT_TSPEED: + output_data("TERMINAL-SPEED"); + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_IS: + output_data(" IS %.*s", length-2, (char *)pointer+2); + break; + default: + if (pointer[1] == 1) + output_data(" SEND"); + else + output_data(" %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + } + break; + + case TELOPT_LFLOW: + output_data("TOGGLE-FLOW-CONTROL"); + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case LFLOW_OFF: + output_data(" OFF"); break; + case LFLOW_ON: + output_data(" ON"); break; + case LFLOW_RESTART_ANY: + output_data(" RESTART-ANY"); break; + case LFLOW_RESTART_XON: + output_data(" RESTART-XON"); break; + default: + output_data(" %d (unknown)", pointer[1]); + } + for (i = 2; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + + case TELOPT_NAWS: + output_data("NAWS"); + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + if (length == 2) { + output_data(" ?%d?", pointer[1]); + break; + } + output_data(" %d %d (%d)", + pointer[1], pointer[2], + (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); + if (length == 4) { + output_data(" ?%d?", pointer[3]); + break; + } + output_data(" %d %d (%d)", + pointer[3], pointer[4], + (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); + for (i = 5; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + + case TELOPT_LINEMODE: + output_data("LINEMODE "); + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case WILL: + output_data("WILL "); + goto common; + case WONT: + output_data("WONT "); + goto common; + case DO: + output_data("DO "); + goto common; + case DONT: + output_data("DONT "); + common: + if (length < 3) { + output_data("(no option??\?)"); + break; + } + switch (pointer[2]) { + case LM_FORWARDMASK: + output_data("Forward Mask"); + for (i = 3; i < length; i++) { + output_data(" %x", pointer[i]); + } + break; + default: + output_data("%d (unknown)", pointer[2]); + for (i = 3; i < length; i++) { + output_data(" %d", pointer[i]); + } + break; + } + break; + + case LM_SLC: + output_data("SLC"); + for (i = 2; i < length - 2; i += 3) { + if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC])); + else + output_data(" %d", pointer[i+SLC_FUNC]); + switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { + case SLC_NOSUPPORT: + output_data(" NOSUPPORT"); break; + case SLC_CANTCHANGE: + output_data(" CANTCHANGE"); break; + case SLC_VARIABLE: + output_data(" VARIABLE"); break; + case SLC_DEFAULT: + output_data(" DEFAULT"); break; + } + output_data("%s%s%s", + pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); + if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| + SLC_FLUSHOUT| SLC_LEVELBITS)) { + output_data("(0x%x)", pointer[i+SLC_FLAGS]); + } + output_data(" %d;", pointer[i+SLC_VALUE]); + if ((pointer[i+SLC_VALUE] == IAC) && + (pointer[i+SLC_VALUE+1] == IAC)) + i++; + } + for (; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + + case LM_MODE: + output_data("MODE "); + if (length < 3) { + output_data("(no mode??\?)"); + break; + } + { + char tbuf[32]; + sprintf(tbuf, "%s%s%s%s%s", + pointer[2]&MODE_EDIT ? "|EDIT" : "", + pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", + pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", + pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", + pointer[2]&MODE_ACK ? "|ACK" : ""); + output_data("%s", tbuf[1] ? &tbuf[1] : "0"); + } + if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { + output_data(" (0x%x)", pointer[2]); + } + for (i = 3; i < length; i++) { + output_data(" ?0x%x?", pointer[i]); + } + break; + default: + output_data("%d (unknown)", pointer[1]); + for (i = 2; i < length; i++) { + output_data(" %d", pointer[i]); + } + } + break; + + case TELOPT_STATUS: { + const char *cp; + int j, k; + + output_data("STATUS"); + + switch (pointer[1]) { + default: + if (pointer[1] == TELQUAL_SEND) + output_data(" SEND"); + else + output_data(" %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + case TELQUAL_IS: + output_data(" IS\r\n"); + + for (i = 2; i < length; i++) { + switch(pointer[i]) { + case DO: cp = "DO"; goto common2; + case DONT: cp = "DONT"; goto common2; + case WILL: cp = "WILL"; goto common2; + case WONT: cp = "WONT"; goto common2; + common2: + i++; + if (TELOPT_OK(pointer[i])) + output_data(" %s %s", cp, TELOPT(pointer[i])); + else + output_data(" %s %d", cp, pointer[i]); + + output_data("\r\n"); + break; + + case SB: + output_data(" SB "); + i++; + j = k = i; + while (j < length) { + if (pointer[j] == SE) { + if (j+1 == length) + break; + if (pointer[j+1] == SE) + j++; + else + break; + } + pointer[k++] = pointer[j++]; + } + printsub(0, &pointer[i], k - i); + if (i < length) { + output_data(" SE"); + i = j; + } else + i = j - 1; + + output_data("\r\n"); + + break; + + default: + output_data(" %d", pointer[i]); + break; + } + } + break; + } + break; + } + + case TELOPT_XDISPLOC: + output_data("X-DISPLAY-LOCATION "); + switch (pointer[1]) { + case TELQUAL_IS: + output_data("IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + output_data("SEND"); + break; + default: + output_data("- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + + case TELOPT_NEW_ENVIRON: + output_data("NEW-ENVIRON "); + goto env_common1; + case TELOPT_OLD_ENVIRON: + output_data("OLD-ENVIRON"); + env_common1: + switch (pointer[1]) { + case TELQUAL_IS: + output_data("IS "); + goto env_common; + case TELQUAL_SEND: + output_data("SEND "); + goto env_common; + case TELQUAL_INFO: + output_data("INFO "); + env_common: + { + int noquote = 2; + for (i = 2; i < length; i++ ) { + switch (pointer[i]) { + case NEW_ENV_VAR: + output_data("%s", "\" VAR " + noquote); + noquote = 2; + break; + + case NEW_ENV_VALUE: + output_data("%s", "\" VALUE " + noquote); + noquote = 2; + break; + + case ENV_ESC: + output_data("%s", "\" ESC " + noquote); + noquote = 2; + break; + + case ENV_USERVAR: + output_data("%s", "\" USERVAR " + noquote); + noquote = 2; + break; + + default: + if (isprint(pointer[i]) && pointer[i] != '"') { + if (noquote) { + output_data("\""); + noquote = 0; + } + output_data("%c", pointer[i]); + } else { + output_data("\" %03o " + noquote, + pointer[i]); + noquote = 2; + } + break; + } + } + if (!noquote) + output_data("\""); + break; + } + } + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + output_data("AUTHENTICATION"); + + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_REPLY: + case TELQUAL_IS: + output_data(" %s ", (pointer[1] == TELQUAL_IS) ? + "IS" : "REPLY"); + if (AUTHTYPE_NAME_OK(pointer[2])) + output_data("%s ", AUTHTYPE_NAME(pointer[2])); + else + output_data("%d ", pointer[2]); + if (length < 3) { + output_data("(partial suboption??\?)"); + break; + } + output_data("%s|%s", + ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + + { + char buf[512]; + auth_printsub(&pointer[1], length - 1, (unsigned char*)buf, sizeof(buf)); + output_data("%s", buf); + } + break; + + case TELQUAL_SEND: + i = 2; + output_data(" SEND "); + while (i < length) { + if (AUTHTYPE_NAME_OK(pointer[i])) + output_data("%s ", AUTHTYPE_NAME(pointer[i])); + else + output_data("%d ", pointer[i]); + if (++i >= length) { + output_data("(partial suboption??\?)"); + break; + } + output_data("%s|%s ", + ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + ++i; + } + break; + + case TELQUAL_NAME: + output_data(" NAME \"%.*s\"", length - 2, pointer + 2); + break; + + default: + for (i = 2; i < length; i++) { + output_data(" ?%d?", pointer[i]); + } + break; + } + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + output_data("ENCRYPT"); + if (length < 2) { + output_data(" (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case ENCRYPT_START: + output_data(" START"); + break; + + case ENCRYPT_END: + output_data(" END"); + break; + + case ENCRYPT_REQSTART: + output_data(" REQUEST-START"); + break; + + case ENCRYPT_REQEND: + output_data(" REQUEST-END"); + break; + + case ENCRYPT_IS: + case ENCRYPT_REPLY: + output_data(" %s ", (pointer[1] == ENCRYPT_IS) ? + "IS" : "REPLY"); + if (length < 3) { + output_data(" (partial suboption??\?)"); + break; + } + if (ENCTYPE_NAME_OK(pointer[2])) + output_data("%s ", ENCTYPE_NAME(pointer[2])); + else + output_data(" %d (unknown)", pointer[2]); + + { + char buf[512]; + encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf)); + output_data("%s", buf); + } + break; + + case ENCRYPT_SUPPORT: + i = 2; + output_data(" SUPPORT "); + while (i < length) { + if (ENCTYPE_NAME_OK(pointer[i])) + output_data("%s ", ENCTYPE_NAME(pointer[i])); + else + output_data("%d ", pointer[i]); + i++; + } + break; + + case ENCRYPT_ENC_KEYID: + output_data(" ENC_KEYID"); + goto encommon; + + case ENCRYPT_DEC_KEYID: + output_data(" DEC_KEYID"); + goto encommon; + + default: + output_data(" %d (unknown)", pointer[1]); + encommon: + for (i = 2; i < length; i++) { + output_data(" %d", pointer[i]); + } + break; + } + break; +#endif /* ENCRYPTION */ + + default: + if (TELOPT_OK(pointer[0])) + output_data("%s (unknown)", TELOPT(pointer[0])); + else + output_data("%d (unknown)", pointer[i]); + for (i = 1; i < length; i++) { + output_data(" %d", pointer[i]); + } + break; + } + output_data("\r\n"); +} + +/* + * Dump a data buffer in hex and ascii to the output data stream. + */ +void +printdata(const char *tag, char *ptr, int cnt) +{ + int i; + char xbuf[30]; + + while (cnt) { + /* flush net output buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < 80) { + netflush(); + } + + /* add a line of output */ + output_data("%s: ", tag); + for (i = 0; i < 20 && cnt; i++) { + output_data("%02x", *ptr); + if (isprint(*ptr)) { + xbuf[i] = *ptr; + } else { + xbuf[i] = '.'; + } + if (i % 2) { + output_data(" "); + } + cnt--; + ptr++; + } + xbuf[i] = '\0'; + output_data(" %s\r\n", xbuf ); + } +} +#endif /* DIAGNOSTICS */ diff --git a/remote_cmds/telnetd.tproj/vasprintf.c b/remote_cmds/telnetd.tproj/vasprintf.c new file mode 100644 index 0000000..ad4aed6 --- /dev/null +++ b/remote_cmds/telnetd.tproj/vasprintf.c @@ -0,0 +1,66 @@ +/* $OpenBSD: vasprintf.c,v 1.4 1998/06/21 22:13:47 millert Exp $ */ + +/* + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * 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. The name of the author 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 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 AUTHOR 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 defined(LIBC_RCS) && !defined(lint) +static char rcsid[] = "$FreeBSD: src/lib/libc/stdio/vasprintf.c,v 1.12 2001/01/24 13:00:47 deischen Exp $"; +#endif /* LIBC_RCS and not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> + +int +vasprintf(str, fmt, ap) + char **str; + const char *fmt; + va_list ap; +{ + int ret; + FILE f; + + f._file = -1; + f._flags = __SWR | __SSTR ; + f._bf._base = f._p = (unsigned char *)malloc(128); + if (f._bf._base == NULL) { + *str = NULL; + errno = ENOMEM; + return (-1); + } + f._bf._size = f._w = 127; /* Leave room for the NULL */ + ret = vfprintf(&f, fmt, ap); + *f._p = '\0'; + f._bf._base = realloc(f._bf._base, f._bf._size + 1); + if (f._bf._base == NULL) { + errno = ENOMEM; + ret = -1; + } + *str = (char *)f._bf._base; + return (ret); +} diff --git a/remote_cmds/tftp.tproj/Makefile b/remote_cmds/tftp.tproj/Makefile new file mode 100644 index 0000000..6ed62a0 --- /dev/null +++ b/remote_cmds/tftp.tproj/Makefile @@ -0,0 +1,12 @@ +Project = tftp + +HFILES = extern.h tftpsubs.h +CFILES = main.c tftp.c tftpsubs.c +MANPAGES = tftp.1 + +Extra_CC_Flags = -Wall -Werror -fPIE +Extra_CC_Flags += -D__FBSDID=__RCSID +Extra_LD_Flags = -dead_strip -pie +Extra_LD_Flags += -ledit + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make diff --git a/remote_cmds/tftp.tproj/extern.h b/remote_cmds/tftp.tproj/extern.h new file mode 100644 index 0000000..fdf3d31 --- /dev/null +++ b/remote_cmds/tftp.tproj/extern.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/tftp/extern.h,v 1.3 2002/03/22 01:42:33 imp Exp $ + */ + +void recvfile(int, char *, char *); +void xmitfile(int, char *, char *); diff --git a/remote_cmds/tftp.tproj/main.c b/remote_cmds/tftp.tproj/main.c new file mode 100644 index 0000000..f223726 --- /dev/null +++ b/remote_cmds/tftp.tproj/main.c @@ -0,0 +1,920 @@ +/* $NetBSD: main.c,v 1.19 2003/10/02 23:31:52 itojun Exp $ */ + +/* + * 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. 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 +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/tftp/main.c,v 1.22 2005/10/19 15:37:42 stefanf Exp $"); + +/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ + +/* + * TFTP User Program -- Command Interface. + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/param.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> +#include <arpa/tftp.h> + +#include <ctype.h> +#include <fcntl.h> +#include <err.h> +#include <histedit.h> +#include <netdb.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define MAXLINE 200 +#define TIMEOUT 5 /* secs between rexmt's */ +#define MAXSEGSIZE 65464 +struct sockaddr_storage peeraddr; +int f; +int trace; +int verbose; +int tsize=0; +int tout=0; +int def_blksize=SEGSIZE; +int blksize=SEGSIZE; +int connected; +char mode[32]; +char line[MAXLINE]; +int margc; +#define MAX_MARGV 20 +char *margv[MAX_MARGV]; +jmp_buf toplevel; +volatile int txrx_error; + +void get(int, char **); +void help(int, char **); +void intr(int); +void modecmd(int, char **); +void put(int, char **); +void quit(int, char **); +void setascii(int, char **); +void setbinary(int, char **); +void setpeer0(char *, const char *); +void setpeer(int, char **); +void setrexmt(int, char **); +void settimeout(int, char **); +void settrace(int, char **); +void setverbose(int, char **); +#ifdef __APPLE__ +void setblksize(int, char **); +void settsize(int, char **); +void settimeoutopt(int, char **); +#endif +void status(int, char **); +#ifdef __APPLE__ +char *tail(char *); +int main(int, char *[]); +void intr(int); +struct cmd *getcmd(char *); +#endif + +static void command(void) __dead2; +static const char *command_prompt(void); + +static void getusage(char *); +static void makeargv(void); +static void putusage(char *); +static void settftpmode(const char *); + +char *tail(char *); +struct cmd *getcmd(char *); + +#define HELPINDENT (sizeof("connect")) + +struct cmd { + const char *name; + char *help; + void (*handler)(int, char **); +}; + +char vhelp[] = "toggle verbose mode"; +char thelp[] = "toggle packet tracing"; +#ifdef __APPLE__ +char tshelp[] = "toggle extended tsize option"; +char tohelp[] = "toggle extended timeout option"; +char blhelp[] = "set an alternative blocksize (def. 512)"; +#endif +char chelp[] = "connect to remote tftp"; +char qhelp[] = "exit tftp"; +char hhelp[] = "print help information"; +char shelp[] = "send file"; +char rhelp[] = "receive file"; +char mhelp[] = "set file transfer mode"; +char sthelp[] = "show current status"; +char xhelp[] = "set per-packet retransmission timeout"; +char ihelp[] = "set total retransmission timeout"; +char ashelp[] = "set mode to netascii"; +char bnhelp[] = "set mode to octet"; + +struct cmd cmdtab[] = { + { "connect", chelp, setpeer }, + { "mode", mhelp, modecmd }, + { "put", shelp, put }, + { "get", rhelp, get }, + { "quit", qhelp, quit }, + { "verbose", vhelp, setverbose }, +#ifdef __APPLE__ + { "blksize", blhelp, setblksize }, + { "tsize", tshelp, settsize }, +#endif + { "trace", thelp, settrace }, + { "status", sthelp, status }, + { "binary", bnhelp, setbinary }, + { "ascii", ashelp, setascii }, + { "rexmt", xhelp, setrexmt }, + { "timeout", ihelp, settimeout }, +#ifdef __APPLE__ + { "tout", tohelp, settimeoutopt }, +#endif + { "?", hhelp, help }, + { NULL, NULL, NULL } +}; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + + f = -1; + strcpy(mode, "netascii"); + signal(SIGINT, intr); + + setprogname(argv[0]); + while ((c = getopt(argc, argv, "e")) != -1) { + switch (c) { + case 'e': + blksize = MAXSEGSIZE; + strcpy(mode, "octet"); + tsize = 1; + tout = 1; + break; + default: + printf("usage: %s [-e] host-name [port]\n", + getprogname()); + exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc >= 1) { + if (setjmp(toplevel) != 0) + exit(txrx_error); + argc++; + argv--; + setpeer(argc, argv); + } + if (setjmp(toplevel) != 0) + (void)putchar('\n'); + command(); + return (0); +} + +char hostname[MAXHOSTNAMELEN]; + +void +setpeer0(host, port) + char *host; + const char *port; +{ + struct addrinfo hints, *res0, *res; + int error, soopt; + struct sockaddr_storage ss; + const char *cause = "unknown"; + + if (connected) { + close(f); + f = -1; + } + connected = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_CANONNAME; + if (!port) + port = "tftp"; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("%s", gai_strerror(error)); + return; + } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_addrlen > sizeof(peeraddr)) + continue; + f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (f < 0) { + cause = "socket"; + continue; + } + + memset(&ss, 0, sizeof(ss)); + ss.ss_family = res->ai_family; + ss.ss_len = res->ai_addrlen; + if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) { + cause = "bind"; + close(f); + f = -1; + continue; + } + + break; + } + + if (f >= 0) { + soopt = 65536; + if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt)) + < 0) { + close(f); + f = -1; + cause = "setsockopt SNDBUF"; + } + if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt)) + < 0) { + close(f); + f = -1; + cause = "setsockopt RCVBUF"; + } + } + + if (f < 0) + warn("%s", cause); + else { + /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ + memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); + if (res->ai_canonname) { + (void) strlcpy(hostname, res->ai_canonname, + sizeof(hostname)); + } else + (void) strlcpy(hostname, host, sizeof(hostname)); + connected = 1; + } + + freeaddrinfo(res0); +} + +void +setpeer(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2) { + strcpy(line, "Connect "); + printf("(to) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if ((argc < 2) || (argc > 3)) { + printf("usage: %s [-e] host-name [port]\n", getprogname()); + return; + } + if (argc == 3) + setpeer0(argv[1], argv[2]); + else + setpeer0(argv[1], NULL); +} + +struct modes { + const char *m_name; + const char *m_mode; +} modes[] = { + { "ascii", "netascii" }, + { "netascii", "netascii" }, + { "binary", "octet" }, + { "image", "octet" }, + { "octet", "octet" }, +/* { "mail", "mail" }, */ + { 0, 0 } +}; + +void +modecmd(argc, argv) + int argc; + char *argv[]; +{ + struct modes *p; + const char *sep; + + if (argc < 2) { + printf("Using %s mode to transfer files.\n", mode); + return; + } + if (argc == 2) { + for (p = modes; p->m_name; p++) + if (strcmp(argv[1], p->m_name) == 0) + break; + if (p->m_name) { + settftpmode(p->m_mode); + return; + } + printf("%s: unknown mode\n", argv[1]); + /* drop through and print usage message */ + } + + printf("usage: %s [", argv[0]); + sep = " "; + for (p = modes; p->m_name; p++) { + printf("%s%s", sep, p->m_name); + if (*sep == ' ') + sep = " | "; + } + printf(" ]\n"); + return; +} + +void +setbinary(argc, argv) + int argc __unused; + char *argv[] __unused; +{ + + settftpmode("octet"); +} + +void +setascii(argc, argv) + int argc __unused; + char *argv[] __unused; +{ + + settftpmode("netascii"); +} + +static void +settftpmode(newmode) + const char *newmode; +{ + strcpy(mode, newmode); + if (verbose) + printf("mode set to %s\n", mode); +} + + +/* + * Send file(s). + */ +void +put(argc, argv) + int argc; + char *argv[]; +{ + int fd; + int n; + char *cp, *targ; +#ifdef __APPLE__ + char targbuf[PATH_MAX]; +#endif /* __APPLE__ */ + + if (argc < 2) { + strcpy(line, "send "); + printf("(file) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + putusage(argv[0]); + return; + } + targ = argv[argc - 1]; + if (rindex(argv[argc - 1], ':')) { + char *lcp; + + for (n = 1; n < argc - 1; n++) + if (index(argv[n], ':')) { + putusage(argv[0]); + return; + } + lcp = argv[argc - 1]; + targ = rindex(lcp, ':'); + *targ++ = 0; + if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { + lcp[strlen(lcp) - 1] = '\0'; + lcp++; + } + setpeer0(lcp, NULL); + } + if (!connected) { + printf("No target machine specified.\n"); + return; + } + if (argc < 4) { + cp = argc == 2 ? tail(targ) : argv[1]; + fd = open(cp, O_RDONLY); + if (fd < 0) { + warn("%s", cp); + return; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + cp, hostname, targ, mode); + xmitfile(fd, targ, mode); + return; + } + /* this assumes the target is a directory */ + /* on a remote unix system. hmmmm. */ +#ifdef __APPLE__ + snprintf(targbuf, sizeof(targbuf), "%s/", targ); + cp = targbuf + strlen(targbuf); +#else /* !__APPLE__ */ + cp = index(targ, '\0'); + *cp++ = '/'; +#endif /* __APPLE__ */ + for (n = 1; n < argc - 1; n++) { +#ifdef __APPLE__ + strlcpy(cp, tail(argv[n]), sizeof(targbuf) - (cp - targbuf)); +#else /* !__APPLE__ */ + strcpy(cp, tail(argv[n])); +#endif /* __APPLE__ */ + fd = open(argv[n], O_RDONLY); + if (fd < 0) { + warn("%s", argv[n]); + continue; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", +#ifdef __APPLE__ + argv[n], hostname, targbuf, mode); +#else /* !__APPLE__ */ + argv[n], hostname, targ, mode); +#endif /* __APPLE__ */ + xmitfile(fd, targ, mode); + } +} + +static void +putusage(s) + char *s; +{ + printf("usage: %s file ... host:target, or\n", s); + printf(" %s file ... target (when already connected)\n", s); +} + +/* + * Receive file(s). + */ +void +get(argc, argv) + int argc; + char *argv[]; +{ + int fd; + int n; + char *cp; + char *src; + + if (argc < 2) { + strcpy(line, "get "); + printf("(files) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + getusage(argv[0]); + return; + } + if (!connected) { + for (n = 1; n < argc ; n++) + if (rindex(argv[n], ':') == 0) { + getusage(argv[0]); + return; + } + } + for (n = 1; n < argc ; n++) { + src = rindex(argv[n], ':'); + if (src == NULL) + src = argv[n]; + else { + char *lcp; + + *src++ = 0; + lcp = argv[n]; + if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { + lcp[strlen(lcp) - 1] = '\0'; + lcp++; + } + setpeer0(lcp, NULL); + if (!connected) + continue; + } + if (argc < 4) { + cp = argc == 3 ? argv[2] : tail(src); + fd = creat(cp, 0644); + if (fd < 0) { + warn("%s", cp); + return; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + recvfile(fd, src, mode); + break; + } + cp = tail(src); /* new .. jdg */ + fd = creat(cp, 0644); + if (fd < 0) { + warn("%s", cp); + continue; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode); + recvfile(fd, src, mode); + } +} + +static void +getusage(s) + char *s; +{ + printf("usage: %s host:file host:file ... file, or\n", s); + printf(" %s file file ... file if connected\n", s); +} + +void +setblksize(argc, argv) + int argc; + char *argv[]; +{ + int t; + + if (argc < 2) { + strcpy(line, "blksize "); + printf("(blksize) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 8 || t > 65464) + printf("%s: bad value\n", argv[1]); + else + blksize = t; +} + +int def_rexmtval = TIMEOUT; +int rexmtval = TIMEOUT; + +void +setrexmt(argc, argv) + int argc; + char *argv[]; +{ + int t; + + if (argc < 2) { + strcpy(line, "Rexmt-timeout "); + printf("(value) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + rexmtval = t; +} + +int maxtimeout = 5 * TIMEOUT; + +void +settimeout(argc, argv) + int argc; + char *argv[]; +{ + int t; + + if (argc < 2) { + strcpy(line, "Maximum-timeout "); + printf("(value) "); + fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + maxtimeout = t; +} + +void +status(argc, argv) + int argc __unused; + char *argv[] __unused; +{ + if (connected) + printf("Connected to %s.\n", hostname); + else + printf("Not connected.\n"); + printf("Mode: %s Verbose: %s Tracing: %s\n", mode, + verbose ? "on" : "off", trace ? "on" : "off"); + printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", + rexmtval, maxtimeout); +} + +void +intr(dummy) + int dummy __unused; +{ + + signal(SIGALRM, SIG_IGN); + alarm(0); + longjmp(toplevel, -1); +} + +char * +tail(filename) + char *filename; +{ + char *s; + + while (*filename) { + s = rindex(filename, '/'); + if (s == NULL) + break; + if (s[1]) + return (s + 1); + *s = '\0'; + } + return (filename); +} + +static const char * +command_prompt() +{ + + return ("tftp> "); +} + +/* + * Command parser. + */ +static void +command() +{ + HistEvent he; + struct cmd *c; + static EditLine *el; + static History *hist; + const char *bp; + char *cp; + int len, num, vrbose; + + vrbose = isatty(0); + if (vrbose) { + el = el_init("tftp", stdin, stdout, stderr); + hist = history_init(); + history(hist, &he, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hist); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_PROMPT, command_prompt); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + } + for (;;) { + if (vrbose) { + if ((bp = el_gets(el, &num)) == NULL || num == 0) + exit(0); + len = (num > MAXLINE) ? MAXLINE : num; + memcpy(line, bp, len); + line[len] = '\0'; + history(hist, &he, H_ENTER, bp); + } else { + if (fgets(line, sizeof line , stdin) == 0) { + if (feof(stdin)) { + exit(txrx_error); + } else { + continue; + } + } + } + if ((cp = strchr(line, '\n'))) + *cp = '\0'; + if (line[0] == 0) + continue; + makeargv(); + if (margc == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + (*c->handler)(margc, margv); + } +} + +struct cmd * +getcmd(name) + char *name; +{ + const char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = 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. + */ +static void +makeargv() +{ + char *cp; + char **argp = margv; + + margc = 0; + if ((cp = strchr(line, '\n'))) + *cp = '\0'; + for (cp = line; margc < MAX_MARGV - 1 && *cp;) { + while (isspace((unsigned char)*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace((unsigned char)*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; +} + +void +quit(argc, argv) + int argc __unused; + char *argv[] __unused; +{ + exit(txrx_error); +} + +/* + * Help command. + */ +void +help(argc, argv) + int argc; + char *argv[]; +{ + struct cmd *c; + + if (argc == 1) { + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->name; c++) + printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); + return; + } + 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\n", c->help); + } +} + +void +settrace(argc, argv) + int argc __unused; + char **argv __unused; +{ + trace = !trace; + printf("Packet tracing %s.\n", trace ? "on" : "off"); +} + +void +setverbose(argc, argv) + int argc __unused; + char **argv __unused; +{ + verbose = !verbose; + printf("Verbose mode %s.\n", verbose ? "on" : "off"); +} + +void +settsize(argc, argv) + int argc __unused; + char **argv __unused; +{ + tsize = !tsize; + printf("Tsize mode %s.\n", tsize ? "on" : "off"); +} + +void +settimeoutopt(argc, argv) + int argc __unused; + char **argv __unused; +{ + tout = !tout; + printf("Timeout option %s.\n", tout ? "on" : "off"); +} diff --git a/remote_cmds/tftp.tproj/tftp.1 b/remote_cmds/tftp.tproj/tftp.1 new file mode 100644 index 0000000..fe867ca --- /dev/null +++ b/remote_cmds/tftp.tproj/tftp.1 @@ -0,0 +1,243 @@ +.\" $NetBSD: tftp.1,v 1.18 2003/08/07 11:16:14 agc Exp $ +.\" +.\" Copyright (c) 1990, 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. 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. +.\" +.\" @(#)tftp.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/tftp/tftp.1,v 1.20 2007/11/07 07:56:57 ru Exp $ +.\" +.Dd June 11, 2003 +.Dt TFTP 1 +.Os +.Sh NAME +.Nm tftp +.Nd "trivial file transfer program" +.Sh SYNOPSIS +.Nm +.Op Fl e +.Op Ar host +.Op Ar port +.Sh DESCRIPTION +The +.Nm +utility is the user interface to the Internet +.Tn TFTP +(Trivial File Transfer Protocol), +which allows users to transfer files to and from a remote machine. +The remote +.Ar host +(and optional +.Ar port ) +may be specified on the command line, in which case +.Nm +uses +.Ar host +(and +.Ar port ) +as the default for future transfers (see the +.Cm connect +command below). +.Pp +The optional +.Fl e +argument sets a binary transfer mode as well as setting the extended options +as if +.Cm tout , +.Cm tsize , +and +.Cm blksize 65464 , +had been given. +.Sh COMMANDS +Once +.Nm +is running, it issues the prompt +.Ql tftp\*[Gt] +and recognizes the following commands: +.Pp +.Bl -tag -width verbose -compact +.It Cm \&? Ar command-name ... +Print help information. +.Pp +.It Cm ascii +Shorthand for +.Ic mode Cm ascii . +.Pp +.It Cm binary +Shorthand for +.Ic mode Cm binary . +.Pp +.It Cm blksize Ar blk-size +Set the tftp blksize option to +.Ar blk-size +octets (8-bit bytes). Since the number of blocks in a tftp +.Cm get +or +.Cm put +is 65535, the default block size of 512 bytes only allows a maximum of +just under 32 megabytes to be transferred. The value given for +.Ar blk-size +must be between 8 and 65464, inclusive. +Note that many servers will not respect this option. +.Pp +.It Cm connect Ar host-name Op Ar port +Set the +.Ar host +(and optionally +.Ar port ) +for transfers. +Note that the +.Tn TFTP +protocol, unlike the +.Tn FTP +protocol, +does not maintain connections between transfers; thus, the +.Cm connect +command does not actually create a connection, +but merely remembers what host is to be used for transfers. +You do not have to use the +.Cm connect +command; the remote host can be specified as part of the +.Cm get +or +.Cm put +commands. +.Pp +.It Cm get Ar filename +.It Cm get Ar remotename localname +.It Cm get Ar file1 file2 ... fileN +Get one or more files from the remote host. +When using the +.Ar host +argument, the +.Ar host +will be used as default host for future transfers. +If +.Ar localname +is specified, the file is stored locally as +.Ar localname , +otherwise the original filename is used. +Note that it is not possible to download two files at a time, only +one, three, or more than three files, at a time. +.Pp +To specify an IPv6 numeric address for a host, wrap it using square +brackets like +.Dq Li [3ffe:2900:e00c:ffee::1234] : Ns Ar file +to disambiguate the +colons used in the IPv6 address from the colon separating the host and +the filename. +.Pp +.It Cm mode Ar transfer-mode +Set the mode for transfers; +.Ar transfer-mode +may be one of +.Cm ascii +or +.Cm binary . +The default is +.Cm ascii . +.Pp +.It Cm put Ar file +.It Cm put Ar localfile remotefile +.It Cm put Ar file1 file2 ... fileN remote-directory +Put a file or set of files to the specified +remote file or directory. +The destination +can be in one of two forms: +a filename on the remote host, if the host has already been specified, +or a string of the form +.Ar hosts:filename +to specify both a host and filename at the same time. +If the latter form is used, +the hostname specified becomes the default for future transfers. +When +.Ar remotename +is specified, the file is stored remotely as +.Ar remotename , +otherwise the original filename is used. +If the +.Ar remote-directory +argument is used, the remote host is assumed to be a +.Ux +machine. +To specify an IPv6 numeric address for a +.Ar host , +see the example under the +.Cm get +command. +.Pp +.It Cm quit +Exit +.Nm . +An end of file also exits. +.Pp +.It Cm rexmt Ar retransmission-timeout +Set the per-packet retransmission timeout, in seconds. +.Pp +.It Cm status +Show current status. +.Pp +.It Cm timeout Ar total-transmission-timeout +Set the total transmission timeout, in seconds. +.Pp +.It Cm tout +Toggle the tftp "timeout" option. If enabled, the client will pass its +.Ar retransmission-timeout +to the server. +Note that many servers will not respect this option. +.Pp +.It Cm trace +Toggle packet tracing. +.Pp +.It Cm tsize +Toggle the tftp "tsize" option. If enabled, the client will pass and +request the filesize of a file at the beginning of a file transfer. +Note that many servers will not respect this option. +.Pp +.It Cm verbose +Toggle verbose mode. +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +IPv6 support was implemented by WIDE/KAME project in 1999. +TFTP options were implemented by Wasabi Systems, Inc., in 2003, +and first appeared in +.Nx 2.0 . +.Sh SECURITY CONSIDERATIONS +Because there is no user-login or validation within +the +.Tn TFTP +protocol, the remote site will probably have some +sort of file-access restrictions in place. +The +exact methods are specific to each site and therefore +difficult to document here. +.Pp +Files larger than 33488896 octets (65535 blocks) cannot be transferred +without client and server supporting blocksize negotiation (RFC1783). diff --git a/remote_cmds/tftp.tproj/tftp.c b/remote_cmds/tftp.tproj/tftp.c new file mode 100644 index 0000000..56c9750 --- /dev/null +++ b/remote_cmds/tftp.tproj/tftp.c @@ -0,0 +1,742 @@ +/* $NetBSD: tftp.c,v 1.18 2003/08/07 11:16:14 agc Exp $ */ + +/* + * 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. 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 sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/tftp/tftp.c,v 1.13 2006/09/28 21:22:21 matteo Exp $"); + +/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ + +/* + * TFTP User Program -- Protocol Machines + */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> +#include <arpa/tftp.h> + +#include <err.h> +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#ifdef __APPLE__ +#include <stdlib.h> +#endif +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> + +#include "extern.h" +#include "tftpsubs.h" + +extern struct sockaddr_storage peeraddr; /* filled in by main */ +extern int f; /* the opened socket */ +extern int trace; +extern int verbose; +extern int def_rexmtval; +extern int rexmtval; +extern int maxtimeout; +extern volatile int txrx_error; +#ifdef __APPLE__ +extern int tsize; +extern int tout; +extern int def_blksize; +extern int blksize; +#endif + +char ackbuf[PKTSIZE]; +int timeout; +extern jmp_buf toplevel; +jmp_buf timeoutbuf; + +static void nak(int, struct sockaddr *); +#ifdef __APPLE__ +static int makerequest(int, const char *, struct tftphdr *, const char *, off_t); +#else +static int makerequest(int, const char *, struct tftphdr *, const char *); +#endif +static void printstats(const char *, unsigned long); +static void startclock(void); +static void stopclock(void); +static void timer(int); +static void tpacket(const char *, struct tftphdr *, int); +static int sockaddrcmp(struct sockaddr *, struct sockaddr *); + +static void get_options(struct tftphdr *, int); + +static void +get_options(struct tftphdr *ap, int size) +{ + unsigned long val; + char *opt, *endp, *nextopt, *valp; + int l; + + size -= 2; /* skip over opcode */ + opt = ap->th_stuff; + endp = opt + size - 1; + *endp = '\0'; + + while (opt < endp) { + l = strlen(opt) + 1; + valp = opt + l; + if (valp < endp) { + val = strtoul(valp, NULL, 10); + l = strlen(valp) + 1; + nextopt = valp + l; + if (val == ULONG_MAX && errno == ERANGE) { + /* Report illegal value */ + opt = nextopt; + continue; + } + } else { + /* Badly formed OACK */ + break; + } + if (strcmp(opt, "tsize") == 0) { + /* cool, but we'll ignore it */ + } else if (strcmp(opt, "timeout") == 0) { + if (val >= 1 && val <= 255) { + rexmtval = val; + } else { + /* Report error? */ + } + } else if (strcmp(opt, "blksize") == 0) { + if (val >= 8 && val <= MAXSEGSIZE) { + blksize = val; + } else { + /* Report error? */ + } + } else { + /* unknown option */ + } + opt = nextopt; + } +} + +/* + * Send the requested file. + */ +void +xmitfile(fd, name, mode) + int fd; + char *name; + char *mode; +{ + struct tftphdr *ap; /* data and ack packets */ + struct tftphdr *dp; + int n; + volatile unsigned int block; + volatile int size, convert; + volatile unsigned long amount; + struct sockaddr_storage from; + struct stat sbuf; + off_t filesize=0; + socklen_t fromlen; + FILE *file; + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ + + startclock(); /* start stat's clock */ + dp = r_init(); /* reset fillbuf/read-ahead code */ + ap = (struct tftphdr *)ackbuf; + if (tsize) { + if (fstat(fd, &sbuf) == 0) { + filesize = sbuf.st_size; + } else { + filesize = -1ULL; + } + } + file = fdopen(fd, "r"); + convert = !strcmp(mode, "netascii"); + block = 0; + amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); + + signal(SIGALRM, timer); + do { + if (block == 0) + size = makerequest(WRQ, name, dp, mode, filesize) - 4; + else { + /* size = read(fd, dp->th_data, SEGSIZE); */ + size = readit(file, &dp, blksize, convert); + if (size < 0) { + nak(errno + 100, (struct sockaddr *)&peer); + break; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + } + timeout = 0; + (void) setjmp(timeoutbuf); +send_data: + if (trace) + tpacket("sent", dp, size + 4); + n = sendto(f, dp, size + 4, 0, + (struct sockaddr *)&peer, peer.ss_len); + if (n != size + 4) { + warn("sendto"); + goto abort; + } + if (block) + read_ahead(file, blksize, convert); + for ( ; ; ) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + warn("recvfrom"); + goto abort; + } + if (!serv.ss_family) + serv = from; + else if (!sockaddrcmp((struct sockaddr *)&serv, + (struct sockaddr *)&from)) { + warn("server address/port mismatch"); + goto abort; + } + peer = from; + if (trace) + tpacket("received", ap, n); + /* should verify packet came from server */ + ap->th_opcode = ntohs(ap->th_opcode); + if (ap->th_opcode == ERROR) { + printf("Error code %d: %s\n", ap->th_code, + ap->th_msg); + txrx_error = 1; + goto abort; + } + if (ap->th_opcode == ACK) { + int j; + + ap->th_block = ntohs(ap->th_block); + if (ap->th_block == 0 && block == 0) { + /* + * If the extended options are enabled, + * the server just refused 'em all. + * The only one that _really_ + * matters is blksize, but we'll + * clear timeout, too. + */ + blksize = def_blksize; + rexmtval = def_rexmtval; + } + if (ap->th_block == (u_short)block) { + break; + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", + j); + } + if (ap->th_block == (u_short)(block-1)) { + goto send_data; + } + } + if (ap->th_opcode == OACK) { + if (block == 0) { + blksize = def_blksize; + rexmtval = def_rexmtval; + get_options(ap, n); + break; + } + } + } + if (block > 0) + amount += size; + block++; + } while (size == blksize || block == 1); +abort: + fclose(file); + stopclock(); + if (amount > 0) + printstats("Sent", amount); + txrx_error = 1; +} + +/* + * Receive a file. + */ +void +recvfile(fd, name, mode) + int fd; + char *name; + char *mode; +{ + struct tftphdr *ap; + struct tftphdr *dp; + int n, oack=0; + volatile unsigned int block; + volatile int size, firsttrip; + volatile unsigned long amount; + struct sockaddr_storage from; + socklen_t fromlen; + int readlen; + FILE *file; + volatile int convert; /* true if converting crlf -> lf */ + struct sockaddr_storage peer; + struct sockaddr_storage serv; /* valid server port number */ + + startclock(); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + file = fdopen(fd, "w"); + convert = !strcmp(mode, "netascii"); + block = 1; + firsttrip = 1; + amount = 0; + memcpy(&peer, &peeraddr, peeraddr.ss_len); + memset(&serv, 0, sizeof(serv)); + + signal(SIGALRM, timer); + do { + short rx_opcode; + + if (firsttrip) { + size = makerequest(RRQ, name, ap, mode, 0); + readlen = PKTSIZE; + firsttrip = 0; + } else { + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)(block)); + readlen = blksize+4; + size = 4; + block++; + } + timeout = 0; + (void) setjmp(timeoutbuf); +send_ack: + if (trace) + tpacket("sent", ap, size); + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peer, + peer.ss_len) != size) { + alarm(0); + warn("sendto"); + goto abort; + } + write_behind(file, convert); + for ( ; ; ) { + int j; + unsigned short rx_block; + + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, dp, readlen, 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + warn("recvfrom"); + goto abort; + } + if (serv.ss_family != AF_UNSPEC + && !sockaddrcmp((struct sockaddr *)&serv, + (struct sockaddr *)&from)) { + /* ignore this packet */ + if (trace && verbose) + tpacket("ignored", dp, n); + continue; + } + if (trace) + tpacket("received", dp, n); + /* should verify client address */ + rx_opcode = ntohs(dp->th_opcode); + switch (rx_opcode) { + case ERROR: + printf("Error code %d: %s\n", dp->th_code, + dp->th_msg); + txrx_error = 1; + if (serv.ss_family == AF_UNSPEC) { + peer = from; + } + goto abort; + case DATA: + rx_block = ntohs(dp->th_block); + if (rx_block == (u_short)block) { + if (block == 1 && !oack) { + /* no OACK, revert to defaults */ + blksize = def_blksize; + rexmtval = def_rexmtval; + } + if (serv.ss_family == AF_UNSPEC) { + peer = serv = from; + } + goto next_packet; + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", j); + } + if (rx_block == (u_short)(block - 1) + && serv.ss_family != AF_UNSPEC) { + goto send_ack; /* resend ack */ + } + break; + case OACK: + if (block == 1) { + if (serv.ss_family == AF_UNSPEC) { + serv = peer = from; + } + oack = 1; + blksize = def_blksize; + rexmtval = def_rexmtval; + get_options(dp, n); + ap->th_opcode = htons(ACK); + ap->th_block = 0; + readlen = blksize+4; + size = 4; + goto send_ack; + } + break; + default: + break; + } + } +next_packet: + /* size = write(fd, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, convert); + if (size < 0) { + nak(errno + 100, (struct sockaddr *)&peer); + break; + } + amount += size; + } while (size == blksize); +abort: /* ok to ack, since user */ + ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ + ap->th_block = htons((u_short)block); + (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer, + peer.ss_len); + write_behind(file, convert); /* flush last buffer */ + fclose(file); + stopclock(); + if (amount > 0) + printstats("Received", amount); + txrx_error = 1; +} + +static int +#ifdef __APPLE__ +makerequest(request, name, tp, mode, filesize) +#else +makerequest(request, name, tp, mode) +#endif + int request; + const char *name; + struct tftphdr *tp; + const char *mode; +#ifdef __APPLE__ + off_t filesize; +#endif +{ + char *cp; + + tp->th_opcode = htons((u_short)request); + cp = tp->th_stuff; + strcpy(cp, name); + cp += strlen(name); + *cp++ = '\0'; + strcpy(cp, mode); + cp += strlen(mode); + *cp++ = '\0'; +#ifdef __APPLE__ + if (tsize) { + strcpy(cp, "tsize"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%lu", (unsigned long) filesize); + cp += strlen(cp); + *cp++ = '\0'; + } + if (tout) { + strcpy(cp, "timeout"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%d", rexmtval); + cp += strlen(cp); + *cp++ = '\0'; + } + if (blksize != SEGSIZE) { + strcpy(cp, "blksize"); + cp += strlen(cp); + *cp++ = '\0'; + sprintf(cp, "%d", blksize); + cp += strlen(cp); + *cp++ = '\0'; + } +#endif + return (cp - (char *)tp); +} + +struct errmsg { + int e_code; + const char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Option negotiation failed" }, + { -1, 0 } +}; + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +static void +nak(error, peer) + int error; + struct sockaddr *peer; +{ + struct errmsg *pe; + struct tftphdr *tp; + int length; + size_t msglen; + + tp = (struct tftphdr *)ackbuf; + tp->th_opcode = htons((u_short)ERROR); + msglen = sizeof(ackbuf) - (&tp->th_msg[0] - ackbuf); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + tp->th_code = EUNDEF; + strlcpy(tp->th_msg, strerror(error - 100), msglen); + } else { + tp->th_code = htons((u_short)error); + strlcpy(tp->th_msg, pe->e_msg, msglen); + } + length = strlen(tp->th_msg); + msglen = &tp->th_msg[length + 1] - ackbuf; + if (trace) + tpacket("sent", tp, (int)msglen); + if (sendto(f, ackbuf, msglen, 0, peer, peer->sa_len) != msglen) + warn("nak"); +} + +static void +tpacket(s, tp, n) + const char *s; + struct tftphdr *tp; + int n; +{ + static const char *opcodes[] = + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file, *endp, *opt = NULL, *spc; + u_short op = ntohs(tp->th_opcode); + int i, o; + + if (op < RRQ || op > OACK) + printf("%s opcode=%x ", s, op); + else + printf("%s %s ", s, opcodes[op]); + switch (op) { + + case RRQ: + case WRQ: + n -= 2; + cp = tp->th_stuff; + endp = cp + n - 1; + if (*endp != '\0') { /* Shouldn't happen, but... */ + *endp = '\0'; + } + file = cp; + cp = strchr(cp, '\0') + 1; + printf("<file=%s, mode=%s", file, cp); + cp = strchr(cp, '\0') + 1; + o = 0; + while (cp < endp) { + i = strlen(cp) + 1; + if (o) { + printf(", %s=%s", opt, cp); + } else { + opt = cp; + } + o = (o+1) % 2; + cp += i; + } + printf(">\n"); + break; + + case DATA: + printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); + break; + + case ACK: + printf("<block=%d>\n", ntohs(tp->th_block)); + break; + + case ERROR: + printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); + break; + + case OACK: + o = 0; + n -= 2; + cp = tp->th_stuff; + endp = cp + n - 1; + if (*endp != '\0') { /* Shouldn't happen, but... */ + *endp = '\0'; + } + printf("<"); + spc = ""; + while (cp < endp) { + i = strlen(cp) + 1; + if (o) { + printf("%s%s=%s", spc, opt, cp); + spc = ", "; + } else { + opt = cp; + } + o = (o+1) % 2; + cp += i; + } + printf(">\n"); + break; + } +} + +struct timeval tstart; +struct timeval tstop; + +static void +startclock() +{ + + (void)gettimeofday(&tstart, NULL); +} + +static void +stopclock() +{ + + (void)gettimeofday(&tstop, NULL); +} + +static void +printstats(direction, amount) + const char *direction; + unsigned long amount; +{ + double delta; + /* compute delta in 1/10's second units */ + delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - + ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); + delta = delta/10.; /* back to seconds */ + printf("%s %ld bytes in %.1f seconds", direction, amount, delta); + if (verbose) + printf(" [%.0f bits/sec]", (amount*8.)/delta); + putchar('\n'); +} + +static void +timer(sig) + int sig __unused; +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) { + printf("Transfer timed out.\n"); + longjmp(toplevel, -1); + } + txrx_error = 1; + longjmp(timeoutbuf, 1); +} + +typedef union { + struct sockaddr * addr; + struct sockaddr_in * in; + struct sockaddr_in6 * in6; +} sockaddr_union; + +static int +sockaddrcmp(struct sockaddr * sa, struct sockaddr * sb) +{ + sockaddr_union a; + sockaddr_union b; + + if (sa->sa_len != sb->sa_len + || sa->sa_family != sb->sa_family) { + return 0; + } + a.addr = sa; + b.addr = sb; + switch (sa->sa_family) { + case AF_INET: + if (a.in->sin_port != b.in->sin_port + || a.in->sin_addr.s_addr != b.in->sin_addr.s_addr) { + return 0; + } + break; + case AF_INET6: + if (a.in6->sin6_port != b.in6->sin6_port + || !IN6_ARE_ADDR_EQUAL(&a.in6->sin6_addr, + &b.in6->sin6_addr)) { + return 0; + } + break; + default: + if (bcmp(sa, sb, sa->sa_len) != 0) { + return 0; + } + break; + } + return 1; +} diff --git a/remote_cmds/tftp.tproj/tftpsubs.c b/remote_cmds/tftp.tproj/tftpsubs.c new file mode 100644 index 0000000..5dbaa87 --- /dev/null +++ b/remote_cmds/tftp.tproj/tftpsubs.c @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD: src/usr.bin/tftp/tftpsubs.c,v 1.6 2005/02/14 17:42:58 stefanf Exp $"); + +#ifndef lint +__attribute__((__used__)) +static const char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; +#endif + +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/tftp.h> + +#include <stdio.h> +#include <unistd.h> + +#include "tftpsubs.h" + +#ifdef __APPLE__ +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[MAXPKTSIZE]; /* room for data packet */ +} bfs[2]; +#else +#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */ + +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[PKTSIZE]; /* room for data packet */ +} bfs[2]; +#endif + + /* Values for bf.counter */ +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ +/* [-1 .. SEGSIZE] = size of data in the data buffer */ + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ + +static struct tftphdr *rw_init(int); + +struct tftphdr *w_init(void) { return rw_init(0); } /* write-behind */ +struct tftphdr *r_init(void) { return rw_init(1); } /* read-ahead */ + +static struct tftphdr * +rw_init(x) /* init for either read-ahead or write-behind */ + int x; /* zero for write-behind, one for read-head */ +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct tftphdr *)bfs[0].buf; +} + + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +int +readit(file, dpp, amt, convert) + FILE *file; /* file opened for read */ + struct tftphdr **dpp; + int amt; + int convert; /* if true, convert to ascii */ +{ + struct bf *b; + + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ + + b = &bfs[current]; /* look at new buffer */ + if (b->counter == BF_FREE) /* if it's empty */ + read_ahead(file, amt, convert); /* fill it */ +/* assert(b->counter != BF_FREE);*//* check */ + *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ + return b->counter; +} + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr,lf and cr -> cr, nul + */ +void +read_ahead(file, amt, convert) + FILE *file; /* file opened for read */ + int amt; /* number of bytes to read */ + int convert; /* if true, convert to ascii */ +{ + int i; + char *p; + int c; + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = (struct tftphdr *)b->buf; + + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, amt); + return; + } + + p = dp->th_data; + for (i = 0 ; i < amt; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + c = getc(file); + if (c == EOF) break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer + from the queue. Calls write_behind only if next buffer not + available. + */ +int +writeit(file, dpp, ct, convert) + FILE *file; + struct tftphdr **dpp; + int ct, convert; +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + (void)write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct tftphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ +} + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR,NUL -> CR and CR,LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +int +write_behind(file, convert) + FILE *file; + int convert; +{ + char *buf; + int count; + int ct; + char *p; + int c; /* current character */ + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct tftphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; + + if (count <= 0) return -1; /* nak logic? */ + + if (convert == 0) + return write(fileno(file), buf, count); + + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else + if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); +skipit: + prevchar = c; + } + return count; +} + + +/* When an error has occurred, it is possible that the two sides + * are out of synch. Ie: that what I think is the other side's + * response to packet N is really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up + * for us on the network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting + * when trace is active). + */ + +int +synchnet(f) + int f; /* socket to flush */ +{ + int i, j = 0; + char rbuf[PKTSIZE]; + struct sockaddr_storage from; + socklen_t fromlen; + + while (1) { + (void) ioctl(f, FIONREAD, &i); + if (i) { + j++; + fromlen = sizeof from; + (void) recvfrom(f, rbuf, sizeof (rbuf), 0, + (struct sockaddr *)&from, &fromlen); + } else { + return(j); + } + } +} diff --git a/remote_cmds/tftp.tproj/tftpsubs.h b/remote_cmds/tftp.tproj/tftpsubs.h new file mode 100644 index 0000000..808acb2 --- /dev/null +++ b/remote_cmds/tftp.tproj/tftpsubs.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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. + * + * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/tftp/tftpsubs.h,v 1.2 2002/03/22 01:42:34 imp Exp $ + */ + +/* + * Prototypes for read-ahead/write-behind subroutines for tftp user and + * server. + */ +struct tftphdr *r_init(void); +#ifdef __APPLE__ +void read_ahead(FILE *, int, int); +int readit(FILE *, struct tftphdr **, int, int); +#else +void read_ahead(FILE *, int); +int readit(FILE *, struct tftphdr **, int); +#endif + +int synchnet(int); + +struct tftphdr *w_init(void); +int write_behind(FILE *, int); +int writeit(FILE *, struct tftphdr **, int, int); diff --git a/remote_cmds/tftpd.tproj/DERIVED_FILES b/remote_cmds/tftpd.tproj/DERIVED_FILES new file mode 100644 index 0000000..6e6534d --- /dev/null +++ b/remote_cmds/tftpd.tproj/DERIVED_FILES @@ -0,0 +1,5 @@ +# The following files have been derived from another project. If you do any +# changes to them here, make sure you mirror them there too. + +argon_usrbin/tftp/tftpsubs.h +argon_usrbin/tftp/tftpsubs.c diff --git a/remote_cmds/tftpd.tproj/Makefile b/remote_cmds/tftpd.tproj/Makefile new file mode 100644 index 0000000..e48f484 --- /dev/null +++ b/remote_cmds/tftpd.tproj/Makefile @@ -0,0 +1,16 @@ +Project = tftpd +Install_Dir = /usr/libexec + +HFILES = tftpsubs.h +CFILES = tftpd.c ../tftp.tproj/tftpsubs.c +MANPAGES = tftpd.8 +LAUNCHD_PLISTS = tftp.plist + +Extra_CC_Flags = -Wall -Werror -fPIE +Extra_CC_Flags += -D__FBSDID=__RCSID +Extra_LD_Flags = -dead_strip -pie + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make + +after_install: + $(MKDIR) "$(DSTROOT)/private/tftpboot" diff --git a/remote_cmds/tftpd.tproj/tftp.plist b/remote_cmds/tftpd.tproj/tftp.plist new file mode 100644 index 0000000..e31e08b --- /dev/null +++ b/remote_cmds/tftpd.tproj/tftp.plist @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <true/> + <key>Label</key> + <string>com.apple.tftpd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/tftpd</string> + <string>-i</string> + <string>/private/tftpboot</string> + </array> + <key>inetdCompatibility</key> + <dict> + <key>Wait</key> + <true/> + </dict> + <key>InitGroups</key> + <true/> + <key>Sockets</key> + <dict> + <key>Listeners</key> + <dict> + <key>SockServiceName</key> + <string>tftp</string> + <key>SockType</key> + <string>dgram</string> + </dict> + </dict> +</dict> +</plist> diff --git a/remote_cmds/tftpd.tproj/tftpd.8 b/remote_cmds/tftpd.tproj/tftpd.8 new file mode 100644 index 0000000..14df0f1 --- /dev/null +++ b/remote_cmds/tftpd.tproj/tftpd.8 @@ -0,0 +1,233 @@ +.\" $NetBSD: tftpd.8,v 1.21 2003/08/07 09:46:53 agc Exp $ +.\" +.\" Copyright (c) 1983, 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. 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. +.\" +.\" from: @(#)tftpd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 11, 2003 +.Dt TFTPD 8 +.Os +.Sh NAME +.Nm tftpd +.Nd +.Tn DARPA +Internet Trivial File Transfer Protocol server +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl g Ar group +.Op Fl i +.Op Fl l +.Op Fl n +.Op Fl s Ar directory +.Op Fl u Ar user +.Op Ar directory ... +.Sh DESCRIPTION +.Nm +is a server which supports the +.Tn DARPA +Trivial File Transfer Protocol. +The +.Tn TFTP +server operates at the port indicated in the +.Ql tftp +service description; see +.Xr services 5 . +This server should not be started manually; instead, it should be run using +.Xr launchd 8 +using the plist +.Pa /System/Library/LaunchDaemons/tftp.plist . +It may be started using the +.Xr launchctl 1 +load command; refer to the documentation for that utility for more information. +.Pp +The use of +.Xr tftp 1 +does not require an account or password on the remote system. +Due to the lack of authentication information, +.Nm +will allow only publicly readable files to be accessed. +Filenames beginning in ``\|\fB.\|.\fP\|/'' or +containing ``/\|\fB.\|.\fP\|/'' are not allowed. +Files may be written to only if they already exist and are publicly writable. +.Pp +Note that this extends the concept of +.Qq public +to include +all users on all hosts that can be reached through the network; +this may not be appropriate on all systems, and its implications +should be considered before enabling tftp service. +The server should have the user ID with the lowest possible privilege. +.Pp +Access to files may be restricted by invoking +.Nm +with a list of directories by including up to 20 pathnames +as server program arguments in +.Pa /System/Library/LaunchDaemons/tftp.plist . +In this case access is restricted to files whose +names are prefixed by the one of the given directories. +The given directories are also treated as a search path for +relative filename requests. +.Pp +The options are: +.Bl -tag -width "directory" +.It Fl d +Enable verbose debugging messages to +.Xr syslogd 8 . +.It Fl g Ar group +Change gid to that of +.Ar group +on startup. +If this isn't specified, the gid is set to that of the +.Ar user +specified with +.Fl u . +.It Fl i +Enable insecure mode, no +.Xr realpath 3 . +.It Fl l +Logs all requests using +.Xr syslog 3 . +.It Fl n +Suppresses negative acknowledgement of requests for nonexistent +relative filenames. +.It Fl s Ar directory +.Nm +will +.Xr chroot 2 +to +.Ar directory +on startup. +This is recommended for security reasons (so that files other than +those in the +.Pa /tftpboot +directory aren't accessible). +If the remote host passes the directory name as part of the +file name to transfer, you may have to create a symbolic link +from +.Sq tftpboot +to +.Sq \&. +under +.Pa /tftpboot . +.It Fl u Ar user +Change uid to that of +.Ar user +on startup. +If +.Fl u +isn't given, +.Ar user +defaults to +.Dq nobody . +If +.Fl g +isn't also given, change the gid to that of +.Ar user +as well. +.El +.Sh SEE ALSO +.Xr tftp 1 , +.Xr launchd 8 , +.Xr launchctl 1 , +.Xr launchd.plist 5 +.Rs +.%R RFC +.%N 1350 +.%D July 1992 +.%T "The TFTP Protocol (Revision 2)" +.Re +.Rs +.%R RFC +.%N 2347 +.%D May 1998 +.%T "TFTP Option Extension" +.Re +.Rs +.%R RFC +.%N 2348 +.%D May 1998 +.%T "TFTP Blocksize Option" +.Re +.Rs +.%R RFC +.%N 2349 +.%D May 1998 +.%T "TFTP Timeout Interval and Transfer Size Options" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +The +.Fl s +flag appeared in +.Nx 1.0 . +.Pp +The +.Fl g +and +.Fl u +flags appeared in +.Nx 1.4 . +.Pp +IPv6 support was implemented by WIDE/KAME project in 1999. +.Pp +TFTP options were implemented by Wasabi Systems, Inc., in 2003, +and first appeared in +NetBSD 2.0 . +.Sh BUGS +Files larger than 33488896 octets (65535 blocks) cannot be transferred +without client and server supporting blocksize negotiation (RFCs +2347 and 2348). +.Pp +Many tftp clients will not transfer files over 16744448 octets (32767 blocks). +.Sh SECURITY CONSIDERATIONS +You are +.Em strongly +advised to set up +.Nm +using the +.Fl s +flag in conjunction with the name of the directory that +contains the files that +.Nm +will serve to remote hosts (e.g., +.Pa /tftpboot ) . +This ensures that only the files that should be served +to remote hosts can be accessed by them. +.Pp +Because there is no user-login or validation within +the +.Tn TFTP +protocol, the remote site will probably have some +sort of file-access restrictions in place. +The exact methods are specific to each site and therefore +difficult to document here. diff --git a/remote_cmds/tftpd.tproj/tftpd.c b/remote_cmds/tftpd.tproj/tftpd.c new file mode 100644 index 0000000..8e4c118 --- /dev/null +++ b/remote_cmds/tftpd.tproj/tftpd.c @@ -0,0 +1,1238 @@ +/* $NetBSD: tftpd.c,v 1.28 2004/05/05 20:15:45 kleink Exp $ */ +/* + * 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. 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 +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; +#endif +__attribute__((__used__)) +static const char rcsid[] = + "$FreeBSD: src/libexec/tftpd/tftpd.c,v 1.38 2007/11/23 00:05:29 edwin Exp $"; +#endif /* not lint */ + +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton + * <guyton@rand-unix>. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/tftp.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <netdb.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "tftpsubs.h" + +#define DEFAULTUSER "nobody" + +#define TIMEOUT 5 +#define MAX_TIMEOUTS 5 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 5*TIMEOUT; + +char buf[MAXPKTSIZE]; +char ackbuf[PKTSIZE]; +char oackbuf[PKTSIZE]; +struct sockaddr_storage from; +socklen_t fromlen; +int debug; + +int tftp_opt_tsize = 0; +int tftp_blksize = SEGSIZE; +int tftp_tsize = 0; + +/* + * Null-terminated directory prefix list for absolute pathname requests and + * search list for relative pathname requests. + * + * MAXDIRS should be at least as large as the number of arguments that + * inetd allows (currently 20). + */ +#define MAXDIRS 20 +static struct dirlist { + const char *name; + int len; +} dirs[MAXDIRS+1]; +static int suppress_naks; +static int logging; +static int insecure=0; +static int secure; +static char *securedir; + +struct formats; + +static const char *errtomsg(int); +static void nak(int); +#ifndef __APPLE__ +static void oack(void); +#endif + +static void timer(int); +static void justquit(int); + +static void tftp(struct tftphdr *, int); +static void usage(void); +static char *verifyhost(struct sockaddr *); +int main(int, char **); +void recvfile(struct formats *, int, int); +void xmitfile(struct formats *, int, int); +static const char *opcode(int); +int validate_access(char **, int); + +struct formats { + const char *f_mode; + int (*f_validate)(char **, int); + void (*f_send)(struct formats *, int, int); + void (*f_recv)(struct formats *, int, int); + int f_convert; +} formats[] = { + { "netascii", validate_access, xmitfile, recvfile, 1 }, + { "octet", validate_access, xmitfile, recvfile, 0 }, + { 0 } +}; + +static void +usage(void) +{ + + syslog(LOG_ERR, + "Usage: %s [-diln] [-u user] [-g group] [-s directory] [directory ...]", + getprogname()); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_storage me; + struct passwd *pwent; + struct group *grent; + struct tftphdr *tp; + char *tgtuser, *tgtgroup, *ep; + int n, ch, on, fd; + socklen_t len; + int soopt; + uid_t curuid, tgtuid; + gid_t curgid, tgtgid; + long nid; + + n = 0; + fd = 0; + tzset(); + openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); + tgtuser = DEFAULTUSER; + tgtgroup = NULL; + curuid = getuid(); + curgid = getgid(); + + while ((ch = getopt(argc, argv, "dg:ilns:u:")) != -1) + switch (ch) { + case 'd': + debug++; + break; + + case 'g': + tgtgroup = optarg; + break; + + case 'i': + insecure = 1; + break; + + case 'l': + logging = 1; + break; + + case 'n': + suppress_naks = 1; + break; + + case 's': + secure = 1; + securedir = optarg; + break; + + case 'u': + tgtuser = optarg; + break; + + default: + usage(); + break; + } + + if (optind < argc) { + struct dirlist *dirp; + + /* Get list of directory prefixes. Skip relative pathnames. */ + for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; + optind++) { + if (argv[optind][0] == '/') { + dirp->name = argv[optind]; + dirp->len = strlen(dirp->name); + dirp++; + } + } + } + + if (*tgtuser == '\0' || (tgtgroup != NULL && *tgtgroup == '\0')) + usage(); + + nid = (strtol(tgtuser, &ep, 10)); + if (*ep == '\0') { + if (nid > UID_MAX) { + syslog(LOG_ERR, "uid %ld is too large", nid); + exit(1); + } + pwent = getpwuid((uid_t)nid); + } else + pwent = getpwnam(tgtuser); + if (pwent == NULL) { + syslog(LOG_ERR, "unknown user `%s'", tgtuser); + exit(1); + } + tgtuid = pwent->pw_uid; + tgtgid = pwent->pw_gid; + + if (tgtgroup != NULL) { + nid = (strtol(tgtgroup, &ep, 10)); + if (*ep == '\0') { + if (nid > GID_MAX) { + syslog(LOG_ERR, "gid %ld is too large", nid); + exit(1); + } + grent = getgrgid((gid_t)nid); + } else + grent = getgrnam(tgtgroup); + if (grent != NULL) + tgtgid = grent->gr_gid; + else { + syslog(LOG_ERR, "unknown group `%s'", tgtgroup); + exit(1); + } + } + + if (secure) { + if (chdir(securedir) < 0) { + syslog(LOG_ERR, "chdir %s: %m", securedir); + exit(1); + } + if (chroot(".")) { + syslog(LOG_ERR, "chroot: %m"); + exit(1); + } + } + + if (logging) + syslog(LOG_DEBUG, "running as user `%s' (%d), group `%s' (%d)", + tgtuser, tgtuid, tgtgroup ? tgtgroup : "(unspecified)", + tgtgid); + if (curgid != tgtgid) { + if (setgid(tgtgid)) { + syslog(LOG_ERR, "setgid to %d: %m", (int)tgtgid); + exit(1); + } + if (setgroups(0, NULL)) { + syslog(LOG_ERR, "setgroups: %m"); + exit(1); + } + } + + if (curuid != tgtuid) { + if (setuid(tgtuid)) { + syslog(LOG_ERR, "setuid to %d: %m", (int)tgtuid); + exit(1); + } + } + + on = 1; + if (ioctl(fd, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(fd, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the tftp port, and the next request + * to come in will start up a new instance of tftpd. + * + * We do this so that inetd can run tftpd in "wait" mode. + * The problem with tftpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * tftp port before we do our receive, so more than one + * instance of tftpd may be started up. Worse, if tftpd + * break before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + { + int i, pid; + socklen_t j; + + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some request, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one tftpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(fd, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else { + break; + } + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + exit(1); + } else if (pid != 0) { + exit(0); + } + } + + /* + * remember what address this was sent to, so we can respond on the + * same interface + */ + len = sizeof(me); + if (getsockname(fd, (struct sockaddr *)&me, &len) == 0) { + switch (me.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&me)->sin_port = 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&me)->sin6_port = 0; + break; + default: + /* unsupported */ + break; + } + } else { + memset(&me, 0, sizeof(me)); + me.ss_family = from.ss_family; + me.ss_len = from.ss_len; + } + alarm(0); + close(fd); + close(1); + peer = socket(from.ss_family, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(1); + } + if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(1); + } + if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) { + syslog(LOG_ERR, "connect: %m"); + exit(1); + } + soopt = 65536; /* larger than we'll ever need */ + if (setsockopt(peer, SOL_SOCKET, SO_SNDBUF, (void *) &soopt, sizeof(soopt)) < 0) { + syslog(LOG_ERR, "set SNDBUF: %m"); + exit(1); + } + if (setsockopt(peer, SOL_SOCKET, SO_RCVBUF, (void *) &soopt, sizeof(soopt)) < 0) { + syslog(LOG_ERR, "set RCVBUF: %m"); + exit(1); + } + + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(1); +} + +static int +blk_handler(struct tftphdr *tp, char *opt, char *val, char *ack, + int *ackl, int *ec) +{ + unsigned long bsize; + char *endp; + int l; + + /* + * On these failures, we could just ignore the blocksize option. + * Perhaps that should be a command-line option. + */ + errno = 0; + bsize = strtoul(val, &endp, 10); + if ((bsize == ULONG_MAX && errno == ERANGE) || *endp) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "illegal value %s for blksize option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + if (bsize < 8 || bsize > 65464) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "out of range value %s for blksize option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + + tftp_blksize = bsize; + strcpy(ack + *ackl, "blksize"); + *ackl += 8; + l = sprintf(ack + *ackl, "%lu", bsize); + *ackl += l + 1; + + return 0; +} + +static int +timeout_handler(struct tftphdr *tp, char *opt, char *val, char *ack, + int *ackl, int *ec) +{ + unsigned long tout; + char *endp; + int l; + + errno = 0; + tout = strtoul(val, &endp, 10); + if ((tout == ULONG_MAX && errno == ERANGE) || *endp) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "illegal value %s for timeout option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + if (tout < 1 || tout > 255) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "out of range value %s for timeout option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + + rexmtval = tout; + strcpy(ack + *ackl, "timeout"); + *ackl += 8; + l = sprintf(ack + *ackl, "%lu", tout); + *ackl += l + 1; + + /* + * Arbitrarily pick a maximum timeout on a request to 3 + * retransmissions if the interval timeout is more than + * one minute. Longest possible timeout is therefore + * 3 * 255 - 1, or 764 seconds. + */ + if (rexmtval > 60) { + maxtimeout = rexmtval * 3; + } else { + maxtimeout = rexmtval * 5; + } + + return 0; +} + +static int +tsize_handler(struct tftphdr *tp, char *opt, char *val, char *ack, + int *ackl, int *ec) +{ + unsigned long fsize; + char *endp; + + /* + * Maximum file even with extended tftp is 65535 blocks of + * length 65464, or 4290183240 octets (4784056 less than 2^32). + * unsigned long is at least 32 bits on all NetBSD archs. + */ + + errno = 0; + fsize = strtoul(val, &endp, 10); + if ((fsize == ULONG_MAX && errno == ERANGE) || *endp) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "illegal value %s for tsize option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + if (fsize > (unsigned long) 65535 * 65464) { + syslog(LOG_NOTICE, "%s: %s request for %s: " + "out of range value %s for tsize option", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + tp->th_stuff, val); + return 0; + } + + tftp_opt_tsize = 1; + tftp_tsize = fsize; + /* + * We will report this later -- either replying with the fsize (WRQ) + * or replying with the actual filesize (RRQ). + */ + + return 0; +} + +struct tftp_options { + char *o_name; + int (*o_handler)(struct tftphdr *, char *, char *, char *, + int *, int *); +} options[] = { + { "blksize", blk_handler }, + { "timeout", timeout_handler }, + { "tsize", tsize_handler }, + { NULL, NULL } +}; + +enum opt_enum { + OPT_TSIZE = 0, + OPT_TIMEOUT, +}; + +/* + * Get options for an extended tftp session. Stuff the ones we + * recognize in oackbuf. + */ +static int +get_options(struct tftphdr *tp, char *cp, int size, char *ackb, + int *alen, int *err) +{ + struct tftp_options *op; + char *option, *value, *endp; + int r, rv=0, ec=0; + + endp = cp + size; + while (cp < endp) { + option = cp; + while (*cp && cp < endp) { + *cp = tolower(*cp); + cp++; + } + if (*cp) { + /* if we have garbage at the end, just ignore it */ + break; + } + cp++; /* skip over NUL */ + value = cp; + while (*cp && cp < endp) { + cp++; + } + if (*cp) { + /* if we have garbage at the end, just ignore it */ + break; + } + cp++; + for (op = options; op->o_name; op++) { + if (strcmp(op->o_name, option) == 0) + break; + } + if (op->o_name) { + r = op->o_handler(tp, option, value, ackb, alen, &ec); + if (r < 0) { + rv = -1; + break; + } + rv++; + } /* else ignore unknown options */ + } + + if (rv < 0) + *err = ec; + + return rv; +} + +/* + * Handle initial connection protocol. + */ +static void +tftp(struct tftphdr *tp, int size) +{ + struct formats *pf; + char *cp; + char *filename, *mode; + int first, ecode=0, alen, etftp=0, r; + + first = 1; + mode = NULL; + + filename = cp = tp->th_stuff; +again: + while (cp < buf + size) { + if (*cp == '\0') + break; + cp++; + } + if (*cp != '\0') { + nak(EBADOP); + exit(1); + } + if (first) { + mode = ++cp; + first = 0; + goto again; + } + for (cp = mode; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) + if (strcmp(pf->f_mode, mode) == 0) + break; + if (pf->f_mode == 0) { + nak(EBADOP); + exit(1); + } + /* + * cp currently points to the NUL byte following the mode. + * + * If we have some valid options, then let's assume that we're + * now dealing with an extended tftp session. Note that if we + * don't get any options, then we *must* assume that we do not + * have an extended tftp session. If we get options, we fill + * in the ack buf to acknowledge them. If we skip that, then + * the client *must* assume that we are not using an extended + * session. + */ + size -= (++cp - (char *) tp); + if (size > 0 && *cp) { + alen = 2; /* Skip over opcode */ + r = get_options(tp, cp, size, oackbuf, &alen, &ecode); + if (r > 0) { + etftp = 1; + } else if (r < 0) { + nak(ecode); + exit(1); + } + } + ecode = (*pf->f_validate)(&filename, tp->th_opcode); + if (logging) { + syslog(LOG_INFO, "%s: %s request for %s: %s", + verifyhost((struct sockaddr *)&from), + tp->th_opcode == WRQ ? "write" : "read", + filename, errtomsg(ecode)); + } + if (ecode) { + /* + * Avoid storms of naks to a RRQ broadcast for a relative + * bootfile pathname from a diskless Sun. + */ + if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) + exit(0); + nak(ecode); + exit(1); + } + + if (etftp) { + struct tftphdr *oack_h; + + if (tftp_opt_tsize) { + int l; + + strcpy(oackbuf + alen, "tsize"); + alen += 6; + l = sprintf(oackbuf + alen, "%u", tftp_tsize); + alen += l + 1; + } + oack_h = (struct tftphdr *) oackbuf; + oack_h->th_opcode = htons(OACK); + } + + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf, etftp, alen); + else + (*pf->f_send)(pf, etftp, alen); + exit(0); +} + + +FILE *file; + +/* + * Validate file access. Since we + * have no uid or gid, for now require + * file to exist and be publicly + * readable/writable. + * If we were invoked with arguments + * from inetd then the file must also be + * in one of the given directory prefixes. + */ +int +validate_access(char **filep, int mode) +{ + struct stat stbuf; + struct dirlist *dirp; + static char pathname[MAXPATHLEN]; + int fd; + char *filename; +#ifdef __APPLE__ + static char resolved_path[PATH_MAX+1]; + bzero(resolved_path,PATH_MAX+1); + if(insecure) { + filename = *filep; + } else { + if (realpath(*filep, resolved_path)==NULL) { + return (EACCESS); + } + filename = resolved_path; + } +#else + filename = *filep; +#endif + /* + * Prevent tricksters from getting around the directory restrictions + */ + if (strstr(filename, "/../")) + return (EACCESS); + + if (*filename == '/') { + /* + * Allow the request if it's in one of the approved locations. + * Special case: check the null prefix ("/") by looking + * for length = 1 and relying on the arg. processing that + * it's a /. + */ + for (dirp = dirs; dirp->name != NULL; dirp++) { + if (dirp->len == 1 || + (!strncmp(filename, dirp->name, dirp->len) && + filename[dirp->len] == '/')) + break; + } + /* If directory list is empty, allow access to any file */ + if (dirp->name == NULL && dirp != dirs) + return (EACCESS); + if (stat(filename, &stbuf) < 0) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + if (!S_ISREG(stbuf.st_mode)) + return (ENOTFOUND); + if (mode == RRQ) { + if ((stbuf.st_mode & S_IROTH) == 0) + return (EACCESS); + } else { + if ((stbuf.st_mode & S_IWOTH) == 0) + return (EACCESS); + } + } else { + /* + * Relative file name: search the approved locations for it. + */ + + if (!strncmp(filename, "../", 3)) + return (EACCESS); + + /* + * Find the first file that exists in any of the directories, + * check access on it. + */ + if (dirs[0].name != NULL) { + for (dirp = dirs; dirp->name != NULL; dirp++) { + snprintf(pathname, sizeof pathname, "%s/%s", + dirp->name, filename); + if (stat(pathname, &stbuf) == 0 && + (stbuf.st_mode & S_IFMT) == S_IFREG) { + break; + } + } + if (dirp->name == NULL) + return (ENOTFOUND); + if (mode == RRQ && !(stbuf.st_mode & S_IROTH)) + return (EACCESS); + if (mode == WRQ && !(stbuf.st_mode & S_IWOTH)) + return (EACCESS); + filename = pathname; + *filep = filename; + } else { + /* + * If there's no directory list, take our cue from the + * absolute file request check above (*filename == '/'), + * and allow access to anything. + */ + if (stat(filename, &stbuf) < 0) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + if (!S_ISREG(stbuf.st_mode)) + return (ENOTFOUND); + if (mode == RRQ) { + if ((stbuf.st_mode & S_IROTH) == 0) + return (EACCESS); + } else { + if ((stbuf.st_mode & S_IWOTH) == 0) + return (EACCESS); + } + *filep = filename; + } + } + + if (tftp_opt_tsize && mode == RRQ) + tftp_tsize = (unsigned long) stbuf.st_size; + + fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY | O_TRUNC); + if (fd < 0) + return (errno + 100); + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) { + close(fd); + return (errno + 100); + } + return (0); +} + +int timeout; +jmp_buf timeoutbuf; + +void +timer(int dummy) +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(1); + longjmp(timeoutbuf, 1); +} + +static const char * +opcode(int code) +{ + static char buf[64]; + + switch (code) { + case RRQ: + return "RRQ"; + case WRQ: + return "WRQ"; + case DATA: + return "DATA"; + case ACK: + return "ACK"; + case ERROR: + return "ERROR"; + case OACK: + return "OACK"; + default: + (void)snprintf(buf, sizeof(buf), "*code %d*", code); + return buf; + } +} + +/* + * Send the requested file. + */ +void +xmitfile(struct formats *pf, int etftp, int acklength) +{ + volatile unsigned int block; + struct tftphdr *dp; + struct tftphdr *ap; /* ack packet */ + int size, n; + int first = 1; + long amount = 0; + + signal(SIGALRM, timer); + ap = (struct tftphdr *)ackbuf; + if (etftp) { + dp = (struct tftphdr *)oackbuf; + size = acklength - 4; + block = 0; + } else { + dp = r_init(); + size = 0; + block = 1; + } + + do { + if (block > 0) { + size = readit(file, &dp, tftp_blksize, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + } + timeout = 0; + (void)setjmp(timeoutbuf); + +send_data: + if (!etftp && debug) + syslog(LOG_DEBUG, "Send DATA %u", block); + if ((n = send(peer, dp, size + 4, 0)) != size + 4) { + if (errno != ENOBUFS) { + syslog(LOG_ERR, "tftpd: write: %m"); + goto abort; + } + } + amount += size; + if (block) + read_ahead(file, tftp_blksize, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, tftp_blksize, 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + switch (ap->th_opcode) { + case ERROR: + goto abort; + + case ACK: + if (ap->th_block == 0 && first && block == 0) { + first = 0; + etftp = 0; + acklength = 0; + dp = r_init(); + goto done; + } + if (ap->th_block == (u_short)block) + goto done; + if (debug) + syslog(LOG_DEBUG, "Resync ACK %u != %u", + (unsigned int)ap->th_block, block); + /* Re-synchronize with the other side */ + (void) synchnet(peer, tftp_blksize); + if (ap->th_block == (u_short)(block-1)) + goto send_data; + default: + syslog(LOG_INFO, "Received %s in sendfile\n", + opcode(dp->th_opcode)); + } + + } +done: + if (debug) + syslog(LOG_DEBUG, "Received ACK for block %u", block); + block++; + } while (size == tftp_blksize || block == 1); +abort: + if (logging) { + syslog(LOG_DEBUG, "%s: request completed (%ld bytes)", + verifyhost((struct sockaddr *)&from), amount); + } + (void) fclose(file); +} + +void +justquit(int dummy) +{ + + exit(0); +} + +/* + * Receive a file. + */ +void +recvfile(struct formats *pf, int etftp, int acklength) +{ + volatile unsigned int block; + struct tftphdr *dp; + struct tftphdr *ap; /* ack buffer */ + int n, size; + + signal(SIGALRM, timer); + dp = w_init(); + ap = (struct tftphdr *)oackbuf; + block = 0; + do { + timeout = 0; + if (etftp == 0) { + ap = (struct tftphdr *)ackbuf; + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)block); + acklength = 4; + } + if (debug) + syslog(LOG_DEBUG, "Sending ACK for block %u\n", block); + block++; + (void) setjmp(timeoutbuf); +send_ack: + if (send(peer, ap, acklength, 0) != acklength) { + syslog(LOG_ERR, "tftpd: write: %m"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, tftp_blksize + 4, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m"); + goto abort; + } + etftp = 0; + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohs((u_short)dp->th_block); + if (debug) + syslog(LOG_DEBUG, "Received %s for block %u", + opcode(dp->th_opcode), + (unsigned int)dp->th_block); + + switch (dp->th_opcode) { + case ERROR: + goto abort; + case DATA: + if (dp->th_block == (u_short)block) + goto done; /* normal */ + if (debug) + syslog(LOG_DEBUG, "Resync %u != %u", + (unsigned int)dp->th_block, block); + /* Re-synchronize with the other side */ + (void) synchnet(peer, tftp_blksize); + if (dp->th_block == (u_short)(block-1)) + goto send_ack; /* rexmit */ + break; + default: + syslog(LOG_INFO, "Received %s in recvfile\n", + opcode(dp->th_opcode)); + break; + } + } +done: + if (debug) + syslog(LOG_DEBUG, "Got block %u", block); + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == tftp_blksize); + write_behind(file, pf->f_convert); + if (logging) { + syslog(LOG_DEBUG, "%s: request completed", + verifyhost((struct sockaddr *)&from)); + } + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_short)(block)); + if (debug) + syslog(LOG_DEBUG, "Send final ACK %u", block); + (void) send(peer, ackbuf, 4, 0); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } +abort: + return; +} + +const struct errmsg { + int e_code; + const char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { EOPTNEG, "Option negotiation failed" }, + { -1, 0 } +}; + +static const char * +errtomsg(int error) +{ + static char ebuf[20]; + const struct errmsg *pe; + + if (error == 0) + return ("success"); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + return (pe->e_msg); + snprintf(ebuf, sizeof(ebuf), "error %d", error); + return (ebuf); +} + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +static void +nak(int error) +{ + const struct errmsg *pe; + struct tftphdr *tp; + int length; + size_t msglen; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + msglen = sizeof(buf) - (&tp->th_msg[0] - buf); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + strlcpy(tp->th_msg, strerror(error - 100), msglen); + } else { + tp->th_code = htons((u_short)error); + strlcpy(tp->th_msg, pe->e_msg, msglen); + } + if (debug) + syslog(LOG_DEBUG, "Send NACK %s", tp->th_msg); + length = strlen(tp->th_msg); + msglen = &tp->th_msg[length + 1] - buf; + if (send(peer, buf, msglen, 0) != msglen) + syslog(LOG_ERR, "nak: %m"); +} + +static char * +verifyhost(struct sockaddr *fromp) +{ + static char hbuf[MAXHOSTNAMELEN]; + + if (getnameinfo(fromp, fromp->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0)) + if (getnameinfo(fromp, fromp->sa_len, hbuf, sizeof(hbuf), + NULL, 0, NI_NUMERICHOST)) + strlcpy(hbuf, "?", sizeof(hbuf)); + return (hbuf); +} + +#ifndef __APPLE__ +/* + * Send an oack packet (option acknowledgement). + */ +static void +oack(void) +{ + struct tftphdr *tp, *ap; + int size, i, n; + char *bp; + + tp = (struct tftphdr *)buf; + bp = buf + 2; + size = sizeof(buf) - 2; + tp->th_opcode = htons((u_short)OACK); + for (i = 0; options[i].o_type != NULL; i++) { + if (options[i].o_request) { + n = snprintf(bp, size, "%s%c%d", options[i].o_type, + 0, options[i].o_reply); + bp += n+1; + size -= n+1; + if (size < 0) { + syslog(LOG_ERR, "oack: buffer overflow"); + exit(1); + } + } + } + size = bp - buf; + ap = (struct tftphdr *)ackbuf; + signal(SIGALRM, timer); + timeouts = 0; + + (void)setjmp(timeoutbuf); + if (send(peer, buf, size, 0) != size) { + syslog(LOG_INFO, "oack: %m"); + exit(1); + } + + for (;;) { + alarm(rexmtval); + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "recv: %m"); + exit(1); + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + if (ap->th_opcode == ERROR) + exit(1); + if (ap->th_opcode == ACK && ap->th_block == 0) + break; + } +} +#endif /* !__APPLE__ */ diff --git a/remote_cmds/tftpd.tproj/tftpsubs.c b/remote_cmds/tftpd.tproj/tftpsubs.c new file mode 100644 index 0000000..90f75bb --- /dev/null +++ b/remote_cmds/tftpd.tproj/tftpsubs.c @@ -0,0 +1,285 @@ +/* $NetBSD: tftpsubs.c,v 1.8 2003/08/07 11:16:14 agc Exp $ */ + +/* + * 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. 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 <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: tftpsubs.c,v 1.8 2003/08/07 11:16:14 agc Exp $"); +#endif +#endif /* not lint */ + +/* Simple minded read-ahead/write-behind subroutines for tftp user and + server. Written originally with multiple buffers in mind, but current + implementation has two buffer logic wired in. + + Todo: add some sort of final error check so when the write-buffer + is finally flushed, the caller can detect if the disk filled up + (or had an i/o error) and return a nak to the other side. + + Jim Guyton 10/85 + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/tftp.h> + +#include <stdio.h> +#include <unistd.h> + +#include "tftpsubs.h" + +struct bf { + int counter; /* size of data in buffer, or flag */ + char buf[MAXPKTSIZE]; /* room for data packet */ +} bfs[2]; + + /* Values for bf.counter */ +#define BF_ALLOC -3 /* alloc'd but not yet filled */ +#define BF_FREE -2 /* free */ +/* [-1 .. SEGSIZE] = size of data in the data buffer */ + +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ + + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ + +static struct tftphdr *rw_init __P((int)); + +struct tftphdr * +w_init() /* write-behind */ +{ + return rw_init(0); +} + +struct tftphdr * +r_init() /* read-ahead */ +{ + return rw_init(1); +} + +static struct tftphdr * +rw_init(x) /* init for either read-ahead or write-behind */ + int x; /* zero for write-behind, one for read-head */ +{ + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct tftphdr *)bfs[0].buf; +} + +/* Have emptied current buffer by sending to net and getting ack. + Free it and return next buffer filled with data. + */ +int +readit(file, dpp, amt, convert) + FILE *file; /* file opened for read */ + struct tftphdr **dpp; + int amt; + int convert; /* if true, convert to ascii */ +{ + struct bf *b; + + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ + + b = &bfs[current]; /* look at new buffer */ + if (b->counter == BF_FREE) /* if it's empty */ + read_ahead(file, amt, convert); /* fill it */ +/* assert(b->counter != BF_FREE);*//* check */ + *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ + return b->counter; +} + +/* + * fill the input buffer, doing ascii conversions if requested + * conversions are lf -> cr,lf and cr -> cr, nul + */ +void +read_ahead(file, amt, convert) + FILE *file; /* file opened for read */ + int amt; /* number of bytes to read */ + int convert; /* if true, convert to ascii */ +{ + int i; + char *p; + int c; + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ + + dp = (struct tftphdr *)b->buf; + + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, amt); + return; + } + + p = dp->th_data; + for (i = 0 ; i < amt; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else c = '\0'; /* cr to cr,nul */ + newline = 0; + } + else { + c = getc(file); + if (c == EOF) break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); +} + +/* Update count associated with the buffer, get new buffer + from the queue. Calls write_behind only if next buffer not + available. + */ +int +writeit(file, dpp, ct, convert) + FILE *file; + struct tftphdr **dpp; + int ct, convert; +{ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + (void)write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct tftphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ +} + +/* + * Output a buffer to a file, converting from netascii if requested. + * CR,NUL -> CR and CR,LF => LF. + * Note spec is undefined if we get CR as last byte of file or a + * CR followed by anything else. In this case we leave it alone. + */ +int +write_behind(file, convert) + FILE *file; + int convert; +{ + char *buf; + int count; + int ct; + char *p; + int c; /* current character */ + struct bf *b; + struct tftphdr *dp; + + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ + + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct tftphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; + + if (count <= 0) return -1; /* nak logic? */ + + if (convert == 0) + return write(fileno(file), buf, count); + + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else + if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); +skipit: + prevchar = c; + } + return count; +} + + +/* When an error has occurred, it is possible that the two sides + * are out of synch. Ie: that what I think is the other side's + * response to packet N is really their response to packet N-1. + * + * So, to try to prevent that, we flush all the input queued up + * for us on the network connection on our host. + * + * We return the number of packets we flushed (mostly for reporting + * when trace is active). + */ + +int +synchnet(f, bsize) + int f; /* socket to flush */ + int bsize; /* size of buffer to sync */ +{ + int i, j = 0; + char rbuf[PKTSIZE]; + struct sockaddr_storage from; + socklen_t fromlen; + + while (1) { + (void) ioctl(f, FIONREAD, &i); + if (i) { + j++; + fromlen = sizeof from; + (void) recvfrom(f, rbuf, sizeof (rbuf), 0, + (struct sockaddr *)&from, &fromlen); + } else { + return(j); + } + } +} diff --git a/remote_cmds/tftpd.tproj/tftpsubs.h b/remote_cmds/tftpd.tproj/tftpsubs.h new file mode 100644 index 0000000..98284f8 --- /dev/null +++ b/remote_cmds/tftpd.tproj/tftpsubs.h @@ -0,0 +1,48 @@ +/* $NetBSD: tftpsubs.h,v 1.4 2003/08/07 11:16:14 agc Exp $ */ + +/* + * Copyright (c) 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. 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. + * + * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Prototypes for read-ahead/write-behind subroutines for tftp user and + * server. + */ + + +struct tftphdr *r_init __P((void)); +void read_ahead __P((FILE *, int, int)); +int readit __P((FILE *, struct tftphdr **, int, int)); + +int synchnet __P((int, int)); + +struct tftphdr *w_init __P((void)); +int write_behind __P((FILE *, int)); +int writeit __P((FILE *, struct tftphdr **, int, int)); diff --git a/remote_cmds/wall.tproj/Makefile b/remote_cmds/wall.tproj/Makefile new file mode 100644 index 0000000..08da621 --- /dev/null +++ b/remote_cmds/wall.tproj/Makefile @@ -0,0 +1,16 @@ +Project = wall +Install_Dir = /usr/bin + +HFILES = ttymsg.h +CFILES = ttymsg.c wall.c +MANPAGES = wall.1 + +Extra_CC_Flags = -Wall -Werror -mdynamic-no-pic +Extra_LD_Flags = -dead_strip + +Extra_CC_Flags += -D__FBSDID=__RCSID + +include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make + +#Install_Program_Mode = 02555 +#Install_Program_Group = tty diff --git a/remote_cmds/wall.tproj/ttymsg.c b/remote_cmds/wall.tproj/ttymsg.c new file mode 100644 index 0000000..ce94f87 --- /dev/null +++ b/remote_cmds/wall.tproj/ttymsg.c @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD: src/usr.bin/wall/ttymsg.c,v 1.12 2006/01/27 08:52:14 ume Exp $"); + +#ifndef lint +__attribute__((__used__)) +static const char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93"; +#endif + +#include <sys/types.h> +#include <sys/uio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "ttymsg.h" + +/* + * Display the contents of a uio structure on a terminal. Used by wall(1), + * syslogd(8), and talkd(8). Forks and finishes in child if write would block, + * waiting up to tmout seconds. Returns pointer to error string on unexpected + * error; string is not newline-terminated. Various "normal" errors are + * ignored (exclusive-use, lack of permission, etc.). + */ +const char * +ttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout) +{ + struct iovec localiov[7]; + ssize_t left, wret; + int cnt, fd; + static char device[MAXNAMLEN] = _PATH_DEV; + static char errbuf[1024]; + char *p; + int forked; + + forked = 0; + if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0]))) + return ("too many iov's (change code in wall/ttymsg.c)"); + + p = device + sizeof(_PATH_DEV) - 1; +#ifdef __APPLE__ + /* radar:13145432 */ + strlcpy(p, line, sizeof(device) - (sizeof(_PATH_DEV) - 1)); +#else + strlcpy(p, line, sizeof(device)); +#endif + if (strncmp(p, "pts/", 4) == 0) + p += 4; + if (strchr(p, '/') != NULL) { + /* A slash is an attempt to break security... */ + (void) snprintf(errbuf, sizeof(errbuf), + "Too many '/' in \"%s\"", device); + return (errbuf); + } + + /* + * open will fail on slip lines or exclusive-use lines + * if not running as root; not an error. + */ + if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { +#ifndef __APPLE__ + if (errno == EBUSY || errno == EACCES) + return (NULL); +#endif /* !__APPLE__ */ + (void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device, + strerror(errno)); + return (errbuf); + } + + for (cnt = 0, left = 0; cnt < iovcnt; ++cnt) + left += iov[cnt].iov_len; + + for (;;) { + wret = writev(fd, iov, iovcnt); + if (wret >= left) + break; + if (wret >= 0) { + left -= wret; + if (iov != localiov) { + bcopy(iov, localiov, + iovcnt * sizeof(struct iovec)); + iov = localiov; + } + for (cnt = 0; (size_t)wret >= iov->iov_len; ++cnt) { + wret -= iov->iov_len; + ++iov; + --iovcnt; + } + if (wret) { + iov->iov_base = (char *)iov->iov_base + wret; + iov->iov_len -= wret; + } + continue; + } + if (errno == EWOULDBLOCK) { + int cpid; + + if (forked) { + (void) close(fd); + _exit(1); + } + cpid = fork(); + if (cpid < 0) { + (void) snprintf(errbuf, sizeof(errbuf), + "fork: %s", strerror(errno)); + (void) close(fd); + return (errbuf); + } + if (cpid) { /* parent */ + (void) close(fd); + return (NULL); + } + forked++; + /* wait at most tmout seconds */ + (void) signal(SIGALRM, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); /* XXX */ + (void) sigsetmask(0); + (void) alarm((u_int)tmout); + (void) fcntl(fd, F_SETFL, 0); /* clear O_NONBLOCK */ + continue; + } + /* + * We get ENODEV on a slip line if we're running as root, + * and EIO if the line just went away. + */ + if (errno == ENODEV || errno == EIO) + break; + (void) close(fd); + if (forked) + _exit(1); + (void) snprintf(errbuf, sizeof(errbuf), + "%s: %s", device, strerror(errno)); + return (errbuf); + } + + (void) close(fd); + if (forked) + _exit(0); + return (NULL); +} diff --git a/remote_cmds/wall.tproj/ttymsg.h b/remote_cmds/wall.tproj/ttymsg.h new file mode 100644 index 0000000..625c22d --- /dev/null +++ b/remote_cmds/wall.tproj/ttymsg.h @@ -0,0 +1,3 @@ +/* $FreeBSD: src/usr.bin/wall/ttymsg.h,v 1.1 2001/09/09 14:23:31 dd Exp $ */ + +const char *ttymsg(struct iovec *, int, const char *, int); diff --git a/remote_cmds/wall.tproj/wall.1 b/remote_cmds/wall.tproj/wall.1 new file mode 100644 index 0000000..7c92e63 --- /dev/null +++ b/remote_cmds/wall.tproj/wall.1 @@ -0,0 +1,83 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)wall.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/wall/wall.1,v 1.11 2004/07/17 04:15:27 tjr Exp $ +.\" +.Dd July 17, 2004 +.Dt WALL 1 +.Os +.Sh NAME +.Nm wall +.Nd write a message to users +.Sh SYNOPSIS +.Nm +.Op Fl g Ar group +.Op Ar file +.Sh DESCRIPTION +The +.Nm +utility displays the contents of +.Ar file +or, by default, its standard input, on the terminals of all +currently logged in users. +.Pp +Only the super-user can write on the +terminals of users who have chosen +to deny messages or are using a program which +automatically denies messages. +.Bl -tag -width indent +.It Fl g +Send messages to users in this group. +This option may be specified +multiple times, and any user in any of the specified groups will +receive the message. +.El +.Sh SEE ALSO +.Xr mesg 1 , +.Xr talk 1 , +.Xr write 1 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in PWB UNIX. +.Sh BUGS +The sender's +.Ev LC_CTYPE +setting is used to determine which characters are safe to write to a +terminal, not the receiver's (which +.Nm +has no way of knowing). +.Pp +The +.Nm +utility does not recognize multibyte characters. diff --git a/remote_cmds/wall.tproj/wall.c b/remote_cmds/wall.tproj/wall.c new file mode 100644 index 0000000..b856f7b --- /dev/null +++ b/remote_cmds/wall.tproj/wall.c @@ -0,0 +1,361 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD: src/usr.bin/wall/wall.c,v 1.25 2008/01/15 07:40:30 das Exp $"); + +#ifndef lint +__attribute__((__used__)) +static const char copyright[] = +"@(#) Copyright (c) 1988, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +__attribute__((__used__)) +static const char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93"; +#endif + +/* + * This program is not related to David Wall, whose Stanford Ph.D. thesis + * is entitled "Mechanisms for Broadcast and Selective Broadcast". + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <err.h> +#include <grp.h> +#include <locale.h> +#include <paths.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#ifdef __APPLE__ +#include <utmpx.h> +#else +#include <utmp.h> +#endif + +#include "ttymsg.h" + +static void makemsg(char *); +static void usage(void); + +struct wallgroup { + struct wallgroup *next; + char *name; + gid_t gid; +} *grouplist; +int nobanner; +int mbufsize; +char *mbuf; + +#ifndef __APPLE__ +static int +ttystat(char *line, int sz) +{ + struct stat sb; + char ttybuf[MAXPATHLEN]; + + (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); + if (stat(ttybuf, &sb) == 0) { + return (0); + } else + return (-1); +} +#endif + +int +main(int argc, char *argv[]) +{ + struct iovec iov; +#ifdef __APPLE__ + // rdar://problem/4433603 + struct utmpx *u; +#else + struct utmp utmp; +#endif + int ch; + int ingroup; +#ifndef __APPLE__ + FILE *fp; +#endif + struct wallgroup *g; + struct group *grp; + char **np; + const char *p; + struct passwd *pw; +#ifdef __APPLE__ + char line[sizeof(u->ut_line) + 1]; + char username[sizeof(u->ut_user) + 1]; +#else + char line[sizeof(utmp.ut_line) + 1]; + char username[sizeof(utmp.ut_name) + 1]; +#endif + + (void)setlocale(LC_CTYPE, ""); + + while ((ch = getopt(argc, argv, "g:n")) != -1) + switch (ch) { + case 'n': + /* undoc option for shutdown: suppress banner */ + if (geteuid() == 0) + nobanner = 1; + break; + case 'g': + g = (struct wallgroup *)malloc(sizeof *g); + g->next = grouplist; + g->name = optarg; + g->gid = -1; + grouplist = g; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc > 1) + usage(); + + for (g = grouplist; g; g = g->next) { + grp = getgrnam(g->name); + if (grp != NULL) + g->gid = grp->gr_gid; + else + warnx("%s: no such group", g->name); + } + + makemsg(*argv); + +#ifdef __APPLE__ + setutxent(); +#else + if (!(fp = fopen(_PATH_UTMP, "r"))) + err(1, "cannot read %s", _PATH_UTMP); +#endif + iov.iov_base = mbuf; + iov.iov_len = mbufsize; + /* NOSTRICT */ +#ifdef __APPLE__ + while ((u = getutxent()) != NULL) { + if (!u->ut_user[0] || u->ut_type != USER_PROCESS) + continue; +#else + while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) { + if (!utmp.ut_name[0]) + continue; + if (ttystat(utmp.ut_line, UT_LINESIZE) != 0) + continue; +#endif + if (grouplist) { + ingroup = 0; +#ifdef __APPLE__ + strlcpy(username, u->ut_user, sizeof(username)); +#else + strlcpy(username, utmp.ut_name, sizeof(utmp.ut_name)); +#endif + pw = getpwnam(username); + if (!pw) + continue; + for (g = grouplist; g && ingroup == 0; g = g->next) { + if (g->gid == (gid_t)-1) + continue; + if (g->gid == pw->pw_gid) + ingroup = 1; + else if ((grp = getgrgid(g->gid)) != NULL) { + for (np = grp->gr_mem; *np; np++) { + if (strcmp(*np, username) == 0) { + ingroup = 1; + break; + } + } + } + } + if (ingroup == 0) + continue; + } +#ifdef __APPLE__ + strlcpy(line, u->ut_line, sizeof(line)); +#else + strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); + line[sizeof(utmp.ut_line)] = '\0'; +#endif + if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL) + warnx("%s", p); + } + exit(0); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); + exit(1); +} + +void +makemsg(char *fname) +{ + int cnt; + unsigned char ch; + struct tm *lt; + struct passwd *pw; + struct stat sbuf; + time_t now; + FILE *fp; + int fd; + char *p, hostname[MAXHOSTNAMELEN], lbuf[256], tmpname[64]; + const char *tty; + const char *whom; + gid_t egid; + + (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); + if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) + err(1, "can't open temporary file"); + (void)unlink(tmpname); + + if (!nobanner) { + tty = ttyname(STDERR_FILENO); + if (tty == NULL) + tty = "no tty"; + + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + (void)gethostname(hostname, sizeof(hostname)); + (void)time(&now); + lt = localtime(&now); + + /* + * all this stuff is to blank out a square for the message; + * we wrap message lines at column 79, not 80, because some + * terminals wrap after 79, some do not, and we can't tell. + * Which means that we may leave a non-blank character + * in column 80, but that can't be helped. + */ + (void)fprintf(fp, "\r%79s\r\n", " "); + (void)snprintf(lbuf, sizeof(lbuf), + "Broadcast Message from %s@%s", + whom, hostname); + (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); + (void)snprintf(lbuf, sizeof(lbuf), + " (%s) at %d:%02d %s...", tty, + lt->tm_hour, lt->tm_min, lt->tm_zone); + (void)fprintf(fp, "%-79.79s\r\n", lbuf); + } + (void)fprintf(fp, "%79s\r\n", " "); + + if (fname) { + egid = getegid(); + setegid(getgid()); + if (freopen(fname, "r", stdin) == NULL) + err(1, "can't read %s", fname); + setegid(egid); + } + while (fgets(lbuf, sizeof(lbuf), stdin)) { + for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) { + if (ch == '\r') { + putc('\r', fp); + cnt = 0; + continue; + } else if (ch == '\n') { + for (; cnt < 79; ++cnt) + putc(' ', fp); + putc('\r', fp); + putc('\n', fp); + break; + } + if (cnt == 79) { + putc('\r', fp); + putc('\n', fp); + cnt = 0; + } +#ifdef __APPLE__ + else // rdar://problem/3066405 +#endif + if (((ch & 0x80) && ch < 0xA0) || + /* disable upper controls */ + (!isprint(ch) && !isspace(ch) && + ch != '\a' && ch != '\b') + ) { + if (ch & 0x80) { + ch &= 0x7F; + putc('M', fp); + if (++cnt == 79) { + putc('\r', fp); + putc('\n', fp); + cnt = 0; + } + putc('-', fp); + if (++cnt == 79) { + putc('\r', fp); + putc('\n', fp); + cnt = 0; + } + } + if (iscntrl(ch)) { + ch ^= 040; + putc('^', fp); + if (++cnt == 79) { + putc('\r', fp); + putc('\n', fp); + cnt = 0; + } + } + } +#ifdef __APPLE__ + // rdar://problem/4557295 + if (ch != '\n') +#endif + putc(ch, fp); + } + } + (void)fprintf(fp, "%79s\r\n", " "); + rewind(fp); + + if (fstat(fd, &sbuf)) + err(1, "can't stat temporary file"); + mbufsize = sbuf.st_size; + if (!(mbuf = malloc((u_int)mbufsize))) + err(1, "out of memory"); + if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) + err(1, "can't read temporary file"); + (void)close(fd); +} |